cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

qemu-trace-stap (5623B)


      1#!/usr/bin/env python3
      2# -*- python -*-
      3#
      4# Copyright (C) 2019 Red Hat, Inc
      5#
      6# QEMU SystemTap Trace Tool
      7#
      8# This program is free software; you can redistribute it and/or modify
      9# it under the terms of the GNU General Public License as published by
     10# the Free Software Foundation; either version 2 of the License, or
     11# (at your option) any later version.
     12#
     13# This program is distributed in the hope that it will be useful,
     14# but WITHOUT ANY WARRANTY; without even the implied warranty of
     15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16# GNU General Public License for more details.
     17#
     18# You should have received a copy of the GNU General Public License
     19# along with this program; if not, see <http://www.gnu.org/licenses/>.
     20
     21import argparse
     22import copy
     23import os.path
     24import re
     25import subprocess
     26import sys
     27
     28
     29def probe_prefix(binary):
     30    dirname, filename = os.path.split(binary)
     31    return re.sub("-", ".", filename) + ".log"
     32
     33
     34def which(binary):
     35    for path in os.environ["PATH"].split(os.pathsep):
     36        if os.path.exists(os.path.join(path, binary)):
     37                return os.path.join(path, binary)
     38
     39    print("Unable to find '%s' in $PATH" % binary)
     40    sys.exit(1)
     41
     42
     43def tapset_dir(binary):
     44    dirname, filename = os.path.split(binary)
     45    if dirname == '':
     46        thisfile = which(binary)
     47    else:
     48        thisfile = os.path.realpath(binary)
     49        if not os.path.exists(thisfile):
     50            print("Unable to find '%s'" % thisfile)
     51            sys.exit(1)
     52
     53    basedir = os.path.split(thisfile)[0]
     54    tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset")
     55    return os.path.realpath(tapset)
     56
     57
     58def cmd_run(args):
     59    prefix = probe_prefix(args.binary)
     60    tapsets = tapset_dir(args.binary)
     61
     62    if args.verbose:
     63        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
     64
     65    probes = []
     66    for probe in args.probes:
     67        probes.append("probe %s.%s {}" % (prefix, probe))
     68    if len(probes) == 0:
     69        print("At least one probe pattern must be specified")
     70        sys.exit(1)
     71
     72    script = " ".join(probes)
     73    if args.verbose:
     74        print("Compiling script '%s'" % script)
     75        script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script
     76
     77    # We request an 8MB buffer, since the stap default 1MB buffer
     78    # can be easily overflowed by frequently firing QEMU traces
     79    stapargs = ["stap", "-s", "8", "-I", tapsets ]
     80    if args.pid is not None:
     81        stapargs.extend(["-x", args.pid])
     82    stapargs.extend(["-e", script])
     83    subprocess.call(stapargs)
     84
     85
     86def cmd_list(args):
     87    tapsets = tapset_dir(args.binary)
     88
     89    if args.verbose:
     90        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
     91
     92    def print_probes(verbose, name):
     93        prefix = probe_prefix(args.binary)
     94        offset = len(prefix) + 1
     95        script = prefix + "." + name
     96
     97        if verbose:
     98            print("Listing probes with name '%s'" % script)
     99        proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script],
    100                                stdout=subprocess.PIPE,
    101                                universal_newlines=True)
    102        out, err = proc.communicate()
    103        if proc.returncode != 0:
    104            print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary))
    105            sys.exit(1)
    106
    107        for line in out.splitlines():
    108            if line.startswith(prefix):
    109                print("%s" % line[offset:])
    110
    111    if len(args.probes) == 0:
    112        print_probes(args.verbose, "*")
    113    else:
    114        for probe in args.probes:
    115            print_probes(args.verbose, probe)
    116
    117
    118def main():
    119    parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool")
    120    parser.add_argument("-v", "--verbose", help="Print verbose progress info",
    121                        action='store_true')
    122
    123    subparser = parser.add_subparsers(help="commands")
    124    subparser.required = True
    125    subparser.dest = "command"
    126
    127    runparser = subparser.add_parser("run", help="Run a trace session",
    128                                     formatter_class=argparse.RawDescriptionHelpFormatter,
    129                                     epilog="""
    130
    131To watch all trace points on the qemu-system-x86_64 binary:
    132
    133   %(argv0)s run qemu-system-x86_64
    134
    135To only watch the trace points matching the qio* and qcrypto* patterns
    136
    137   %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*'
    138""" % {"argv0": sys.argv[0]})
    139    runparser.set_defaults(func=cmd_run)
    140    runparser.add_argument("--pid", "-p", dest="pid",
    141                           help="Restrict tracing to a specific process ID")
    142    runparser.add_argument("binary", help="QEMU system or user emulator binary")
    143    runparser.add_argument("probes", help="Probe names or wildcards",
    144                           nargs=argparse.REMAINDER)
    145
    146    listparser = subparser.add_parser("list", help="List probe points",
    147                                      formatter_class=argparse.RawDescriptionHelpFormatter,
    148                                      epilog="""
    149
    150To list all trace points on the qemu-system-x86_64 binary:
    151
    152   %(argv0)s list qemu-system-x86_64
    153
    154To only list the trace points matching the qio* and qcrypto* patterns
    155
    156   %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*'
    157""" % {"argv0": sys.argv[0]})
    158    listparser.set_defaults(func=cmd_list)
    159    listparser.add_argument("binary", help="QEMU system or user emulator binary")
    160    listparser.add_argument("probes", help="Probe names or wildcards",
    161                            nargs=argparse.REMAINDER)
    162
    163    args = parser.parse_args()
    164
    165    args.func(args)
    166    sys.exit(0)
    167
    168if __name__ == '__main__':
    169    main()