diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c
index 567950a7ed..60b66f16ff 100644
--- a/accel/tcg/monitor.c
+++ b/accel/tcg/monitor.c
@@ -11,14 +11,19 @@
 #include "qapi/error.h"
 #include "qapi/type-helpers.h"
 #include "qapi/qapi-commands-machine.h"
+#include "qapi/qmp/qdict.h"
 #include "monitor/monitor.h"
+#include "monitor/hmp.h"
 #include "sysemu/cpus.h"
 #include "sysemu/cpu-timers.h"
 #include "sysemu/tcg.h"
 #include "tcg/tcg.h"
 #include "exec/tb-stats.h"
+#include "exec/tb-flush.h"
 #include "internal.h"
+#include "tb-context.h"
 
+enum TbstatsCmd { TBS_CMD_START, TBS_CMD_STOP, TBS_CMD_STATUS };
 
 static void dump_drift_info(GString *buf)
 {
@@ -87,6 +92,99 @@ HumanReadableText *qmp_x_query_opcount(Error **errp)
     return human_readable_text_from_str(buf);
 }
 
+struct TbstatsCommand {
+    enum TbstatsCmd cmd;
+    uint32_t flag;
+    Monitor *mon;
+};
+
+static void do_hmp_tbstats_safe(CPUState *cpu, run_on_cpu_data icmd)
+{
+    struct TbstatsCommand *cmdinfo = icmd.host_ptr;
+    int cmd = cmdinfo->cmd;
+    uint32_t flag = cmdinfo->flag;
+    Monitor *mon = cmdinfo->mon;
+
+    switch (cmd) {
+    case TBS_CMD_START:
+        if (tb_stats_collection_enabled()) {
+            monitor_printf(mon, "TB information already being recorded\n");
+            break;
+        }
+
+        set_tbstats_flag(flag);
+        enable_collect_tb_stats();
+        tb_flush(cpu);
+        break;
+    case TBS_CMD_STOP:
+        if (tb_stats_collection_disabled()) {
+            monitor_printf(mon, "TB information not being recorded\n");
+            break;
+        }
+
+        /* Dissalloc all TBStatistics structures and stop creating new ones */
+        disable_collect_tb_stats();
+        clean_tbstats();
+        tb_flush(cpu);
+        break;
+    case TBS_CMD_STATUS:
+        if (tb_stats_collection_enabled()) {
+            uint32_t flag = get_tbstats_flag();
+            monitor_printf(mon, "tb_stats is enabled with flag:\n");
+            monitor_printf(mon, "    EXEC: %d\n", !!(flag & TB_EXEC_STATS));
+            monitor_printf(mon, "     JIT: %d\n", !!(flag & TB_JIT_STATS));
+        } else {
+            monitor_printf(mon, "tb_stats is disabled\n");
+        }
+        break;
+    default: /* INVALID */
+        g_assert_not_reached();
+        break;
+    }
+
+    g_free(cmdinfo);
+}
+
+void hmp_tbstats(Monitor *mon, const QDict *qdict)
+{
+    if (!tcg_enabled()) {
+        monitor_printf(mon, "Only available with accel=tcg\n");
+        return;
+    }
+
+    char *cmd = (char *) qdict_get_try_str(qdict, "command");
+    enum TbstatsCmd icmd = -1;
+
+    if (strcmp(cmd, "start") == 0) {
+        icmd = TBS_CMD_START;
+    } else if (strcmp(cmd, "stop") == 0) {
+        icmd = TBS_CMD_STOP;
+    } else if (strcmp(cmd, "status") == 0) {
+        icmd = TBS_CMD_STATUS;
+    } else {
+        monitor_printf(mon, "Invalid command\n");
+        return;
+    }
+
+    char *sflag = (char *) qdict_get_try_str(qdict, "flag");
+    uint32_t flag = TB_EXEC_STATS | TB_JIT_STATS;
+    if (sflag) {
+        if (strcmp(sflag, "jit") == 0) {
+            flag = TB_JIT_STATS;
+        } else if (strcmp(sflag, "exec") == 0) {
+            flag = TB_EXEC_STATS;
+        }
+    }
+
+    struct TbstatsCommand *tbscommand = g_new0(struct TbstatsCommand, 1);
+    tbscommand->cmd = icmd;
+    tbscommand->flag = flag;
+    tbscommand->mon = mon;
+    async_safe_run_on_cpu(first_cpu, do_hmp_tbstats_safe,
+                          RUN_ON_CPU_HOST_PTR(tbscommand));
+
+}
+
 static void hmp_tcg_register(void)
 {
     monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
diff --git a/accel/tcg/tb-stats.c b/accel/tcg/tb-stats.c
index 11322359c7..c90dde37d0 100644
--- a/accel/tcg/tb-stats.c
+++ b/accel/tcg/tb-stats.c
@@ -91,6 +91,18 @@ void dump_jit_profile_info(GString *buf)
     g_free(jpi);
 }
 
+static void free_tbstats(void *p, uint32_t hash, void *userp)
+{
+    g_free(p);
+}
+
+void clean_tbstats(void)
+{
+    /* remove all tb_stats */
+    qht_iter(&tb_ctx.tb_stats, free_tbstats, NULL);
+    qht_destroy(&tb_ctx.tb_stats);
+}
+
 void init_tb_stats_htable(void)
 {
     if (!tb_ctx.tb_stats.map && tb_stats_collection_enabled()) {
@@ -115,6 +127,11 @@ bool tb_stats_collection_enabled(void)
     return tcg_collect_tb_stats == TB_STATS_RUNNING;
 }
 
+bool tb_stats_collection_disabled(void)
+{
+    return tcg_collect_tb_stats == TB_STATS_STOPPED;
+}
+
 uint32_t get_tbstats_flag(void)
 {
     return tbstats_flag;
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 2cbd0f77a0..639e9d9e53 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1670,6 +1670,22 @@ SRST
   Executes a qemu-io command on the given block device.
 ERST
 
+#if defined(CONFIG_TCG)
+    {
+        .name       = "tb_stats",
+        .args_type  = "command:s,flag:s?",
+        .params     = "command [flag]",
+        .help       = "Control tb statistics collection:"
+                        "tb_stats (start|stop|status) [all|jit|exec]",
+        .cmd        = hmp_tbstats,
+    },
+#endif
+
+SRST
+``tb_stats`` *command* *flag*
+  Control recording tb statistics
+ERST
+
     {
         .name       = "qom-list",
         .args_type  = "path:s?",
diff --git a/include/exec/tb-stats-flags.h b/include/exec/tb-stats-flags.h
index 286ca93bcc..d8b844be99 100644
--- a/include/exec/tb-stats-flags.h
+++ b/include/exec/tb-stats-flags.h
@@ -19,6 +19,7 @@
 void enable_collect_tb_stats(void);
 void disable_collect_tb_stats(void);
 bool tb_stats_collection_enabled(void);
+bool tb_stats_collection_disabled(void);
 
 uint32_t get_tbstats_flag(void);
 void set_tbstats_flag(uint32_t flag);
diff --git a/include/exec/tb-stats.h b/include/exec/tb-stats.h
index 7c445856c9..ef6e8b6388 100644
--- a/include/exec/tb-stats.h
+++ b/include/exec/tb-stats.h
@@ -31,6 +31,8 @@
 #include "exec/tb-stats-flags.h"
 #include "tcg/tcg.h"
 
+enum SortBy { SORT_BY_HOTNESS, SORT_BY_HG /* Host/Guest */, SORT_BY_SPILLS };
+
 typedef struct TBStatistics TBStatistics;
 
 /*
@@ -90,4 +92,6 @@ bool tb_stats_enabled(TranslationBlock *tb, uint32_t flag);
 
 void dump_jit_profile_info(GString *buf);
 
+void clean_tbstats(void);
+
 #endif
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 13f9a2dedb..2e7f141754 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -181,5 +181,6 @@ void hmp_ioport_write(Monitor *mon, const QDict *qdict);
 void hmp_boot_set(Monitor *mon, const QDict *qdict);
 void hmp_info_mtree(Monitor *mon, const QDict *qdict);
 void hmp_info_cryptodev(Monitor *mon, const QDict *qdict);
+void hmp_tbstats(Monitor *mon, const QDict *qdict);
 
 #endif
