]>
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 | ||
62dd1048 DB |
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 | |
2adf2164 | 79 | stapargs = ["stap", "-s", "8", "-I", tapsets ] |
62dd1048 DB |
80 | if args.pid is not None: |
81 | stapargs.extend(["-x", args.pid]) | |
82 | stapargs.extend(["-e", script]) | |
2adf2164 | 83 | subprocess.call(stapargs) |
62dd1048 DB |
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) | |
2adf2164 | 99 | proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script], |
3f009716 | 100 | stdout=subprocess.PIPE, |
2adf2164 | 101 | universal_newlines=True) |
62dd1048 DB |
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() |