18cc04fd9SPhilippe Mathieu-Daudé /*
28cc04fd9SPhilippe Mathieu-Daudé * SPDX-License-Identifier: LGPL-2.1-or-later
38cc04fd9SPhilippe Mathieu-Daudé *
48cc04fd9SPhilippe Mathieu-Daudé * QEMU TCG statistics
58cc04fd9SPhilippe Mathieu-Daudé *
68cc04fd9SPhilippe Mathieu-Daudé * Copyright (c) 2003-2005 Fabrice Bellard
78cc04fd9SPhilippe Mathieu-Daudé */
88cc04fd9SPhilippe Mathieu-Daudé
98cc04fd9SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
108cc04fd9SPhilippe Mathieu-Daudé #include "qemu/accel.h"
118cc04fd9SPhilippe Mathieu-Daudé #include "qemu/qht.h"
128cc04fd9SPhilippe Mathieu-Daudé #include "qapi/error.h"
138cc04fd9SPhilippe Mathieu-Daudé #include "system/cpu-timers.h"
148cc04fd9SPhilippe Mathieu-Daudé #include "exec/icount.h"
158cc04fd9SPhilippe Mathieu-Daudé #include "hw/core/cpu.h"
168cc04fd9SPhilippe Mathieu-Daudé #include "tcg/tcg.h"
178cc04fd9SPhilippe Mathieu-Daudé #include "internal-common.h"
188cc04fd9SPhilippe Mathieu-Daudé #include "tb-context.h"
198cc04fd9SPhilippe Mathieu-Daudé #include <math.h>
208cc04fd9SPhilippe Mathieu-Daudé
dump_drift_info(GString * buf)218cc04fd9SPhilippe Mathieu-Daudé static void dump_drift_info(GString *buf)
228cc04fd9SPhilippe Mathieu-Daudé {
238cc04fd9SPhilippe Mathieu-Daudé if (!icount_enabled()) {
248cc04fd9SPhilippe Mathieu-Daudé return;
258cc04fd9SPhilippe Mathieu-Daudé }
268cc04fd9SPhilippe Mathieu-Daudé
278cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
288cc04fd9SPhilippe Mathieu-Daudé (cpu_get_clock() - icount_get()) / SCALE_MS);
298cc04fd9SPhilippe Mathieu-Daudé if (icount_align_option) {
308cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
318cc04fd9SPhilippe Mathieu-Daudé -max_delay / SCALE_MS);
328cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
338cc04fd9SPhilippe Mathieu-Daudé max_advance / SCALE_MS);
348cc04fd9SPhilippe Mathieu-Daudé } else {
358cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay NA\n");
368cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance NA\n");
378cc04fd9SPhilippe Mathieu-Daudé }
388cc04fd9SPhilippe Mathieu-Daudé }
398cc04fd9SPhilippe Mathieu-Daudé
dump_accel_info(AccelState * accel,GString * buf)4023204530SPhilippe Mathieu-Daudé static void dump_accel_info(AccelState *accel, GString *buf)
418cc04fd9SPhilippe Mathieu-Daudé {
428cc04fd9SPhilippe Mathieu-Daudé bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
438cc04fd9SPhilippe Mathieu-Daudé "one-insn-per-tb",
448cc04fd9SPhilippe Mathieu-Daudé &error_fatal);
458cc04fd9SPhilippe Mathieu-Daudé
468cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Accelerator settings:\n");
478cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
488cc04fd9SPhilippe Mathieu-Daudé one_insn_per_tb ? "on" : "off");
498cc04fd9SPhilippe Mathieu-Daudé }
508cc04fd9SPhilippe Mathieu-Daudé
print_qht_statistics(struct qht_stats hst,GString * buf)518cc04fd9SPhilippe Mathieu-Daudé static void print_qht_statistics(struct qht_stats hst, GString *buf)
528cc04fd9SPhilippe Mathieu-Daudé {
538cc04fd9SPhilippe Mathieu-Daudé uint32_t hgram_opts;
548cc04fd9SPhilippe Mathieu-Daudé size_t hgram_bins;
558cc04fd9SPhilippe Mathieu-Daudé char *hgram;
568cc04fd9SPhilippe Mathieu-Daudé double avg;
578cc04fd9SPhilippe Mathieu-Daudé
588cc04fd9SPhilippe Mathieu-Daudé if (!hst.head_buckets) {
598cc04fd9SPhilippe Mathieu-Daudé return;
608cc04fd9SPhilippe Mathieu-Daudé }
618cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash buckets %zu/%zu "
628cc04fd9SPhilippe Mathieu-Daudé "(%0.2f%% head buckets used)\n",
638cc04fd9SPhilippe Mathieu-Daudé hst.used_head_buckets, hst.head_buckets,
648cc04fd9SPhilippe Mathieu-Daudé (double)hst.used_head_buckets /
658cc04fd9SPhilippe Mathieu-Daudé hst.head_buckets * 100);
668cc04fd9SPhilippe Mathieu-Daudé
678cc04fd9SPhilippe Mathieu-Daudé hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
688cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
698cc04fd9SPhilippe Mathieu-Daudé if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
708cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_NODECIMAL;
718cc04fd9SPhilippe Mathieu-Daudé }
728cc04fd9SPhilippe Mathieu-Daudé hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
738cc04fd9SPhilippe Mathieu-Daudé avg = qdist_avg(&hst.occupancy);
748cc04fd9SPhilippe Mathieu-Daudé if (!isnan(avg)) {
758cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash occupancy "
768cc04fd9SPhilippe Mathieu-Daudé "%0.2f%% avg chain occ. "
778cc04fd9SPhilippe Mathieu-Daudé "Histogram: %s\n",
788cc04fd9SPhilippe Mathieu-Daudé avg * 100, hgram);
798cc04fd9SPhilippe Mathieu-Daudé }
808cc04fd9SPhilippe Mathieu-Daudé g_free(hgram);
818cc04fd9SPhilippe Mathieu-Daudé
828cc04fd9SPhilippe Mathieu-Daudé hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
838cc04fd9SPhilippe Mathieu-Daudé hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
848cc04fd9SPhilippe Mathieu-Daudé if (hgram_bins > 10) {
858cc04fd9SPhilippe Mathieu-Daudé hgram_bins = 10;
868cc04fd9SPhilippe Mathieu-Daudé } else {
878cc04fd9SPhilippe Mathieu-Daudé hgram_bins = 0;
888cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
898cc04fd9SPhilippe Mathieu-Daudé }
908cc04fd9SPhilippe Mathieu-Daudé hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
918cc04fd9SPhilippe Mathieu-Daudé avg = qdist_avg(&hst.chain);
928cc04fd9SPhilippe Mathieu-Daudé if (!isnan(avg)) {
938cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
948cc04fd9SPhilippe Mathieu-Daudé "Histogram: %s\n",
958cc04fd9SPhilippe Mathieu-Daudé avg, hgram);
968cc04fd9SPhilippe Mathieu-Daudé }
978cc04fd9SPhilippe Mathieu-Daudé g_free(hgram);
988cc04fd9SPhilippe Mathieu-Daudé }
998cc04fd9SPhilippe Mathieu-Daudé
1008cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats {
1018cc04fd9SPhilippe Mathieu-Daudé size_t nb_tbs;
1028cc04fd9SPhilippe Mathieu-Daudé size_t host_size;
1038cc04fd9SPhilippe Mathieu-Daudé size_t target_size;
1048cc04fd9SPhilippe Mathieu-Daudé size_t max_target_size;
1058cc04fd9SPhilippe Mathieu-Daudé size_t direct_jmp_count;
1068cc04fd9SPhilippe Mathieu-Daudé size_t direct_jmp2_count;
1078cc04fd9SPhilippe Mathieu-Daudé size_t cross_page;
1088cc04fd9SPhilippe Mathieu-Daudé };
1098cc04fd9SPhilippe Mathieu-Daudé
tb_tree_stats_iter(gpointer key,gpointer value,gpointer data)1108cc04fd9SPhilippe Mathieu-Daudé static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
1118cc04fd9SPhilippe Mathieu-Daudé {
1128cc04fd9SPhilippe Mathieu-Daudé const TranslationBlock *tb = value;
1138cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats *tst = data;
1148cc04fd9SPhilippe Mathieu-Daudé
1158cc04fd9SPhilippe Mathieu-Daudé tst->nb_tbs++;
1168cc04fd9SPhilippe Mathieu-Daudé tst->host_size += tb->tc.size;
1178cc04fd9SPhilippe Mathieu-Daudé tst->target_size += tb->size;
1188cc04fd9SPhilippe Mathieu-Daudé if (tb->size > tst->max_target_size) {
1198cc04fd9SPhilippe Mathieu-Daudé tst->max_target_size = tb->size;
1208cc04fd9SPhilippe Mathieu-Daudé }
1218cc04fd9SPhilippe Mathieu-Daudé #ifndef CONFIG_USER_ONLY
1228cc04fd9SPhilippe Mathieu-Daudé if (tb->page_addr[1] != -1) {
1238cc04fd9SPhilippe Mathieu-Daudé tst->cross_page++;
1248cc04fd9SPhilippe Mathieu-Daudé }
1258cc04fd9SPhilippe Mathieu-Daudé #endif
1268cc04fd9SPhilippe Mathieu-Daudé if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
1278cc04fd9SPhilippe Mathieu-Daudé tst->direct_jmp_count++;
1288cc04fd9SPhilippe Mathieu-Daudé if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
1298cc04fd9SPhilippe Mathieu-Daudé tst->direct_jmp2_count++;
1308cc04fd9SPhilippe Mathieu-Daudé }
1318cc04fd9SPhilippe Mathieu-Daudé }
1328cc04fd9SPhilippe Mathieu-Daudé return false;
1338cc04fd9SPhilippe Mathieu-Daudé }
1348cc04fd9SPhilippe Mathieu-Daudé
tlb_flush_counts(size_t * pfull,size_t * ppart,size_t * pelide)1358cc04fd9SPhilippe Mathieu-Daudé static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
1368cc04fd9SPhilippe Mathieu-Daudé {
1378cc04fd9SPhilippe Mathieu-Daudé CPUState *cpu;
1388cc04fd9SPhilippe Mathieu-Daudé size_t full = 0, part = 0, elide = 0;
1398cc04fd9SPhilippe Mathieu-Daudé
1408cc04fd9SPhilippe Mathieu-Daudé CPU_FOREACH(cpu) {
1418cc04fd9SPhilippe Mathieu-Daudé full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
1428cc04fd9SPhilippe Mathieu-Daudé part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
1438cc04fd9SPhilippe Mathieu-Daudé elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
1448cc04fd9SPhilippe Mathieu-Daudé }
1458cc04fd9SPhilippe Mathieu-Daudé *pfull = full;
1468cc04fd9SPhilippe Mathieu-Daudé *ppart = part;
1478cc04fd9SPhilippe Mathieu-Daudé *pelide = elide;
1488cc04fd9SPhilippe Mathieu-Daudé }
1498cc04fd9SPhilippe Mathieu-Daudé
tcg_dump_flush_info(GString * buf)1508cc04fd9SPhilippe Mathieu-Daudé static void tcg_dump_flush_info(GString *buf)
1518cc04fd9SPhilippe Mathieu-Daudé {
1528cc04fd9SPhilippe Mathieu-Daudé size_t flush_full, flush_part, flush_elide;
1538cc04fd9SPhilippe Mathieu-Daudé
1548cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB flush count %u\n",
1558cc04fd9SPhilippe Mathieu-Daudé qatomic_read(&tb_ctx.tb_flush_count));
1568cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB invalidate count %u\n",
1578cc04fd9SPhilippe Mathieu-Daudé qatomic_read(&tb_ctx.tb_phys_invalidate_count));
1588cc04fd9SPhilippe Mathieu-Daudé
1598cc04fd9SPhilippe Mathieu-Daudé tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
1608cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
1618cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
1628cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
1638cc04fd9SPhilippe Mathieu-Daudé }
1648cc04fd9SPhilippe Mathieu-Daudé
dump_exec_info(GString * buf)1658cc04fd9SPhilippe Mathieu-Daudé static void dump_exec_info(GString *buf)
1668cc04fd9SPhilippe Mathieu-Daudé {
1678cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats tst = {};
1688cc04fd9SPhilippe Mathieu-Daudé struct qht_stats hst;
1698cc04fd9SPhilippe Mathieu-Daudé size_t nb_tbs;
1708cc04fd9SPhilippe Mathieu-Daudé
1718cc04fd9SPhilippe Mathieu-Daudé tcg_tb_foreach(tb_tree_stats_iter, &tst);
1728cc04fd9SPhilippe Mathieu-Daudé nb_tbs = tst.nb_tbs;
1738cc04fd9SPhilippe Mathieu-Daudé /* XXX: avoid using doubles ? */
1748cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Translation buffer state:\n");
1758cc04fd9SPhilippe Mathieu-Daudé /*
1768cc04fd9SPhilippe Mathieu-Daudé * Report total code size including the padding and TB structs;
1778cc04fd9SPhilippe Mathieu-Daudé * otherwise users might think "-accel tcg,tb-size" is not honoured.
1788cc04fd9SPhilippe Mathieu-Daudé * For avg host size we use the precise numbers from tb_tree_stats though.
1798cc04fd9SPhilippe Mathieu-Daudé */
1808cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "gen code size %zu/%zu\n",
1818cc04fd9SPhilippe Mathieu-Daudé tcg_code_size(), tcg_code_capacity());
1828cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
1838cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
1848cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? tst.target_size / nb_tbs : 0,
1858cc04fd9SPhilippe Mathieu-Daudé tst.max_target_size);
1868cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB avg host size %zu bytes "
1878cc04fd9SPhilippe Mathieu-Daudé "(expansion ratio: %0.1f)\n",
1888cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? tst.host_size / nb_tbs : 0,
1898cc04fd9SPhilippe Mathieu-Daudé tst.target_size ?
1908cc04fd9SPhilippe Mathieu-Daudé (double)tst.host_size / tst.target_size : 0);
1918cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
1928cc04fd9SPhilippe Mathieu-Daudé tst.cross_page,
1938cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
1948cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
1958cc04fd9SPhilippe Mathieu-Daudé "(2 jumps=%zu %zu%%)\n",
1968cc04fd9SPhilippe Mathieu-Daudé tst.direct_jmp_count,
1978cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
1988cc04fd9SPhilippe Mathieu-Daudé tst.direct_jmp2_count,
1998cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
2008cc04fd9SPhilippe Mathieu-Daudé
2018cc04fd9SPhilippe Mathieu-Daudé qht_statistics_init(&tb_ctx.htable, &hst);
2028cc04fd9SPhilippe Mathieu-Daudé print_qht_statistics(hst, buf);
2038cc04fd9SPhilippe Mathieu-Daudé qht_statistics_destroy(&hst);
2048cc04fd9SPhilippe Mathieu-Daudé
2058cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "\nStatistics:\n");
2068cc04fd9SPhilippe Mathieu-Daudé tcg_dump_flush_info(buf);
2078cc04fd9SPhilippe Mathieu-Daudé }
2088cc04fd9SPhilippe Mathieu-Daudé
tcg_get_stats(AccelState * accel,GString * buf)209*cf4305edSPhilippe Mathieu-Daudé void tcg_get_stats(AccelState *accel, GString *buf)
2108cc04fd9SPhilippe Mathieu-Daudé {
211*cf4305edSPhilippe Mathieu-Daudé dump_accel_info(accel, buf);
2128cc04fd9SPhilippe Mathieu-Daudé dump_exec_info(buf);
2138cc04fd9SPhilippe Mathieu-Daudé dump_drift_info(buf);
2148cc04fd9SPhilippe Mathieu-Daudé }
215*cf4305edSPhilippe Mathieu-Daudé
tcg_dump_stats(GString * buf)216*cf4305edSPhilippe Mathieu-Daudé void tcg_dump_stats(GString *buf)
217*cf4305edSPhilippe Mathieu-Daudé {
218*cf4305edSPhilippe Mathieu-Daudé tcg_get_stats(current_accel(), buf);
219*cf4305edSPhilippe Mathieu-Daudé }
220