diff --git a/.gitignore b/.gitignore
index 09c2363acf..068dc1c1ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,8 @@
 /config-target.*
 /config.status
 /config-temp
+/instrument-root.h
+/instrument/generated-tcg-tracers.h
 /trace-events-all
 /trace/generated-events.h
 /trace/generated-events.c
@@ -116,6 +118,7 @@ tags
 TAGS
 docker-src.*
 *~
+instrument.h
 trace.h
 trace.c
 trace-ust.h
diff --git a/Makefile b/Makefile
index 38814f9a61..375bd313d7 100644
--- a/Makefile
+++ b/Makefile
@@ -191,6 +191,29 @@ trace-dtrace-root.h: trace-dtrace-root.dtrace
 
 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
+
+GENERATED_FILES += $(INSTRUMENT_HEADERS)
+
+%/instrument.h: %/instrument.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+%/instrument.h-timestamp: $(SRC_PATH)/%/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=$(call trace-group-name,$@) \
+		--format=instr-h \
+		--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)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=instr-h \
+		--backends=$(TRACE_INSTRUMENT_BACKEND) \
+		$< > $@,"GEN","$(@:%-timestamp=%)")
+
 # Don't try to regenerate Makefile or configure
 # We don't generate any of them
 Makefile: ;
@@ -806,7 +829,8 @@ endif
 
 .SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
 	$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
-	$(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp)
+	$(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp) \
+	$(INSTRUMENT_HEADERS) $(INSTRUMENT_HEADERS:%=%-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 bfd5a6ceb1..8e2b3770c4 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -98,6 +98,10 @@ util-obj-y +=  trace/
 target-obj-y += trace/
 
 ######################################################################
+# instrumentation
+util-obj-y += instrument/
+
+######################################################################
 # guest agent
 
 # FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
diff --git a/configure b/configure
index a3f0522e8f..75d899b40c 100755
--- a/configure
+++ b/configure
@@ -351,6 +351,8 @@ pie=""
 qom_cast_debug="yes"
 trace_backends="log"
 trace_file="trace"
+trace_instrument="no"
+trace_instrument_backend="none"
 spice=""
 rbd=""
 smartcard=""
@@ -5282,6 +5284,7 @@ echo "Trace backends    $trace_backends"
 if have_backend "simple"; then
 echo "Trace output file $trace_file-<pid>"
 fi
+echo "Trace instrumentation $trace_instrument"
 echo "spice support     $spice $(echo_version $spice $spice_protocol_version/$spice_server_version)"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
@@ -5989,6 +5992,11 @@ else
 fi
 QEMU_INCLUDES="-I\$(SRC_PATH)/tcg $QEMU_INCLUDES"
 
+##########################################
+# trace instrumentation
+echo "TRACE_INSTRUMENT_BACKEND=instr-$trace_instrument_backend" >> $config_host_mak
+##########################################
+
 echo "TOOLS=$tools" >> $config_host_mak
 echo "ROMS=$roms" >> $config_host_mak
 echo "MAKE=$make" >> $config_host_mak
diff --git a/include/trace-tcg.h b/include/trace-tcg.h
index da68608c85..a626c5020c 100644
--- a/include/trace-tcg.h
+++ b/include/trace-tcg.h
@@ -2,5 +2,6 @@
 #define TRACE_TCG_H
 
 #include "trace/generated-tcg-tracers.h"
+#include "instrument/generated-tcg-tracers.h"
 
 #endif /* TRACE_TCG_H */
diff --git a/instrument/Makefile.objs b/instrument/Makefile.objs
new file mode 100644
index 0000000000..d1edd1696e
--- /dev/null
+++ b/instrument/Makefile.objs
@@ -0,0 +1,14 @@
+# -*- mode: makefile -*-
+
+######################################################################
+# QEMU trace->instrument interface
+
+$(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp
+	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
+$(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
+	@mkdir -p $(dir $@)
+	$(call quiet-command,$(TRACETOOL) \
+		--group=root \
+		--format=instr-tcg-h \
+		--backend=$(TRACE_INSTRUMENT_BACKEND) \
+		$< > $@,"GEN","$(patsubst %-timestamp,%,$@)")
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index d4c204a472..6d382157c0 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -161,7 +161,7 @@ class Event(object):
                       "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
                       "\s*")
 
-    _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
+    _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu", "instrument"])
 
     def __init__(self, name, props, fmt, args, orig=None,
                  event_trans=None, event_exec=None):
@@ -269,13 +269,15 @@ class Event(object):
 
     QEMU_TRACE               = "trace_%(name)s"
     QEMU_TRACE_NOCHECK       = "_nocheck__" + QEMU_TRACE
+    QEMU_TRACE_BACKEND       = "_backend__" + QEMU_TRACE
     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
+    QEMU_TRACE_TCG_BACKEND   = "_backend__" + QEMU_TRACE_TCG
     QEMU_DSTATE              = "_TRACE_%(NAME)s_DSTATE"
     QEMU_EVENT               = "_TRACE_%(NAME)s_EVENT"
 
     def api(self, fmt=None):
         if fmt is None:
-            fmt = Event.QEMU_TRACE
+            fmt = Event.QEMU_TRACE_BACKEND
         return fmt % {"name": self.name, "NAME": self.name.upper()}
 
     def transform(self, *trans):
diff --git a/scripts/tracetool/backend/instr_none.py b/scripts/tracetool/backend/instr_none.py
new file mode 100644
index 0000000000..56508cfcbe
--- /dev/null
+++ b/scripts/tracetool/backend/instr_none.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+No-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
+
+
+##################################################
+# instr-tcg-h
+
+def generate_instr_tcg_h(event, group):
+    out('    %(backend)s(%(argnames)s);',
+        backend=event.api(event.QEMU_TRACE_TCG_BACKEND),
+        argnames=", ".join(event.args.names()))
+
+
+##################################################
+# instr-h
+
+def generate_instr_h(event, group):
+    out('    %(backend)s(%(argnames)s);',
+        backend=event.api(event.QEMU_TRACE_BACKEND),
+        argnames=", ".join(event.args.names()))
diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py
index aecf249d66..2e423079db 100644
--- a/scripts/tracetool/format/h.py
+++ b/scripts/tracetool/format/h.py
@@ -87,4 +87,10 @@ def generate(events, backend, group):
 
     backend.generate_end(events, group)
 
-    out('#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper())
+    out('')
+    if group == "root":
+        out('#include "instrument-root.h"')
+    else:
+        out('#include "instrument.h"')
+    out('',
+        '#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/instr_h.py b/scripts/tracetool/format/instr_h.py
new file mode 100644
index 0000000000..2b80b3d038
--- /dev/null
+++ b/scripts/tracetool/format/instr_h.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+/instrument-root.h
+.../instrument.h
+
+"""
+
+__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):
+    out('/* This file is autogenerated by tracetool, do not edit. */',
+        '',
+        '#ifndef INSTRUMENT_%s_GENERATED_TRACERS_H' % group.upper(),
+        '#define INSTRUMENT_%s_GENERATED_TRACERS_H' % group.upper(),
+        '')
+    backend.generate_begin(events, group)
+
+    for e in events:
+        out('static inline void %(qemu)s(%(args)s)',
+            '{',
+            qemu=e.api(e.QEMU_TRACE),
+            args=e.args)
+
+        if "tcg-trans" in e.properties or "instrument" not in e.properties:
+            # "*_trans" never called directly (and never instrumented)
+            out('    %(backend)s(%(argnames)s);',
+                backend=e.api(e.QEMU_TRACE_BACKEND),
+                argnames=", ".join(e.args.names()))
+        else:
+            backend.generate(e, group)
+
+        out('}')
+
+    backend.generate_end(events, group)
+    out('',
+        '#endif  /* INSTRUMENT_%s_GENERATED_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/instr_tcg_h.py b/scripts/tracetool/format/instr_tcg_h.py
new file mode 100644
index 0000000000..6abf97d368
--- /dev/null
+++ b/scripts/tracetool/format/instr_tcg_h.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+instrument/generated-tcg-tracers.h
+
+"""
+
+__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, Arguments
+import tracetool.vcpu
+
+
+API = True
+
+
+def generate(events, backend, group):
+    events = [e.original for e in events
+              if "tcg-trans" in e.properties]
+
+    out('/* This file is autogenerated by tracetool, do not edit. */',
+        '',
+        '#ifndef INSTRUMENT_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
+        '#define INSTRUMENT_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
+        '')
+    backend.generate_begin(events, group)
+
+    for e in events:
+        args_api = tracetool.vcpu.transform_args("tcg_h", e)
+
+        out('static inline void %(qemu)s(%(args)s)',
+            '{',
+            qemu=e.api(e.QEMU_TRACE_TCG),
+            args=args_api)
+
+        if "instrument" in e.properties:
+            e = e.copy()
+            e.args = args_api
+            backend.generate(e, group)
+        else:
+            out('    %(backend)s(%(argnames)s);',
+                backend=e.api(e.QEMU_TRACE_TCG_BACKEND),
+                argnames=", ".join(args_api.names()))
+
+        out('}')
+
+    backend.generate_end(events, group)
+    out('',
+        '#endif  /* INSTRUMENT_%s_GENERATED_TCG_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py
index 1651cc3f71..3363502ffc 100644
--- a/scripts/tracetool/format/tcg_h.py
+++ b/scripts/tracetool/format/tcg_h.py
@@ -51,7 +51,7 @@ def generate(events, backend, group):
 
         out('static inline void %(name_tcg)s(%(args)s)',
             '{',
-            name_tcg=e.original.api(e.QEMU_TRACE_TCG),
+            name_tcg=e.original.api(e.QEMU_TRACE_TCG_BACKEND),
             args=tracetool.vcpu.transform_args("tcg_h", e.original))
 
         if "disable" not in e.properties:
@@ -72,8 +72,8 @@ def generate(events, backend, group):
                 '    if (%(cond)s) {',
                 '        gen_helper_%(name_exec)s(%(argnames_exec)s);',
                 '    }',
-                name_trans=e.original.event_trans.api(e.QEMU_TRACE),
-                name_exec=e.original.event_exec.api(e.QEMU_TRACE),
+                name_trans=e.original.event_trans.api(e.QEMU_TRACE_BACKEND),
+                name_exec=e.original.event_exec.api(e.QEMU_TRACE_BACKEND),
                 argnames_trans=", ".join(args_trans.names()),
                 argnames_exec=", ".join(args_exec.names()),
                 cond=cond)
diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py
index bbbd6ad0f4..e9ba62a136 100644
--- a/scripts/tracetool/format/tcg_helper_c.py
+++ b/scripts/tracetool/format/tcg_helper_c.py
@@ -74,7 +74,7 @@ def generate(events, backend, group):
             # NOTE: the check was already performed at TCG-generation time
             '    %(name)s(%(args_call)s);',
             '}',
-            name_tcg="helper_%s_proxy" % e.api(),
+            name_tcg="helper_%s_proxy" % e.api(e.QEMU_TRACE_BACKEND),
             name=e.api(e.QEMU_TRACE_NOCHECK),
             args_api=e_args_api,
             args_call=", ".join(e_args_call.casted()),
diff --git a/scripts/tracetool/format/tcg_helper_h.py b/scripts/tracetool/format/tcg_helper_h.py
index 6b184b641b..17ef0021eb 100644
--- a/scripts/tracetool/format/tcg_helper_h.py
+++ b/scripts/tracetool/format/tcg_helper_h.py
@@ -44,6 +44,6 @@ def generate(events, backend, group):
         out(fmt,
             flags=flags,
             argc=len(args),
-            name=e.api() + "_proxy",
+            name=e.api(e.QEMU_TRACE_BACKEND) + "_proxy",
             types=types,
             )
diff --git a/scripts/tracetool/format/tcg_helper_wrapper_h.py b/scripts/tracetool/format/tcg_helper_wrapper_h.py
index ff53447512..377af4615b 100644
--- a/scripts/tracetool/format/tcg_helper_wrapper_h.py
+++ b/scripts/tracetool/format/tcg_helper_wrapper_h.py
@@ -54,7 +54,7 @@ def generate(events, backend, group):
             for (type_, name) in args_tcg_compat
         ]
 
-        gen_name = "gen_helper_" + e.api()
+        gen_name = "gen_helper_" + e.api(e.QEMU_TRACE_BACKEND)
 
         out('static inline void %(name)s(%(args)s)',
             '{',
