@@ -1485,6 +1485,7 @@ M: Lluís Vilanova <vilanova@ac.upc.edu>
M: Stefan Hajnoczi <stefanha@redhat.com>
S: Maintained
F: docs/instrument.txt
+F: instrument/
Checkpatch
S: Odd Fixes
@@ -97,6 +97,10 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
util-obj-y += trace/
target-obj-y += trace/
+######################################################################
+# instrument
+target-obj-y += instrument/
+
######################################################################
# guest agent
@@ -6034,6 +6034,8 @@ fi
echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
if test "$instrument" = "yes"; then
+ LDFLAGS="-rdynamic $LDFLAGS" # limit symbols available to clients
+ LIBS="-ldl $LIBS"
echo "CONFIG_INSTRUMENT=y" >> $config_host_mak
fi
new file mode 100644
@@ -0,0 +1,4 @@
+# -*- mode: makefile -*-
+
+target-obj-y += cmdline.o
+target-obj-$(CONFIG_INSTRUMENT) += load.o
new file mode 100644
@@ -0,0 +1,124 @@
+/*
+ * Control instrumentation during program (de)initialization.
+ *
+ * Copyright (C) 2012-2017 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 "qemu/osdep.h"
+#include <dlfcn.h>
+#include "instrument/cmdline.h"
+#include "instrument/load.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+
+
+QemuOptsList qemu_instr_opts = {
+ .name = "instrument",
+ .implied_opt_name = "file",
+ .merge_lists = true,
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_instr_opts.head),
+ .desc = {
+ {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "arg",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+void instr_opt_parse(const char *optarg, char **path,
+ int *argc, const char ***argv)
+{
+ const char *arg;
+ QemuOptsIter iter;
+ QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("instrument"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ } else {
+#if !defined(CONFIG_INSTRUMENT)
+ error_report("instrumentation not enabled on this build");
+ exit(1);
+#endif
+ }
+
+
+ arg = qemu_opt_get(opts, "file");
+ if (arg != NULL) {
+ g_free(*path);
+ *path = g_strdup(arg);
+ }
+
+ qemu_opt_iter_init(&iter, opts, "arg");
+ while ((arg = qemu_opt_iter_next(&iter)) != NULL) {
+ *argv = realloc(*argv, sizeof(**argv) * (*argc + 1));
+ (*argv)[*argc] = g_strdup(arg);
+ (*argc)++;
+ }
+
+ qemu_opts_del(opts);
+}
+
+void instr_init(const char *path, int argc, const char **argv)
+{
+#if defined(CONFIG_INSTRUMENT)
+ InstrLoadError err;
+ int64_t handle;
+
+ if (path == NULL) {
+ return;
+ }
+
+ if (atexit(instr_fini) != 0) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+
+ err = instr_load(path, argc, argv, &handle);
+ switch (err) {
+ case INSTR_LOAD_OK:
+ return;
+ case INSTR_LOAD_TOO_MANY:
+ error_report("instrument: tried to load too many libraries");
+ break;
+ case INSTR_LOAD_ERROR:
+ error_report("instrument: library initialization returned non-zero");
+ break;
+ case INSTR_LOAD_DLERROR:
+ error_report("instrument: error loading library: %s", dlerror());
+ break;
+ }
+#else
+ error_report("instrument: not available");
+#endif
+
+ exit(1);
+}
+
+void instr_fini(void)
+{
+#if defined(CONFIG_INSTRUMENT)
+ InstrUnloadError err = instr_unload_all();
+
+ switch (err) {
+ case INSTR_UNLOAD_OK:
+ return;
+ case INSTR_UNLOAD_INVALID:
+ /* the user might have already unloaded it */
+ return;
+ case INSTR_UNLOAD_DLERROR:
+ error_report("instrument: error unloading library: %s", dlerror());
+ break;
+ }
+#else
+ error_report("instrument: not available");
+#endif
+
+ exit(1);
+}
new file mode 100644
@@ -0,0 +1,49 @@
+/*
+ * Control instrumentation during program (de)initialization.
+ *
+ * Copyright (C) 2012-2017 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__CMDLINE_H
+#define INSTRUMENT__CMDLINE_H
+
+
+/**
+ * Definition of QEMU options describing instrumentation subsystem
+ * configuration.
+ */
+extern QemuOptsList qemu_instr_opts;
+
+/**
+ * instr_opt_parse:
+ * @optarg: A string argument of --instrument command line argument
+ *
+ * Initialize instrument subsystem.
+ */
+void instr_opt_parse(const char *optarg, char **path,
+ int *argc, const char ***argv);
+
+/**
+ * instr_init:
+ * @path: Path to dynamic trace instrumentation library.
+ * @argc: Number of arguments to the library's #qi_init routine.
+ * @argv: Arguments to the library's #qi_init routine.
+ *
+ * Load and initialize the given instrumentation library. Calls exit() if the
+ * library's initialization function returns a non-zero value.
+ *
+ * Installs instr_fini() as an atexit() callback.
+ */
+void instr_init(const char *path, int argc, const char **argv);
+
+/**
+ * instr_fini:
+ *
+ * Deinitialize and unload all instrumentation libraries.
+ */
+void instr_fini(void);
+
+#endif /* INSTRUMENT__CMDLINE_H */
new file mode 100644
@@ -0,0 +1,176 @@
+/*
+ * Interface for (un)loading instrumentation libraries.
+ *
+ * Copyright (C) 2012-2017 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 "qemu/osdep.h"
+#include "qemu-common.h"
+
+#include <dlfcn.h>
+#include "instrument/load.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+
+
+typedef int64_t InstrHandleID;
+
+typedef struct InstrHandle
+{
+ InstrHandleID id;
+ void *dlhandle;
+ QSLIST_ENTRY(InstrHandle) list;
+} InstrHandle;
+
+
+static InstrHandleID handle_last_id;
+static QSLIST_HEAD(, InstrHandle) handles = QSLIST_HEAD_INITIALIZER(handles);
+static QemuMutex instr_lock;
+
+
+static InstrHandle *handle_get(void)
+{
+ InstrHandle *res = g_malloc0(sizeof(InstrHandle));
+ res->id = handle_last_id++;
+ QSLIST_INSERT_HEAD(&handles, res, list);
+ return res;
+}
+
+static bool handle_put(InstrHandleID id)
+{
+ InstrHandle *prev = NULL;
+ InstrHandle *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 InstrHandle *handle_find(InstrHandleID id)
+{
+ InstrHandle *handle;
+ QSLIST_FOREACH(handle, &handles, list) {
+ if (handle->id == id) {
+ return handle;
+ }
+ }
+ return NULL;
+}
+
+InstrLoadError instr_load(const char * path, int argc, const char ** argv,
+ int64_t *handle_id)
+{
+ InstrLoadError res;
+ InstrHandle * handle;
+ int (*main_cb)(int, const char **);
+ int main_res;
+
+ qemu_rec_mutex_lock(&instr_lock);
+
+ *handle_id = -1;
+
+ 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.
+ */
+ res = INSTR_LOAD_TOO_MANY;
+ goto out;
+ }
+
+ handle = handle_get();
+ handle->dlhandle = dlopen(path, RTLD_NOW);
+ if (handle->dlhandle == NULL) {
+ res = INSTR_LOAD_DLERROR;
+ goto err;
+ }
+
+ main_cb = dlsym(handle->dlhandle, "main");
+ if (main_cb == NULL) {
+ res = INSTR_LOAD_DLERROR;
+ goto err;
+ }
+
+ main_res = main_cb(argc, argv);
+
+ if (main_res != 0) {
+ res = INSTR_LOAD_ERROR;
+ goto err;
+ }
+
+ *handle_id = handle->id;
+ res = INSTR_LOAD_OK;
+ goto out;
+
+err:
+ handle_put(handle->id);
+out:
+ qemu_rec_mutex_unlock(&instr_lock);
+ return res;
+}
+
+InstrUnloadError instr_unload(int64_t handle_id)
+{
+ InstrLoadError res;
+
+ qemu_rec_mutex_lock(&instr_lock);
+
+ InstrHandle *handle = handle_find(handle_id);
+ if (handle == NULL) {
+ res = INSTR_UNLOAD_INVALID;
+ goto out;
+ }
+
+ /* this should never fail */
+ if (dlclose(handle->dlhandle) < 0) {
+ res = INSTR_UNLOAD_DLERROR;
+ } else {
+ res = INSTR_UNLOAD_OK;
+ }
+ handle_put(handle->id);
+
+out:
+ qemu_rec_mutex_unlock(&instr_lock);
+ return res;
+}
+
+InstrUnloadError instr_unload_all(void)
+{
+ InstrUnloadError res = INSTR_UNLOAD_OK;
+
+ qemu_rec_mutex_lock(&instr_lock);
+ while (true) {
+ InstrHandle *handle = QSLIST_FIRST(&handles);
+ if (handle == NULL) {
+ break;
+ } else {
+ res = instr_unload(handle->id);
+ if (res != INSTR_UNLOAD_OK) {
+ break;
+ }
+ }
+ }
+ qemu_rec_mutex_unlock(&instr_lock);
+
+ return res;
+}
+
+static void __attribute__((constructor)) instr_lock_init(void)
+{
+ qemu_rec_mutex_init(&instr_lock);
+}
new file mode 100644
@@ -0,0 +1,83 @@
+/*
+ * Interface for (un)loading instrumentation libraries.
+ *
+ * Copyright (C) 2012-2017 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_LOAD_H
+#define INSTRUMENT_LOAD_H
+
+#include "qemu/osdep.h"
+
+#include "qemu/queue.h"
+#include "qemu/thread.h"
+
+
+/**
+ * InstrLoadError:
+ * @INSTR_LOAD_OK: Correctly loaded.
+ * @INSTR_LOAD_TOO_MANY: Tried to load too many instrumentation libraries.
+ * @INSTR_LOAD_ERROR: The library's main() function returned a non-zero value.
+ * @INSTR_LOAD_DLERROR: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_load().
+ */
+typedef enum {
+ INSTR_LOAD_OK,
+ INSTR_LOAD_TOO_MANY,
+ INSTR_LOAD_ERROR,
+ INSTR_LOAD_DLERROR,
+} InstrLoadError;
+
+/**
+ * InstrUnloadError:
+ * @INSTR_UNLOAD_OK: Correctly unloaded.
+ * @INSTR_UNLOAD_INVALID: Invalid handle.
+ * @INSTR_UNLOAD_DLERROR: Error with libdl (see dlerror).
+ *
+ * Error codes for instr_unload().
+ */
+typedef enum {
+ INSTR_UNLOAD_OK,
+ INSTR_UNLOAD_INVALID,
+ INSTR_UNLOAD_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.
+ * @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_unload_all:
+ *
+ * Unload all instrumentation libraries.
+ *
+ * Returns: Whether any library could not be unloaded.
+ */
+InstrUnloadError instr_unload_all(void);
+
+#endif /* INSTRUMENT_LOAD_H */
Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> --- MAINTAINERS | 1 Makefile.objs | 4 + configure | 2 + instrument/Makefile.objs | 4 + instrument/cmdline.c | 124 ++++++++++++++++++++++++++++++++ instrument/cmdline.h | 49 +++++++++++++ instrument/load.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ instrument/load.h | 83 ++++++++++++++++++++++ 8 files changed, 443 insertions(+) create mode 100644 instrument/Makefile.objs create mode 100644 instrument/cmdline.c create mode 100644 instrument/cmdline.h create mode 100644 instrument/load.c create mode 100644 instrument/load.h