[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v15 07/10] tb-stats: Adding info [tb-list|tb] commands to HMP (WI
From: |
Fei Wu |
Subject: |
[PATCH v15 07/10] tb-stats: Adding info [tb-list|tb] commands to HMP (WIP) |
Date: |
Wed, 7 Jun 2023 20:24:08 +0800 |
From: "Vanderson M. do Rosario" <vandersonmr2@gmail.com>
These commands allow the exploration of TBs generated by the TCG.
Understand which one hotter, with more guest/host instructions... and
examine their guest, host and IR code.
The goal of this command is to allow the dynamic exploration of TCG
behavior and code quality. Therefore, for now, a corresponding QMP
command is not worthwhile.
[AJB: WIP - we still can't be safely sure a translation will succeed]
Example of output:
TB id:0 | phys:0x34d54 virt:0x0000000000034d54 flags:0x0000f0
| exec:4828932/0 guest inst cov:16.38%
| trans:1 ints: g:3 op:82 op_opt:34 spills:3
| h/g (host bytes / guest insts): 90.666664
TB id:1 | phys:0x34d0d virt:0x0000000000034d0d flags:0x0000f0
| exec:4825842/0 guest inst cov:21.82%
| trans:1 ints: g:4 op:80 op_opt:38 spills:2
| h/g (host bytes / guest insts): 84.000000
----------------
0x00034d0d: 89 de movl %ebx, %esi
0x00034d0f: 26 8b 0e movl %es:(%esi), %ecx
0x00034d12: 26 f6 46 08 80 testb $0x80, %es:8(%esi)
0x00034d17: 75 3b jne 0x34d54
------------------------------
Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Vanderson M. do Rosario <vandersonmr2@gmail.com>
Message-Id: <20190829173437.5926-10-vandersonmr2@gmail.com>
[AJB: fix authorship, dropped coverset]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Fei Wu <fei2.wu@intel.com>
---
accel/tcg/monitor.c | 69 ++++++++++++++++
accel/tcg/tb-stats.c | 176 ++++++++++++++++++++++++++++++++++++++++
disas/disas.c | 2 +
hmp-commands-info.hx | 16 ++++
include/exec/tb-stats.h | 25 ++++++
include/monitor/hmp.h | 2 +
6 files changed, 290 insertions(+)
diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c
index 094150e4af..d43c62b248 100644
--- a/accel/tcg/monitor.c
+++ b/accel/tcg/monitor.c
@@ -8,17 +8,21 @@
#include "qemu/osdep.h"
#include "qemu/accel.h"
+#include "qemu/log.h"
#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 "monitor/hmp-target.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/tcg.h"
#include "exec/tb-stats.h"
#include "exec/tb-flush.h"
+#include "disas/disas.h"
+#include "tb-context.h"
#include "internal.h"
#include "tb-context.h"
@@ -186,6 +190,71 @@ void hmp_tbstats(Monitor *mon, const QDict *qdict)
}
+void hmp_info_tblist(Monitor *mon, const QDict *qdict)
+{
+ int number_int;
+ const char *sortedby_str = NULL;
+ g_autoptr(GString) buf = g_string_new("");
+
+ if (!tcg_enabled()) {
+ monitor_printf(mon, "Only available with accel=tcg\n");
+ return;
+ }
+ if (!tb_ctx.tb_stats.map) {
+ monitor_printf(mon, "no TB information recorded\n");
+ return;
+ }
+
+ number_int = qdict_get_try_int(qdict, "number", 10);
+ sortedby_str = qdict_get_try_str(qdict, "sortedby");
+
+ int sortedby = SORT_BY_HOTNESS;
+ if (sortedby_str == NULL || strcmp(sortedby_str, "hotness") == 0) {
+ sortedby = SORT_BY_HOTNESS;
+ } else if (strcmp(sortedby_str, "hg") == 0) {
+ sortedby = SORT_BY_HG;
+ } else if (strcmp(sortedby_str, "spills") == 0) {
+ sortedby = SORT_BY_SPILLS;
+ } else {
+ monitor_printf(mon, "valid sort options are: hotness hg spills\n");
+ return;
+ }
+
+ dump_tblist_info(buf, number_int, sortedby);
+ monitor_printf(mon, "%s", buf->str);
+}
+
+void hmp_info_tb(Monitor *mon, const QDict *qdict)
+{
+ const int id = qdict_get_int(qdict, "id");
+ g_autoptr(GString) buf = g_string_new("");
+
+ if (!tcg_enabled()) {
+ monitor_printf(mon, "Only available with accel=tcg\n");
+ return;
+ }
+
+ TBStatistics *tbs = get_tbstats_by_id(id);
+ if (tbs == NULL) {
+ monitor_printf(mon, "TB %d information is not recorded\n", id);
+ return;
+ }
+
+ monitor_printf(mon, "\n------------------------------\n\n");
+
+ int valid_tb_num = dump_tb_info(buf, tbs, id);
+ monitor_printf(mon, "%s", buf->str);
+
+ if (valid_tb_num > 0) {
+ unsigned num_inst = tbs->code.num_guest_inst / tbs->translations.total;
+
+ monitor_printf(mon, "\n----------------n\n");
+ // FIXME: cannot disas
+ monitor_disas(mon, mon_get_cpu(mon), tbs->phys_pc, num_inst, true);
+ monitor_printf(mon, "\n------------------------------\n\n");
+ }
+}
+
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 7c7f700c89..53a4f448dc 100644
--- a/accel/tcg/tb-stats.c
+++ b/accel/tcg/tb-stats.c
@@ -11,12 +11,16 @@
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
+#include "qapi/error.h"
#include "qemu/qemu-print.h"
+#include "qemu/log.h"
#include "exec/tb-stats.h"
#include "tb-context.h"
+#include "internal.h"
+
/* TBStatistic collection controls */
enum TBStatsStatus {
TB_STATS_STOPPED = 0,
@@ -26,6 +30,8 @@ enum TBStatsStatus {
static enum TBStatsStatus tcg_collect_tb_stats;
static uint32_t tbstats_flag;
+static GPtrArray *last_search;
+
struct jit_profile_info {
uint64_t translations;
uint64_t aborted;
@@ -42,6 +48,18 @@ struct jit_profile_info {
#define stat_per_translation(stat, name) \
(stat->translations.total ? stat->name / stat->translations.total : 0)
+TBStatistics *get_tbstats_by_id(int id)
+{
+ if (!last_search) {
+ return NULL;
+ }
+
+ if (id < 0 || id >= last_search->len) {
+ return NULL;
+ }
+ return g_ptr_array_index(last_search, id);
+}
+
/* accumulate the statistics from all TBs */
static void collect_jit_profile_info(void *p, uint32_t hash, void *userp)
{
@@ -98,6 +116,10 @@ static void free_tbstats(void *p, uint32_t hash, void
*userp)
void clean_tbstats(void)
{
+ if (last_search) {
+ g_ptr_array_free(last_search, true);
+ }
+
/* remove all tb_stats */
qht_iter(&tb_ctx.tb_stats, free_tbstats, NULL);
qht_destroy(&tb_ctx.tb_stats);
@@ -129,6 +151,154 @@ void init_tb_stats_htable(void)
}
}
+static void collect_tb_stats(void *p, uint32_t hash, void *userp)
+{
+ int *count = userp;
+
+ g_ptr_array_add(last_search, p);
+ (*count)++;
+}
+
+static void count_invalid_tbs(gpointer data, gpointer user_data)
+{
+ TranslationBlock *tb = (TranslationBlock *) data;
+ unsigned *counter = (unsigned *) user_data;
+ if (tb->cflags & CF_INVALID) {
+ *counter = *counter + 1;
+ }
+}
+
+int dump_tb_info(GString *buf, TBStatistics *tbs, int id)
+{
+ unsigned g = stat_per_translation(tbs, code.num_guest_inst);
+ unsigned ops = stat_per_translation(tbs, code.num_tcg_ops);
+ unsigned ops_opt = stat_per_translation(tbs, code.num_tcg_ops_opt);
+ unsigned spills = stat_per_translation(tbs, code.spills);
+ unsigned h = stat_per_translation(tbs, code.out_len);
+ unsigned act = tbs->tbs->len;
+ unsigned invalid = 0;
+
+ float guest_host_prop = g ? ((float) h / g) : 0;
+
+ g_ptr_array_foreach(tbs->tbs, &count_invalid_tbs, &invalid);
+
+ g_string_append_printf(buf,
+ "TB id:%d | phys:0x"TB_PAGE_ADDR_FMT" virt:0x"TARGET_FMT_lx
+ " flags:0x%08x %d inv/%d\n",
+ id, tbs->phys_pc, tbs->pc, tbs->flags, invalid, act);
+
+ if (tbs_stats_enabled(tbs, TB_EXEC_STATS)) {
+ g_string_append_printf(buf,
+ "\t| exec:%lu/%lu guest inst cov:%.2f%%\n",
+ tbs->executions.normal,
+ tbs->executions.atomic, tbs->executions.coverage / 100.0f);
+ }
+
+ if (tbs_stats_enabled(tbs, TB_JIT_STATS)) {
+ g_string_append_printf(buf,
+ "\t| trans:%lu ints: g:%u op:%u op_opt:%u spills:%d"
+ "\n\t| h/g (host bytes / guest insts): %f\n",
+ tbs->translations.total, g, ops, ops_opt, spills,
+ guest_host_prop);
+ }
+
+ g_string_append_printf(buf, "\n");
+
+ return act - invalid;
+}
+
+static gint
+inverse_sort_tbs_spills(gconstpointer p1, gconstpointer p2)
+{
+ const TBStatistics *tbs1 = *(TBStatistics **) p1;
+ const TBStatistics *tbs2 = *(TBStatistics **) p2;
+ unsigned long c1 = stat_per_translation(tbs1, code.spills);
+ unsigned long c2 = stat_per_translation(tbs2, code.spills);
+ return c1 < c2 ? 1 : c1 == c2 ? 0 : -1;
+}
+
+static gint
+inverse_sort_tbs_hotness(gconstpointer p1, gconstpointer p2)
+{
+ const TBStatistics *tbs1 = *(TBStatistics **) p1;
+ const TBStatistics *tbs2 = *(TBStatistics **) p2;
+ unsigned long c1 = stat_per_translation(tbs1, executions.normal);
+ unsigned long c2 = stat_per_translation(tbs2, executions.normal);
+ return c1 < c2 ? 1 : c1 == c2 ? 0 : -1;
+}
+
+static gint
+inverse_sort_tbs_hg(gconstpointer p1, gconstpointer p2)
+{
+ const TBStatistics *tbs1 = *(TBStatistics **) p1;
+ const TBStatistics *tbs2 = *(TBStatistics **) p2;
+
+ if (tbs1->code.num_guest_inst == 0) {
+ return -1;
+ }
+ if (tbs2->code.num_guest_inst == 0) {
+ return 1;
+ }
+
+ unsigned long c1 = tbs1->code.out_len / tbs1->code.num_guest_inst;
+ unsigned long c2 = tbs2->code.out_len / tbs2->code.num_guest_inst;
+ return c1 < c2 ? 1 : c1 == c2 ? 0 : -1;
+}
+
+static void calculate_last_search_coverages(void)
+{
+ uint64_t total_exec_count = 0;
+
+ /* Compute total execution count for all tbs */
+ for (int i = 0; i < last_search->len; ++i) {
+ TBStatistics *tbs = g_ptr_array_index(last_search, i);
+ total_exec_count +=
+ (tbs->executions.atomic + tbs->executions.normal)
+ * stat_per_translation(tbs, code.num_guest_inst);
+ }
+
+ for (int i = 0; i < last_search->len; ++i) {
+ TBStatistics *tbs = g_ptr_array_index(last_search, i);
+ uint64_t tb_total_execs =
+ (tbs->executions.atomic + tbs->executions.normal)
+ * stat_per_translation(tbs, code.num_guest_inst);
+ tbs->executions.coverage =
+ (10000 * tb_total_execs) / (total_exec_count + 1);
+ }
+}
+
+void dump_tblist_info(GString *buf, int total, int sort_by)
+{
+ int array_size = 0;
+
+ if (last_search) {
+ g_ptr_array_free(last_search, true);
+ }
+ last_search = g_ptr_array_new();
+
+ qht_iter(&tb_ctx.tb_stats, collect_tb_stats, &array_size);
+
+ calculate_last_search_coverages();
+
+ if (sort_by == SORT_BY_HOTNESS) {
+ g_ptr_array_sort(last_search, (GCompareFunc)inverse_sort_tbs_hotness);
+ } else if (sort_by == SORT_BY_SPILLS) {
+ g_ptr_array_sort(last_search, (GCompareFunc)inverse_sort_tbs_spills);
+ } else if (sort_by == SORT_BY_HG) {
+ g_ptr_array_sort(last_search, (GCompareFunc)inverse_sort_tbs_hg);
+ } else {
+ return;
+ }
+
+ array_size = (array_size > total) ? total : array_size;
+ g_ptr_array_set_size(last_search, array_size);
+
+ for (int i = 0; i < last_search->len; ++i) {
+ TBStatistics *tbs = g_ptr_array_index(last_search, i);
+ dump_tb_info(buf, tbs, i);
+ }
+}
+
void enable_collect_tb_stats(void)
{
tcg_collect_tb_stats = TB_STATS_RUNNING;
@@ -166,3 +336,9 @@ bool tb_stats_enabled(TranslationBlock *tb, uint32_t flag)
tb->tb_stats &&
(tbstats_flag & flag);
}
+
+bool tbs_stats_enabled(struct TBStatistics *tbs, uint32_t flag)
+{
+ return tb_stats_collection_enabled() &&
+ (tbstats_flag & flag);
+}
diff --git a/disas/disas.c b/disas/disas.c
index 0d2d06c2ec..3b19b51fca 100644
--- a/disas/disas.c
+++ b/disas/disas.c
@@ -8,6 +8,8 @@
#include "hw/core/cpu.h"
#include "exec/memory.h"
+#include "qemu/log-for-trace.h"
+
/* Filled in by elfload.c. Simplistic, but will do for now. */
struct syminfo *syminfos = NULL;
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index f5b37eb74a..ace1ecc3c5 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -262,6 +262,22 @@ ERST
.params = "",
.help = "show dynamic compiler info",
},
+ {
+ .name = "tb-list",
+ .args_type = "number:i?,sortedby:s?",
+ .params = "[number sortedby]",
+ .help = "show a [number] translated blocks sorted by [sortedby]"
+ "sortedby opts: hotness hg spills",
+ .cmd = hmp_info_tblist,
+ },
+ {
+ .name = "tb",
+ .args_type = "id:i,flags:s?",
+ .params = "id [flag1,flag2,...]",
+ .help = "show information about one translated block by id."
+ "dump flags can be used to set dump code level: out_asm
in_asm op",
+ .cmd = hmp_info_tb,
+ },
#endif
SRST
diff --git a/include/exec/tb-stats.h b/include/exec/tb-stats.h
index 77974b4b7f..4cdf41af6c 100644
--- a/include/exec/tb-stats.h
+++ b/include/exec/tb-stats.h
@@ -54,6 +54,8 @@ struct TBStatistics {
struct {
unsigned long normal;
unsigned long atomic;
+ /* filled only when dumping x% cover set */
+ uint16_t coverage;
} executions;
/* JIT Stats - protected by lock */
@@ -89,6 +91,7 @@ bool tb_stats_cmp(const void *ap, const void *bp);
void init_tb_stats_htable(void);
bool tb_stats_enabled(TranslationBlock *tb, uint32_t flag);
+bool tbs_stats_enabled(struct TBStatistics *tbs, uint32_t flag);
void dump_jit_profile_info(GString *buf);
@@ -102,4 +105,26 @@ void clean_tbstats(void);
*/
void tbstats_reset_tbs(void);
+/**
+ * dump_tbs_info: report the hottest blocks
+ *
+ * @buf: output buffer
+ * @total: the limit of hotblocks
+ * @sort_by: property in which the dump will be sorted
+ *
+ * Report the hottest blocks to either the log or monitor
+ */
+void dump_tblist_info(GString *buf, int total, int sort_by);
+
+/**
+ * dump_tb_info: dump information about one TB
+ *
+ * @buf: output buffer
+ * @tbs: the tbs to dump
+ * @id: the display id of the block (from previous search)
+ */
+int dump_tb_info(GString *buf, TBStatistics *tbs, int id);
+
+TBStatistics *get_tbstats_by_id(int id);
+
#endif
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 2e7f141754..acdd6f1561 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -182,5 +182,7 @@ 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);
+void hmp_info_tblist(Monitor *mon, const QDict *qdict);
+void hmp_info_tb(Monitor *mon, const QDict *qdict);
#endif
--
2.25.1
- [PATCH v15 00/10] TCG code quality tracking, Fei Wu, 2023/06/07
- [PATCH v15 01/10] accel/tcg: remove CONFIG_PROFILER, Fei Wu, 2023/06/07
- [PATCH v15 02/10] accel/tcg: introduce TBStatistics structure, Fei Wu, 2023/06/07
- [PATCH v15 03/10] accel: collecting TB execution count, Fei Wu, 2023/06/07
- [PATCH v15 04/10] accel/tcg: add jit stats to TBStatistics, Fei Wu, 2023/06/07
- [PATCH v15 05/10] monitor: adding tb_stats hmp command, Fei Wu, 2023/06/07
- [PATCH v15 07/10] tb-stats: Adding info [tb-list|tb] commands to HMP (WIP),
Fei Wu <=
- [PATCH v15 06/10] tb-stats: reset the tracked TBs on a tb_flush, Fei Wu, 2023/06/07
- [PATCH v15 09/10] tb-stats: dump hot TBs at the end of the execution, Fei Wu, 2023/06/07
- [PATCH v15 08/10] debug: add -d tb_stats to control TBStatistics, Fei Wu, 2023/06/07
- [PATCH v15 10/10] docs: add tb-stats how to, Fei Wu, 2023/06/07
- Re: [PATCH v15 00/10] TCG code quality tracking, Wu, Fei, 2023/06/12
- Re: [PATCH v15 00/10] TCG code quality tracking, Wu, Fei, 2023/06/12