diff --git a/Makefile b/Makefile
index c8be326790..ee54b7e17a 100644
--- a/Makefile
+++ b/Makefile
@@ -688,6 +688,7 @@ ifeq ($(TRACE_INSTRUMENT_BACKEND),instr-dynamic)
 	$(INSTALL_DIR) "$(DESTDIR)$(includedir)/qemu-instr"
 	$(INSTALL_DATA) $(SRC_PATH)/instrument/qemu-instr/types.h "$(DESTDIR)$(includedir)/qemu-instr/"
 	$(INSTALL_DATA) $(SRC_PATH)/instrument/qemu-instr/visibility-internal.h "$(DESTDIR)$(includedir)/qemu-instr/"
+	$(INSTALL_DATA) $(SRC_PATH)/instrument/qemu-instr/control.h "$(DESTDIR)$(includedir)/qemu-instr/"
 	$(INSTALL_DATA) $(BUILD_DIR)/instrument/qemu-instr/events.h "$(DESTDIR)$(includedir)/qemu-instr/"
 endif
 
diff --git a/configure b/configure
index ca61665874..320309e0c0 100755
--- a/configure
+++ b/configure
@@ -6019,10 +6019,14 @@ QEMU_INCLUDES="-I\$(SRC_PATH)/tcg $QEMU_INCLUDES"
 echo "TRACE_INSTRUMENT_BACKEND=instr-$trace_instrument_backend" >> $config_host_mak
 if test "$trace_instrument" = "yes"; then
     echo "CONFIG_INSTRUMENT=y" >> $config_host_mak
+    libs_qga="-ldl $libs_qga"
+    LDFLAGS="-rdynamic $LDFLAGS"
 fi
 if test -n "$instrument_events"; then
     echo "CONFIG_INSTRUMENT_EVENTS=$instrument_events" >> $config_host_mak
 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 c2289aba85..244936aa8c 100644
--- a/instrument/Makefile.objs
+++ b/instrument/Makefile.objs
@@ -38,3 +38,8 @@ $(obj)/qemu-instr/events.h-timestamp: $(BUILD_DIR)/trace-events-all $(BUILD_DIR)
 		--backend=$(TRACE_INSTRUMENT_BACKEND) \
 		$(TRACETOOL_INSTR_STATIC) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
+
+######################################################################
+# Control code
+
+target-obj-y += control.o
diff --git a/instrument/control.c b/instrument/control.c
new file mode 100644
index 0000000000..309228f15d
--- /dev/null
+++ b/instrument/control.c
@@ -0,0 +1,316 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * 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/control.h"
+#include "qemu/thread.h"
+#include "qemu-instr/control.h"
+#include "qemu/error-report.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 QemuMutex instr_lock;
+
+
+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;
+    }
+}
+
+#if defined(CONFIG_INSTRUMENT)
+static Handle *handle_find(HandleID id)
+{
+    Handle *handle;
+    QSLIST_FOREACH(handle, &handles, list) {
+        if (handle->id == id) {
+            return handle;
+        }
+    }
+    return NULL;
+}
+#endif
+
+static bool instr_available(void)
+{
+#if defined(CONFIG_INSTRUMENT)
+    return true;
+#else
+    return false;
+#endif
+}
+
+InstrLoadError instr_load(const char * path, int argc, const char ** argv,
+                          int64_t *handle_id)
+{
+    InstrLoadError res;
+
+    qemu_rec_mutex_lock(&instr_lock);
+
+    *handle_id = -1;
+    if (!instr_available()) {
+        res = INSTR_LOAD_UNAVAILABLE;
+        goto out;
+    }
+
+    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 like
+         *      "too many open libraries".
+         */
+        res = INSTR_LOAD_UNAVAILABLE;
+        goto out;
+    }
+
+    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;
+    res = INSTR_LOAD_OK;
+    goto out;
+
+err:
+    handle_put(handle->id);
+    res = INSTR_LOAD_ERROR;
+out:
+    qemu_rec_mutex_unlock(&instr_lock);
+    return res;
+}
+
+static inline bool instr_event_is_instrument(TraceEvent *ev)
+{
+#if defined(CONFIG_INSTRUMENT)
+    return ev->is_instr;
+#else
+    return false;
+#endif
+}
+
+InstrUnloadError instr_unload(int64_t handle_id)
+{
+    InstrLoadError res;
+
+    qemu_rec_mutex_lock(&instr_lock);
+
+    if (!instr_available()) {
+        res = INSTR_UNLOAD_UNAVAILABLE;
+        goto out;
+    }
+
+#if defined(CONFIG_INSTRUMENT)
+    Handle *handle = handle_find(handle_id);
+    if (handle == NULL) {
+        res = INSTR_UNLOAD_INVALID;
+        goto out;
+    }
+
+    handle->fini();
+
+    TraceEventIter iter;
+    TraceEvent *ev = NULL;
+    trace_event_iter_init(&iter, NULL);
+    while ((ev = trace_event_iter_next(&iter)) != NULL) {
+        if (instr_event_is_instrument(ev)) {
+            *ev->instr_cb = ev->instr_cb_default;
+        }
+    }
+
+    /* this should never fail */
+    if (dlclose(handle->dlhandle) < 0) {
+        res = INSTR_UNLOAD_ERROR;
+    } else {
+        res = INSTR_UNLOAD_OK;
+    }
+    handle_put(handle->id);
+#endif
+
+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) {
+        Handle *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;
+}
+
+void qi_ctrl_event_set(QIEvent *ev, void *cb)
+{
+    TraceEvent *tev = (TraceEvent *)ev;
+
+    if (unlikely(tev == NULL)) {
+        error_report("qi_ctrl_event_set: event is NULL");
+        return;
+    }
+
+#if defined(CONFIG_INSTRUMENT)
+    if (unlikely(!tev->is_instr)) {
+        error_report("qi_ctrl_event_set: event is not instrumentable");
+        return;
+    }
+
+    if (cb == NULL) {
+        *tev->instr_cb = tev->instr_cb_default;
+    } else {
+        *tev->instr_cb = cb;
+    }
+#else
+    assert(false);
+#endif  /* defined(CONFIG_INSTRUMENT) */
+}
+
+bool qi_trace_event_get_state_static(QIEvent *ev)
+{
+    if (unlikely(ev == NULL)) {
+        error_report("qi_trace_event_get_state_static: event is NULL");
+        return false;
+    }
+    return trace_event_get_state_static((TraceEvent *)ev);
+}
+
+bool qi_trace_event_get_state_dynamic(QIEvent *ev)
+{
+    if (unlikely(ev == NULL)) {
+        error_report("qi_trace_event_get_state_dynamic: event is NULL");
+        return false;
+    }
+    return trace_event_get_state_dynamic((TraceEvent *)ev);
+}
+
+bool qi_trace_event_get_vcpu_state_dynamic(QICPU *vcpu, QIEvent *ev)
+{
+    TraceEvent *tev = (TraceEvent *)ev;
+
+    if (unlikely(vcpu == NULL)) {
+        error_report("qi_trace_event_get_vcpu_state_dynamic: vcpu is NULL");
+        return false;
+    }
+    if (unlikely(tev == NULL)) {
+        error_report("qi_trace_event_get_vcpu_state_dynamic: event is NULL");
+        return false;
+    }
+    if (unlikely(!trace_event_is_vcpu(tev))) {
+        error_report("qi_trace_event_get_vcpu_state_dynamic: event does not have 'vcpu' property");
+        return false;
+    }
+    return trace_event_get_vcpu_state_dynamic((CPUState *)vcpu, tev);
+}
+
+void qi_trace_event_set_state_dynamic(QIEvent *ev, bool state)
+{
+    TraceEvent *tev = (TraceEvent *)ev;
+
+    if (unlikely(tev == NULL)) {
+        error_report("qi_trace_event_set_state_dynamic: event is NULL");
+        return;
+    }
+    if (unlikely(!trace_event_get_state_static(tev))) {
+        error_report("qi_trace_event_set_state_dynamic: event is statically disabled");
+        return;
+    }
+    trace_event_set_state_dynamic(tev, state);
+}
+
+void qi_trace_event_set_vcpu_state_dynamic(QICPU *vcpu, QIEvent *ev, bool state)
+{
+    TraceEvent *tev = (TraceEvent *)ev;
+
+    if (unlikely(vcpu == NULL)) {
+        error_report("qi_trace_event_set_vcpu_state_dynamic: vcpu is NULL");
+        return;
+    }
+    if (unlikely(tev == NULL)) {
+        error_report("qi_trace_event_set_vcpu_state_dynamic: event is NULL");
+        return;
+    }
+    if (unlikely(!trace_event_is_vcpu(tev))) {
+        error_report("qi_trace_event_set_vcpu_state_dynamic: event does not have 'vcpu' property");
+        return;
+    }
+    if (unlikely(!trace_event_get_state_static(tev))) {
+        error_report("qi_trace_event_set_vcpu_state_dynamic: event is statically disabled");
+        return;
+    }
+    trace_event_set_vcpu_state_dynamic((CPUState *)vcpu, tev, state);
+}
+
+static void __attribute__((constructor)) instr_lock_init(void)
+{
+    qemu_rec_mutex_init(&instr_lock);
+}
diff --git a/instrument/control.h b/instrument/control.h
new file mode 100644
index 0000000000..a83a350faf
--- /dev/null
+++ b/instrument/control.h
@@ -0,0 +1,86 @@
+/*
+ * Interface for controlling dynamic trace instrumentation.
+ *
+ * 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__CONTROL_H
+#define INSTRUMENT__CONTROL_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "qapi-types.h"
+#include "trace/control.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().
+ *
+ * Warning: Keep in sync with #InstrLoadCode
+ */
+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().
+ *
+ * Warning: Keep in sync with #InstrUnloadCode
+ */
+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_unload_all:
+ *
+ * Unload all instrumentation libraries.
+ *
+ * Returns: Whether any library could not be unloaded.
+ */
+InstrUnloadError instr_unload_all(void);
+
+#endif  /* INSTRUMENT__CONTROL_H */
diff --git a/instrument/qemu-instr/control.h b/instrument/qemu-instr/control.h
new file mode 100644
index 0000000000..83f20d42f2
--- /dev/null
+++ b/instrument/qemu-instr/control.h
@@ -0,0 +1,128 @@
+/*
+ * Interface for controlling the state of events.
+ *
+ * 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 QI__CONTROL_H
+#define QI__CONTROL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <qemu-instr/types.h>
+#include <qemu-instr/visibility-internal.h>
+
+
+/**
+ * SECTION:control
+ * @section_id: qi-control
+ * @title: Event control API for QEMU event instrumentation
+ */
+
+/**
+ * qi_ctrl_event_set:
+ * @ev: Event descriptor.
+ * @cb: Pointer to instrumentation callback.
+ *
+ * Set the instrumentation callback for the given event.
+ *
+ * @cb can also be set to #NULL to restore the default callback.
+ */
+QI_VPUBLIC void qi_ctrl_event_set(QIEvent *ev, void *cb);
+
+/**
+ * qi_trace_event_get_state:
+ * @ev: Event identifier name.
+ *
+ * Get the tracing state of an event (both static and dynamic).
+ *
+ * If the event has the disabled property, the check will have no performance
+ * impact.
+ */
+#define qi_trace_event_get_state(ev)                                    \
+    ((ev ##_ENABLED) && qi_trace_event_get_state_dynamic(ev))
+
+/**
+ * qi_trace_event_get_vcpu_state:
+ * @vcpu: Target vCPU.
+ * @ev: Event identifier name.
+ *
+ * Get the tracing state of an event (both static and dynamic) for the given
+ * vCPU.
+ *
+ * If the event has the disabled property, the check will have no performance
+ * impact.
+ */
+#define qi_trace_event_get_vcpu_state(vcpu, ev)                         \
+    ((ev ##_ENABLED) &&                                                 \
+     qi_trace_event_get_vcpu_state_dynamic(vcpu, ev))
+
+/**
+ * qi_trace_event_get_state_static:
+ * @ev: Event identifier.
+ *
+ * Get the static tracing state of an event.
+ *
+ * Use the define 'QI_EVENT_${EVENT}_ENABLED' for compile-time checks (it will
+ * be set to 1 or 0 according to the presence of the disabled property).
+ */
+QI_VPUBLIC bool qi_trace_event_get_state_static(QIEvent *ev);
+
+/**
+ * qi_trace_event_get_state_dynamic:
+ * @ev: Event identifier.
+ *
+ * Get the dynamic tracing state of an event.
+ */
+QI_VPUBLIC bool qi_trace_event_get_state_dynamic(QIEvent *ev);
+
+/**
+ * qi_trace_event_get_vcpu_state_dynamic:
+ * @vcpu: Target vCPU.
+ * @ev: Event identifier.
+ *
+ * Get the dynamic tracing state of an event for the given vCPU.
+ */
+QI_VPUBLIC bool qi_trace_event_get_vcpu_state_dynamic(QICPU *vcpu,
+                                                      QIEvent *ev);
+
+/**
+ * qi_trace_event_set_state_dynamic:
+ * @ev: Event identifier.
+ * @state: Target tracing state.
+ *
+ * Set the dynamic tracing state of an event.
+ *
+ * Pre-condition: qi_trace_event_get_state_static(ev) == true
+ */
+QI_VPUBLIC void qi_trace_event_set_state_dynamic(QIEvent *ev, bool state);
+
+/**
+ * qi_trace_event_set_vcpu_state_dynamic:
+ * @vcpu: Target vCPU.
+ * @ev: Event identifier.
+ * @state: Target tracing state.
+ *
+ * Set the dynamic tracing state of an event for the given vCPU.
+ *
+ * Pre-condition: qi_trace_event_get_state_static(ev) == true
+ *
+ * Note: Changes for execution-time events with the 'tcg' property will not be
+ *       propagated until the next TB is executed (iff executing in TCG mode).
+ */
+QI_VPUBLIC void qi_trace_event_set_vcpu_state_dynamic(QICPU *vcpu,
+                                                      QIEvent *ev, bool state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* QI__CONTROL_H */
diff --git a/instrument/qemu-instr/types.h b/instrument/qemu-instr/types.h
index d3d26bbf73..58a84b6d01 100644
--- a/instrument/qemu-instr/types.h
+++ b/instrument/qemu-instr/types.h
@@ -25,6 +25,14 @@ extern "C" {
  */
 
 /**
+ * QIEvent:
+ *
+ * Opaque structure defining an instrumentation event.
+ */
+struct QIEvent;
+typedef struct QIEvent QIEvent;
+
+/**
  * QICPU:
  *
  * Opaque guest CPU pointer.
diff --git a/scripts/tracetool/backend/instr_dynamic.py b/scripts/tracetool/backend/instr_dynamic.py
index 6f9f8e49fb..2efd4a7eb1 100644
--- a/scripts/tracetool/backend/instr_dynamic.py
+++ b/scripts/tracetool/backend/instr_dynamic.py
@@ -90,7 +90,8 @@ def generate_instr_tcg_c(event, group):
     qargs = ["(%s)%s" % (arg[0], arg[1])
              for arg in iargs]
 
-    out('QI_VPUBLIC void %(nop)s(%(api_args)s)',
+    out('QIEvent *%(qevent)s = (QIEvent *)&%(tevent)s;',
+        'QI_VPUBLIC void %(nop)s(%(api_args)s)',
         '{',
         '}',
         'QI_VPUBLIC void %(trace)s(%(api_args)s)',
@@ -99,7 +100,10 @@ def generate_instr_tcg_c(event, group):
         '}',
         'QI_VPUBLIC void %(gen)s(%(api_args)s)',
         '{',
+        # does not exist when event is disabled
+        '#if %(teventu)s_ENABLED',
         '     %(b_gen)s(%(qgargs)s);',
+        '#endif',
         '}',
         'QI_VPUBLIC void %(trace_gen)s(%(api_args)s)',
         '{',
@@ -107,6 +111,9 @@ def generate_instr_tcg_c(event, group):
         '}',
         'void *%(qi_cb)s_cb = %(trace_gen)s;',
         '',
+        qevent=event.api(event.QI_TRACE_INSTRUMENT).upper(),
+        tevent=event.api(event.QEMU_EVENT),
+        teventu=event.api(event.QEMU_TRACE).upper(),
         api_args=api_args,
         nop=event.api(event.QI_TRACE_NOP),
         trace=event.api(event.QI_TRACE_TRACE),
@@ -133,7 +140,8 @@ def generate_instr_c(event, group):
     args = event.args.transform(TCG_2_QI_TCG)
     qargs = ["(%s)%s" % (arg[0], arg[1])
              for arg in event.args]
-    out('QI_VPUBLIC void %(nop)s(%(args)s)',
+    out('QIEvent *%(qevent)s = (QIEvent *)&%(tevent)s;',
+        'QI_VPUBLIC void %(nop)s(%(args)s)',
         '{',
         '}',
         'QI_VPUBLIC void %(trace)s(%(args)s)',
@@ -142,6 +150,8 @@ def generate_instr_c(event, group):
         '}',
         'void *%(qi_cb)s_cb = %(qi_trace)s;',
         '',
+        qevent=event.api(event.QI_TRACE_INSTRUMENT).upper(),
+        tevent=event.api(event.QEMU_EVENT),
         args=args,
         nop=event.api(event.QI_TRACE_NOP),
         trace=event.api(event.QI_TRACE_TRACE),
diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py
index 833c05a022..0b8bb98d24 100644
--- a/scripts/tracetool/format/c.py
+++ b/scripts/tracetool/format/c.py
@@ -6,7 +6,7 @@ trace/generated-tracers.c
 """
 
 __author__     = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__  = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__  = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
 __license__    = "GPL version 2 or (at your option) any later version"
 
 __maintainer__ = "Stefan Hajnoczi"
@@ -39,18 +39,49 @@ def generate(events, backend, group):
             vcpu_id = 0
         else:
             vcpu_id = "TRACE_VCPU_EVENT_NONE"
+
+        if "instrument" in e.properties:
+            is_instr = "true"
+            out('#if defined(CONFIG_INSTRUMENT)')
+            if "tcg-trans" in e.properties:
+                instr_cb = e.original.api(e.QI_TRACE_INSTRUMENT_TCG) + "_cb"
+                instr_cb_default = e.api(e.QI_TRACE_TRACEGEN)
+                out('static void *_tmp__%(name1)s;',
+                    'extern void *%(name1)s __attribute__((weak, alias("_tmp__%(name1)s")));',
+                    name1=instr_cb)
+            else:
+                instr_cb = e.api(e.QI_TRACE_INSTRUMENT) + "_cb"
+                instr_cb_default = e.api(e.QI_TRACE_TRACE)
+            out('static void _tmp__%(name2)s(void) {};',
+                'void %(name2)s(void) __attribute__((weak, alias("_tmp__%(name2)s")));',
+                '#endif',
+                name2=instr_cb_default)
+            instr_cb = "&" + instr_cb
+        else:
+            is_instr = "false"
+            instr_cb = "NULL"
+            instr_cb_default = "NULL"
+
         out('TraceEvent %(event)s = {',
             '    .id = 0,',
             '    .vcpu_id = %(vcpu_id)s,',
             '    .name = \"%(name)s\",',
             '    .sstate = %(sstate)s,',
-            '    .dstate = &%(dstate)s ',
+            '    .dstate = &%(dstate)s,',
+            '#if defined(CONFIG_INSTRUMENT)',
+            '    .is_instr = %(is_instr)s,',
+            '    .instr_cb = %(instr_cb)s,',
+            '    .instr_cb_default = %(instr_cb_default)s,',
+            '#endif',
             '};',
             event = e.api(e.QEMU_EVENT),
             vcpu_id = vcpu_id,
             name = e.name,
             sstate = "TRACE_%s_ENABLED" % e.name.upper(),
-            dstate = e.api(e.QEMU_DSTATE))
+            dstate = e.api(e.QEMU_DSTATE),
+            is_instr=is_instr,
+            instr_cb=instr_cb,
+            instr_cb_default=instr_cb_default)
 
     out('TraceEvent *%(group)s_trace_events[] = {',
         group = group.lower())
diff --git a/scripts/tracetool/format/instr_api_h.py b/scripts/tracetool/format/instr_api_h.py
index d85f64df45..812a5d44ec 100644
--- a/scripts/tracetool/format/instr_api_h.py
+++ b/scripts/tracetool/format/instr_api_h.py
@@ -40,13 +40,24 @@ def generate(events, backend, group):
         '')
 
     for e in events:
+        if "disable" in e.properties:
+            enabled = 0
+        else:
+            enabled = 1
+
         if "tcg-trans" in e.properties:
             args = tracetool.vcpu.transform_args("tcg_h", e.original)
             args = args.transform(TCG_2_QI_TCG)
         else:
             args = e.args.transform(TCG_2_QI_TCG)
-        out('QI_VPUBLIC void %(nop)s(%(args)s);',
+
+        out('#define %(qeventu)s_ENABLED %(enabled)d',
+            'extern QI_VPUBLIC QIEvent *%(event)s;',
+            'QI_VPUBLIC void %(nop)s(%(args)s);',
             'QI_VPUBLIC void %(trace)s(%(args)s);',
+            qeventu=e.api(e.QI_TRACE_INSTRUMENT).upper(),
+            enabled=enabled,
+            event=e.api(e.QI_TRACE_INSTRUMENT).upper(),
             args=args,
             nop=e.api(e.QI_TRACE_NOP),
             trace=e.api(e.QI_TRACE_TRACE))
diff --git a/trace/event-internal.h b/trace/event-internal.h
index f63500b37e..b12cc110b6 100644
--- a/trace/event-internal.h
+++ b/trace/event-internal.h
@@ -1,7 +1,7 @@
 /*
  * Interface for configuring and controlling the state of tracing events.
  *
- * Copyright (C) 2012-2016 Lluís Vilanova <vilanova@ac.upc.edu>
+ * 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.
@@ -23,6 +23,9 @@
  * @name: Event name.
  * @sstate: Static tracing state.
  * @dstate: Dynamic tracing state
+ * @is_instr: Whether the event is instrumentable.
+ * @instr_cb: Current instrumentation callback.
+ * @instr_cb_default: Default instrumentation callback.
  *
  * Interpretation of @dstate depends on whether the event has the 'vcpu'
  *  property:
@@ -37,6 +40,11 @@ typedef struct TraceEvent {
     const char * name;
     const bool sstate;
     uint16_t *dstate;
+#if defined(CONFIG_INSTRUMENT)
+    bool is_instr;
+    void **instr_cb;
+    void *instr_cb_default;
+#endif
 } TraceEvent;
 
 void trace_event_set_state_dynamic_init(TraceEvent *ev, bool state);
