diff --git a/cpu-all.h b/cpu-all.h
index 5f47ab8..5a4c2f6 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -243,21 +243,23 @@ extern unsigned long reserved_va;
 
 #if defined(CONFIG_USER_ONLY)
 
+#include "trace.h"
+
 /* if user mode, no other memory access functions */
-#define ldub(p) ldub_raw(p)
-#define ldsb(p) ldsb_raw(p)
-#define lduw(p) lduw_raw(p)
-#define ldsw(p) ldsw_raw(p)
-#define ldl(p) ldl_raw(p)
-#define ldq(p) ldq_raw(p)
-#define ldfl(p) ldfl_raw(p)
-#define ldfq(p) ldfq_raw(p)
-#define stb(p, v) stb_raw(p, v)
-#define stw(p, v) stw_raw(p, v)
-#define stl(p, v) stl_raw(p, v)
-#define stq(p, v) stq_raw(p, v)
-#define stfl(p, v) stfl_raw(p, v)
-#define stfq(p, v) stfq_raw(p, v)
+#define ldub(p)    ({ trace_vmem(p, 1, 0); ldub_raw(p);    })
+#define ldsb(p)    ({ trace_vmem(p, 1, 0); ldsb_raw(p);    })
+#define lduw(p)    ({ trace_vmem(p, 2, 0); lduw_raw(p);    })
+#define ldsw(p)    ({ trace_vmem(p, 2, 0); ldsw_raw(p);    })
+#define ldl(p)     ({ trace_vmem(p, 4, 0); ldl_raw(p);     })
+#define ldq(p)     ({ trace_vmem(p, 8, 0); ldq_raw(p);     })
+#define ldfl(p)    ({ trace_vmem(p, 4, 0); ldfl_raw(p);    })
+#define ldfq(p)    ({ trace_vmem(p, 8, 0); ldfq_raw(p);    })
+#define stb(p, v)  ({ trace_vmem(p, 1, 1); stb_raw(p, v);  })
+#define stw(p, v)  ({ trace_vmem(p, 2, 1); stw_raw(p, v);  })
+#define stl(p, v)  ({ trace_vmem(p, 4, 1); stl_raw(p, v);  })
+#define stq(p, v)  ({ trace_vmem(p, 8, 1); stq_raw(p, v);  })
+#define stfl(p, v) ({ trace_vmem(p, 4, 1); stfl_raw(p, v); })
+#define stfq(p, v) ({ trace_vmem(p, 8, 1); stfq_raw(p, v); })
 
 #define ldub_code(p) ldub_raw(p)
 #define ldsb_code(p) ldsb_raw(p)
@@ -266,20 +268,20 @@ extern unsigned long reserved_va;
 #define ldl_code(p) ldl_raw(p)
 #define ldq_code(p) ldq_raw(p)
 
-#define ldub_kernel(p) ldub_raw(p)
-#define ldsb_kernel(p) ldsb_raw(p)
-#define lduw_kernel(p) lduw_raw(p)
-#define ldsw_kernel(p) ldsw_raw(p)
-#define ldl_kernel(p) ldl_raw(p)
-#define ldq_kernel(p) ldq_raw(p)
-#define ldfl_kernel(p) ldfl_raw(p)
-#define ldfq_kernel(p) ldfq_raw(p)
-#define stb_kernel(p, v) stb_raw(p, v)
-#define stw_kernel(p, v) stw_raw(p, v)
-#define stl_kernel(p, v) stl_raw(p, v)
-#define stq_kernel(p, v) stq_raw(p, v)
-#define stfl_kernel(p, v) stfl_raw(p, v)
-#define stfq_kernel(p, vt) stfq_raw(p, v)
+#define ldub_kernel(p)     ({ trace_vmem(p, 1, 0); ldub_raw(p);    })
+#define ldsb_kernel(p)     ({ trace_vmem(p, 1, 0); ldsb_raw(p);    })
+#define lduw_kernel(p)     ({ trace_vmem(p, 2, 0); lduw_raw(p);    })
+#define ldsw_kernel(p)     ({ trace_vmem(p, 2, 0); ldsw_raw(p);    })
+#define ldl_kernel(p)      ({ trace_vmem(p, 4, 0); ldl_raw(p);     })
+#define ldq_kernel(p)      ({ trace_vmem(p, 8, 0); ldq_raw(p);     })
+#define ldfl_kernel(p)     ({ trace_vmem(p, 4, 0); ldfl_raw(p);    })
+#define ldfq_kernel(p)     ({ trace_vmem(p, 8, 0); ldfq_raw(p);    })
+#define stb_kernel(p, v)   ({ trace_vmem(p, 1, 1); stb_raw(p, v);  })
+#define stw_kernel(p, v)   ({ trace_vmem(p, 2, 1); stw_raw(p, v);  })
+#define stl_kernel(p, v)   ({ trace_vmem(p, 4, 1); stl_raw(p, v);  })
+#define stq_kernel(p, v)   ({ trace_vmem(p, 8, 1); stq_raw(p, v);  })
+#define stfl_kernel(p, v)  ({ trace_vmem(p, 4, 1); stfl_raw(p, v); })
+#define stfq_kernel(p, vt) ({ trace_vmem(p, 8, 1); stfq_raw(p, v); })
 
 #endif /* defined(CONFIG_USER_ONLY) */
 
diff --git a/exec-all.h b/exec-all.h
index c211242..2c97ff1 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -309,6 +309,8 @@ void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx,
 #include "softmmu_defs.h"
 
 #define ACCESS_TYPE (NB_MMU_MODES + 1)
+/* do not trace '*_code' accesses during instruction disassembly */
+#define TRACE_TCG_CODE_ACCESSOR 1
 #define MEMSUFFIX _code
 #define env cpu_single_env
 
@@ -325,6 +327,7 @@ void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx,
 #include "softmmu_header.h"
 
 #undef ACCESS_TYPE
+#undef TRACE_TCG_CODE_ACCESSOR
 #undef MEMSUFFIX
 #undef env
 
diff --git a/softmmu_header.h b/softmmu_header.h
index 818d7b6..a50c71f 100644
--- a/softmmu_header.h
+++ b/softmmu_header.h
@@ -25,6 +25,9 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
+
+#include "trace.h"
+
 #if DATA_SIZE == 8
 #define SUFFIX q
 #define USUFFIX q
@@ -88,6 +91,10 @@ static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
     unsigned long physaddr;
     int mmu_idx;
 
+#if !defined(TRACE_TCG_CODE_ACCESSOR)
+    trace_vmem(ptr, DATA_SIZE, 0);
+#endif
+
     addr = ptr;
     page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
     mmu_idx = CPU_MMU_INDEX;
@@ -109,6 +116,10 @@ static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
     unsigned long physaddr;
     int mmu_idx;
 
+#if !defined(TRACE_TCG_CODE_ACCESSOR)
+    trace_vmem(ptr, DATA_SIZE, 0);
+#endif
+
     addr = ptr;
     page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
     mmu_idx = CPU_MMU_INDEX;
@@ -134,6 +145,10 @@ static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE
     unsigned long physaddr;
     int mmu_idx;
 
+#if !defined(TRACE_TCG_CODE_ACCESSOR)
+    trace_vmem(ptr, DATA_SIZE, 1);
+#endif
+
     addr = ptr;
     page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
     mmu_idx = CPU_MMU_INDEX;
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 82e04e7..8b738f0 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -2522,3 +2522,9 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
                                                  TCGV_PTR_TO_NAT(A), (B))
 #define tcg_gen_ext_i32_ptr(R, A) tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A))
 #endif /* TCG_TARGET_REG_BITS != 32 */
+
+/* To avoid a circular dependency with helper.h, overload tcg_gen_qemu_*
+ * routines with preprocessor macros to insert TCG virtual memory access
+ * tracing.
+ */
+#include "trace/tcg-op-internal.h"
diff --git a/trace-events b/trace-events
index 9b315c5..14ea7ef 100644
--- a/trace-events
+++ b/trace-events
@@ -660,3 +660,12 @@ disable tcg vbbl(uint64_t vaddr) "vaddr=0x%016"PRIx64
 #
 # vaddr : instruction's virtual address
 disable tcg vfetch(uint64_t vaddr) "vaddr=0x%016"PRIx64
+
+# Start virtual memory access (before any potential access violation)
+#
+# Targets: all
+#
+# vaddr : access' virtual address
+# size  : access' size (bytes)
+# write : whether the access is a write
+disable tcg vmem(TCGv vaddr, uint8_t size, uint8_t write) "vaddr=0x%016"PRIx64" size=%d write=%d"
diff --git a/trace/tcg-op-internal.h b/trace/tcg-op-internal.h
new file mode 100644
index 0000000..e19657d
--- /dev/null
+++ b/trace/tcg-op-internal.h
@@ -0,0 +1,74 @@
+/* -*- mode: c -*-
+ * Copyright (c) 2011 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.
+ */
+
+/**
+ * @file Capture TCG code generation for virtual memory accesses.
+ *
+ * Assumes that no other lower-level call will be performed by target
+ * architecture disassembly code on TCG instructions for accessing memory.
+ */
+
+#ifndef TRACE__TCG_OP_INTERNAL_H
+#define TRACE__TCG_OP_INTERNAL_H
+
+#define tcg_gen_qemu_ld8u(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 1, 0);              \
+        (tcg_gen_qemu_ld8u)(arg, addr, mem_index);      \
+    } while (0)
+#define tcg_gen_qemu_ld8s(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 1, 0);              \
+        (tcg_gen_qemu_ld8s)(arg, addr, mem_index);      \
+    } while (0)
+#define tcg_gen_qemu_ld16u(arg, addr, mem_index)        \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 2, 0);              \
+        (tcg_gen_qemu_ld16u)(arg, addr, mem_index);     \
+    } while (0)
+#define tcg_gen_qemu_ld16s(arg, addr, mem_index)        \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 2, 0);              \
+        (tcg_gen_qemu_ld16s)(arg, addr, mem_index);     \
+    } while (0)
+#define tcg_gen_qemu_ld32u(arg, addr, mem_index)        \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 4, 0);              \
+        (tcg_gen_qemu_ld32u)(arg, addr, mem_index);     \
+    } while (0)
+#define tcg_gen_qemu_ld32s(arg, addr, mem_index)        \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 4, 0);              \
+        (tcg_gen_qemu_ld32s)(arg, addr, mem_index);     \
+    } while (0)
+#define tcg_gen_qemu_ld64(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 8, 0);              \
+        (tcg_gen_qemu_ld64)(arg, addr, mem_index);      \
+    } while (0)
+#define tcg_gen_qemu_st8(arg, addr, mem_index)          \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 1, 1);              \
+        (tcg_gen_qemu_st8)(arg, addr, mem_index);       \
+    } while (0)
+#define tcg_gen_qemu_st16(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 2, 1);              \
+        (tcg_gen_qemu_st16)(arg, addr, mem_index);      \
+    } while (0)
+#define tcg_gen_qemu_st32(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 4, 1);              \
+        (tcg_gen_qemu_st32)(arg, addr, mem_index);      \
+    } while (0)
+#define tcg_gen_qemu_st64(arg, addr, mem_index)         \
+    do {                                                \
+        gen_helper_trace_vmem(addr, 8, 1);              \
+        (tcg_gen_qemu_st64)(arg, addr, mem_index);      \
+    } while (0)
+
+#endif  /* TRACE__TCG_OP_INTERNAL_H */
