@@ -1491,6 +1491,7 @@ M: Lluís Vilanova <vilanova@ac.upc.edu>
M: Stefan Hajnoczi <stefanha@redhat.com>
S: Maintained
F: docs/instrument.txt
+F: instrument/
TPM
S: Orphan
@@ -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
@@ -6025,6 +6025,9 @@ fi
echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
if test "$instrument" = "yes"; then
+ LDFLAGS="-rdynamic $LDFLAGS" # limit symbols available to clients
+ QEMU_CFLAGS="-fvisibility=hidden $QEMU_CFLAGS"
+ LIBS="-ldl $LIBS"
echo "CONFIG_INSTRUMENT=y" >> $config_host_mak
fi
new file mode 100644
@@ -0,0 +1,4 @@
+# -*- mode: makefile -*-
+
+target-obj-$(CONFIG_INSTRUMENT) += cmdline.o
+target-obj-$(CONFIG_INSTRUMENT) += load.o
new file mode 100644
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+ if (path == NULL) {
+ return;
+ }
+
+ if (atexit(instr_fini) != 0) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+
+ const char *id = "cmdline";
+ err = instr_load(path, argc, argv, &id);
+ switch (err) {
+ case INSTR_LOAD_OK:
+ error_report("instrument: loaded library with ID '%s'", id);
+ return;
+ case INSTR_LOAD_TOO_MANY:
+ error_report("instrument: tried to load too many libraries");
+ break;
+ case INSTR_LOAD_ID_EXISTS:
+ g_assert_not_reached();
+ 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,51 @@
+/*
+ * 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
+
+#include "qemu/typedefs.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,166 @@
+/*
+ * 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 struct InstrHandle {
+ char *id;
+ void *dlhandle;
+ QLIST_ENTRY(InstrHandle) list;
+} InstrHandle;
+
+
+static unsigned int handle_auto_id;
+static QLIST_HEAD(, InstrHandle) handles = QLIST_HEAD_INITIALIZER(handles);
+static QemuMutex instr_lock;
+
+
+static InstrHandle *handle_new(const char **id)
+{
+ /* instr_lock is locked */
+ InstrHandle *res = g_malloc0(sizeof(InstrHandle));
+ if (!*id) {
+ *id = g_strdup_printf("lib%d", handle_auto_id);
+ handle_auto_id++;
+ }
+ res->id = g_strdup(*id);
+ QLIST_INSERT_HEAD(&handles, res, list);
+ return res;
+}
+
+static void handle_destroy(InstrHandle *handle)
+{
+ /* instr_lock is locked */
+ QLIST_REMOVE(handle, list);
+ g_free(handle->id);
+ g_free(handle);
+}
+
+static InstrHandle *handle_find(const char *id)
+{
+ /* instr_lock is locked */
+ InstrHandle *handle;
+ QLIST_FOREACH(handle, &handles, list) {
+ if (strcmp(handle->id, id) == 0) {
+ return handle;
+ }
+ }
+ return NULL;
+}
+
+InstrLoadError instr_load(const char *path, int argc, const char **argv,
+ const char **id)
+{
+ InstrLoadError res;
+ InstrHandle *handle;
+ int (*main_cb)(int, const char **);
+ int main_res;
+
+ qemu_rec_mutex_lock(&instr_lock);
+
+ if (*id && handle_find(*id)) {
+ res = INSTR_LOAD_ID_EXISTS;
+ goto out;
+ }
+
+ if (!QLIST_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_new(id);
+ 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;
+ }
+
+ res = INSTR_LOAD_OK;
+ goto out;
+
+err:
+ handle_destroy(handle);
+out:
+ qemu_rec_mutex_unlock(&instr_lock);
+ return res;
+}
+
+InstrUnloadError instr_unload(const char *id)
+{
+ InstrUnloadError res;
+
+ qemu_rec_mutex_lock(&instr_lock);
+
+ InstrHandle *handle = handle_find(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_destroy(handle);
+
+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 = QLIST_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,88 @@
+/*
+ * 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 "qapi-types.h"
+#include "qemu/queue.h"
+#include "qemu/thread.h"
+
+
+/**
+ * InstrLoadError:
+ * @INSTR_LOAD_OK: Correctly loaded.
+ * @INSTR_LOAD_ID_EXISTS: Tried to load an instrumentation libraries with an
+ * existing ID.
+ * @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_ID_EXISTS,
+ 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.
+ * @id: Instrumentation library id.
+ *
+ * Load a dynamic trace instrumentation library.
+ *
+ * Returns: Whether the library could be loaded.
+ */
+InstrLoadError instr_load(const char *path, int argc, const char **argv,
+ const char **id);
+
+/**
+ * instr_unload:
+ * @id: Instrumentation library id passed to instr_load().
+ *
+ * Unload the given instrumentation library.
+ *
+ * Returns: Whether the library could be unloaded.
+ */
+InstrUnloadError instr_unload(const char *id);
+
+/**
+ * instr_unload_all:
+ *
+ * Unload all instrumentation libraries.
+ *
+ * Returns: Whether any library could not be unloaded.
+ */
+InstrUnloadError instr_unload_all(void);
+
+#endif /* INSTRUMENT_LOAD_H */
@@ -13,6 +13,7 @@ stub-obj-y += error-printf.o
stub-obj-y += fdset.o
stub-obj-y += gdbstub.o
stub-obj-y += get-vm-name.o
+stub-obj-y += instrument.o
stub-obj-y += iothread.o
stub-obj-y += iothread-lock.o
stub-obj-y += is-daemonized.o
new file mode 100644
@@ -0,0 +1,18 @@
+/*
+ * Instrumentation placeholders.
+ *
+ * Copyright (C) 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 "instrument/cmdline.h"
+
+
+void instr_init(const char *path, int argc, const char **argv)
+{
+}
+void instr_fini(void)
+{
+}
Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> --- MAINTAINERS | 1 Makefile.objs | 4 + configure | 3 + instrument/Makefile.objs | 4 + instrument/cmdline.c | 128 +++++++++++++++++++++++++++++++++++ instrument/cmdline.h | 51 ++++++++++++++ instrument/load.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++ instrument/load.h | 88 ++++++++++++++++++++++++ stubs/Makefile.objs | 1 stubs/instrument.c | 18 +++++ 10 files changed, 464 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 create mode 100644 stubs/instrument.c