diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt
index 5768a0b7a2..17fcd12469 100644
--- a/docs/devel/tracing.txt
+++ b/docs/devel/tracing.txt
@@ -439,3 +439,12 @@ If the translating vCPU has address 0xc1 and code is later executed by vCPU
     baz_trans cpu=0xc1 a=0xd3
     // at guest code execution
     baz_exec cpu=0xc2 a=0xd3
+
+=== "instrument" ===
+
+When compiling QEMU with trace instrumentation enabled, the "instrument"
+property lets you provide your own implementation for that trace event. This
+implementation can override and/or wrap the backend-specific tracing code
+(regardless of the tracing backend).
+
+See "docs/instrumentation.txt" for more information.
diff --git a/docs/instrumentation.txt b/docs/instrumentation.txt
new file mode 100644
index 0000000000..3d650ed09e
--- /dev/null
+++ b/docs/instrumentation.txt
@@ -0,0 +1,264 @@
+= Trace instrumentation =
+
+== Introduction ==
+
+Trace instrumentation allows users to execute their own code when QEMU raises
+one of its tracing events (see "docs/devel/tracing.txt"). This is more efficient
+than instrumenting events with the "dtrace" backend, since the user will run
+native instrumentation code and has more options to interact with the dynamic
+tracing and instrumentation facilities of QEMU.
+
+When applied to guest code events (i.e., those with the "guest_" prefix, like
+guest memory accesses), this turns QEMU into a fairly efficient and guest
+architecture-agnostic dynamic binary instrumentation framework. It works on all
+QEMU-supported architectures, as well as works in both 'user' (standalone
+application) and 'system' (full-system emulation) modes.
+
+Look at the headers installed by QEMU on the "qemu-instr" directory for further
+information beyond this document.
+
+
+== Selecting the events to instrument ==
+
+You must first select which events must be instrumentable before compiling QEMU
+by prefixing them with the "instrument" property, and removing the "disable"
+property if it is present.
+
+To get the full list of files defining events:
+
+    find /path/to/qemu-source -name trace-events
+
+To avoid modifying QEMU's sources, you can pass the "--with-instrument-events"
+argument to configure with one event name per line.
+
+
+== Instrumenting guest code ==
+
+QEMU emulates all guest instructions when executing in TCG mode (as opposed to
+using native hardware virtualization with KVM). Instructions are decompiled and
+translated into the intermediate TCG language. Then, the TCG compiler translates
+TCG code into the native host code that QEMU will execute.
+
+All events relating to guest code are named "guest_*". In addition, all events
+with the "tcg" property (see "docs/devel/tracing.txt") can be instrumented at
+two levels:
+
+* Translation
+
+  Raised when generating TCG code (e.g., translate a memory access instruction
+  from the guest).
+
+  Note: This level only exists for events with the "tcg" property.
+
+* Execution
+
+  Raised when executing the native host code generated by the TCG compiler
+  (e.g., execute a memory access instruction from the guest).
+
+  Note: All events without the "tcg" property are raised at execution time
+        (e.g., CPU hotplug).
+
+Note: Events with the "tcg" property (e.g., 'guest_mem_before') are internally
+      translated into two events to differentiate the translation and execution
+      levels (e.g., 'guest_mem_before_trans' and 'guest_mem_before_exec').
+
+Note: All guest events have a "Mode" and "Target" line describing when they are
+      available (e.g., TCG, KVM, etc.).
+
+
+== Setting instrumentation callbacks ==
+
+Function qi_ctrl_event_set() in "qemu-instr/control.h" can be used to set the
+instrumentation callback on each event to a user-specified function. Header
+"qemu-instr/events.h" provides the event identifiers and some pre-defined
+callbacks:
+
+* QI_EVENT_${EVENT}
+
+  Event identifier, passed to functions in "qemu-instr/control.h".
+
+* qi_event_${event}_nop
+
+  Do nothing.
+
+* qi_event_${event}_trace
+
+  Trace the event using whatever tracing backend QEMU has been configured with.
+
+* qi_event_${event}_gen_exec
+
+  Generate TCG code to raise the corresponding event when the TCG-generated code
+  is executed. Otherwise, the event will not be instrumented at execution time,
+  resulting in zero-overhead when executing the guest code.
+
+  Only available for translation-time events.
+
+* qi_event_${event}_trace_and_gen_exec
+
+  Combines 'qi_event_${event}_trace' and 'qi_event_${event}_gen_exec' in a
+  single call.
+
+  Only available for translation-time events.
+
+
+== Loading an instrumentation library ==
+
+There are two ways two load an instrumentation library:
+
+* Using the command-line "-instr" argument.
+
+* Using the "instr-load" and "instr-unload" commands in the HMP and QMP
+  interfaces.
+
+
+=== Example ===
+
+1. Configure QEMU with the selected events to instrument:
+
+    # instrument guest_cpu_enter and guest_mem_before
+    cat >/tmp/my-events <<EOF
+    guest_cpu_enter
+    guest_mem_before
+    EOF
+    mkdir -p /path/to/qemu-build
+    cd /path/to/qemu-build
+    /path/to/qemu-source/configure \
+      --enable-trace-instrument \
+      --with-instrument-events=/tmp/my-events \
+      --prefix=/path/to/qemu-install
+
+2. Build and install QEMU:
+
+    make install
+
+3. Create the "Makefile" to build the instrumentation library:
+
+    mkdir -p /tmp/my-instrument
+    
+    cat > /tmp/my-instrument/Makefile <<EOF
+    QEMU_PATH=/tmp/qemu-install/
+    
+    CFLAGS += -g
+    CFLAGS += -O3
+    CFLAGS += -Werror -Wall
+    CFLAGS += -I$(QEMU_PATH)/include
+    
+    all: libtrace-instrument.la
+    
+    libtrace-instrument.la: instrument.lo
+            libtool --mode=link --tag=CC $(CC) -module -rpath /usr/local/lib -o $@ $^
+    
+    %.lo: %.c
+            libtool --mode=compile --tag=CC $(CC) $(CFLAGS) -c $^
+    
+    clean:
+            $(RM) -f *.o *.so *.lo
+            $(RM) -Rf .libs
+    EOF
+
+4. Write your instrumentation library:
+
+    cat > /tmp/my-instrument/instrument.c <<EOF
+    #include <stdio.h>
+    #include <assert.h>
+    
+    #include <qemu-instr/events.h>      /* get event declarations */
+    #include <qemu-instr/control.h>     /* manipulate events */
+    
+    /* as documented in QEMU's event description */
+    struct mem_info {
+        uint8_t size_shift : 2;
+        bool    sign_extend: 1;
+        uint8_t endianness : 1;
+        bool    store      : 1;
+    };
+    
+    /* the address for the memory access is not known at translation time */
+    void guest_mem_before_trans(QICPU *cpu, QITCGv_cpu tcg_cpu,
+                                QITCGv vaddr, uint8_t info)
+    {
+        struct mem_info *mi = (struct mem_info*)&info;
+        qi_event_guest_mem_before_trans_trace(cpu, tcg_cpu, vaddr, info);
+        if (mi->store) {
+            /* generate at execution time only for memory writes */
+            qi_event_guest_mem_before_trans_gen_exec(cpu, tcg_cpu, vaddr, info);
+        }
+    }
+    
+    /* called when QEMU executes a memory access */
+    void guest_mem_before_exec(QICPU *cpu, uint64_t vaddr, uint8_t info)
+    {
+        struct mem_info *mi = (struct mem_info*)&info;
+        if (mi->store) {
+            /* if called by TCG code, we'll only get writes (see above) */
+            qi_event_guest_mem_before_exec_trace(cpu, vaddr, info);
+        }
+    }
+    
+    /* called every time QEMU hotplugs a CPU */
+    void guest_cpu_enter(QICPU *cpu)
+    {
+        /* call the original tracing routine */
+        qi_event_guest_cpu_enter_trace(cpu);
+    
+        /* disable instrumentation and tracing after the first call */
+        static bool found = false;
+        if (found) {
+            QIEvent *ev = QI_EVENT_GUEST_CPU_ENTER;
+            qi_ctrl_event_set(ev, NULL);
+            qi_trace_event_set_state_dynamic(ev, false);
+        } else {
+            found = true;
+        }
+    }
+    
+    
+    /* mandatory initialization callback */
+    void qi_init(int argc, const char **argv)
+    {
+        int i;
+        printf("init!\n");
+        printf("    argc :: %d\n", argc);
+        for (i = 0; i < argc; i++) {
+            printf("            -> %s\n", argv[i]);
+        }
+
+        /* instrument and trace events */
+        QIEvent *ev;
+
+        ev = QI_EVENT_GUEST_CPU_ENTER;
+        qi_ctrl_event_set(ev, guest_cpu_enter);
+        qi_trace_event_set_state_dynamic(ev, true);
+    
+        ev = QI_EVENT_GUEST_MEM_BEFORE_TRANS;
+        qi_ctrl_event_set(ev, guest_mem_before_trans);
+        qi_trace_event_set_state_dynamic(ev, true);
+    
+        ev = QI_EVENT_GUEST_MEM_BEFORE_EXEC;
+        qi_ctrl_event_set(ev, guest_mem_before_exec);
+        qi_trace_event_set_state_dynamic(ev, true);
+    }
+    
+    /* mandatory finalization callback */
+    void qi_fini(void)
+    {
+        fprintf(stderr, "fini!\n");
+    
+        /* ensure all tracing is disabled */
+        qi_trace_event_set_state_dynamic(QI_EVENT_GUEST_CPU_ENTER, false);
+        qi_trace_event_set_state_dynamic(QI_EVENT_GUEST_MEM_BEFORE_TRANS, false);
+        qi_trace_event_set_state_dynamic(QI_EVENT_GUEST_MEM_BEFORE_EXEC, false);
+    
+        /* instrumentation callbacks are automatically reset by QEMU */
+    }
+    EOF
+
+5. Compile the instrumentation library:
+
+    make -C /tmp/my-instrument
+
+6. Start QEMU with the instrumentation library:
+
+    /tmp/qemu-install/bin/qemu-system-x86_64 \
+        -instr file=/tmp/my-dinstrument/.libs/libtrace-instrument.so, \
+               arg=foo,arg=bar
