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 <vilan...@ac.upc.edu> --- Makefile.objs | 2 configure | 3 + instrument/Makefile.objs | 6 + instrument/control-internal.h | 38 ++++++++ instrument/control.c | 139 ++++++++++++++++++++++++++++ instrument/control.h | 134 +++++++++++++++++++++++++++ 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, 342 insertions(+) create mode 100644 instrument/control-internal.h create mode 100644 instrument/control.c create mode 100644 instrument/control.h diff --git a/Makefile.objs b/Makefile.objs index 5f8ea2d..4fb565b 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -94,6 +94,7 @@ common-obj-y += disas/ # instrumentation tools-obj-y += instrument/ +target-obj-y += instrument/ ###################################################################### # guest agent @@ -114,5 +115,6 @@ nested-vars += \ qga-obj-y \ block-obj-y \ tools-obj-y \ + target-obj-y \ common-obj-y dummy := $(call unnest-vars) diff --git a/configure b/configure index 41089cc..d2e0b90 100755 --- a/configure +++ b/configure @@ -3925,7 +3925,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 diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs index cae520d..e571c71 100644 --- a/instrument/Makefile.objs +++ b/instrument/Makefile.objs @@ -60,3 +60,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 diff --git a/instrument/control-internal.h b/instrument/control-internal.h new file mode 100644 index 0000000..19b342f --- /dev/null +++ b/instrument/control-internal.h @@ -0,0 +1,38 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012-2013 Lluís Vilanova <vilan...@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_INTERNAL_H +#define INSTRUMENT__CONTROL_INTERNAL_H + +#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 +} + +#endif /* INSTRUMENT__CONTROL_INTERNAL_H */ diff --git a/instrument/control.c b/instrument/control.c new file mode 100644 index 0000000..c6ed36f --- /dev/null +++ b/instrument/control.c @@ -0,0 +1,139 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012-2013 Lluís Vilanova <vilan...@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, int argc, const char ** argv) +{ + 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)(int, 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(argc, argv); + } +#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 +} diff --git a/instrument/control.h b/instrument/control.h new file mode 100644 index 0000000..0924fc8 --- /dev/null +++ b/instrument/control.h @@ -0,0 +1,134 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012-2013 Lluís Vilanova <vilan...@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. + * @argc: Number of arguments passed to the initialization function of the library. + * @argv: Arguments 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, int argc, const char ** argv); + +/** + * 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 */ diff --git a/rules.mak b/rules.mak index edc2552..e0767a9 100644 --- a/rules.mak +++ b/rules.mak @@ -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,$^) diff --git a/scripts/tracetool/backend/instr_dynamic.py b/scripts/tracetool/backend/instr_dynamic.py index a07ee64..12d0503 100644 --- a/scripts/tracetool/backend/instr_dynamic.py +++ b/scripts/tracetool/backend/instr_dynamic.py @@ -40,6 +40,9 @@ def qemu_h(events): def api_h(events): out('#include <qemu-instr/visibility-internal.h>', '', + 'QI_VPUBLIC void qi_init(int argc, const char **argv);', + 'QI_VPUBLIC void qi_fini(void);', + '', ) for e in events: diff --git a/scripts/tracetool/backend/instr_static.py b/scripts/tracetool/backend/instr_static.py index b010596..ecebbda 100644 --- a/scripts/tracetool/backend/instr_static.py +++ b/scripts/tracetool/backend/instr_static.py @@ -46,6 +46,9 @@ def qemu_h(events): def api_h(events): out('#include "trace/generated-tracers.h"', '', + 'void qi_init(int argc, const char **argv);', + 'void qi_fini(void);', + '', ) for e in events: diff --git a/scripts/tracetool/format/events_c.py b/scripts/tracetool/format/events_c.py index 7531c66..d7b4f86 100644 --- a/scripts/tracetool/format/events_c.py +++ b/scripts/tracetool/format/events_c.py @@ -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, diff --git a/trace/event-internal.h b/trace/event-internal.h index aed4050..415aec6 100644 --- a/trace/event-internal.h +++ b/trace/event-internal.h @@ -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;