qemu/scripts/qemu-trace-stap
<<
>>
Prefs
   1#!/usr/bin/python
   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
  21from __future__ import print_function
  22
  23import argparse
  24import copy
  25import os.path
  26import re
  27import subprocess
  28import sys
  29
  30
  31def probe_prefix(binary):
  32    dirname, filename = os.path.split(binary)
  33    return re.sub("-", ".", filename) + ".log"
  34
  35
  36def which(binary):
  37    for path in os.environ["PATH"].split(os.pathsep):
  38        if os.path.exists(os.path.join(path, binary)):
  39                return os.path.join(path, binary)
  40
  41    print("Unable to find '%s' in $PATH" % binary)
  42    sys.exit(1)
  43
  44
  45def tapset_dir(binary):
  46    dirname, filename = os.path.split(binary)
  47    if dirname == '':
  48        thisfile = which(binary)
  49    else:
  50        thisfile = os.path.realpath(binary)
  51        if not os.path.exists(thisfile):
  52            print("Unable to find '%s'" % thisfile)
  53            sys.exit(1)
  54
  55    basedir = os.path.split(thisfile)[0]
  56    tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset")
  57    return os.path.realpath(tapset)
  58
  59
  60def tapset_env(tapset_dir):
  61    tenv = copy.copy(os.environ)
  62    tenv["SYSTEMTAP_TAPSET"] = tapset_dir
  63    return tenv
  64
  65def cmd_run(args):
  66    prefix = probe_prefix(args.binary)
  67    tapsets = tapset_dir(args.binary)
  68
  69    if args.verbose:
  70        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
  71
  72    probes = []
  73    for probe in args.probes:
  74        probes.append("probe %s.%s {}" % (prefix, probe))
  75    if len(probes) == 0:
  76        print("At least one probe pattern must be specified")
  77        sys.exit(1)
  78
  79    script = " ".join(probes)
  80    if args.verbose:
  81        print("Compiling script '%s'" % script)
  82        script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script
  83
  84    # We request an 8MB buffer, since the stap default 1MB buffer
  85    # can be easily overflowed by frequently firing QEMU traces
  86    stapargs = ["stap", "-s", "8"]
  87    if args.pid is not None:
  88        stapargs.extend(["-x", args.pid])
  89    stapargs.extend(["-e", script])
  90    subprocess.call(stapargs, env=tapset_env(tapsets))
  91
  92
  93def cmd_list(args):
  94    tapsets = tapset_dir(args.binary)
  95
  96    if args.verbose:
  97        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
  98
  99    def print_probes(verbose, name):
 100        prefix = probe_prefix(args.binary)
 101        offset = len(prefix) + 1
 102        script = prefix + "." + name
 103
 104        if verbose:
 105            print("Listing probes with name '%s'" % script)
 106        proc = subprocess.Popen(["stap", "-l", script],
 107                                stdout=subprocess.PIPE, env=tapset_env(tapsets))
 108        out, err = proc.communicate()
 109        if proc.returncode != 0:
 110            print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary))
 111            sys.exit(1)
 112
 113        for line in out.splitlines():
 114            if line.startswith(prefix):
 115                print("%s" % line[offset:])
 116
 117    if len(args.probes) == 0:
 118        print_probes(args.verbose, "*")
 119    else:
 120        for probe in args.probes:
 121            print_probes(args.verbose, probe)
 122
 123
 124def main():
 125    parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool")
 126    parser.add_argument("-v", "--verbose", help="Print verbose progress info",
 127                        action='store_true')
 128
 129    subparser = parser.add_subparsers(help="commands")
 130    subparser.required = True
 131    subparser.dest = "command"
 132
 133    runparser = subparser.add_parser("run", help="Run a trace session",
 134                                     formatter_class=argparse.RawDescriptionHelpFormatter,
 135                                     epilog="""
 136
 137To watch all trace points on the qemu-system-x86_64 binary:
 138
 139   %(argv0)s run qemu-system-x86_64
 140
 141To only watch the trace points matching the qio* and qcrypto* patterns
 142
 143   %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*'
 144""" % {"argv0": sys.argv[0]})
 145    runparser.set_defaults(func=cmd_run)
 146    runparser.add_argument("--pid", "-p", dest="pid",
 147                           help="Restrict tracing to a specific process ID")
 148    runparser.add_argument("binary", help="QEMU system or user emulator binary")
 149    runparser.add_argument("probes", help="Probe names or wildcards",
 150                           nargs=argparse.REMAINDER)
 151
 152    listparser = subparser.add_parser("list", help="List probe points",
 153                                      formatter_class=argparse.RawDescriptionHelpFormatter,
 154                                      epilog="""
 155
 156To list all trace points on the qemu-system-x86_64 binary:
 157
 158   %(argv0)s list qemu-system-x86_64
 159
 160To only list the trace points matching the qio* and qcrypto* patterns
 161
 162   %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*'
 163""" % {"argv0": sys.argv[0]})
 164    listparser.set_defaults(func=cmd_list)
 165    listparser.add_argument("binary", help="QEMU system or user emulator binary")
 166    listparser.add_argument("probes", help="Probe names or wildcards",
 167                            nargs=argparse.REMAINDER)
 168
 169    args = parser.parse_args()
 170
 171    args.func(args)
 172    sys.exit(0)
 173
 174if __name__ == '__main__':
 175    main()
 176