xref: /qemu/scripts/qemu-trace-stap (revision ce315328f8e9bf5201db4217f3ffe0784110aa4b)
13f009716SStefan Hajnoczi#!/usr/bin/env python3
262dd1048SDaniel P. Berrangé# -*- python -*-
362dd1048SDaniel P. Berrangé#
462dd1048SDaniel P. Berrangé# Copyright (C) 2019 Red Hat, Inc
562dd1048SDaniel P. Berrangé#
662dd1048SDaniel P. Berrangé# QEMU SystemTap Trace Tool
762dd1048SDaniel P. Berrangé#
862dd1048SDaniel P. Berrangé# This program is free software; you can redistribute it and/or modify
962dd1048SDaniel P. Berrangé# it under the terms of the GNU General Public License as published by
1062dd1048SDaniel P. Berrangé# the Free Software Foundation; either version 2 of the License, or
1162dd1048SDaniel P. Berrangé# (at your option) any later version.
1262dd1048SDaniel P. Berrangé#
1362dd1048SDaniel P. Berrangé# This program is distributed in the hope that it will be useful,
1462dd1048SDaniel P. Berrangé# but WITHOUT ANY WARRANTY; without even the implied warranty of
1562dd1048SDaniel P. Berrangé# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1662dd1048SDaniel P. Berrangé# GNU General Public License for more details.
1762dd1048SDaniel P. Berrangé#
1862dd1048SDaniel P. Berrangé# You should have received a copy of the GNU General Public License
1962dd1048SDaniel P. Berrangé# along with this program; if not, see <http://www.gnu.org/licenses/>.
2062dd1048SDaniel P. Berrangé
2162dd1048SDaniel P. Berrangéimport argparse
2262dd1048SDaniel P. Berrangéimport copy
2362dd1048SDaniel P. Berrangéimport os.path
2462dd1048SDaniel P. Berrangéimport re
2562dd1048SDaniel P. Berrangéimport subprocess
2662dd1048SDaniel P. Berrangéimport sys
2762dd1048SDaniel P. Berrangé
2862dd1048SDaniel P. Berrangé
2962dd1048SDaniel P. Berrangédef probe_prefix(binary):
3062dd1048SDaniel P. Berrangé    dirname, filename = os.path.split(binary)
3162dd1048SDaniel P. Berrangé    return re.sub("-", ".", filename) + ".log"
3262dd1048SDaniel P. Berrangé
3362dd1048SDaniel P. Berrangé
3462dd1048SDaniel P. Berrangédef which(binary):
3562dd1048SDaniel P. Berrangé    for path in os.environ["PATH"].split(os.pathsep):
3662dd1048SDaniel P. Berrangé        if os.path.exists(os.path.join(path, binary)):
3762dd1048SDaniel P. Berrangé                return os.path.join(path, binary)
3862dd1048SDaniel P. Berrangé
3962dd1048SDaniel P. Berrangé    print("Unable to find '%s' in $PATH" % binary)
4062dd1048SDaniel P. Berrangé    sys.exit(1)
4162dd1048SDaniel P. Berrangé
4262dd1048SDaniel P. Berrangé
4362dd1048SDaniel P. Berrangédef tapset_dir(binary):
4462dd1048SDaniel P. Berrangé    dirname, filename = os.path.split(binary)
4562dd1048SDaniel P. Berrangé    if dirname == '':
4662dd1048SDaniel P. Berrangé        thisfile = which(binary)
4762dd1048SDaniel P. Berrangé    else:
4862dd1048SDaniel P. Berrangé        thisfile = os.path.realpath(binary)
4962dd1048SDaniel P. Berrangé        if not os.path.exists(thisfile):
5062dd1048SDaniel P. Berrangé            print("Unable to find '%s'" % thisfile)
5162dd1048SDaniel P. Berrangé            sys.exit(1)
5262dd1048SDaniel P. Berrangé
5362dd1048SDaniel P. Berrangé    basedir = os.path.split(thisfile)[0]
5462dd1048SDaniel P. Berrangé    tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset")
5562dd1048SDaniel P. Berrangé    return os.path.realpath(tapset)
5662dd1048SDaniel P. Berrangé
5762dd1048SDaniel P. Berrangé
5862dd1048SDaniel P. Berrangédef cmd_run(args):
59*9976be39SDaniel P. Berrangé    stap = which("stap")
6062dd1048SDaniel P. Berrangé    prefix = probe_prefix(args.binary)
6162dd1048SDaniel P. Berrangé    tapsets = tapset_dir(args.binary)
6262dd1048SDaniel P. Berrangé
6362dd1048SDaniel P. Berrangé    if args.verbose:
6462dd1048SDaniel P. Berrangé        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
6562dd1048SDaniel P. Berrangé
6662dd1048SDaniel P. Berrangé    probes = []
6762dd1048SDaniel P. Berrangé    for probe in args.probes:
6862dd1048SDaniel P. Berrangé        probes.append("probe %s.%s {}" % (prefix, probe))
6962dd1048SDaniel P. Berrangé    if len(probes) == 0:
7062dd1048SDaniel P. Berrangé        print("At least one probe pattern must be specified")
7162dd1048SDaniel P. Berrangé        sys.exit(1)
7262dd1048SDaniel P. Berrangé
7362dd1048SDaniel P. Berrangé    script = " ".join(probes)
7462dd1048SDaniel P. Berrangé    if args.verbose:
7562dd1048SDaniel P. Berrangé        print("Compiling script '%s'" % script)
7662dd1048SDaniel P. Berrangé        script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script
7762dd1048SDaniel P. Berrangé
7862dd1048SDaniel P. Berrangé    # We request an 8MB buffer, since the stap default 1MB buffer
7962dd1048SDaniel P. Berrangé    # can be easily overflowed by frequently firing QEMU traces
80*9976be39SDaniel P. Berrangé    stapargs = [stap, "-s", "8", "-I", tapsets ]
8162dd1048SDaniel P. Berrangé    if args.pid is not None:
8262dd1048SDaniel P. Berrangé        stapargs.extend(["-x", args.pid])
8362dd1048SDaniel P. Berrangé    stapargs.extend(["-e", script])
842adf2164SGerd Hoffmann    subprocess.call(stapargs)
8562dd1048SDaniel P. Berrangé
8662dd1048SDaniel P. Berrangé
8762dd1048SDaniel P. Berrangédef cmd_list(args):
88*9976be39SDaniel P. Berrangé    stap = which("stap")
8962dd1048SDaniel P. Berrangé    tapsets = tapset_dir(args.binary)
9062dd1048SDaniel P. Berrangé
9162dd1048SDaniel P. Berrangé    if args.verbose:
9262dd1048SDaniel P. Berrangé        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
9362dd1048SDaniel P. Berrangé
9462dd1048SDaniel P. Berrangé    def print_probes(verbose, name):
9562dd1048SDaniel P. Berrangé        prefix = probe_prefix(args.binary)
9662dd1048SDaniel P. Berrangé        offset = len(prefix) + 1
9762dd1048SDaniel P. Berrangé        script = prefix + "." + name
9862dd1048SDaniel P. Berrangé
9962dd1048SDaniel P. Berrangé        if verbose:
10062dd1048SDaniel P. Berrangé            print("Listing probes with name '%s'" % script)
101*9976be39SDaniel P. Berrangé        proc = subprocess.Popen([stap, "-I", tapsets, "-l", script],
1023f009716SStefan Hajnoczi                                stdout=subprocess.PIPE,
1032adf2164SGerd Hoffmann                                universal_newlines=True)
10462dd1048SDaniel P. Berrangé        out, err = proc.communicate()
10562dd1048SDaniel P. Berrangé        if proc.returncode != 0:
10662dd1048SDaniel P. Berrangé            print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary))
10762dd1048SDaniel P. Berrangé            sys.exit(1)
10862dd1048SDaniel P. Berrangé
10962dd1048SDaniel P. Berrangé        for line in out.splitlines():
11062dd1048SDaniel P. Berrangé            if line.startswith(prefix):
11162dd1048SDaniel P. Berrangé                print("%s" % line[offset:])
11262dd1048SDaniel P. Berrangé
11362dd1048SDaniel P. Berrangé    if len(args.probes) == 0:
11462dd1048SDaniel P. Berrangé        print_probes(args.verbose, "*")
11562dd1048SDaniel P. Berrangé    else:
11662dd1048SDaniel P. Berrangé        for probe in args.probes:
11762dd1048SDaniel P. Berrangé            print_probes(args.verbose, probe)
11862dd1048SDaniel P. Berrangé
11962dd1048SDaniel P. Berrangé
12062dd1048SDaniel P. Berrangédef main():
12162dd1048SDaniel P. Berrangé    parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool")
12262dd1048SDaniel P. Berrangé    parser.add_argument("-v", "--verbose", help="Print verbose progress info",
12362dd1048SDaniel P. Berrangé                        action='store_true')
12462dd1048SDaniel P. Berrangé
12562dd1048SDaniel P. Berrangé    subparser = parser.add_subparsers(help="commands")
12662dd1048SDaniel P. Berrangé    subparser.required = True
12762dd1048SDaniel P. Berrangé    subparser.dest = "command"
12862dd1048SDaniel P. Berrangé
12962dd1048SDaniel P. Berrangé    runparser = subparser.add_parser("run", help="Run a trace session",
13062dd1048SDaniel P. Berrangé                                     formatter_class=argparse.RawDescriptionHelpFormatter,
13162dd1048SDaniel P. Berrangé                                     epilog="""
13262dd1048SDaniel P. Berrangé
13362dd1048SDaniel P. BerrangéTo watch all trace points on the qemu-system-x86_64 binary:
13462dd1048SDaniel P. Berrangé
13562dd1048SDaniel P. Berrangé   %(argv0)s run qemu-system-x86_64
13662dd1048SDaniel P. Berrangé
13762dd1048SDaniel P. BerrangéTo only watch the trace points matching the qio* and qcrypto* patterns
13862dd1048SDaniel P. Berrangé
13962dd1048SDaniel P. Berrangé   %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*'
14062dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]})
14162dd1048SDaniel P. Berrangé    runparser.set_defaults(func=cmd_run)
14262dd1048SDaniel P. Berrangé    runparser.add_argument("--pid", "-p", dest="pid",
14362dd1048SDaniel P. Berrangé                           help="Restrict tracing to a specific process ID")
14462dd1048SDaniel P. Berrangé    runparser.add_argument("binary", help="QEMU system or user emulator binary")
14562dd1048SDaniel P. Berrangé    runparser.add_argument("probes", help="Probe names or wildcards",
14662dd1048SDaniel P. Berrangé                           nargs=argparse.REMAINDER)
14762dd1048SDaniel P. Berrangé
14862dd1048SDaniel P. Berrangé    listparser = subparser.add_parser("list", help="List probe points",
14962dd1048SDaniel P. Berrangé                                      formatter_class=argparse.RawDescriptionHelpFormatter,
15062dd1048SDaniel P. Berrangé                                      epilog="""
15162dd1048SDaniel P. Berrangé
15262dd1048SDaniel P. BerrangéTo list all trace points on the qemu-system-x86_64 binary:
15362dd1048SDaniel P. Berrangé
15462dd1048SDaniel P. Berrangé   %(argv0)s list qemu-system-x86_64
15562dd1048SDaniel P. Berrangé
15662dd1048SDaniel P. BerrangéTo only list the trace points matching the qio* and qcrypto* patterns
15762dd1048SDaniel P. Berrangé
15862dd1048SDaniel P. Berrangé   %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*'
15962dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]})
16062dd1048SDaniel P. Berrangé    listparser.set_defaults(func=cmd_list)
16162dd1048SDaniel P. Berrangé    listparser.add_argument("binary", help="QEMU system or user emulator binary")
16262dd1048SDaniel P. Berrangé    listparser.add_argument("probes", help="Probe names or wildcards",
16362dd1048SDaniel P. Berrangé                            nargs=argparse.REMAINDER)
16462dd1048SDaniel P. Berrangé
16562dd1048SDaniel P. Berrangé    args = parser.parse_args()
16662dd1048SDaniel P. Berrangé
16762dd1048SDaniel P. Berrangé    args.func(args)
16862dd1048SDaniel P. Berrangé    sys.exit(0)
16962dd1048SDaniel P. Berrangé
17062dd1048SDaniel P. Berrangéif __name__ == '__main__':
17162dd1048SDaniel P. Berrangé    main()
172