From patchwork Tue Mar 26 14:01:17 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Llu=C3=ADs_Vilanova?= X-Patchwork-Id: 231221 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CC3642C0090 for ; Wed, 27 Mar 2013 01:11:46 +1100 (EST) Received: from localhost ([::1]:59500 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UKUTb-0002Xh-6E for incoming@patchwork.ozlabs.org; Tue, 26 Mar 2013 10:03:47 -0400 Received: from eggs.gnu.org ([208.118.235.92]:36528) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UKURF-0007By-Lq for qemu-devel@nongnu.org; Tue, 26 Mar 2013 10:01:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UKURD-00085B-1T for qemu-devel@nongnu.org; Tue, 26 Mar 2013 10:01:21 -0400 Received: from roura.ac.upc.es ([147.83.33.10]:37445) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UKURC-00084w-K5 for qemu-devel@nongnu.org; Tue, 26 Mar 2013 10:01:18 -0400 Received: from gw.ac.upc.edu (gw.ac.upc.es [147.83.30.3]) by roura.ac.upc.es (8.13.8/8.13.8) with ESMTP id r2QE1Hqa005137 for ; Tue, 26 Mar 2013 15:01:17 +0100 Received: from localhost (unknown [84.88.51.85]) by gw.ac.upc.edu (Postfix) with ESMTP id 766046B01C4 for ; Tue, 26 Mar 2013 15:01:17 +0100 (CET) From: =?utf-8?b?TGx1w61z?= Vilanova To: qemu-devel@nongnu.org Date: Tue, 26 Mar 2013 15:01:17 +0100 Message-Id: <20130326140117.4471.1875.stgit@fimbulvetr.bsc.es> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <20130326140021.4471.99597.stgit@fimbulvetr.bsc.es> References: <20130326140021.4471.99597.stgit@fimbulvetr.bsc.es> User-Agent: StGit/0.16 MIME-Version: 1.0 X-MIME-Autoconverted: from 8bit to quoted-printable by roura.ac.upc.es id r2QE1Hqa005137 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 147.83.33.10 Subject: [Qemu-devel] [PATCH 10/22] instrument: Add internal control interface X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 diff --git a/Makefile.objs b/Makefile.objs index 12b67bf..fd6779e 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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) diff --git a/configure b/configure index 55c2fcc..e1ac5ff 100755 --- a/configure +++ b/configure @@ -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 diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs index ce6cfe5..0d3b70b 100644 --- a/instrument/Makefile.objs +++ b/instrument/Makefile.objs @@ -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 diff --git a/instrument/control-internal.h b/instrument/control-internal.h new file mode 100644 index 0000000..742f67a --- /dev/null +++ b/instrument/control-internal.h @@ -0,0 +1,33 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012 Lluís Vilanova + * + * 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 +#include + +#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 +} diff --git a/instrument/control.c b/instrument/control.c new file mode 100644 index 0000000..43f76f1 --- /dev/null +++ b/instrument/control.c @@ -0,0 +1,139 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012 Lluís Vilanova + * + * 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 + +#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 +} diff --git a/instrument/control.h b/instrument/control.h new file mode 100644 index 0000000..c917eb1 --- /dev/null +++ b/instrument/control.h @@ -0,0 +1,133 @@ +/* + * Interface for controlling dynamic trace instrumentation. + * + * Copyright (C) 2012 Lluís Vilanova + * + * 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 */ 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 e8de5a6..5125a16 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 ', '', + 'QI_VPUBLIC void qi_init(const char *args);', + '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 5a067b8..1f5520f 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(const char *args);', + '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 73b080d..ea23dce 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 fb5629b..813f692 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;