aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--Makefile26
-rw-r--r--Makefile.target11
-rw-r--r--docs/devel/tracing.txt4
-rw-r--r--hw/display/qxl.c14
-rw-r--r--hw/display/trace-events2
-rw-r--r--hw/gpio/trace-events2
-rw-r--r--hw/vfio/pci.c2
-rw-r--r--hw/vfio/trace-events2
-rwxr-xr-xscripts/qemu-trace-stap175
-rw-r--r--scripts/qemu-trace-stap.texi140
-rw-r--r--scripts/tracetool/__init__.py6
-rw-r--r--scripts/tracetool/format/log_stap.py127
-rw-r--r--trace-events4
-rw-r--r--vl.c7
15 files changed, 502 insertions, 21 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index b334b53..234e5c4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2011,6 +2011,7 @@ F: trace-events
F: qemu-option-trace.texi
F: scripts/tracetool.py
F: scripts/tracetool/
+F: scripts/qemu-trace-stap*
F: docs/devel/tracing.txt
T: git https://github.com/stefanha/qemu.git tracing
diff --git a/Makefile b/Makefile
index de898ea..1278a3e 100644
--- a/Makefile
+++ b/Makefile
@@ -145,7 +145,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
%/trace.h: %/trace.h-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=$(call trace-group-name,$@) \
--format=h \
@@ -154,7 +154,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
%/trace.c: %/trace.c-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=$(call trace-group-name,$@) \
--format=c \
@@ -163,7 +163,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
%/trace-ust.h: %/trace-ust.h-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
+%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=$(call trace-group-name,$@) \
--format=ust-events-h \
@@ -187,7 +187,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
trace-root.h: trace-root.h-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=root \
--format=h \
@@ -196,7 +196,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
trace-root.c: trace-root.c-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=root \
--format=c \
@@ -205,7 +205,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
trace-ust-root.h: trace-ust-root.h-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
+trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=root \
--format=ust-events-h \
@@ -214,7 +214,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
trace-ust-all.h: trace-ust-all.h-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
+trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=all \
--format=ust-events-h \
@@ -223,7 +223,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
trace-ust-all.c: trace-ust-all.c-timestamp
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
-trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y)
+trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--group=all \
--format=ust-events-c \
@@ -305,6 +305,9 @@ DOCS+=docs/qemu-cpu-models.7
ifdef CONFIG_VIRTFS
DOCS+=fsdev/virtfs-proxy-helper.1
endif
+ifdef CONFIG_TRACE_SYSTEMTAP
+DOCS+=scripts/qemu-trace-stap.1
+endif
else
DOCS=
endif
@@ -699,6 +702,9 @@ ifneq ($(TOOLS),)
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
endif
+ifdef CONFIG_TRACE_SYSTEMTAP
+ $(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
+endif
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
@@ -738,6 +744,9 @@ endif
ifneq ($(HELPERS-y),)
$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
endif
+ifdef CONFIG_TRACE_SYSTEMTAP
+ $(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir)
+endif
ifneq ($(BLOBS),)
set -e; for x in $(BLOBS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
@@ -852,6 +861,7 @@ qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
qemu-ga.8: qemu-ga.texi
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
+scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
diff --git a/Makefile.target b/Makefile.target
index 39f72e8..401dc1e 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -45,7 +45,7 @@ config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak
ifdef CONFIG_TRACE_SYSTEMTAP
-stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
+stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
ifdef CONFIG_USER_ONLY
TARGET_TYPE=user
@@ -84,6 +84,14 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
+$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
+ $(call quiet-command,$(TRACETOOL) \
+ --group=all \
+ --format=log-stap \
+ --backends=$(TRACE_BACKENDS) \
+ --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
+ $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
+
else
stap:
endif
@@ -227,6 +235,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
+ $(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
endif
GENERATED_FILES += config-target.h
diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt
index bc52f12..056aa56 100644
--- a/docs/devel/tracing.txt
+++ b/docs/devel/tracing.txt
@@ -317,6 +317,10 @@ probes:
--target-name x86_64 \
<trace-events-all >qemu.stp
+To facilitate simple usage of systemtap where there merely needs to be printf
+logging of certain probes, a helper script "qemu-trace-stap" is provided.
+Consult its manual page for guidance on its usage.
+
== Trace event properties ==
Each event in the "trace-events-all" file can be prefixed with a space-separated
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 8e9a65e..da8fd5a 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1763,10 +1763,16 @@ async_common:
qxl_set_mode(d, val, 0);
break;
case QXL_IO_LOG:
- trace_qxl_io_log(d->id, d->ram->log_buf);
- if (d->guestdebug) {
- fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), d->ram->log_buf);
+ if (TRACE_QXL_IO_LOG_ENABLED || d->guestdebug) {
+ /* We cannot trust the guest to NUL terminate d->ram->log_buf */
+ char *log_buf = g_strndup((const char *)d->ram->log_buf,
+ sizeof(d->ram->log_buf));
+ trace_qxl_io_log(d->id, log_buf);
+ if (d->guestdebug) {
+ fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), log_buf);
+ }
+ g_free(log_buf);
}
break;
case QXL_IO_RESET:
diff --git a/hw/display/trace-events b/hw/display/trace-events
index 5a48c6c..387c6b8 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -72,7 +72,7 @@ qxl_interface_update_area_complete_rest(int qid, uint32_t num_updated_rects) "%d
qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d"
qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d"
qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s"
-qxl_io_log(int qid, const uint8_t *log_buf) "%d %s"
+qxl_io_log(int qid, const char *log_buf) "%d %s"
qxl_io_read_unexpected(int qid) "%d"
qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)"
qxl_io_write(int qid, const char *mode, uint64_t addr, const char *aname, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " (%s) val=%"PRIu64" size=%u async=%d"
diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events
index cb41a89..5d4dd20 100644
--- a/hw/gpio/trace-events
+++ b/hw/gpio/trace-events
@@ -4,4 +4,4 @@
nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64
nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64
nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
-nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 \ No newline at end of file
+nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index c0cb1ec..dd12f36 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2581,7 +2581,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
if (ret) {
/* This can fail for an old kernel or legacy PCI dev */
- trace_vfio_populate_device_get_irq_info_failure();
+ trace_vfio_populate_device_get_irq_info_failure(strerror(errno));
} else if (irq_info.count == 1) {
vdev->pci_aer = true;
} else {
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index a85e866..f41ca96 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -37,7 +37,7 @@ vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent de
vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d"
vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s"
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
-vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m"
+vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s"
vfio_realize(const char *name, int group_id) " (%s) group %d"
vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d"
vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x"
diff --git a/scripts/qemu-trace-stap b/scripts/qemu-trace-stap
new file mode 100755
index 0000000..91d1051
--- /dev/null
+++ b/scripts/qemu-trace-stap
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+# -*- 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/>.
+
+from __future__ import print_function
+
+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 tapset_env(tapset_dir):
+ tenv = copy.copy(os.environ)
+ tenv["SYSTEMTAP_TAPSET"] = tapset_dir
+ return tenv
+
+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
+ stapargs = ["stap", "-s", "8"]
+ if args.pid is not None:
+ stapargs.extend(["-x", args.pid])
+ stapargs.extend(["-e", script])
+ subprocess.call(stapargs, env=tapset_env(tapsets))
+
+
+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)
+ proc = subprocess.Popen(["stap", "-l", script],
+ stdout=subprocess.PIPE, env=tapset_env(tapsets))
+ 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()
diff --git a/scripts/qemu-trace-stap.texi b/scripts/qemu-trace-stap.texi
new file mode 100644
index 0000000..07bb9eb
--- /dev/null
+++ b/scripts/qemu-trace-stap.texi
@@ -0,0 +1,140 @@
+@example
+@c man begin SYNOPSIS
+@command{qemu-trace-stap} @var{GLOBAL-OPTIONS} @var{COMMAND} @var{COMMAND-OPTIONS} @var{ARGS...}
+@c man end
+@end example
+
+@c man begin DESCRIPTION
+
+The @command{qemu-trace-stap} program facilitates tracing of the execution
+of QEMU emulators using SystemTap.
+
+It is required to have the SystemTap runtime environment installed to use
+this program, since it is a wrapper around execution of the @command{stap}
+program.
+
+@c man end
+
+@c man begin OPTIONS
+
+The following global options may be used regardless of which command
+is executed:
+
+@table @option
+@item @var{--verbose}, @var{-v}
+
+Display verbose information about command execution.
+
+@end table
+
+The following commands are valid:
+
+@table @option
+
+@item @var{list} @var{BINARY} @var{PATTERN...}
+
+List all the probe names provided by @var{BINARY} that match
+@var{PATTERN}.
+
+If @var{BINARY} is not an absolute path, it will be located by searching
+the directories listed in the @code{$PATH} environment variable.
+
+@var{PATTERN} is a plain string that is used to filter the results of
+this command. It may optionally contain a @code{*} wildcard to facilitate
+matching multiple probes without listing each one explicitly. Multiple
+@var{PATTERN} arguments may be given, causing listing of probes that match
+any of the listed names. If no @var{PATTERN} is given, the all possible
+probes will be listed.
+
+For example, to list all probes available in the @command{qemu-system-x86_64}
+binary:
+
+@example
+$ qemu-trace-stap list qemu-system-x86_64
+@end example
+
+To filter the list to only cover probes related to QEMU's cryptographic
+subsystem, in a binary outside @code{$PATH}
+
+@example
+$ qemu-trace-stap list /opt/qemu/4.0.0/bin/qemu-system-x86_64 'qcrypto*'
+@end example
+
+
+@item @var{run} @var{OPTIONS} @var{BINARY} @var{PATTERN...}
+
+Run a trace session, printing formatted output any time a process that is
+executing @var{BINARY} triggers a probe matching @var{PATTERN}.
+
+If @var{BINARY} is not an absolute path, it will be located by searching
+the directories listed in the @code{$PATH} environment variable.
+
+@var{PATTERN} is a plain string that matches a probe name shown by the
+@var{list} command. It may optionally contain a @code{*} wildcard to
+facilitate matching multiple probes without listing each one explicitly.
+Multiple @var{PATTERN} arguments may be given, causing all matching probes
+to be monitored. At least one @var{PATTERN} is required, since stap is not
+capable of tracing all known QEMU probes concurrently without overflowing
+its trace buffer.
+
+Invocation of this command does not need to be synchronized with
+invocation of the QEMU process(es). It will match probes on all
+existing running processes and all future launched processes,
+unless told to only monitor a specific process.
+
+Valid command specific options are:
+
+@table @option
+@item @var{--pid=PID}, @var{-p PID}
+
+Restrict the tracing session so that it only triggers for the process
+identified by @code{PID}.
+
+@end table
+
+For example, to monitor all processes executing @command{qemu-system-x86_64}
+as found on $PATH, displaying all I/O related probes:
+
+@example
+$ qemu-trace-stap run qemu-system-x86_64 'qio*'
+@end example
+
+To monitor only the QEMU process with PID 1732
+
+@example
+$ qemu-trace-stap run --pid=1732 qemu-system-x86_64 'qio*'
+@end example
+
+To monitor QEMU processes running an alternative binary outside of
+@code{$PATH}, displaying verbose information about setup of the
+tracing environment:
+
+@example
+$ qemu-trace-stap -v run /opt/qemu/4.0.0/qemu-system-x86_64 'qio*'
+@end example
+
+@end table
+
+@c man end
+
+@ignore
+
+@setfilename qemu-trace-stap
+@settitle QEMU SystemTap trace tool
+
+@c man begin LICENSE
+
+Copyright (C) 2019 Red Hat, Inc.
+
+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.
+
+@c man end
+
+@c man begin SEEALSO
+qemu(1), stap(1)
+@c man end
+
+@end ignore
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index 0e3c9e1..6fca674 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -274,6 +274,10 @@ class Event(object):
props = groups["props"].split()
fmt = groups["fmt"]
fmt_trans = groups["fmt_trans"]
+ if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1:
+ raise ValueError("Event format '%m' is forbidden, pass the error "
+ "as an explicit trace argument")
+
if len(fmt_trans) > 0:
fmt = [fmt_trans, fmt]
args = Arguments.build(groups["args"])
@@ -350,6 +354,8 @@ def read_events(fobj, fname):
events = []
for lineno, line in enumerate(fobj, 1):
+ if line[-1] != '\n':
+ raise ValueError("%s does not end with a new line" % fname)
if not line.strip():
continue
if line.lstrip().startswith('#'):
diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py
new file mode 100644
index 0000000..3ccbc09
--- /dev/null
+++ b/scripts/tracetool/format/log_stap.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Generate .stp file that printfs log messages (DTrace with SystemTAP only).
+"""
+
+__author__ = "Daniel P. Berrange <berrange@redhat.com>"
+__copyright__ = "Copyright (C) 2014-2019, Red Hat, Inc."
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Daniel Berrange"
+__email__ = "berrange@redhat.com"
+
+import re
+
+from tracetool import out
+from tracetool.backend.dtrace import binary, probeprefix
+from tracetool.backend.simple import is_string
+from tracetool.format.stap import stap_escape
+
+def global_var_name(name):
+ return probeprefix().replace(".", "_") + "_" + name
+
+STATE_SKIP = 0
+STATE_LITERAL = 1
+STATE_MACRO = 2
+
+def c_macro_to_format(macro):
+ if macro.startswith("PRI"):
+ return macro[3]
+
+ if macro == "TARGET_FMT_plx":
+ return "%016x"
+
+ raise Exception("Unhandled macro '%s'" % macro)
+
+def c_fmt_to_stap(fmt):
+ state = 0
+ bits = []
+ literal = ""
+ macro = ""
+ escape = 0;
+ for i in range(len(fmt)):
+ if fmt[i] == '\\':
+ if escape:
+ escape = 0
+ else:
+ escape = 1
+ if state != STATE_LITERAL:
+ raise Exception("Unexpected escape outside string literal")
+ literal = literal + fmt[i]
+ elif fmt[i] == '"' and not escape:
+ if state == STATE_LITERAL:
+ state = STATE_SKIP
+ bits.append(literal)
+ literal = ""
+ else:
+ if state == STATE_MACRO:
+ bits.append(c_macro_to_format(macro))
+ state = STATE_LITERAL
+ elif fmt[i] == ' ' or fmt[i] == '\t':
+ if state == STATE_MACRO:
+ bits.append(c_macro_to_format(macro))
+ macro = ""
+ state = STATE_SKIP
+ elif state == STATE_LITERAL:
+ literal = literal + fmt[i]
+ else:
+ escape = 0
+ if state == STATE_SKIP:
+ state = STATE_MACRO
+
+ if state == STATE_LITERAL:
+ literal = literal + fmt[i]
+ else:
+ macro = macro + fmt[i]
+
+ if state == STATE_MACRO:
+ bits.append(c_macro_to_format(macro))
+ elif state == STATE_LITERAL:
+ bits.append(literal)
+
+ fmt = re.sub("%(\d*)z(x|u|d)", "%\\1\\2", "".join(bits))
+ return fmt
+
+def generate(events, backend, group):
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '')
+
+ for event_id, e in enumerate(events):
+ if 'disable' in e.properties:
+ continue
+
+ out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?',
+ '{',
+ probeprefix=probeprefix(),
+ name=e.name)
+
+ # Get references to userspace strings
+ for type_, name in e.args:
+ name = stap_escape(name)
+ if is_string(type_):
+ out(' try {',
+ ' arg%(name)s_str = %(name)s ? ' +
+ 'user_string_n(%(name)s, 512) : "<null>"',
+ ' } catch {}',
+ name=name)
+
+ # Determine systemtap's view of variable names
+ fields = ["pid()", "gettimeofday_ns()"]
+ for type_, name in e.args:
+ name = stap_escape(name)
+ if is_string(type_):
+ fields.append("arg" + name + "_str")
+ else:
+ fields.append(name)
+
+ # Emit the entire record in a single SystemTap printf()
+ arg_str = ', '.join(arg for arg in fields)
+ fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n"
+ out(' printf("%(fmt_str)s", %(arg_str)s)',
+ fmt_str=fmt_str, arg_str=arg_str)
+
+ out('}')
+
+ out()
diff --git a/trace-events b/trace-events
index 4fd2cb4..e66afc5 100644
--- a/trace-events
+++ b/trace-events
@@ -34,9 +34,9 @@ cpu_out(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u"
balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu"
# vl.c
-vm_state_notify(int running, int reason) "running %d reason %d"
+vm_state_notify(int running, int reason, const char *reason_str) "running %d reason %d (%s)"
load_file(const char *name, const char *path) "name %s location %s"
-runstate_set(int new_state) "new state %d"
+runstate_set(int current_state, const char *current_state_str, int new_state, const char *new_state_str) "current_run_state %d (%s) new_state %d (%s)"
system_wakeup_request(int reason) "reason=%d"
qemu_system_shutdown_request(int reason) "reason=%d"
qemu_system_powerdown_request(void) ""
diff --git a/vl.c b/vl.c
index bc9fbec..9cf0fbe 100644
--- a/vl.c
+++ b/vl.c
@@ -731,6 +731,9 @@ void runstate_set(RunState new_state)
{
assert(new_state < RUN_STATE__MAX);
+ trace_runstate_set(current_run_state, RunState_str(current_run_state),
+ new_state, RunState_str(current_run_state));
+
if (current_run_state == new_state) {
return;
}
@@ -741,7 +744,7 @@ void runstate_set(RunState new_state)
RunState_str(new_state));
abort();
}
- trace_runstate_set(new_state);
+
current_run_state = new_state;
}
@@ -1554,7 +1557,7 @@ void vm_state_notify(int running, RunState state)
{
VMChangeStateEntry *e, *next;
- trace_vm_state_notify(running, state);
+ trace_vm_state_notify(running, state, RunState_str(state));
QLIST_FOREACH_SAFE(e, &vm_change_state_head, entries, next) {
e->cb(e->opaque, running, state);