1*62dd1048SDaniel P. Berrangé#!/usr/bin/python 2*62dd1048SDaniel P. Berrangé# -*- python -*- 3*62dd1048SDaniel P. Berrangé# 4*62dd1048SDaniel P. Berrangé# Copyright (C) 2019 Red Hat, Inc 5*62dd1048SDaniel P. Berrangé# 6*62dd1048SDaniel P. Berrangé# QEMU SystemTap Trace Tool 7*62dd1048SDaniel P. Berrangé# 8*62dd1048SDaniel P. Berrangé# This program is free software; you can redistribute it and/or modify 9*62dd1048SDaniel P. Berrangé# it under the terms of the GNU General Public License as published by 10*62dd1048SDaniel P. Berrangé# the Free Software Foundation; either version 2 of the License, or 11*62dd1048SDaniel P. Berrangé# (at your option) any later version. 12*62dd1048SDaniel P. Berrangé# 13*62dd1048SDaniel P. Berrangé# This program is distributed in the hope that it will be useful, 14*62dd1048SDaniel P. Berrangé# but WITHOUT ANY WARRANTY; without even the implied warranty of 15*62dd1048SDaniel P. Berrangé# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*62dd1048SDaniel P. Berrangé# GNU General Public License for more details. 17*62dd1048SDaniel P. Berrangé# 18*62dd1048SDaniel P. Berrangé# You should have received a copy of the GNU General Public License 19*62dd1048SDaniel P. Berrangé# along with this program; if not, see <http://www.gnu.org/licenses/>. 20*62dd1048SDaniel P. Berrangé 21*62dd1048SDaniel P. Berrangéfrom __future__ import print_function 22*62dd1048SDaniel P. Berrangé 23*62dd1048SDaniel P. Berrangéimport argparse 24*62dd1048SDaniel P. Berrangéimport copy 25*62dd1048SDaniel P. Berrangéimport os.path 26*62dd1048SDaniel P. Berrangéimport re 27*62dd1048SDaniel P. Berrangéimport subprocess 28*62dd1048SDaniel P. Berrangéimport sys 29*62dd1048SDaniel P. Berrangé 30*62dd1048SDaniel P. Berrangé 31*62dd1048SDaniel P. Berrangédef probe_prefix(binary): 32*62dd1048SDaniel P. Berrangé dirname, filename = os.path.split(binary) 33*62dd1048SDaniel P. Berrangé return re.sub("-", ".", filename) + ".log" 34*62dd1048SDaniel P. Berrangé 35*62dd1048SDaniel P. Berrangé 36*62dd1048SDaniel P. Berrangédef which(binary): 37*62dd1048SDaniel P. Berrangé for path in os.environ["PATH"].split(os.pathsep): 38*62dd1048SDaniel P. Berrangé if os.path.exists(os.path.join(path, binary)): 39*62dd1048SDaniel P. Berrangé return os.path.join(path, binary) 40*62dd1048SDaniel P. Berrangé 41*62dd1048SDaniel P. Berrangé print("Unable to find '%s' in $PATH" % binary) 42*62dd1048SDaniel P. Berrangé sys.exit(1) 43*62dd1048SDaniel P. Berrangé 44*62dd1048SDaniel P. Berrangé 45*62dd1048SDaniel P. Berrangédef tapset_dir(binary): 46*62dd1048SDaniel P. Berrangé dirname, filename = os.path.split(binary) 47*62dd1048SDaniel P. Berrangé if dirname == '': 48*62dd1048SDaniel P. Berrangé thisfile = which(binary) 49*62dd1048SDaniel P. Berrangé else: 50*62dd1048SDaniel P. Berrangé thisfile = os.path.realpath(binary) 51*62dd1048SDaniel P. Berrangé if not os.path.exists(thisfile): 52*62dd1048SDaniel P. Berrangé print("Unable to find '%s'" % thisfile) 53*62dd1048SDaniel P. Berrangé sys.exit(1) 54*62dd1048SDaniel P. Berrangé 55*62dd1048SDaniel P. Berrangé basedir = os.path.split(thisfile)[0] 56*62dd1048SDaniel P. Berrangé tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset") 57*62dd1048SDaniel P. Berrangé return os.path.realpath(tapset) 58*62dd1048SDaniel P. Berrangé 59*62dd1048SDaniel P. Berrangé 60*62dd1048SDaniel P. Berrangédef tapset_env(tapset_dir): 61*62dd1048SDaniel P. Berrangé tenv = copy.copy(os.environ) 62*62dd1048SDaniel P. Berrangé tenv["SYSTEMTAP_TAPSET"] = tapset_dir 63*62dd1048SDaniel P. Berrangé return tenv 64*62dd1048SDaniel P. Berrangé 65*62dd1048SDaniel P. Berrangédef cmd_run(args): 66*62dd1048SDaniel P. Berrangé prefix = probe_prefix(args.binary) 67*62dd1048SDaniel P. Berrangé tapsets = tapset_dir(args.binary) 68*62dd1048SDaniel P. Berrangé 69*62dd1048SDaniel P. Berrangé if args.verbose: 70*62dd1048SDaniel P. Berrangé print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) 71*62dd1048SDaniel P. Berrangé 72*62dd1048SDaniel P. Berrangé probes = [] 73*62dd1048SDaniel P. Berrangé for probe in args.probes: 74*62dd1048SDaniel P. Berrangé probes.append("probe %s.%s {}" % (prefix, probe)) 75*62dd1048SDaniel P. Berrangé if len(probes) == 0: 76*62dd1048SDaniel P. Berrangé print("At least one probe pattern must be specified") 77*62dd1048SDaniel P. Berrangé sys.exit(1) 78*62dd1048SDaniel P. Berrangé 79*62dd1048SDaniel P. Berrangé script = " ".join(probes) 80*62dd1048SDaniel P. Berrangé if args.verbose: 81*62dd1048SDaniel P. Berrangé print("Compiling script '%s'" % script) 82*62dd1048SDaniel P. Berrangé script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script 83*62dd1048SDaniel P. Berrangé 84*62dd1048SDaniel P. Berrangé # We request an 8MB buffer, since the stap default 1MB buffer 85*62dd1048SDaniel P. Berrangé # can be easily overflowed by frequently firing QEMU traces 86*62dd1048SDaniel P. Berrangé stapargs = ["stap", "-s", "8"] 87*62dd1048SDaniel P. Berrangé if args.pid is not None: 88*62dd1048SDaniel P. Berrangé stapargs.extend(["-x", args.pid]) 89*62dd1048SDaniel P. Berrangé stapargs.extend(["-e", script]) 90*62dd1048SDaniel P. Berrangé subprocess.call(stapargs, env=tapset_env(tapsets)) 91*62dd1048SDaniel P. Berrangé 92*62dd1048SDaniel P. Berrangé 93*62dd1048SDaniel P. Berrangédef cmd_list(args): 94*62dd1048SDaniel P. Berrangé tapsets = tapset_dir(args.binary) 95*62dd1048SDaniel P. Berrangé 96*62dd1048SDaniel P. Berrangé if args.verbose: 97*62dd1048SDaniel P. Berrangé print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) 98*62dd1048SDaniel P. Berrangé 99*62dd1048SDaniel P. Berrangé def print_probes(verbose, name): 100*62dd1048SDaniel P. Berrangé prefix = probe_prefix(args.binary) 101*62dd1048SDaniel P. Berrangé offset = len(prefix) + 1 102*62dd1048SDaniel P. Berrangé script = prefix + "." + name 103*62dd1048SDaniel P. Berrangé 104*62dd1048SDaniel P. Berrangé if verbose: 105*62dd1048SDaniel P. Berrangé print("Listing probes with name '%s'" % script) 106*62dd1048SDaniel P. Berrangé proc = subprocess.Popen(["stap", "-l", script], 107*62dd1048SDaniel P. Berrangé stdout=subprocess.PIPE, env=tapset_env(tapsets)) 108*62dd1048SDaniel P. Berrangé out, err = proc.communicate() 109*62dd1048SDaniel P. Berrangé if proc.returncode != 0: 110*62dd1048SDaniel P. Berrangé print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary)) 111*62dd1048SDaniel P. Berrangé sys.exit(1) 112*62dd1048SDaniel P. Berrangé 113*62dd1048SDaniel P. Berrangé for line in out.splitlines(): 114*62dd1048SDaniel P. Berrangé if line.startswith(prefix): 115*62dd1048SDaniel P. Berrangé print("%s" % line[offset:]) 116*62dd1048SDaniel P. Berrangé 117*62dd1048SDaniel P. Berrangé if len(args.probes) == 0: 118*62dd1048SDaniel P. Berrangé print_probes(args.verbose, "*") 119*62dd1048SDaniel P. Berrangé else: 120*62dd1048SDaniel P. Berrangé for probe in args.probes: 121*62dd1048SDaniel P. Berrangé print_probes(args.verbose, probe) 122*62dd1048SDaniel P. Berrangé 123*62dd1048SDaniel P. Berrangé 124*62dd1048SDaniel P. Berrangédef main(): 125*62dd1048SDaniel P. Berrangé parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool") 126*62dd1048SDaniel P. Berrangé parser.add_argument("-v", "--verbose", help="Print verbose progress info", 127*62dd1048SDaniel P. Berrangé action='store_true') 128*62dd1048SDaniel P. Berrangé 129*62dd1048SDaniel P. Berrangé subparser = parser.add_subparsers(help="commands") 130*62dd1048SDaniel P. Berrangé subparser.required = True 131*62dd1048SDaniel P. Berrangé subparser.dest = "command" 132*62dd1048SDaniel P. Berrangé 133*62dd1048SDaniel P. Berrangé runparser = subparser.add_parser("run", help="Run a trace session", 134*62dd1048SDaniel P. Berrangé formatter_class=argparse.RawDescriptionHelpFormatter, 135*62dd1048SDaniel P. Berrangé epilog=""" 136*62dd1048SDaniel P. Berrangé 137*62dd1048SDaniel P. BerrangéTo watch all trace points on the qemu-system-x86_64 binary: 138*62dd1048SDaniel P. Berrangé 139*62dd1048SDaniel P. Berrangé %(argv0)s run qemu-system-x86_64 140*62dd1048SDaniel P. Berrangé 141*62dd1048SDaniel P. BerrangéTo only watch the trace points matching the qio* and qcrypto* patterns 142*62dd1048SDaniel P. Berrangé 143*62dd1048SDaniel P. Berrangé %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*' 144*62dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]}) 145*62dd1048SDaniel P. Berrangé runparser.set_defaults(func=cmd_run) 146*62dd1048SDaniel P. Berrangé runparser.add_argument("--pid", "-p", dest="pid", 147*62dd1048SDaniel P. Berrangé help="Restrict tracing to a specific process ID") 148*62dd1048SDaniel P. Berrangé runparser.add_argument("binary", help="QEMU system or user emulator binary") 149*62dd1048SDaniel P. Berrangé runparser.add_argument("probes", help="Probe names or wildcards", 150*62dd1048SDaniel P. Berrangé nargs=argparse.REMAINDER) 151*62dd1048SDaniel P. Berrangé 152*62dd1048SDaniel P. Berrangé listparser = subparser.add_parser("list", help="List probe points", 153*62dd1048SDaniel P. Berrangé formatter_class=argparse.RawDescriptionHelpFormatter, 154*62dd1048SDaniel P. Berrangé epilog=""" 155*62dd1048SDaniel P. Berrangé 156*62dd1048SDaniel P. BerrangéTo list all trace points on the qemu-system-x86_64 binary: 157*62dd1048SDaniel P. Berrangé 158*62dd1048SDaniel P. Berrangé %(argv0)s list qemu-system-x86_64 159*62dd1048SDaniel P. Berrangé 160*62dd1048SDaniel P. BerrangéTo only list the trace points matching the qio* and qcrypto* patterns 161*62dd1048SDaniel P. Berrangé 162*62dd1048SDaniel P. Berrangé %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*' 163*62dd1048SDaniel P. Berrangé""" % {"argv0": sys.argv[0]}) 164*62dd1048SDaniel P. Berrangé listparser.set_defaults(func=cmd_list) 165*62dd1048SDaniel P. Berrangé listparser.add_argument("binary", help="QEMU system or user emulator binary") 166*62dd1048SDaniel P. Berrangé listparser.add_argument("probes", help="Probe names or wildcards", 167*62dd1048SDaniel P. Berrangé nargs=argparse.REMAINDER) 168*62dd1048SDaniel P. Berrangé 169*62dd1048SDaniel P. Berrangé args = parser.parse_args() 170*62dd1048SDaniel P. Berrangé 171*62dd1048SDaniel P. Berrangé args.func(args) 172*62dd1048SDaniel P. Berrangé sys.exit(0) 173*62dd1048SDaniel P. Berrangé 174*62dd1048SDaniel P. Berrangéif __name__ == '__main__': 175*62dd1048SDaniel P. Berrangé main() 176