xref: /qemu/scripts/qemu-trace-stap (revision 2adf2164)
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):
5962dd1048SDaniel P. Berrangé    prefix = probe_prefix(args.binary)
6062dd1048SDaniel P. Berrangé    tapsets = tapset_dir(args.binary)
6162dd1048SDaniel P. Berrangé
6262dd1048SDaniel P. Berrangé    if args.verbose:
6362dd1048SDaniel P. Berrangé        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
6462dd1048SDaniel P. Berrangé
6562dd1048SDaniel P. Berrangé    probes = []
6662dd1048SDaniel P. Berrangé    for probe in args.probes:
6762dd1048SDaniel P. Berrangé        probes.append("probe %s.%s {}" % (prefix, probe))
6862dd1048SDaniel P. Berrangé    if len(probes) == 0:
6962dd1048SDaniel P. Berrangé        print("At least one probe pattern must be specified")
7062dd1048SDaniel P. Berrangé        sys.exit(1)
7162dd1048SDaniel P. Berrangé
7262dd1048SDaniel P. Berrangé    script = " ".join(probes)
7362dd1048SDaniel P. Berrangé    if args.verbose:
7462dd1048SDaniel P. Berrangé        print("Compiling script '%s'" % script)
7562dd1048SDaniel P. Berrangé        script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script
7662dd1048SDaniel P. Berrangé
7762dd1048SDaniel P. Berrangé    # We request an 8MB buffer, since the stap default 1MB buffer
7862dd1048SDaniel P. Berrangé    # can be easily overflowed by frequently firing QEMU traces
79*2adf2164SGerd Hoffmann    stapargs = ["stap", "-s", "8", "-I", tapsets ]
8062dd1048SDaniel P. Berrangé    if args.pid is not None:
8162dd1048SDaniel P. Berrangé        stapargs.extend(["-x", args.pid])
8262dd1048SDaniel P. Berrangé    stapargs.extend(["-e", script])
83*2adf2164SGerd Hoffmann    subprocess.call(stapargs)
8462dd1048SDaniel P. Berrangé
8562dd1048SDaniel P. Berrangé
8662dd1048SDaniel P. Berrangédef cmd_list(args):
8762dd1048SDaniel P. Berrangé    tapsets = tapset_dir(args.binary)
8862dd1048SDaniel P. Berrangé
8962dd1048SDaniel P. Berrangé    if args.verbose:
9062dd1048SDaniel P. Berrangé        print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary))
9162dd1048SDaniel P. Berrangé
9262dd1048SDaniel P. Berrangé    def print_probes(verbose, name):
9362dd1048SDaniel P. Berrangé        prefix = probe_prefix(args.binary)
9462dd1048SDaniel P. Berrangé        offset = len(prefix) + 1
9562dd1048SDaniel P. Berrangé        script = prefix + "." + name
9662dd1048SDaniel P. Berrangé
9762dd1048SDaniel P. Berrangé        if verbose:
9862dd1048SDaniel P. Berrangé            print("Listing probes with name '%s'" % script)
99*2adf2164SGerd Hoffmann        proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script],
1003f009716SStefan Hajnoczi                                stdout=subprocess.PIPE,
101*2adf2164SGerd Hoffmann                                universal_newlines=True)
10262dd1048SDaniel P. Berrangé        out, err = proc.communicate()
10362dd1048SDaniel P. Berrangé        if proc.returncode != 0:
10462dd1048SDaniel P. Berrangé            print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary))
10562dd1048SDaniel P. Berrangé            sys.exit(1)
10662dd1048SDaniel P. Berrangé
10762dd1048SDaniel P. Berrangé        for line in out.splitlines():
10862dd1048SDaniel P. Berrangé            if line.startswith(prefix):
10962dd1048SDaniel P. Berrangé                print("%s" % line[offset:])
11062dd1048SDaniel P. Berrangé
11162dd1048SDaniel P. Berrangé    if len(args.probes) == 0:
11262dd1048SDaniel P. Berrangé        print_probes(args.verbose, "*")
11362dd1048SDaniel P. Berrangé    else:
11462dd1048SDaniel P. Berrangé        for probe in args.probes:
11562dd1048SDaniel P. Berrangé            print_probes(args.verbose, probe)
11662dd1048SDaniel P. Berrangé
11762dd1048SDaniel P. Berrangé
11862dd1048SDaniel P. Berrangédef main():
11962dd1048SDaniel P. Berrangé    parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool")
12062dd1048SDaniel P. Berrangé    parser.add_argument("-v", "--verbose", help="Print verbose progress info",
12162dd1048SDaniel P. Berrangé                        action='store_true')
12262dd1048SDaniel P. Berrangé
12362dd1048SDaniel P. Berrangé    subparser = parser.add_subparsers(help="commands")
12462dd1048SDaniel P. Berrangé    subparser.required = True
12562dd1048SDaniel P. Berrangé    subparser.dest = "command"
12662dd1048SDaniel P. Berrangé
12762dd1048SDaniel P. Berrangé    runparser = subparser.add_parser("run", help="Run a trace session",
12862dd1048SDaniel P. Berrangé                                     formatter_class=argparse.RawDescriptionHelpFormatter,
12962dd1048SDaniel P. Berrangé                                     epilog="""
13062dd1048SDaniel P. Berrangé
13162dd1048SDaniel P. BerrangéTo watch all trace points on the qemu-system-x86_64 binary:
13262dd1048SDaniel P. Berrangé
13362dd1048SDaniel P. Berrangé   %(argv0)s run qemu-system-x86_64
13462dd1048SDaniel P. Berrangé
13562dd1048SDaniel P. BerrangéTo only watch the trace points matching the qio* and qcrypto* patterns
13662dd1048SDaniel P. Berrangé
13762dd1048SDaniel P. Berrangé   %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*'
13862dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]})
13962dd1048SDaniel P. Berrangé    runparser.set_defaults(func=cmd_run)
14062dd1048SDaniel P. Berrangé    runparser.add_argument("--pid", "-p", dest="pid",
14162dd1048SDaniel P. Berrangé                           help="Restrict tracing to a specific process ID")
14262dd1048SDaniel P. Berrangé    runparser.add_argument("binary", help="QEMU system or user emulator binary")
14362dd1048SDaniel P. Berrangé    runparser.add_argument("probes", help="Probe names or wildcards",
14462dd1048SDaniel P. Berrangé                           nargs=argparse.REMAINDER)
14562dd1048SDaniel P. Berrangé
14662dd1048SDaniel P. Berrangé    listparser = subparser.add_parser("list", help="List probe points",
14762dd1048SDaniel P. Berrangé                                      formatter_class=argparse.RawDescriptionHelpFormatter,
14862dd1048SDaniel P. Berrangé                                      epilog="""
14962dd1048SDaniel P. Berrangé
15062dd1048SDaniel P. BerrangéTo list all trace points on the qemu-system-x86_64 binary:
15162dd1048SDaniel P. Berrangé
15262dd1048SDaniel P. Berrangé   %(argv0)s list qemu-system-x86_64
15362dd1048SDaniel P. Berrangé
15462dd1048SDaniel P. BerrangéTo only list the trace points matching the qio* and qcrypto* patterns
15562dd1048SDaniel P. Berrangé
15662dd1048SDaniel P. Berrangé   %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*'
15762dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]})
15862dd1048SDaniel P. Berrangé    listparser.set_defaults(func=cmd_list)
15962dd1048SDaniel P. Berrangé    listparser.add_argument("binary", help="QEMU system or user emulator binary")
16062dd1048SDaniel P. Berrangé    listparser.add_argument("probes", help="Probe names or wildcards",
16162dd1048SDaniel P. Berrangé                            nargs=argparse.REMAINDER)
16262dd1048SDaniel P. Berrangé
16362dd1048SDaniel P. Berrangé    args = parser.parse_args()
16462dd1048SDaniel P. Berrangé
16562dd1048SDaniel P. Berrangé    args.func(args)
16662dd1048SDaniel P. Berrangé    sys.exit(0)
16762dd1048SDaniel P. Berrangé
16862dd1048SDaniel P. Berrangéif __name__ == '__main__':
16962dd1048SDaniel P. Berrangé    main()
170