@@ -101,6 +101,7 @@ common-obj-y += disas/
# instrumentation
util-obj-y += instrument/
+target-obj-y += instrument/
######################################################################
# guest agent
@@ -120,5 +121,6 @@ nested-vars += \
util-obj-y \
qga-obj-y \
block-obj-y \
+ target-obj-y \
common-obj-y
dummy := $(call unnest-vars)
@@ -3903,7 +3903,10 @@ fi
if test "$trace_instrument" = "dynamic"; then
echo "#define QI_TYPE_DYNAMIC 1" >> $config_qi
echo "CONFIG_TRACE_INSTRUMENT_DYNAMIC=y" >> $config_host_mak
+ libs_qga="-ldl $libs_qga"
fi
+# code requiring it is always compiled-in
+LIBS="-ldl $LIBS"
##########################################
echo "TOOLS=$tools" >> $config_host_mak
@@ -59,3 +59,9 @@ $(LIBTRACE_INSTRUMENT): $(dir $(LIBTRACE_INSTRUMENT))/Makefile force
TARGET_DIR=$(TARGET_DIR)$(dir $@)/ VPATH=$(VPATH) \
SRC_PATH=$(SRC_PATH) V="$(V)" $(notdir $@))
endif
+
+
+######################################################################
+# Control code
+
+target-obj-y += control.o
new file mode 100644
@@ -0,0 +1,33 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "config-host.h"
+
+
+static inline bool instr_dynamic(void)
+{
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static inline bool instr_event_available(TraceEvent *ev)
+{
+ assert(ev != NULL);
+#if defined(CONFIG_TRACE_INSTRUMENT_NONE)
+ return false;
+#else
+ return ev->instr;
+#endif
+}
new file mode 100644
@@ -0,0 +1,139 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "instrument/control.h"
+
+#include <dlfcn.h>
+
+#include "qemu-common.h"
+#include "trace/control.h"
+
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+static void *handle = NULL;
+#endif
+
+
+InstrLoadError instr_load(const char * path, const char * args)
+{
+ if (!instr_dynamic()) {
+ return INSTR_LOAD_UNAVAILABLE;
+ }
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+ if (handle != NULL) {
+ return INSTR_LOAD_LOADED;
+ }
+
+ handle = dlopen(path, RTLD_NOW);
+
+ if (handle == NULL) {
+ return INSTR_LOAD_DL;
+ }
+
+ void (*init)(const char *) = dlsym(handle, "qi_init");
+ if (init == NULL) {
+ return INSTR_LOAD_DL;
+ }
+ void (*fini)(void) = dlsym(handle, "qi_fini");
+ if (fini == NULL) {
+ return INSTR_LOAD_DL;
+ }
+
+ if (init != NULL) {
+ init(args);
+ }
+#endif
+
+ return INSTR_LOAD_OK;
+}
+
+InstrUnloadError instr_unload(void)
+{
+ if (!instr_dynamic()) {
+ return INSTR_UNLOAD_UNAVAILABLE;
+ }
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+ if (handle == NULL) {
+ return INSTR_UNLOAD_UNLOADED;
+ }
+
+ void (*fini)(void) = dlsym(handle, "qi_fini");
+ assert(fini != NULL);
+ fini();
+
+ TraceEvent *ev = NULL;
+ while ((ev = trace_event_pattern("*", ev)) != NULL) {
+ if (instr_event_available(ev)) {
+ instr_event_set(ev, INSTR_CB_NOP);
+ }
+ }
+
+ /* this should never fail */
+ if (dlclose(handle) < 0) {
+ return INSTR_UNLOAD_DL;
+ }
+
+ handle = NULL;
+#endif
+
+ return INSTR_UNLOAD_OK;
+}
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+static void *get_event_symbol(TraceEvent *ev, const char *fmt)
+{
+ assert(ev != NULL);
+
+ char name[1024];
+ assert(strlen(trace_event_get_name(ev)) + strlen(fmt) < 1024);
+ sprintf(name, fmt, trace_event_get_name(ev));
+ return dlsym(handle, name);
+}
+
+static void event_set(TraceEvent *ev, void *cb)
+{
+ if (ev->instr_cb == cb) {
+ return;
+ }
+ assert(instr_event_available(ev));
+ *ev->instr_cb = cb;
+}
+#endif
+
+bool instr_event_set(TraceEvent *ev, void *cb)
+{
+ assert(instr_dynamic());
+ assert(ev != NULL);
+ assert(instr_event_available(ev));
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+ if (cb == INSTR_CB_NOP) {
+ event_set(ev, ev->instr_cb_nop);
+ return true;
+ } else if (cb == INSTR_CB_NEXT) {
+ event_set(ev, ev->instr_cb_backend);
+ return true;
+ } else if (cb == INSTR_CB_AUTO) {
+ void *ptr = get_event_symbol(ev, "qi_event_%s");
+ if (ptr != NULL) {
+ event_set(ev, ptr);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ event_set(ev, cb);
+ return true;
+ }
+#else
+ assert(false);
+#endif
+}
new file mode 100644
@@ -0,0 +1,133 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef INSTRUMENT__CONTROL_H
+#define INSTRUMENT__CONTROL_H
+
+#include "trace/generated-events.h"
+
+
+/**
+ * InstrLoadError:
+ * @INSTR_LOAD_OK: Correctly loaded.
+ * @INSTR_LOAD_UNAVAILABLE: Service not available.
+ * @INSTR_LOAD_LOADED: Already loaded.
+ * @INSTR_LOAD_DL: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_load().
+ */
+typedef enum {
+ INSTR_LOAD_OK,
+ INSTR_LOAD_UNAVAILABLE,
+ INSTR_LOAD_LOADED,
+ INSTR_LOAD_DL,
+} InstrLoadError;
+
+/**
+ * InstrUnloadError:
+ * @INSTR_UNLOAD_OK: Correctly unloaded.
+ * @INSTR_UNLOAD_UNAVAILABLE: Service not available.
+ * @INSTR_UNLOAD_UNLOADED: Already unloaded.
+ * @INSTR_UNLOAD_DL: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_unload().
+ */
+typedef enum {
+ INSTR_UNLOAD_OK, /*< Correctly unloaded */
+ INSTR_UNLOAD_UNAVAILABLE, /*< Service not available */
+ INSTR_UNLOAD_UNLOADED, /*< Already unloaded */
+ INSTR_UNLOAD_DL, /*< Error with libdl (see dlerror) */
+} InstrUnloadError;
+
+/**
+ * instr_load:
+ * @path: Path to the shared library to load.
+ * @args: String passed to the initialization function of the library.
+ *
+ * Load a dynamic trace instrumentation library.
+ *
+ * Returns: Whether the library could be loaded.
+ */
+InstrLoadError instr_load(const char * path, const char * args);
+
+/**
+ * instr_unload:
+ *
+ * Unload the currently loaded instrumentation library.
+ *
+ * Returns: Whether the library could be unloaded.
+ */
+InstrUnloadError instr_unload(void);
+
+
+/**
+ * instr_dynamic:
+ *
+ * Returns: Whether dynamic trace instrumentation is available.
+ */
+static bool instr_dynamic(void);
+
+/**
+ * instr_event_available:
+ *
+ * Returns: Whether the given event has the 'instrument' property.
+ */
+static bool instr_event_available(TraceEvent *ev);
+
+
+/**
+ * INSTR_CB_NOP:
+ *
+ * Set callback to no-operation.
+ * (qi_event_${name}_nop).
+ */
+#define INSTR_CB_NOP ((void*)NULL)
+
+/**
+ * INSTR_CB_NEXT:
+ *
+ * Set callback to the "next logical step"
+ * (qi_event_${name}_trace).
+ */
+#define INSTR_CB_NEXT ((void*)1)
+
+/**
+ * INSTR_CB_AUTO:
+ *
+ * Automatically set callback to proper routine.
+ *
+ * Looks for a symbol name in the instrumentation library matching the event
+ * name (qi_event_${name}).
+ */
+#define INSTR_CB_AUTO ((void*)2)
+
+/**
+ * instr_event_set:
+ * @ev: Tracing event descriptor.
+ * @cb: Pointer to instrumentation callback.
+ *
+ * Set the instrumentation callback for the given event.
+ *
+ * Argument cb can also be set to any of the INSTR_CB_* special values.
+ *
+ * A negative return value indicates that the instrumentation library does not
+ * export the appropriate symbol for the instrumentation routine.
+ *
+ * Pre-condition: instr_dynamic() == true
+ * Pre-condition: instr_event_available(ev) == true
+ *
+ * Returns: Whether the callback could be set (if cb == INSTR_CB_AUTO, always
+ * true otherwise).
+ */
+bool instr_event_set(TraceEvent *ev, void *cb);
+
+
+#include "instrument/control-internal.h"
+
+#endif /* INSTRUMENT__CONTROL_H */
@@ -51,6 +51,9 @@ endif
%.o: %.dtrace
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN $(TARGET_DIR)$@")
+ifdef CONFIG_TRACE_INSTRUMENT_DYNAMIC
+%$(EXESUF): LDFLAGS+=-rdynamic
+endif
%$(EXESUF): %.o
$(call LINK,$^)
@@ -40,6 +40,9 @@ def qemu_h(events):
def api_h(events):
out('#include <qemu-instr/visibility-internal.h>',
'',
+ 'QI_VPUBLIC void qi_init(const char *args);',
+ 'QI_VPUBLIC void qi_fini(void);',
+ '',
)
for e in events:
@@ -46,6 +46,9 @@ def qemu_h(events):
def api_h(events):
out('#include "trace/generated-tracers.h"',
'',
+ 'void qi_init(const char *args);',
+ 'void qi_fini(void);',
+ '',
)
for e in events:
@@ -44,14 +44,19 @@ def begin(events):
out('TraceEvent trace_events[TRACE_EVENT_COUNT] = {')
for e in events:
+ instr = "false"
cb = cb_nop = cb_backend = "NULL"
if "instrument" in e.properties:
+ instr = "true"
cb = "&%s_cb" % e.api(e.QI_TRACE_INSTRUMENT)
cb_nop = e.api(e.QI_TRACE_NOP)
cb_backend = e.api(e.QI_TRACE_BACKEND)
out(' { .id = %(id)s, .name = \"%(name)s\", .sstate = %(sstate)s, .dstate = 0,',
+ '#if !defined(CONFIG_TRACE_INSTRUMENT_NONE)',
+ ' .instr = %(instr)s,',
+ '#endif',
'#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)',
' .instr_cb = %(cb)s,',
' .instr_cb_nop = %(cb_nop)s,',
@@ -59,6 +64,7 @@ def begin(events):
'#endif',
' },',
id = "TRACE_" + e.name.upper(),
+ instr = instr,
cb = cb,
cb_nop = cb_nop,
cb_backend = cb_backend,
@@ -20,6 +20,7 @@
* @name: Event name.
* @sstate: Static tracing state.
* @dstate: Dynamic tracing state.
+ * @instr: Whether the event is instrumentable.
* @instr_cb: Instrumentation callback pointer.
* @instr_cb_nop: Instrumentation callback pointer to no-operation.
* @instr_cb_backend: Instrumentation callback pointer to tracing backend.
@@ -32,6 +33,10 @@ typedef struct TraceEvent {
const bool sstate;
bool dstate;
+#if !defined(CONFIG_TRACE_INSTRUMENT_NONE)
+ const bool instr;
+#endif
+
#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
void **instr_cb;
void *instr_cb_nop;
This interface provides two sets of operations: * Loading/unloading a trace instrumentation library. * Controls the instrumentation callbacks of the tracing events. Note that in the case of static instrumentation, the library is not loaded/unloaded, but is still properly (de)initialized when QEMU starts and exits. Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> --- Makefile.objs | 2 configure | 3 + instrument/Makefile.objs | 6 + instrument/control-internal.h | 33 +++++++ instrument/control.c | 139 ++++++++++++++++++++++++++++ instrument/control.h | 133 +++++++++++++++++++++++++++ rules.mak | 3 + scripts/tracetool/backend/instr_dynamic.py | 3 + scripts/tracetool/backend/instr_static.py | 3 + scripts/tracetool/format/events_c.py | 6 + trace/event-internal.h | 5 + 11 files changed, 336 insertions(+) create mode 100644 instrument/control-internal.h create mode 100644 instrument/control.c create mode 100644 instrument/control.h