diff --git a/linux-user/sh4/trace_info.h b/linux-user/sh4/trace_info.h
new file mode 100644
index 0000000000000..e32f8a07f63d1
--- /dev/null
+++ b/linux-user/sh4/trace_info.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "frame_arch.h"
+
+const uint64_t frame_arch = frame_arch_sh;
+const uint64_t frame_mach = frame_mach_sh4;
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
index b26524429c0dd..699ddd4fe596d 100644
--- a/qobject/block-qdict.c
+++ b/qobject/block-qdict.c
@@ -222,7 +222,7 @@ void qdict_array_split(QDict *src, QList **dst)
     *dst = qlist_new();
 
     for (i = 0; i < UINT_MAX; i++) {
-        QObject *subqobj;
+        QObject *subqobj = NULL;
         bool is_subqdict;
         QDict *subqdict = NULL;
         char indexstr[32], prefix[32];
diff --git a/target/sh4/helper.h b/target/sh4/helper.h
index 8d792f6b5538d..cb274a0c38e3c 100644
--- a/target/sh4/helper.h
+++ b/target/sh4/helper.h
@@ -41,3 +41,12 @@ DEF_HELPER_FLAGS_2(ftrc_FT, TCG_CALL_NO_WG, i32, env, f32)
 DEF_HELPER_FLAGS_2(ftrc_DT, TCG_CALL_NO_WG, i32, env, f64)
 DEF_HELPER_3(fipr, void, env, i32, i32)
 DEF_HELPER_2(ftrv, void, env, i32)
+
+#ifdef HAS_TRACEWRAP
+DEF_HELPER_1(trace_newframe, void, i32)
+DEF_HELPER_2(trace_endframe, void, env, i32)
+DEF_HELPER_2(trace_load_reg, void, i32, i32)
+DEF_HELPER_2(trace_store_reg, void, i32, i32)
+DEF_HELPER_3(trace_load_mem, void, i32, i32, i32)
+DEF_HELPER_3(trace_store_mem, void, i32, i32, i32)
+#endif /* HAS_TRACEWRAP */
diff --git a/target/sh4/meson.build b/target/sh4/meson.build
index 56a57576da76b..1f02c289a01d8 100644
--- a/target/sh4/meson.build
+++ b/target/sh4/meson.build
@@ -7,6 +7,10 @@ sh4_ss.add(files(
   'translate.c',
 ))
 
+if get_option('tracewrap')
+  sh4_ss.add(files('trace_helper.c'))
+endif
+
 sh4_softmmu_ss = ss.source_set()
 sh4_softmmu_ss.add(files('monitor.c'))
 
diff --git a/target/sh4/trace_helper.c b/target/sh4/trace_helper.c
new file mode 100644
index 0000000000000..a619bc926ddc0
--- /dev/null
+++ b/target/sh4/trace_helper.c
@@ -0,0 +1,117 @@
+#include "tracewrap.h"
+#include "cpu.h"
+#include "qemu/log.h"
+#include "exec/helper-proto.h"
+#include "exec/memop.h"
+#include "qemu/log.h"
+
+const char *regs[] = {
+    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+    "r0b", "r1b", "r2b", "r3b", "r4b", "r5b", "r6b", "r7b",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "sr",
+    "gbr", "ssr", "spc", "sgr", "dbr", "vbr", "mach", "macl", "pr"
+};
+static const int reg_max = sizeof(regs) / sizeof(regs[0]);
+
+void HELPER(trace_newframe)(target_ulong pc)
+{
+	qemu_trace_newframe(pc, 0);
+}
+
+void HELPER(trace_endframe)(CPUSH4State *env, target_ulong old_pc)
+{
+	qemu_trace_endframe(env, old_pc, 32);
+}
+
+OperandInfo * load_store_reg(uint32_t reg, uint32_t val, int ls)
+{
+    RegOperand * ro = g_new(RegOperand,1);
+    reg_operand__init(ro);
+    ro->name = g_strdup(reg < reg_max ? regs[reg] : "UNKNOWN");
+
+    OperandInfoSpecific *ois = g_new(OperandInfoSpecific,1);
+    operand_info_specific__init(ois);
+    ois->reg_operand = ro;
+    OperandUsage *ou = g_new(OperandUsage,1);
+    operand_usage__init(ou);
+    if (ls == 0)
+    {
+        ou->read = 1;
+    } else {
+        ou->written = 1;
+    }
+    OperandInfo *oi = g_new(OperandInfo,1);
+    operand_info__init(oi);
+    oi->bit_length = 0;
+    oi->operand_info_specific = ois;
+    oi->operand_usage = ou;
+    oi->value.len = 4;
+    oi->value.data = g_malloc(oi->value.len);
+    memcpy(oi->value.data, &val, 4);
+    return oi;
+}
+
+void HELPER(trace_load_reg)(uint32_t reg, uint32_t val)
+{
+    qemu_log("This register (r%d) was read. Value 0x%x\n", reg, val);
+
+    //r0 always reads 0
+    OperandInfo *oi = load_store_reg(reg, (reg != 0) ? val : 0, 0);
+
+    qemu_trace_add_operand(oi, 0x1);
+}
+
+void HELPER(trace_store_reg)(uint32_t reg, uint32_t val)
+{
+    qemu_log("This register (r%d) was written. Value: 0x%x\n", reg, val);
+
+    OperandInfo *oi = load_store_reg(reg, val, 1);
+
+    qemu_trace_add_operand(oi, 0x2);
+}
+
+OperandInfo *load_store_mem(uint64_t addr, int ls, const void *data, size_t data_size) {
+    MemOperand * mo = g_new(MemOperand, 1);
+    mem_operand__init(mo);
+
+    mo->address = addr;
+
+    OperandInfoSpecific *ois = g_new(OperandInfoSpecific, 1);
+    operand_info_specific__init(ois);
+    ois->mem_operand = mo;
+
+    OperandUsage *ou = g_new(OperandUsage, 1);
+    operand_usage__init(ou);
+    if (ls == 0) {
+        ou->read = 1;
+    } else {
+        ou->written = 1;
+    }
+    OperandInfo *oi = g_new(OperandInfo, 1);
+    operand_info__init(oi);
+    oi->bit_length = data_size * 8;
+    oi->operand_info_specific = ois;
+    oi->operand_usage = ou;
+    oi->value.len = data_size;
+    oi->value.data = g_malloc(oi->value.len);
+    #ifdef BSWAP_NEEDED
+    memcpy_rev(oi->value.data, data, data_size);
+    #else
+    memcpy(oi->value.data, data, data_size);
+    #endif
+    return oi;
+}
+
+void HELPER(trace_load_mem)(uint32_t addr, uint32_t val, uint32_t op)
+{
+    qemu_log("LOAD at 0x%lx size: %d data: 0x%lx\n", (unsigned long) addr, memop_size(op), (unsigned long) val);
+    OperandInfo *oi = load_store_mem(addr, 0, &val, memop_size(op));
+    qemu_trace_add_operand(oi, 0x1);
+}
+
+void HELPER(trace_store_mem)(uint32_t addr, uint32_t val, uint32_t op)
+{
+    qemu_log("STORE at 0x%lx size: %d data: 0x%lx\n", (unsigned long) addr, memop_size(op), (unsigned long) val);
+    OperandInfo *oi = load_store_mem(addr, 1, &val, memop_size(op));
+    qemu_trace_add_operand(oi, 0x2);
+}
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index ce5d674a520e7..0aff9da2172e6 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -185,6 +185,66 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, int flags)
     }
 }
 
+#ifdef HAS_TRACEWRAP
+static void gen_trace_load_reg(uint32_t reg, TCGv var)
+{
+    TCGv_i32 t = tcg_const_i32(reg);
+    gen_helper_trace_load_reg(t, var);
+    tcg_temp_free_i32(t);
+}
+
+static void gen_trace_store_reg(uint32_t reg, TCGv var)
+{
+    TCGv_i32 t = tcg_const_i32(reg);
+    gen_helper_trace_store_reg(t, var);
+    tcg_temp_free_i32(t);
+}
+
+static inline void gen_trace_newframe(uint32_t pc)
+{
+    TCGv_i32 tmp0 = tcg_temp_new_i32();
+    tcg_gen_movi_i32(tmp0, pc);
+    gen_helper_trace_newframe(tmp0);
+    tcg_temp_free_i32(tmp0);
+}
+
+static inline void gen_trace_endframe(uint32_t pc)
+{
+    TCGv_i32 tmp0 = tcg_temp_new_i32();
+    tcg_gen_movi_i32(tmp0, pc);
+    gen_helper_trace_endframe(cpu_env, tmp0);
+    tcg_temp_free_i32(tmp0);
+}
+#endif /* HAS_TRACEWRAP */
+
+static inline void log_load_gpr(uint32_t rx, TCGv var) {
+    #ifdef HAS_TRACEWRAP
+    gen_trace_load_reg(rx, var);
+    #endif
+}
+
+static inline void log_store_gpr(uint32_t rx, TCGv var) {
+    #ifdef HAS_TRACEWRAP
+    gen_trace_store_reg(rx, var);
+    #endif
+}
+
+static inline void log_load_mem(TCGv addr, TCGv val, MemOp op) {
+    #ifdef HAS_TRACEWRAP
+    TCGv_i32 o = tcg_const_i32(op);
+    gen_helper_trace_load_mem(addr, val, o);
+    tcg_temp_free_i32(o);
+    #endif
+}
+
+static inline void log_store_mem(TCGv addr, TCGv val, MemOp op) {
+    #ifdef HAS_TRACEWRAP
+    TCGv_i32 o = tcg_const_i32(op);
+    gen_helper_trace_store_mem(addr, val, o);
+    tcg_temp_free_i32(o);
+    #endif
+}
+
 static void gen_read_sr(TCGv dst)
 {
     TCGv t0 = tcg_temp_new();
@@ -238,6 +298,9 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
         tcg_gen_goto_tb(n);
         tcg_gen_movi_i32(cpu_pc, dest);
         tcg_gen_exit_tb(ctx->base.tb, n);
+        #ifdef HAS_TRACEWRAP
+        gen_trace_endframe(dest);
+        #endif
     } else {
         tcg_gen_movi_i32(cpu_pc, dest);
         if (use_exit_tb(ctx)) {
@@ -529,8 +592,9 @@ static void _decode_opc(DisasContext * ctx)
 	return;
     case 0xd000:		/* mov.l @(disp,PC),Rn */
 	{
-            TCGv addr = tcg_const_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3);
-            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+        TCGv addr = tcg_const_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3);
+        tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+        log_load_mem(addr, REG(B11_8), MO_TESL);
 	    tcg_temp_free(addr);
 	}
 	return;
@@ -552,7 +616,7 @@ static void _decode_opc(DisasContext * ctx)
 
     switch (ctx->opcode & 0xf00f) {
     case 0x6003:		/* mov Rm,Rn */
-	tcg_gen_mov_i32(REG(B11_8), REG(B7_4));
+    	tcg_gen_mov_i32(REG(B11_8), REG(B7_4));
 	return;
     case 0x2000:		/* mov.b Rm,@Rn */
         tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_UB);
@@ -659,8 +723,8 @@ static void _decode_opc(DisasContext * ctx)
 	{
 	    TCGv addr = tcg_temp_new();
 	    tcg_gen_add_i32(addr, REG(B7_4), REG(0));
-            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
-	    tcg_temp_free(addr);
+        tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+ 	    tcg_temp_free(addr);
 	}
 	return;
     case 0x6008:		/* swap.b Rm,Rn */
@@ -2303,9 +2367,18 @@ static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     }
 #endif
 
+#ifdef HAS_TRACEWRAP
+    gen_trace_newframe(ctx->base.pc_next);
+#endif
+
     ctx->opcode = translator_lduw(env, &ctx->base, ctx->base.pc_next);
     decode_opc(ctx);
     ctx->base.pc_next += 2;
+
+#ifdef HAS_TRACEWRAP
+    gen_trace_endframe(ctx->base.pc_next);
+#endif
+
 }
 
 static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
@@ -2326,6 +2399,9 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
     case DISAS_TOO_MANY:
         gen_save_cpu_state(ctx, false);
         gen_goto_tb(ctx, 0, ctx->base.pc_next);
+    #ifdef HAS_TRACEWRAP
+        gen_trace_endframe(ctx->base.pc_next);
+    #endif
         break;
     case DISAS_NORETURN:
         break;