qemu/scripts/qemu-trace-stap
<<
>>
Prefs
   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()
 170