[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 14/24] instrument: Add internal control interface
From: |
Lluís Vilanova |
Subject: |
[Qemu-devel] [PATCH v3 14/24] instrument: Add internal control interface |
Date: |
Sun, 21 Apr 2013 21:12:48 +0200 |
User-agent: |
StGit/0.16 |
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 <address@hidden>
---
Makefile.objs | 2
configure | 3
instrument/Makefile.objs | 6 +
instrument/control-internal.h | 40 +++++
instrument/control.c | 226 ++++++++++++++++++++++++++++
instrument/control.h | 148 ++++++++++++++++++
instrument/qapi-schema.json | 26 +++
qapi-schema.json | 2
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 +
13 files changed, 473 insertions(+)
create mode 100644 instrument/control-internal.h
create mode 100644 instrument/control.c
create mode 100644 instrument/control.h
create mode 100644 instrument/qapi-schema.json
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 b51436f..2be915c 100755
--- a/configure
+++ b/configure
@@ -3925,7 +3925,10 @@ fi
if test "$trace_instrument" = "dynamic"; then
echo "#define QI_TYPE_CONFIG_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..68fc69c
--- /dev/null
+++ b/instrument/control-internal.h
@@ -0,0 +1,40 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012-2013 Lluís Vilanova <address@hidden>
+ *
+ * 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 InstrType instr_type(void)
+{
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+ return INSTR_TYPE_DYNAMIC;
+#elif defined(CONFIG_TRACE_INSTRUMENT_STATE)
+ return INSTR_TYPE_STATIC;
+#else
+ return INSTR_TYPE_NONE;
+#endif
+}
+
+static inline bool instr_event_available(TraceEvent *ev)
+{
+ assert(ev != NULL);
+#if defined(CONFIG_TRACE_INSTRUMENT)
+ return ev->instr;
+#else
+ return false;
+#endif
+}
+
+#endif /* INSTRUMENT__CONTROL_INTERNAL_H */
diff --git a/instrument/control.c b/instrument/control.c
new file mode 100644
index 0000000..7921bf3
--- /dev/null
+++ b/instrument/control.c
@@ -0,0 +1,226 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012-2013 Lluís Vilanova <address@hidden>
+ *
+ * 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"
+
+
+typedef int64_t HandleID;
+
+typedef struct Handle
+{
+ HandleID id;
+ void *dlhandle;
+ void (*init)(int, const char **);
+ void (*fini)(void);
+ QSLIST_ENTRY(Handle) list;
+} Handle;
+
+HandleID handle_last_id;
+QSLIST_HEAD(, Handle) handles = QSLIST_HEAD_INITIALIZER(handles);
+
+
+static Handle *handle_get(void)
+{
+ Handle *res = g_malloc0(sizeof(Handle));
+ res->id = handle_last_id++;
+ QSLIST_INSERT_HEAD(&handles, res, list);
+ return res;
+}
+
+static bool handle_put(HandleID id)
+{
+ Handle *prev = NULL;
+ Handle *handle;
+ QSLIST_FOREACH(handle, &handles, list) {
+ if (handle->id == id) {
+ break;
+ }
+ prev = handle;
+ }
+ if (handle == NULL) {
+ return false;
+ } else {
+ if (prev == NULL) {
+ QSLIST_REMOVE_HEAD(&handles, list);
+ } else {
+ QSLIST_REMOVE_AFTER(prev, list);
+ }
+ g_free(handle);
+ return true;
+ }
+}
+
+static Handle *handle_find(HandleID id)
+{
+ Handle *handle;
+ QSLIST_FOREACH(handle, &handles, list) {
+ if (handle->id == id) {
+ return handle;
+ }
+ }
+ return NULL;
+}
+
+
+bool instr_active(void)
+{
+ switch (instr_type()) {
+ case INSTR_TYPE_NONE:
+ return false;
+ break;
+ case INSTR_TYPE_STATIC:
+ return true;
+ break;
+ case INSTR_TYPE_DYNAMIC:
+ return !QSLIST_EMPTY(&handles);
+ case INSTR_TYPE_MAX:
+ assert(false);
+ }
+ return false;
+}
+
+
+InstrLoadError instr_load(const char * path, int argc, const char ** argv,
+ int64_t *handle_id)
+{
+ /* TODO: not thread safe */
+
+ *handle_id = -1;
+ if (instr_type() != INSTR_TYPE_DYNAMIC) {
+ return INSTR_LOAD_UNAVAILABLE;
+ }
+
+ if (!QSLIST_EMPTY(&handles) > 0) {
+ /* XXX: This is in fact a hard-coded limit, but there's no reason why a
+ * real multi-library implementation should fail with something
lie
+ * "too many open libraries".
+ */
+ return INSTR_LOAD_UNAVAILABLE;
+ }
+
+ Handle * handle = handle_get();
+ handle->dlhandle = dlopen(path, RTLD_NOW);
+ if (handle->dlhandle == NULL) {
+ goto err;
+ }
+
+ handle->init = dlsym(handle->dlhandle, "qi_init");
+ if (handle->init == NULL) {
+ goto err;
+ }
+ handle->fini = dlsym(handle->dlhandle, "qi_fini");
+ if (handle->fini == NULL) {
+ goto err;
+ }
+
+ handle->init(argc, argv);
+
+ *handle_id = handle->id;
+ return INSTR_LOAD_OK;
+
+err:
+ handle_put(handle->id);
+ return INSTR_LOAD_ERROR;
+}
+
+InstrUnloadError instr_unload(int64_t handle_id)
+{
+ /* TODO: not thread safe */
+
+ if (instr_type() != INSTR_TYPE_DYNAMIC) {
+ return INSTR_UNLOAD_UNAVAILABLE;
+ }
+
+ Handle *handle = handle_find(handle_id);
+ if (handle == NULL) {
+ return INSTR_UNLOAD_INVALID;
+ }
+
+ handle->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->dlhandle) < 0) {
+ handle_put(handle->id);
+ return INSTR_UNLOAD_ERROR;
+ }
+
+ handle_put(handle->id);
+ return INSTR_UNLOAD_OK;
+}
+
+#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
+static Handle *get_current_handle(void)
+{
+ /* XXX: We currently have only one */
+ return handle_find(handle_last_id - 1);
+}
+
+static void *get_event_symbol(Handle *handle, TraceEvent *ev, const char *fmt)
+{
+ assert(handle != NULL);
+ 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->dlhandle, 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 /* defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC) */
+
+bool instr_event_set(TraceEvent *ev, void *cb)
+{
+ assert(instr_type() == INSTR_TYPE_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(get_current_handle(),
+ 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 /* defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC) */
+}
diff --git a/instrument/control.h b/instrument/control.h
new file mode 100644
index 0000000..a6a648a
--- /dev/null
+++ b/instrument/control.h
@@ -0,0 +1,148 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * Copyright (C) 2012-2013 Lluís Vilanova <address@hidden>
+ *
+ * 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 "qapi-types.h"
+#include "trace/generated-events.h"
+
+
+/**
+ * InstrLoadError:
+ * @INSTR_LOAD_OK: Correctly loaded.
+ * @INSTR_LOAD_UNAVAILABLE: Service not available.
+ * @INSTR_LOAD_ERROR: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_load().
+ */
+typedef enum {
+ INSTR_LOAD_OK,
+ INSTR_LOAD_UNAVAILABLE,
+ INSTR_LOAD_ERROR,
+} InstrLoadError;
+
+/**
+ * InstrUnloadError:
+ * @INSTR_UNLOAD_OK: Correctly unloaded.
+ * @INSTR_UNLOAD_UNAVAILABLE: Service not available.
+ * @INSTR_UNLOAD_INVALID: Invalid handle.
+ * @INSTR_UNLOAD_ERROR: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_unload().
+ */
+typedef enum {
+ INSTR_UNLOAD_OK,
+ INSTR_UNLOAD_UNAVAILABLE,
+ INSTR_UNLOAD_INVALID,
+ INSTR_UNLOAD_ERROR,
+} 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.
+ * @handle: Instrumentation library handle (undefined in case of error).
+ *
+ * Load a dynamic trace instrumentation library.
+ *
+ * Returns: Whether the library could be loaded.
+ */
+InstrLoadError instr_load(const char * path, int argc, const char ** argv,
+ int64_t *handle);
+
+/**
+ * instr_unload:
+ * @handle: Instrumentation library handle returned by instr_load().
+ *
+ * Unload the given instrumentation library.
+ *
+ * Returns: Whether the library could be unloaded.
+ */
+InstrUnloadError instr_unload(int64_t handle);
+
+
+/**
+ * instr_type:
+ *
+ * Types are defined in QAPI's "instrument/qapi-schema.json"
+ *
+ * Returns: The system's #InstrType.
+ */
+static InstrType instr_type(void);
+
+/**
+ * instr_active:
+ *
+ * Always false when instr_type() is #INSTR_TYPE_NONE; always true when
+ * instr_type() is #INSTR_TYPE_STATIC.
+ *
+ * Returns: Whether an instrumentation library is currently active.
+ */
+bool instr_active(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_type() == #INSTR_TYPE_DYNAMIC
+ * 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/instrument/qapi-schema.json b/instrument/qapi-schema.json
new file mode 100644
index 0000000..e450ddf
--- /dev/null
+++ b/instrument/qapi-schema.json
@@ -0,0 +1,26 @@
+# *-*- Mode: Python -*-*
+#
+# QAPI trace instrumentation control commands.
+#
+# Copyright (C) 2012-2013 Lluís Vilanova <address@hidden>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# @InstrType
+#
+# Instrumentation type supported by the system.
+#
+# @None: No instrumentation support.
+#
+# @Static: Static instrumentation support.
+#
+# @Dynamic: Dynamic instrumentation support.
+#
+# Warning: Keep in sync with #QIType.
+#
+# Since: 1.5
+##
+{ 'enum': 'InstrType',
+ 'data': [ 'None', 'Static', 'Dynamic' ] }
diff --git a/qapi-schema.json b/qapi-schema.json
index db542f6..36e5cbf 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3513,3 +3513,5 @@
'*asl_compiler_rev': 'uint32',
'*file': 'str',
'*data': 'str' }}
+
+include("instrument/qapi-schema.json")
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..7110886 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)
+ const bool instr;
+#endif
+
#if defined(CONFIG_TRACE_INSTRUMENT_DYNAMIC)
void **instr_cb;
void *instr_cb_nop;
- Re: [Qemu-devel] [PATCH v3 05/24] trace: Minimize inclusions of "qemu-common.h" to avoid inclusion loops, (continued)
- [Qemu-devel] [PATCH v3 06/24] instrument: [none] Add null instrumentation, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 07/24] system: [linux] Use absolute include path for linux-headers, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 08/24] instrument: [static] Call statically linked user-provided routines, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 09/24] build: Add variable 'tools-obj-y' for tool-only files, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 10/24] instrument: [dynamic] Call dynamically linked user-provided routines, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 11/24] qapi: Add a primitive to include other files from a QAPI schema file, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 12/24] qapi: [trivial] Set the input root directory when parsing QAPI files, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 13/24] qapi: [trivial] Allow user to use 'args' as an argument name, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 14/24] instrument: Add internal control interface,
Lluís Vilanova <=
- [Qemu-devel] [PATCH v3 15/24] instrument: [qmp, qapi] Add control interface, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 16/24] instrument: [hmp] Add control interface, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 17/24] Let makefiles add entries to the set of target architecture objects, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 18/24] instrument: Add commandline options to start with an instrumentation library, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 19/24] instrument: Add client-side API to enumerate events, Lluís Vilanova, 2013/04/21
- [Qemu-devel] [PATCH v3 20/24] instrument: Add client-side API to control tracing state of events, Lluís Vilanova, 2013/04/21