diff --git a/.gitignore b/.gitignore
index 2a0ea2f..59acb2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ libcacard/trace/generated-tracers.c
 instrument/generated-tracers.h
 instrument/generated-tracers.c
 instrument/qemu-instr/events.h
+instrument/qemu-instr/events-list.h
 *-timestamp
 *-softmmu
 *-darwin-user
diff --git a/Makefile b/Makefile
index 8280273..7916152 100644
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,7 @@ ifdef CONFIG_TRACE_INSTRUMENT_DYNAMIC
 GENERATED_SOURCES += instrument/generated-tracers.c
 endif
 GENERATED_HEADERS += instrument/qemu-instr/events.h
+GENERATED_HEADERS += instrument/qemu-instr/events-list.h
 
 GENERATED_HEADERS += trace/generated-events.h
 GENERATED_SOURCES += trace/generated-events.c
diff --git a/configure b/configure
index d2e0b90..32142f9 100755
--- a/configure
+++ b/configure
@@ -301,6 +301,7 @@ QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS"
 QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
 QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
 QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
+QEMU_CFLAGS="-DBUILDING_QEMU $QEMU_CFLAGS"
 QEMU_INCLUDES="-I. -I\$(SRC_PATH) -I\$(SRC_PATH)/include"
 if test "$debug_info" = "yes"; then
     CFLAGS="-g $CFLAGS"
diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs
index 453330f..1438626 100644
--- a/instrument/Makefile.objs
+++ b/instrument/Makefile.objs
@@ -41,6 +41,15 @@ $(obj)/qemu-instr/events.h-timestamp: $(TRACE_EVENTS) $(BUILD_DIR)/config-host.m
 		< $< > $@,"  GEN   $(patsubst %-timestamp,%,$@)")
 	@cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
 
+$(obj)/qemu-instr/events-list.h: $(obj)/qemu-instr/events-list.h-timestamp
+$(obj)/qemu-instr/events-list.h-timestamp: $(TRACE_EVENTS) $(BUILD_DIR)/config-host.mak
+	$(call quiet-command,$(TRACETOOL) \
+		--format=api-events-h \
+		--backend=$(TRACE_INSTRUMENT_BACKEND) \
+		$(TRACETOOL_INSTR_STATIC) \
+		< $< > $@,"  GEN   $(patsubst %-timestamp,%,$@)")
+	@cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
 
 ######################################################################
 # User code (static instrumentation)
@@ -71,3 +80,7 @@ target-obj-y += control.o
 
 common-obj-$(CONFIG_SOFTMMU) += hmp.o
 common-obj-$(CONFIG_SOFTMMU) += qmp.o
+
+target-obj-y += api-control.o
+
+QEMU_CFLAGS += -I$(BUILD_DIR)/instrument -I$(SRC_PATH)/instrument
diff --git a/instrument/api-control.c b/instrument/api-control.c
new file mode 100644
index 0000000..e3aac49
--- /dev/null
+++ b/instrument/api-control.c
@@ -0,0 +1,14 @@
+/*
+ * Interface for controlling the state of events.
+ *
+ * Copyright (C) 2012-2013 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/qemu-instr/control.h"
+
+#if defined(QI_TYPE_DYNAMIC)
+#include "instrument/qemu-instr/control-internal.h"
+#endif
diff --git a/instrument/qemu-instr/control-internal.h b/instrument/qemu-instr/control-internal.h
new file mode 100644
index 0000000..6e419e7
--- /dev/null
+++ b/instrument/qemu-instr/control-internal.h
@@ -0,0 +1,53 @@
+/*
+ * Interface for controlling the state of events.
+ *
+ * Copyright (C) 2012-2013 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_INTERNAL_H
+#define QI__CONTROL_INTERNAL_H
+
+#include "trace/control.h"
+
+
+QI_IDEF QIEvent* qi_ctrl_event_id(QIEventID id)
+{
+    return (QIEvent*)trace_event_id(id);
+}
+
+QI_IDEF QIEvent* qi_ctrl_event_name(const char *name)
+{
+    return (QIEvent*)trace_event_name(name);
+}
+
+QI_IDEF QIEvent* qi_ctrl_event_pattern(const char *pat, QIEvent *ev)
+{
+    return (QIEvent*)trace_event_pattern(pat, (TraceEvent*)ev);
+}
+
+QI_IDEF bool qi_ctrl_event_is_pattern(const char *str)
+{
+    return trace_event_is_pattern(str);
+}
+
+QI_IDEF QIEventID qi_ctrl_event_count(void)
+{
+    return (QIEventID)trace_event_count();
+}
+
+
+
+QI_IDEF QIEventID qi_ctrl_event_get_id(QIEvent *ev)
+{
+    return (QIEventID)trace_event_get_id((TraceEvent*)ev);
+}
+
+QI_IDEF const char * qi_ctrl_event_get_name(QIEvent *ev)
+{
+    return trace_event_get_name((TraceEvent*)ev);
+}
+
+#endif  /* QI__CONTROL_INTERNAL_H */
diff --git a/instrument/qemu-instr/control.h b/instrument/qemu-instr/control.h
new file mode 100644
index 0000000..d3fadd1
--- /dev/null
+++ b/instrument/qemu-instr/control.h
@@ -0,0 +1,124 @@
+/*
+ * Interface for controlling the state of events.
+ *
+ * Copyright (C) 2012-2013 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 <qemu-instr/config.h>
+#include <qemu-instr/visibility-internal.h>
+#include <qemu-instr/events-list.h>
+
+
+/**
+ * SECTION:control
+ * @section_id: qi-control
+ * @title: QEMU instrumentation event control interface
+ */
+
+/**
+ * QIEvent:
+ *
+ * Opaque structure defining an instrumentation event.
+ */
+struct QIEvent;
+typedef struct QIEvent QIEvent;
+
+/**
+ * QIEventID:
+ *
+ * Unique instrumentation event identifier.
+ *
+ * These are named as 'QI_EVENT_${EVENT}'.
+ *
+ * See also: qemu-instr/events-list.h
+ */
+
+/**
+ * qi_ctrl_event_id:
+ * @id: Event identifier.
+ *
+ * Get an event by its identifier.
+ *
+ * This routine has a constant cost, as opposed to qi_ctrl_event_name() and
+ * qi_ctrl_event_pattern().
+ *
+ * Pre-conditions: @id is valid.
+ *
+ * Returns: Pointer to selected #QIEvent.
+ *
+ */
+QI_IDECL QIEvent* qi_ctrl_event_id(QIEventID id);
+
+/**
+ * qi_ctrl_event_name:
+ * @id: Event name.
+ *
+ * Search an event by its name.
+ *
+ * Returns: Pointer to #QIEvent or #NULL if not found.
+ */
+QI_IDECL QIEvent* qi_ctrl_event_name(const char *name);
+
+/**
+ * qi_ctrl_event_pattern:
+ * @pat: Event name pattern.
+ * @ev: Event to start searching from (not included).
+ *
+ * Iteratively get all events with a given name pattern.
+ *
+ * Returns: pointer to #TraceEvent or #NULL if not found.
+ */
+QI_IDECL QIEvent* qi_ctrl_event_pattern(const char *pat, QIEvent *ev);
+
+/**
+ * qi_ctrl_event_is_pattern:
+ *
+ * Whether the given string is an event name pattern.
+ */
+QI_IDECL bool qi_ctrl_event_is_pattern(const char *str);
+
+/**
+ * qi_ctrl_event_count:
+ *
+ * Return the number of events.
+ */
+QI_IDECL QIEventID qi_ctrl_event_count(void);
+
+
+
+/**
+ * qi_ctrl_event_get_id:
+ *
+ * Get the identifier of an event.
+ */
+QI_IDECL QIEventID qi_ctrl_event_get_id(QIEvent *ev);
+
+/**
+ * qi_ctrl_event_get_name:
+ *
+ * Get the name of an event.
+ */
+QI_IDECL const char * qi_ctrl_event_get_name(QIEvent *ev);
+
+
+#if !defined(QI_TYPE_DYNAMIC)
+#include "instrument/qemu-instr/control-internal.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* QI__CONTROL_H */
diff --git a/instrument/qemu-instr/visibility-internal.h b/instrument/qemu-instr/visibility-internal.h
new file mode 100644
index 0000000..f5a66da
--- /dev/null
+++ b/instrument/qemu-instr/visibility-internal.h
@@ -0,0 +1,94 @@
+/*
+ * Macros for symbol visibility.
+ *
+ * Copyright (C) 2012-2013 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 of QEMU.
+ */
+
+#ifndef QI__VISIBILITY_INTERNAL_H
+#define QI__VISIBILITY_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <qemu-instr/config.h>
+
+
+/**
+ * SECTION:visibility
+ * @section_id: qi-visibility
+ * @title: Symbol visibility
+ *
+ * This code is taken from http://gcc.gnu.org/wiki/Visibility.
+ */
+
+/**
+ * QI_VPUBLIC:
+ *
+ * Make an element public to third-party code.
+ */
+
+/**
+ * QI_VLOCAL:
+ *
+ * Make an element not visible to third-party code.
+ */
+
+#if defined _WIN32 || defined __CYGWIN__
+  #ifdef BUILDING_QEMU
+    #ifdef __GNUC__
+      #define QI_VPUBLIC __attribute__ ((dllexport))
+    #else
+      #define QI_VPUBLIC __declspec(dllexport)
+    #endif
+  #else
+    #ifdef __GNUC__
+      #define QI_VPUBLIC __attribute__ ((dllimport))
+    #else
+      #define QI_VPUBLIC __declspec(dllimport)
+    #endif
+  #endif
+  #define QI_VLOCAL
+#else
+  #if __GNUC__ >= 4
+    #define QI_VPUBLIC __attribute__ ((visibility ("default")))
+    #define QI_VLOCAL  __attribute__ ((visibility ("hidden")))
+  #else
+    #define QI_VPUBLIC
+    #define QI_VLOCAL
+  #endif
+#endif
+
+/**
+ * QI_IDECL:
+ *
+ * Instrumentation-type dependant declaration attribute for inlined routines.
+ */
+
+#if defined(QI_TYPE_DYNAMIC)
+#define QI_IDECL QI_VPUBLIC
+#else
+#define QI_IDECL static
+#endif
+
+/**
+ * QI_IDEF:
+ *
+ * Instrumentation-type dependant definition attribute for inlined routines.
+ */
+
+#if defined(QI_TYPE_DYNAMIC)
+#define QI_IDEF
+#else
+#define QI_IDEF static inline
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* QI__VISIBILITY_INTERNAL_H */
diff --git a/scripts/tracetool/backend/instr_dynamic.py b/scripts/tracetool/backend/instr_dynamic.py
index 12d0503..013893a 100644
--- a/scripts/tracetool/backend/instr_dynamic.py
+++ b/scripts/tracetool/backend/instr_dynamic.py
@@ -88,3 +88,6 @@ def api_c(events):
             args = e.args,
             argnames = ", ".join(e.args.names()),
             )
+
+def api_events_h(events):
+    pass
diff --git a/scripts/tracetool/backend/instr_none.py b/scripts/tracetool/backend/instr_none.py
index a1327c0..2c2fe68 100644
--- a/scripts/tracetool/backend/instr_none.py
+++ b/scripts/tracetool/backend/instr_none.py
@@ -39,3 +39,6 @@ def api_h(events):
             qemu_backend = e.api(e.QEMU_TRACE_BACKEND),
             argnames = ", ".join(e.args.names()),
             )
+
+def api_events_h(events):
+    pass
diff --git a/scripts/tracetool/backend/instr_static.py b/scripts/tracetool/backend/instr_static.py
index ecebbda..df99656 100644
--- a/scripts/tracetool/backend/instr_static.py
+++ b/scripts/tracetool/backend/instr_static.py
@@ -77,3 +77,6 @@ def api_h(events):
             argnames = ", ".join(e.args.names()),
             upper_name = e.name.upper(),
             )
+
+def api_events_h(events):
+    pass
diff --git a/scripts/tracetool/format/api_events_h.py b/scripts/tracetool/format/api_events_h.py
new file mode 100644
index 0000000..95a4e93
--- /dev/null
+++ b/scripts/tracetool/format/api_events_h.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Generate .h with event description for trace instrumentation clients.
+"""
+
+__author__     = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__  = "Copyright 2012-2013, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha@linux.vnet.ibm.com"
+
+
+from tracetool import out
+
+
+def begin(events):
+    out('/* This file is autogenerated by tracetool, do not edit. */',
+        '',
+        '#ifndef QI__EVENTS_LIST_H',
+        '#define QI__EVENTS_LIST_H',
+        '',
+        '#ifdef __cplusplus',
+        'extern "C" {',
+        '#endif',
+        '',
+        )
+
+    # event identifiers
+    out('typedef enum {')
+
+    for e in events:
+        out('    QI_EVENT_%s,' % e.name.upper())
+
+    out('    QI_EVENT_COUNT',
+        '} QIEventID;',
+        '',
+        )
+
+    # static state
+    for e in events:
+        if 'disable' in e.properties:
+            enabled = 0
+        else:
+            enabled = 1
+        out('#define QI_EVENT_%s_ENABLED %d' % (e.name.upper(), enabled))
+
+    out('#ifdef __cplusplus',
+        '}',
+        '#endif',
+        '',
+        '#endif  /* QI__EVENTS_LIST_H */',
+        '',
+        )
