diff --git a/.gitignore b/.gitignore
index 068dc1c1ae..ee2768cb05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,9 @@
 /config.status
 /config-temp
 /instrument-root.h
+/instrument-root.c
 /instrument/generated-tcg-tracers.h
+/instrument/generated-tcg-tracers.c
 /trace-events-all
 /trace/generated-events.h
 /trace/generated-events.c
@@ -119,6 +121,7 @@ TAGS
 docker-src.*
 *~
 instrument.h
+instrument.c
 trace.h
 trace.c
 trace-ust.h
diff --git a/Makefile b/Makefile
index 375bd313d7..fb226bf54b 100644
--- a/Makefile
+++ b/Makefile
@@ -193,8 +193,11 @@ trace-dtrace-root.o: trace-dtrace-root.dtrace
 
 INSTRUMENT_HEADERS = instrument-root.h $(trace-events-subdirs:%=%/instrument.h)
 INSTRUMENT_HEADERS += instrument/generated-tcg-tracers.h
+INSTRUMENT_SOURCES = instrument-root.c $(trace-events-subdirs:%=%/instrument.c)
+INSTRUMENT_SOURCES += instrument/generated-tcg-tracers.c
 
 GENERATED_FILES += $(INSTRUMENT_HEADERS)
+GENERATED_FILES += $(INSTRUMENT_SOURCES)
 
 %/instrument.h: %/instrument.h-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
@@ -205,6 +208,15 @@ GENERATED_FILES += $(INSTRUMENT_HEADERS)
 		--backends=$(TRACE_INSTRUMENT_BACKEND) \
 		$< > $@,"GEN","$(@:%-timestamp=%)")
 
+%/instrument.c: %/instrument.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/instrument.c-timestamp: $(SRC_PATH)/%/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=instr-c \
+		--backends=$(TRACE_INSTRUMENT_BACKEND) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
 instrument-root.h: instrument-root.h-timestamp
 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 instrument-root.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
@@ -214,6 +226,15 @@ instrument-root.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.m
 		--backends=$(TRACE_INSTRUMENT_BACKEND) \
 		$< > $@,"GEN","$(@:%-timestamp=%)")
 
+instrument-root.c: instrument-root.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+instrument-root.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=instr-c \
+		--backends=$(TRACE_INSTRUMENT_BACKEND) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
 # Don't try to regenerate Makefile or configure
 # We don't generate any of them
 Makefile: ;
@@ -390,7 +411,7 @@ Makefile: $(version-obj-y)
 # Build libraries
 
 libqemustub.a: $(stub-obj-y)
-libqemuutil.a: $(util-obj-y) $(trace-obj-y)
+libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(instr-obj-y)
 
 ######################################################################
 
@@ -830,7 +851,8 @@ endif
 .SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
 	$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
 	$(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp) \
-	$(INSTRUMENT_HEADERS) $(INSTRUMENT_HEADERS:%=%-timestamp)
+	$(INSTRUMENT_HEADERS) $(INSTRUMENT_HEADERS:%=%-timestamp) \
+	$(INSTRUMENT_SOURCES) $(INSTRUMENT_SOURCES:%=%-timestamp)
 
 # Include automatically generated dependency files
 # Dependencies in Makefile.objs files come from our recursive subdir rules
diff --git a/Makefile.objs b/Makefile.objs
index 8e2b3770c4..e36a519952 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -100,6 +100,7 @@ target-obj-y += trace/
 ######################################################################
 # instrumentation
 util-obj-y += instrument/
+target-obj-y += instrument/
 
 ######################################################################
 # guest agent
@@ -180,3 +181,6 @@ trace-obj-y += $(trace-events-subdirs:%=%/trace.o)
 trace-obj-$(CONFIG_TRACE_UST) += trace-ust-all.o
 trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace-root.o
 trace-obj-$(CONFIG_TRACE_DTRACE) += $(trace-events-subdirs:%=%/trace-dtrace.o)
+
+instr-obj-$(CONFIG_INSTRUMENT) = instrument-root.o
+instr-obj-y += $(trace-events-subdirs:%=%/instrument.o)
diff --git a/configure b/configure
index 75d899b40c..13191052cb 100755
--- a/configure
+++ b/configure
@@ -897,6 +897,10 @@ for opt do
   ;;
   --with-trace-file=*) trace_file="$optarg"
   ;;
+  --enable-trace-instrument)
+      trace_instrument="yes"
+      trace_instrument_backend="dynamic"
+  ;;
   --enable-gprof) gprof="yes"
   ;;
   --enable-gcov) gcov="yes"
@@ -1427,6 +1431,8 @@ Advanced options (experts only):
                            Available backends: $trace_backend_list
   --with-trace-file=NAME   Full PATH,NAME of file to store traces
                            Default:trace-<pid>
+  --enable-trace-instrument
+                           Enable trace instrumentation
   --disable-slirp          disable SLIRP userspace network connectivity
   --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)
   --oss-lib                path to OSS library
@@ -5995,6 +6001,9 @@ QEMU_INCLUDES="-I\$(SRC_PATH)/tcg $QEMU_INCLUDES"
 ##########################################
 # trace instrumentation
 echo "TRACE_INSTRUMENT_BACKEND=instr-$trace_instrument_backend" >> $config_host_mak
+if test "$trace_instrument" = "yes"; then
+    echo "CONFIG_INSTRUMENT=y" >> $config_host_mak
+fi
 ##########################################
 
 echo "TOOLS=$tools" >> $config_host_mak
diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs
index d1edd1696e..c548bbdd8a 100644
--- a/instrument/Makefile.objs
+++ b/instrument/Makefile.objs
@@ -12,3 +12,15 @@ $(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/
 		--format=instr-tcg-h \
 		--backend=$(TRACE_INSTRUMENT_BACKEND) \
 		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
+
+$(obj)/generated-tcg-tracers.c: $(obj)/generated-tcg-tracers.c-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+$(obj)/generated-tcg-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	@mkdir -p $(dir $@)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=instr-tcg-c \
+		--backend=$(TRACE_INSTRUMENT_BACKEND) \
+		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
+
+target-obj-$(CONFIG_INSTRUMENT) += generated-tcg-tracers.o
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index 6d382157c0..e65349bc33 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -275,6 +275,9 @@ class Event(object):
     QEMU_DSTATE              = "_TRACE_%(NAME)s_DSTATE"
     QEMU_EVENT               = "_TRACE_%(NAME)s_EVENT"
 
+    QI_TRACE_INSTRUMENT      = "qi_event_%(name)s"
+    QI_TRACE_INSTRUMENT_TCG  = QI_TRACE_INSTRUMENT + "_tcg"
+
     def api(self, fmt=None):
         if fmt is None:
             fmt = Event.QEMU_TRACE_BACKEND
diff --git a/scripts/tracetool/backend/instr_dynamic.py b/scripts/tracetool/backend/instr_dynamic.py
new file mode 100644
index 0000000000..f42d3afa8f
--- /dev/null
+++ b/scripts/tracetool/backend/instr_dynamic.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Dynamic instrumentation proxy.
+
+"""
+
+__author__     = "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"
+__email__      = "stefanha@linux.vnet.ibm.com"
+
+
+from tracetool import out
+from tracetool.transform import *
+import tracetool.vcpu
+
+
+##################################################
+# instr-h
+
+def generate_instr_h_begin(events, group):
+    for event in events:
+        if "instrument" not in event.properties:
+            continue
+        out('extern void * %(qi)s_cb;',
+            qi=event.api(event.QI_TRACE_INSTRUMENT))
+    out('')
+
+
+def generate_instr_h(event, group):
+    argtypes = ", ".join(event.args.types())
+    if argtypes == "":
+        argtypes = "void"
+    out('    void (*func)(%(argtypes)s) = %(qi)s_cb;',
+        '    func(%(argnames)s);',
+        qi=event.api(event.QI_TRACE_INSTRUMENT),
+        args=event.args,
+        argnames=", ".join(event.args.names()),
+        argtypes=argtypes)
+
+
+##################################################
+# instr-tcg-h
+
+def generate_instr_tcg_h_begin(events, group):
+    for event in events:
+        if "instrument" not in event.properties:
+            continue
+        out('extern void * %(qi)s_cb;',
+            qi=event.api(event.QI_TRACE_INSTRUMENT_TCG))
+    out('')
+
+
+def generate_instr_tcg_h(event, group):
+    argtypes = ", ".join(event.args.types())
+    if argtypes == "":
+        argtypes = "void"
+    out('    void (*func)(%(argtypes)s) = %(qi)s_cb;',
+        '    func(%(argnames)s);',
+        qi=event.api(event.QI_TRACE_INSTRUMENT_TCG),
+        args=event.args,
+        argnames=", ".join(event.args.names()),
+        argtypes=argtypes)
diff --git a/scripts/tracetool/format/instr_c.py b/scripts/tracetool/format/instr_c.py
new file mode 100644
index 0000000000..987ecbf5c2
--- /dev/null
+++ b/scripts/tracetool/format/instr_c.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+/instrument-root.c
+.../instrument.c
+
+"""
+
+__author__     = "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"
+__email__      = "stefanha@linux.vnet.ibm.com"
+
+
+from tracetool import out
+from tracetool.transform import *
+
+
+def generate(events, backend, group):
+    events = [e for e in events
+              if "tcg-trans" not in e.properties and
+              "instrument" in e.properties]
+
+    if group == "root":
+        header = "trace-root.h"
+    else:
+        header = "trace.h"
+
+    out('/* This file is autogenerated by tracetool, do not edit. */',
+        '',
+        '#include "qemu/osdep.h"',
+        '#include "%s"' % header,
+        '')
+    backend.generate_begin(events, group)
+
+    for e in events:
+        out('void *%(qi_cb)s_cb = %(qi_trace)s;',
+            qi_cb=e.api(e.QI_TRACE_INSTRUMENT),
+            qi_trace=e.api(e.QEMU_TRACE_BACKEND))
+
+    backend.generate_end(events, group)
diff --git a/scripts/tracetool/format/instr_tcg_c.py b/scripts/tracetool/format/instr_tcg_c.py
new file mode 100644
index 0000000000..382709d0bd
--- /dev/null
+++ b/scripts/tracetool/format/instr_tcg_c.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+instrument/generated-tcg-tracers.c
+
+"""
+
+__author__     = "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"
+__email__      = "stefanha@linux.vnet.ibm.com"
+
+
+from tracetool import out
+from tracetool.transform import *
+
+
+def generate(events, backend, group):
+    events = [e.original for e in events
+              if "tcg-trans" in e.properties and
+              "instrument" in e.properties]
+
+    out('/* This file is autogenerated by tracetool, do not edit. */',
+        '',
+        '#include "qemu/osdep.h"',
+        '#include "cpu.h"',
+        '#include "tcg-op.h"',
+        '#include "trace-tcg.h"',
+        '')
+    backend.generate_begin(events, group)
+
+    for e in events:
+        out('void *%(qi_cb)s_cb = %(qi_trace)s;',
+            qi_cb=e.api(e.QI_TRACE_INSTRUMENT_TCG),
+            qi_trace=e.api(e.QEMU_TRACE_TCG_BACKEND))
+
+    backend.generate_end(events, group)
