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