]>
Commit | Line | Data |
---|---|---|
3f009716 | 1 | #!/usr/bin/env python3 |
62dd1048 DB |
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 | ||
62dd1048 DB |
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 tapset_env(tapset_dir): | |
59 | tenv = copy.copy(os.environ) | |
60 | tenv["SYSTEMTAP_TAPSET"] = tapset_dir | |
61 | return tenv | |
62 | ||
63 | def cmd_run(args): | |
64 | prefix = probe_prefix(args.binary) | |
65 | tapsets = tapset_dir(args.binary) | |
66 | ||
67 | if args.verbose: | |
68 | print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) | |
69 | ||
70 | probes = [] | |
71 | for probe in args.probes: | |
72 | probes.append("probe %s.%s {}" % (prefix, probe)) | |
73 | if len(probes) == 0: | |
74 | print("At least one probe pattern must be specified") | |
75 | sys.exit(1) | |
76 | ||
77 | script = " ".join(probes) | |
78 | if args.verbose: | |
79 | print("Compiling script '%s'" % script) | |
80 | script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script | |
81 | ||
82 | # We request an 8MB buffer, since the stap default 1MB buffer | |
83 | # can be easily overflowed by frequently firing QEMU traces | |
84 | stapargs = ["stap", "-s", "8"] | |
85 | if args.pid is not None: | |
86 | stapargs.extend(["-x", args.pid]) | |
87 | stapargs.extend(["-e", script]) | |
88 | subprocess.call(stapargs, env=tapset_env(tapsets)) | |
89 | ||
90 | ||
91 | def cmd_list(args): | |
92 | tapsets = tapset_dir(args.binary) | |
93 | ||
94 | if args.verbose: | |
95 | print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) | |
96 | ||
97 | def print_probes(verbose, name): | |
98 | prefix = probe_prefix(args.binary) | |
99 | offset = len(prefix) + 1 | |
100 | script = prefix + "." + name | |
101 | ||
102 | if verbose: | |
103 | print("Listing probes with name '%s'" % script) | |
104 | proc = subprocess.Popen(["stap", "-l", script], | |
3f009716 SH |
105 | stdout=subprocess.PIPE, |
106 | universal_newlines=True, | |
107 | env=tapset_env(tapsets)) | |
62dd1048 DB |
108 | out, err = proc.communicate() |
109 | if proc.returncode != 0: | |
110 | print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary)) | |
111 | sys.exit(1) | |
112 | ||
113 | for line in out.splitlines(): | |
114 | if line.startswith(prefix): | |
115 | print("%s" % line[offset:]) | |
116 | ||
117 | if len(args.probes) == 0: | |
118 | print_probes(args.verbose, "*") | |
119 | else: | |
120 | for probe in args.probes: | |
121 | print_probes(args.verbose, probe) | |
122 | ||
123 | ||
124 | def main(): | |
125 | parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool") | |
126 | parser.add_argument("-v", "--verbose", help="Print verbose progress info", | |
127 | action='store_true') | |
128 | ||
129 | subparser = parser.add_subparsers(help="commands") | |
130 | subparser.required = True | |
131 | subparser.dest = "command" | |
132 | ||
133 | runparser = subparser.add_parser("run", help="Run a trace session", | |
134 | formatter_class=argparse.RawDescriptionHelpFormatter, | |
135 | epilog=""" | |
136 | ||
137 | To watch all trace points on the qemu-system-x86_64 binary: | |
138 | ||
139 | %(argv0)s run qemu-system-x86_64 | |
140 | ||
141 | To only watch the trace points matching the qio* and qcrypto* patterns | |
142 | ||
143 | %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*' | |
144 | """ % {"argv0": sys.argv[0]}) | |
145 | runparser.set_defaults(func=cmd_run) | |
146 | runparser.add_argument("--pid", "-p", dest="pid", | |
147 | help="Restrict tracing to a specific process ID") | |
148 | runparser.add_argument("binary", help="QEMU system or user emulator binary") | |
149 | runparser.add_argument("probes", help="Probe names or wildcards", | |
150 | nargs=argparse.REMAINDER) | |
151 | ||
152 | listparser = subparser.add_parser("list", help="List probe points", | |
153 | formatter_class=argparse.RawDescriptionHelpFormatter, | |
154 | epilog=""" | |
155 | ||
156 | To list all trace points on the qemu-system-x86_64 binary: | |
157 | ||
158 | %(argv0)s list qemu-system-x86_64 | |
159 | ||
160 | To only list the trace points matching the qio* and qcrypto* patterns | |
161 | ||
162 | %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*' | |
163 | """ % {"argv0": sys.argv[0]}) | |
164 | listparser.set_defaults(func=cmd_list) | |
165 | listparser.add_argument("binary", help="QEMU system or user emulator binary") | |
166 | listparser.add_argument("probes", help="Probe names or wildcards", | |
167 | nargs=argparse.REMAINDER) | |
168 | ||
169 | args = parser.parse_args() | |
170 | ||
171 | args.func(args) | |
172 | sys.exit(0) | |
173 | ||
174 | if __name__ == '__main__': | |
175 | main() |