qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     21 import argparse
     22 import copy
     23 import os.path
     24 import re
     25 import subprocess
     26 import sys
     27 
     28 
     29 def probe_prefix(binary):
     30     dirname, filename = os.path.split(binary)
     31     return re.sub("-", ".", filename) + ".log"
     32 
     33 
     34 def 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 
     43 def 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 
     58 def 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 
     86 def 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 
    118 def 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 
    131 To watch all trace points on the qemu-system-x86_64 binary:
    132 
    133    %(argv0)s run qemu-system-x86_64
    134 
    135 To 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 
    150 To list all trace points on the qemu-system-x86_64 binary:
    151 
    152    %(argv0)s list qemu-system-x86_64
    153 
    154 To 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 
    168 if __name__ == '__main__':
    169     main()