| 
									
										
											  
											
												trace: update qemu-trace-stap to Python 3
qemu-trace-stap does not support Python 3 yet:
  $ scripts/qemu-trace-stap list path/to/qemu-system-x86_64
  Traceback (most recent call last):
    File "scripts/qemu-trace-stap", line 175, in <module>
      main()
    File "scripts/qemu-trace-stap", line 171, in main
      args.func(args)
    File "scripts/qemu-trace-stap", line 118, in cmd_list
      print_probes(args.verbose, "*")
    File "scripts/qemu-trace-stap", line 114, in print_probes
      if line.startswith(prefix):
  TypeError: startswith first arg must be bytes or a tuple of bytes, not str
Now that QEMU requires Python 3.5 or later we can switch to pure Python
3.  Use Popen()'s universal_newlines=True argument to treat stdout as
text instead of binary.
Fixes: 62dd1048c0bd ("trace: add ability to do simple printf logging via systemtap")
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1787395
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20200107112438.383958-1-stefanha@redhat.com
Message-Id: <20200107112438.383958-1-stefanha@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
											
										 
											2020-01-07 11:24:38 +00:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											2019-01-23 12:00:16 +00:00
										 |  |  | # -*- python -*- | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2019 Red Hat, Inc | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # QEMU SystemTap Trace Tool | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  | # it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  | # the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  | # (at your option) any later version. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | # along with this program; if not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import argparse | 
					
						
							|  |  |  | import copy | 
					
						
							|  |  |  | import os.path | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def probe_prefix(binary): | 
					
						
							|  |  |  |     dirname, filename = os.path.split(binary) | 
					
						
							|  |  |  |     return re.sub("-", ".", filename) + ".log" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def which(binary): | 
					
						
							|  |  |  |     for path in os.environ["PATH"].split(os.pathsep): | 
					
						
							|  |  |  |         if os.path.exists(os.path.join(path, binary)): | 
					
						
							|  |  |  |                 return os.path.join(path, binary) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print("Unable to find '%s' in $PATH" % binary) | 
					
						
							|  |  |  |     sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def tapset_dir(binary): | 
					
						
							|  |  |  |     dirname, filename = os.path.split(binary) | 
					
						
							|  |  |  |     if dirname == '': | 
					
						
							|  |  |  |         thisfile = which(binary) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         thisfile = os.path.realpath(binary) | 
					
						
							|  |  |  |         if not os.path.exists(thisfile): | 
					
						
							|  |  |  |             print("Unable to find '%s'" % thisfile) | 
					
						
							|  |  |  |             sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     basedir = os.path.split(thisfile)[0] | 
					
						
							|  |  |  |     tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset") | 
					
						
							|  |  |  |     return os.path.realpath(tapset) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def cmd_run(args): | 
					
						
							|  |  |  |     prefix = probe_prefix(args.binary) | 
					
						
							|  |  |  |     tapsets = tapset_dir(args.binary) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if args.verbose: | 
					
						
							|  |  |  |         print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     probes = [] | 
					
						
							|  |  |  |     for probe in args.probes: | 
					
						
							|  |  |  |         probes.append("probe %s.%s {}" % (prefix, probe)) | 
					
						
							|  |  |  |     if len(probes) == 0: | 
					
						
							|  |  |  |         print("At least one probe pattern must be specified") | 
					
						
							|  |  |  |         sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     script = " ".join(probes) | 
					
						
							|  |  |  |     if args.verbose: | 
					
						
							|  |  |  |         print("Compiling script '%s'" % script) | 
					
						
							|  |  |  |         script = """probe begin { print("Running script, <Ctrl>-c to quit\\n") } """ + script | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # We request an 8MB buffer, since the stap default 1MB buffer | 
					
						
							|  |  |  |     # can be easily overflowed by frequently firing QEMU traces | 
					
						
							| 
									
										
										
										
											2021-06-01 15:24:02 +02:00
										 |  |  |     stapargs = ["stap", "-s", "8", "-I", tapsets ] | 
					
						
							| 
									
										
										
										
											2019-01-23 12:00:16 +00:00
										 |  |  |     if args.pid is not None: | 
					
						
							|  |  |  |         stapargs.extend(["-x", args.pid]) | 
					
						
							|  |  |  |     stapargs.extend(["-e", script]) | 
					
						
							| 
									
										
										
										
											2021-06-01 15:24:02 +02:00
										 |  |  |     subprocess.call(stapargs) | 
					
						
							| 
									
										
										
										
											2019-01-23 12:00:16 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def cmd_list(args): | 
					
						
							|  |  |  |     tapsets = tapset_dir(args.binary) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if args.verbose: | 
					
						
							|  |  |  |         print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def print_probes(verbose, name): | 
					
						
							|  |  |  |         prefix = probe_prefix(args.binary) | 
					
						
							|  |  |  |         offset = len(prefix) + 1 | 
					
						
							|  |  |  |         script = prefix + "." + name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if verbose: | 
					
						
							|  |  |  |             print("Listing probes with name '%s'" % script) | 
					
						
							| 
									
										
										
										
											2021-06-01 15:24:02 +02:00
										 |  |  |         proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script], | 
					
						
							| 
									
										
											  
											
												trace: update qemu-trace-stap to Python 3
qemu-trace-stap does not support Python 3 yet:
  $ scripts/qemu-trace-stap list path/to/qemu-system-x86_64
  Traceback (most recent call last):
    File "scripts/qemu-trace-stap", line 175, in <module>
      main()
    File "scripts/qemu-trace-stap", line 171, in main
      args.func(args)
    File "scripts/qemu-trace-stap", line 118, in cmd_list
      print_probes(args.verbose, "*")
    File "scripts/qemu-trace-stap", line 114, in print_probes
      if line.startswith(prefix):
  TypeError: startswith first arg must be bytes or a tuple of bytes, not str
Now that QEMU requires Python 3.5 or later we can switch to pure Python
3.  Use Popen()'s universal_newlines=True argument to treat stdout as
text instead of binary.
Fixes: 62dd1048c0bd ("trace: add ability to do simple printf logging via systemtap")
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1787395
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20200107112438.383958-1-stefanha@redhat.com
Message-Id: <20200107112438.383958-1-stefanha@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
											
										 
											2020-01-07 11:24:38 +00:00
										 |  |  |                                 stdout=subprocess.PIPE, | 
					
						
							| 
									
										
										
										
											2021-06-01 15:24:02 +02:00
										 |  |  |                                 universal_newlines=True) | 
					
						
							| 
									
										
										
										
											2019-01-23 12:00:16 +00:00
										 |  |  |         out, err = proc.communicate() | 
					
						
							|  |  |  |         if proc.returncode != 0: | 
					
						
							|  |  |  |             print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary)) | 
					
						
							|  |  |  |             sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for line in out.splitlines(): | 
					
						
							|  |  |  |             if line.startswith(prefix): | 
					
						
							|  |  |  |                 print("%s" % line[offset:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(args.probes) == 0: | 
					
						
							|  |  |  |         print_probes(args.verbose, "*") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for probe in args.probes: | 
					
						
							|  |  |  |             print_probes(args.verbose, probe) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool") | 
					
						
							|  |  |  |     parser.add_argument("-v", "--verbose", help="Print verbose progress info", | 
					
						
							|  |  |  |                         action='store_true') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     subparser = parser.add_subparsers(help="commands") | 
					
						
							|  |  |  |     subparser.required = True | 
					
						
							|  |  |  |     subparser.dest = "command" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     runparser = subparser.add_parser("run", help="Run a trace session", | 
					
						
							|  |  |  |                                      formatter_class=argparse.RawDescriptionHelpFormatter, | 
					
						
							|  |  |  |                                      epilog=""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To watch all trace points on the qemu-system-x86_64 binary: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    %(argv0)s run qemu-system-x86_64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To only watch the trace points matching the qio* and qcrypto* patterns | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*' | 
					
						
							|  |  |  | """ % {"argv0": sys.argv[0]}) | 
					
						
							|  |  |  |     runparser.set_defaults(func=cmd_run) | 
					
						
							|  |  |  |     runparser.add_argument("--pid", "-p", dest="pid", | 
					
						
							|  |  |  |                            help="Restrict tracing to a specific process ID") | 
					
						
							|  |  |  |     runparser.add_argument("binary", help="QEMU system or user emulator binary") | 
					
						
							|  |  |  |     runparser.add_argument("probes", help="Probe names or wildcards", | 
					
						
							|  |  |  |                            nargs=argparse.REMAINDER) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     listparser = subparser.add_parser("list", help="List probe points", | 
					
						
							|  |  |  |                                       formatter_class=argparse.RawDescriptionHelpFormatter, | 
					
						
							|  |  |  |                                       epilog=""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To list all trace points on the qemu-system-x86_64 binary: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    %(argv0)s list qemu-system-x86_64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To only list the trace points matching the qio* and qcrypto* patterns | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*' | 
					
						
							|  |  |  | """ % {"argv0": sys.argv[0]}) | 
					
						
							|  |  |  |     listparser.set_defaults(func=cmd_list) | 
					
						
							|  |  |  |     listparser.add_argument("binary", help="QEMU system or user emulator binary") | 
					
						
							|  |  |  |     listparser.add_argument("probes", help="Probe names or wildcards", | 
					
						
							|  |  |  |                             nargs=argparse.REMAINDER) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     args.func(args) | 
					
						
							|  |  |  |     sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |