qemu-trace-stap 5.7 KB

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