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