Message ID | 20231003183058.1639121-12-richard.henderson@linaro.org |
---|---|
State | New |
Headers | show |
Series | TCG code quality tracking | expand |
Richard Henderson <richard.henderson@linaro.org> writes: > From: "Vanderson M. do Rosario" <vandersonmr2@gmail.com> > > These functions will be used together to output statistics. > > Signed-off-by: Vanderson M. do Rosario <vandersonmr2@gmail.com> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org> > Signed-off-by: Fei Wu <fei2.wu@intel.com> > [rth: Split out of a larger patch] > Signed-off-by: Richard Henderson <richard.henderson@linaro.org> > --- > include/tcg/tb-stats.h | 25 +++++++++ > accel/tcg/tb-stats.c | 119 +++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 144 insertions(+) > > diff --git a/include/tcg/tb-stats.h b/include/tcg/tb-stats.h > index 1ec0d13eff..edee73b63b 100644 > --- a/include/tcg/tb-stats.h > +++ b/include/tcg/tb-stats.h > @@ -129,4 +129,29 @@ void tb_stats_reset_tbs(void); > TBStatistics *tb_stats_lookup(tb_page_addr_t phys_pc, vaddr pc, > uint32_t flags, uint64_t flags2); > > +/** > + * tb_stats_collect: > + * @max: maximum number of results > + * @sort: sort function > + * > + * Collect all TBStatistics and return the first @max items, > + * as dictated by the sort criteria. > + */ > +GPtrArray *tb_stats_collect(unsigned max, GCompareFunc sort); > + > +/* Sort functions for tb_stats_collect. */ > +gint tb_stats_sort_by_spills(gconstpointer, gconstpointer); > +gint tb_stats_sort_by_coverage(gconstpointer, gconstpointer); > +gint tb_stats_sort_by_hg(gconstpointer, gconstpointer); > + > +/** > + * tb_stats_dump: > + * @s: structure to dump > + * @index: label to emit > + * > + * Return a string with the rendering of the data in @s; > + * @index is included in the output. > + */ > +GString *tb_stats_dump(TBStatistics *s, unsigned index); > + > #endif /* TCG_TB_STATS_H */ > diff --git a/accel/tcg/tb-stats.c b/accel/tcg/tb-stats.c > index 424c9a90ec..b2c9445b67 100644 > --- a/accel/tcg/tb-stats.c > +++ b/accel/tcg/tb-stats.c > @@ -83,3 +83,122 @@ TBStatistics *tb_stats_lookup(tb_page_addr_t phys_pc, vaddr pc, > } > return s; > } > + > +static void tb_stats_collect_iter(void *p, uint32_t hash, void *u) > +{ > + g_ptr_array_add(u, p); > +} > + > +static void calculate_coverages(GPtrArray *array) > +{ > + double total_exec_count = 0; > + guint i, n = array->len; > + > + for (i = 0; i < n; ++i) { > + TBStatistics *s = g_ptr_array_index(array, i); > + double avg_insns = 1; > + double exec_count; > + > + if (s->translations.total) { > + avg_insns = s->code.num_guest_inst / (double)s->translations.total; > + } > + exec_count = ((double)s->executions.atomic + s->executions.normal) > + / avg_insns; > + s->executions.coverage = exec_count; > + total_exec_count += exec_count; > + } > + > + for (i = 0; i < n; ++i) { > + TBStatistics *s = g_ptr_array_index(array, i); > + s->executions.coverage /= total_exec_count; > + } > +} > + > +GPtrArray *tb_stats_collect(unsigned max, GCompareFunc sort) > +{ > + GPtrArray *array = g_ptr_array_new(); > + > + /* > + * Collect all TBStatistics and sort. > + * Note that coverage data requires both execution and jit collection. > + */ > + qht_iter(&tb_ctx.stats, tb_stats_collect_iter, array); > + calculate_coverages(array); > + g_ptr_array_sort(array, sort); > + > + /* Truncate to the first MAX entries. */ > + if (max < array->len) { > + g_ptr_array_set_size(array, max); > + } > + return array; > +} > + > +gint tb_stats_sort_by_spills(gconstpointer p1, gconstpointer p2) > +{ > + const TBStatistics *s1 = *(TBStatistics **)p1; > + const TBStatistics *s2 = *(TBStatistics **)p2; > + double c1 = (double)s1->code.spills / s1->translations.total; > + double c2 = (double)s2->code.spills / s2->translations.total; > + > + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; > +} > + > +gint tb_stats_sort_by_coverage(gconstpointer p1, gconstpointer p2) > +{ > + const TBStatistics *s1 = *(TBStatistics **)p1; > + const TBStatistics *s2 = *(TBStatistics **)p2; > + double c1 = s1->executions.coverage; > + double c2 = s2->executions.coverage; > + > + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; > +} > + > +gint tb_stats_sort_by_hg(gconstpointer p1, gconstpointer p2) > +{ > + const TBStatistics *s1 = *(TBStatistics **)p1; > + const TBStatistics *s2 = *(TBStatistics **)p2; > + double c1 = (double)s1->code.out_len / s1->code.num_guest_inst; > + double c2 = (double)s2->code.out_len / s2->code.num_guest_inst; > + > + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; > +} > + > +GString *tb_stats_dump(TBStatistics *s, unsigned index) > +{ > + unsigned n = s->tbs->len; > + unsigned invalid = 0; > + GString *buf; > + > + for (unsigned i = 0; i < n; ++i) { > + TranslationBlock *tb = g_ptr_array_index(s->tbs, i); > + if (tb->cflags & CF_INVALID) { > + invalid += 1; > + } > + } > + > + buf = g_string_new(""); > + g_string_append_printf(buf, > + "TB id:%u | phys:0x" TB_PAGE_ADDR_FMT " virt=%" VADDR_PRIx > + " flags:0x%08x invalid:%u/%u\n", > + index, s->phys_pc, s->pc, s->flags, invalid, n - invalid); This is a little unclear in the output maybe: flags:0x%08x translations:%u (of which %u invalidated) and adjust the names appropriately? > + > + if (tb_stats_enabled & TB_STATS_EXEC) { > + g_string_append_printf(buf, > + "\t| exec:%lu/%lu coverage:%.2f%%\n", > + s->executions.normal, s->executions.atomic, > + s->executions.coverage * 100); > + } > + > + if (tb_stats_enabled & TB_STATS_JIT) { > + g_string_append_printf(buf, > + "\t| trans:%lu inst: g:%lu op:%lu op_opt:%lu spills:%ld\n" > + "\t| h/g (host bytes / guest insts): %f\n", > + s->translations.total, > + s->code.num_guest_inst / s->translations.total, > + s->code.num_tcg_ops / s->translations.total, > + s->code.num_tcg_ops_opt / s->translations.total, > + s->code.spills / s->translations.total, > + (double)s->code.out_len / s->code.num_guest_inst); > + } > + return buf; > +}
diff --git a/include/tcg/tb-stats.h b/include/tcg/tb-stats.h index 1ec0d13eff..edee73b63b 100644 --- a/include/tcg/tb-stats.h +++ b/include/tcg/tb-stats.h @@ -129,4 +129,29 @@ void tb_stats_reset_tbs(void); TBStatistics *tb_stats_lookup(tb_page_addr_t phys_pc, vaddr pc, uint32_t flags, uint64_t flags2); +/** + * tb_stats_collect: + * @max: maximum number of results + * @sort: sort function + * + * Collect all TBStatistics and return the first @max items, + * as dictated by the sort criteria. + */ +GPtrArray *tb_stats_collect(unsigned max, GCompareFunc sort); + +/* Sort functions for tb_stats_collect. */ +gint tb_stats_sort_by_spills(gconstpointer, gconstpointer); +gint tb_stats_sort_by_coverage(gconstpointer, gconstpointer); +gint tb_stats_sort_by_hg(gconstpointer, gconstpointer); + +/** + * tb_stats_dump: + * @s: structure to dump + * @index: label to emit + * + * Return a string with the rendering of the data in @s; + * @index is included in the output. + */ +GString *tb_stats_dump(TBStatistics *s, unsigned index); + #endif /* TCG_TB_STATS_H */ diff --git a/accel/tcg/tb-stats.c b/accel/tcg/tb-stats.c index 424c9a90ec..b2c9445b67 100644 --- a/accel/tcg/tb-stats.c +++ b/accel/tcg/tb-stats.c @@ -83,3 +83,122 @@ TBStatistics *tb_stats_lookup(tb_page_addr_t phys_pc, vaddr pc, } return s; } + +static void tb_stats_collect_iter(void *p, uint32_t hash, void *u) +{ + g_ptr_array_add(u, p); +} + +static void calculate_coverages(GPtrArray *array) +{ + double total_exec_count = 0; + guint i, n = array->len; + + for (i = 0; i < n; ++i) { + TBStatistics *s = g_ptr_array_index(array, i); + double avg_insns = 1; + double exec_count; + + if (s->translations.total) { + avg_insns = s->code.num_guest_inst / (double)s->translations.total; + } + exec_count = ((double)s->executions.atomic + s->executions.normal) + / avg_insns; + s->executions.coverage = exec_count; + total_exec_count += exec_count; + } + + for (i = 0; i < n; ++i) { + TBStatistics *s = g_ptr_array_index(array, i); + s->executions.coverage /= total_exec_count; + } +} + +GPtrArray *tb_stats_collect(unsigned max, GCompareFunc sort) +{ + GPtrArray *array = g_ptr_array_new(); + + /* + * Collect all TBStatistics and sort. + * Note that coverage data requires both execution and jit collection. + */ + qht_iter(&tb_ctx.stats, tb_stats_collect_iter, array); + calculate_coverages(array); + g_ptr_array_sort(array, sort); + + /* Truncate to the first MAX entries. */ + if (max < array->len) { + g_ptr_array_set_size(array, max); + } + return array; +} + +gint tb_stats_sort_by_spills(gconstpointer p1, gconstpointer p2) +{ + const TBStatistics *s1 = *(TBStatistics **)p1; + const TBStatistics *s2 = *(TBStatistics **)p2; + double c1 = (double)s1->code.spills / s1->translations.total; + double c2 = (double)s2->code.spills / s2->translations.total; + + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; +} + +gint tb_stats_sort_by_coverage(gconstpointer p1, gconstpointer p2) +{ + const TBStatistics *s1 = *(TBStatistics **)p1; + const TBStatistics *s2 = *(TBStatistics **)p2; + double c1 = s1->executions.coverage; + double c2 = s2->executions.coverage; + + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; +} + +gint tb_stats_sort_by_hg(gconstpointer p1, gconstpointer p2) +{ + const TBStatistics *s1 = *(TBStatistics **)p1; + const TBStatistics *s2 = *(TBStatistics **)p2; + double c1 = (double)s1->code.out_len / s1->code.num_guest_inst; + double c2 = (double)s2->code.out_len / s2->code.num_guest_inst; + + return c1 < c2 ? 1 : c1 == c2 ? 0 : -1; +} + +GString *tb_stats_dump(TBStatistics *s, unsigned index) +{ + unsigned n = s->tbs->len; + unsigned invalid = 0; + GString *buf; + + for (unsigned i = 0; i < n; ++i) { + TranslationBlock *tb = g_ptr_array_index(s->tbs, i); + if (tb->cflags & CF_INVALID) { + invalid += 1; + } + } + + buf = g_string_new(""); + g_string_append_printf(buf, + "TB id:%u | phys:0x" TB_PAGE_ADDR_FMT " virt=%" VADDR_PRIx + " flags:0x%08x invalid:%u/%u\n", + index, s->phys_pc, s->pc, s->flags, invalid, n - invalid); + + if (tb_stats_enabled & TB_STATS_EXEC) { + g_string_append_printf(buf, + "\t| exec:%lu/%lu coverage:%.2f%%\n", + s->executions.normal, s->executions.atomic, + s->executions.coverage * 100); + } + + if (tb_stats_enabled & TB_STATS_JIT) { + g_string_append_printf(buf, + "\t| trans:%lu inst: g:%lu op:%lu op_opt:%lu spills:%ld\n" + "\t| h/g (host bytes / guest insts): %f\n", + s->translations.total, + s->code.num_guest_inst / s->translations.total, + s->code.num_tcg_ops / s->translations.total, + s->code.num_tcg_ops_opt / s->translations.total, + s->code.spills / s->translations.total, + (double)s->code.out_len / s->code.num_guest_inst); + } + return buf; +}