1*8cc04fd9SPhilippe Mathieu-Daudé /* 2*8cc04fd9SPhilippe Mathieu-Daudé * SPDX-License-Identifier: LGPL-2.1-or-later 3*8cc04fd9SPhilippe Mathieu-Daudé * 4*8cc04fd9SPhilippe Mathieu-Daudé * QEMU TCG statistics 5*8cc04fd9SPhilippe Mathieu-Daudé * 6*8cc04fd9SPhilippe Mathieu-Daudé * Copyright (c) 2003-2005 Fabrice Bellard 7*8cc04fd9SPhilippe Mathieu-Daudé */ 8*8cc04fd9SPhilippe Mathieu-Daudé 9*8cc04fd9SPhilippe Mathieu-Daudé #include "qemu/osdep.h" 10*8cc04fd9SPhilippe Mathieu-Daudé #include "qemu/accel.h" 11*8cc04fd9SPhilippe Mathieu-Daudé #include "qemu/qht.h" 12*8cc04fd9SPhilippe Mathieu-Daudé #include "qapi/error.h" 13*8cc04fd9SPhilippe Mathieu-Daudé #include "system/cpu-timers.h" 14*8cc04fd9SPhilippe Mathieu-Daudé #include "exec/icount.h" 15*8cc04fd9SPhilippe Mathieu-Daudé #include "hw/core/cpu.h" 16*8cc04fd9SPhilippe Mathieu-Daudé #include "tcg/tcg.h" 17*8cc04fd9SPhilippe Mathieu-Daudé #include "internal-common.h" 18*8cc04fd9SPhilippe Mathieu-Daudé #include "tb-context.h" 19*8cc04fd9SPhilippe Mathieu-Daudé #include <math.h> 20*8cc04fd9SPhilippe Mathieu-Daudé 21*8cc04fd9SPhilippe Mathieu-Daudé static void dump_drift_info(GString *buf) 22*8cc04fd9SPhilippe Mathieu-Daudé { 23*8cc04fd9SPhilippe Mathieu-Daudé if (!icount_enabled()) { 24*8cc04fd9SPhilippe Mathieu-Daudé return; 25*8cc04fd9SPhilippe Mathieu-Daudé } 26*8cc04fd9SPhilippe Mathieu-Daudé 27*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", 28*8cc04fd9SPhilippe Mathieu-Daudé (cpu_get_clock() - icount_get()) / SCALE_MS); 29*8cc04fd9SPhilippe Mathieu-Daudé if (icount_align_option) { 30*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", 31*8cc04fd9SPhilippe Mathieu-Daudé -max_delay / SCALE_MS); 32*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", 33*8cc04fd9SPhilippe Mathieu-Daudé max_advance / SCALE_MS); 34*8cc04fd9SPhilippe Mathieu-Daudé } else { 35*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay NA\n"); 36*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance NA\n"); 37*8cc04fd9SPhilippe Mathieu-Daudé } 38*8cc04fd9SPhilippe Mathieu-Daudé } 39*8cc04fd9SPhilippe Mathieu-Daudé 40*8cc04fd9SPhilippe Mathieu-Daudé static void dump_accel_info(GString *buf) 41*8cc04fd9SPhilippe Mathieu-Daudé { 42*8cc04fd9SPhilippe Mathieu-Daudé AccelState *accel = current_accel(); 43*8cc04fd9SPhilippe Mathieu-Daudé bool one_insn_per_tb = object_property_get_bool(OBJECT(accel), 44*8cc04fd9SPhilippe Mathieu-Daudé "one-insn-per-tb", 45*8cc04fd9SPhilippe Mathieu-Daudé &error_fatal); 46*8cc04fd9SPhilippe Mathieu-Daudé 47*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Accelerator settings:\n"); 48*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "one-insn-per-tb: %s\n\n", 49*8cc04fd9SPhilippe Mathieu-Daudé one_insn_per_tb ? "on" : "off"); 50*8cc04fd9SPhilippe Mathieu-Daudé } 51*8cc04fd9SPhilippe Mathieu-Daudé 52*8cc04fd9SPhilippe Mathieu-Daudé static void print_qht_statistics(struct qht_stats hst, GString *buf) 53*8cc04fd9SPhilippe Mathieu-Daudé { 54*8cc04fd9SPhilippe Mathieu-Daudé uint32_t hgram_opts; 55*8cc04fd9SPhilippe Mathieu-Daudé size_t hgram_bins; 56*8cc04fd9SPhilippe Mathieu-Daudé char *hgram; 57*8cc04fd9SPhilippe Mathieu-Daudé double avg; 58*8cc04fd9SPhilippe Mathieu-Daudé 59*8cc04fd9SPhilippe Mathieu-Daudé if (!hst.head_buckets) { 60*8cc04fd9SPhilippe Mathieu-Daudé return; 61*8cc04fd9SPhilippe Mathieu-Daudé } 62*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash buckets %zu/%zu " 63*8cc04fd9SPhilippe Mathieu-Daudé "(%0.2f%% head buckets used)\n", 64*8cc04fd9SPhilippe Mathieu-Daudé hst.used_head_buckets, hst.head_buckets, 65*8cc04fd9SPhilippe Mathieu-Daudé (double)hst.used_head_buckets / 66*8cc04fd9SPhilippe Mathieu-Daudé hst.head_buckets * 100); 67*8cc04fd9SPhilippe Mathieu-Daudé 68*8cc04fd9SPhilippe Mathieu-Daudé hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; 69*8cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; 70*8cc04fd9SPhilippe Mathieu-Daudé if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { 71*8cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_NODECIMAL; 72*8cc04fd9SPhilippe Mathieu-Daudé } 73*8cc04fd9SPhilippe Mathieu-Daudé hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); 74*8cc04fd9SPhilippe Mathieu-Daudé avg = qdist_avg(&hst.occupancy); 75*8cc04fd9SPhilippe Mathieu-Daudé if (!isnan(avg)) { 76*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash occupancy " 77*8cc04fd9SPhilippe Mathieu-Daudé "%0.2f%% avg chain occ. " 78*8cc04fd9SPhilippe Mathieu-Daudé "Histogram: %s\n", 79*8cc04fd9SPhilippe Mathieu-Daudé avg * 100, hgram); 80*8cc04fd9SPhilippe Mathieu-Daudé } 81*8cc04fd9SPhilippe Mathieu-Daudé g_free(hgram); 82*8cc04fd9SPhilippe Mathieu-Daudé 83*8cc04fd9SPhilippe Mathieu-Daudé hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; 84*8cc04fd9SPhilippe Mathieu-Daudé hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); 85*8cc04fd9SPhilippe Mathieu-Daudé if (hgram_bins > 10) { 86*8cc04fd9SPhilippe Mathieu-Daudé hgram_bins = 10; 87*8cc04fd9SPhilippe Mathieu-Daudé } else { 88*8cc04fd9SPhilippe Mathieu-Daudé hgram_bins = 0; 89*8cc04fd9SPhilippe Mathieu-Daudé hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; 90*8cc04fd9SPhilippe Mathieu-Daudé } 91*8cc04fd9SPhilippe Mathieu-Daudé hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); 92*8cc04fd9SPhilippe Mathieu-Daudé avg = qdist_avg(&hst.chain); 93*8cc04fd9SPhilippe Mathieu-Daudé if (!isnan(avg)) { 94*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " 95*8cc04fd9SPhilippe Mathieu-Daudé "Histogram: %s\n", 96*8cc04fd9SPhilippe Mathieu-Daudé avg, hgram); 97*8cc04fd9SPhilippe Mathieu-Daudé } 98*8cc04fd9SPhilippe Mathieu-Daudé g_free(hgram); 99*8cc04fd9SPhilippe Mathieu-Daudé } 100*8cc04fd9SPhilippe Mathieu-Daudé 101*8cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats { 102*8cc04fd9SPhilippe Mathieu-Daudé size_t nb_tbs; 103*8cc04fd9SPhilippe Mathieu-Daudé size_t host_size; 104*8cc04fd9SPhilippe Mathieu-Daudé size_t target_size; 105*8cc04fd9SPhilippe Mathieu-Daudé size_t max_target_size; 106*8cc04fd9SPhilippe Mathieu-Daudé size_t direct_jmp_count; 107*8cc04fd9SPhilippe Mathieu-Daudé size_t direct_jmp2_count; 108*8cc04fd9SPhilippe Mathieu-Daudé size_t cross_page; 109*8cc04fd9SPhilippe Mathieu-Daudé }; 110*8cc04fd9SPhilippe Mathieu-Daudé 111*8cc04fd9SPhilippe Mathieu-Daudé static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) 112*8cc04fd9SPhilippe Mathieu-Daudé { 113*8cc04fd9SPhilippe Mathieu-Daudé const TranslationBlock *tb = value; 114*8cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats *tst = data; 115*8cc04fd9SPhilippe Mathieu-Daudé 116*8cc04fd9SPhilippe Mathieu-Daudé tst->nb_tbs++; 117*8cc04fd9SPhilippe Mathieu-Daudé tst->host_size += tb->tc.size; 118*8cc04fd9SPhilippe Mathieu-Daudé tst->target_size += tb->size; 119*8cc04fd9SPhilippe Mathieu-Daudé if (tb->size > tst->max_target_size) { 120*8cc04fd9SPhilippe Mathieu-Daudé tst->max_target_size = tb->size; 121*8cc04fd9SPhilippe Mathieu-Daudé } 122*8cc04fd9SPhilippe Mathieu-Daudé #ifndef CONFIG_USER_ONLY 123*8cc04fd9SPhilippe Mathieu-Daudé if (tb->page_addr[1] != -1) { 124*8cc04fd9SPhilippe Mathieu-Daudé tst->cross_page++; 125*8cc04fd9SPhilippe Mathieu-Daudé } 126*8cc04fd9SPhilippe Mathieu-Daudé #endif 127*8cc04fd9SPhilippe Mathieu-Daudé if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { 128*8cc04fd9SPhilippe Mathieu-Daudé tst->direct_jmp_count++; 129*8cc04fd9SPhilippe Mathieu-Daudé if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { 130*8cc04fd9SPhilippe Mathieu-Daudé tst->direct_jmp2_count++; 131*8cc04fd9SPhilippe Mathieu-Daudé } 132*8cc04fd9SPhilippe Mathieu-Daudé } 133*8cc04fd9SPhilippe Mathieu-Daudé return false; 134*8cc04fd9SPhilippe Mathieu-Daudé } 135*8cc04fd9SPhilippe Mathieu-Daudé 136*8cc04fd9SPhilippe Mathieu-Daudé static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) 137*8cc04fd9SPhilippe Mathieu-Daudé { 138*8cc04fd9SPhilippe Mathieu-Daudé CPUState *cpu; 139*8cc04fd9SPhilippe Mathieu-Daudé size_t full = 0, part = 0, elide = 0; 140*8cc04fd9SPhilippe Mathieu-Daudé 141*8cc04fd9SPhilippe Mathieu-Daudé CPU_FOREACH(cpu) { 142*8cc04fd9SPhilippe Mathieu-Daudé full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); 143*8cc04fd9SPhilippe Mathieu-Daudé part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); 144*8cc04fd9SPhilippe Mathieu-Daudé elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); 145*8cc04fd9SPhilippe Mathieu-Daudé } 146*8cc04fd9SPhilippe Mathieu-Daudé *pfull = full; 147*8cc04fd9SPhilippe Mathieu-Daudé *ppart = part; 148*8cc04fd9SPhilippe Mathieu-Daudé *pelide = elide; 149*8cc04fd9SPhilippe Mathieu-Daudé } 150*8cc04fd9SPhilippe Mathieu-Daudé 151*8cc04fd9SPhilippe Mathieu-Daudé static void tcg_dump_flush_info(GString *buf) 152*8cc04fd9SPhilippe Mathieu-Daudé { 153*8cc04fd9SPhilippe Mathieu-Daudé size_t flush_full, flush_part, flush_elide; 154*8cc04fd9SPhilippe Mathieu-Daudé 155*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB flush count %u\n", 156*8cc04fd9SPhilippe Mathieu-Daudé qatomic_read(&tb_ctx.tb_flush_count)); 157*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB invalidate count %u\n", 158*8cc04fd9SPhilippe Mathieu-Daudé qatomic_read(&tb_ctx.tb_phys_invalidate_count)); 159*8cc04fd9SPhilippe Mathieu-Daudé 160*8cc04fd9SPhilippe Mathieu-Daudé tlb_flush_counts(&flush_full, &flush_part, &flush_elide); 161*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); 162*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); 163*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); 164*8cc04fd9SPhilippe Mathieu-Daudé } 165*8cc04fd9SPhilippe Mathieu-Daudé 166*8cc04fd9SPhilippe Mathieu-Daudé static void dump_exec_info(GString *buf) 167*8cc04fd9SPhilippe Mathieu-Daudé { 168*8cc04fd9SPhilippe Mathieu-Daudé struct tb_tree_stats tst = {}; 169*8cc04fd9SPhilippe Mathieu-Daudé struct qht_stats hst; 170*8cc04fd9SPhilippe Mathieu-Daudé size_t nb_tbs; 171*8cc04fd9SPhilippe Mathieu-Daudé 172*8cc04fd9SPhilippe Mathieu-Daudé tcg_tb_foreach(tb_tree_stats_iter, &tst); 173*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs = tst.nb_tbs; 174*8cc04fd9SPhilippe Mathieu-Daudé /* XXX: avoid using doubles ? */ 175*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Translation buffer state:\n"); 176*8cc04fd9SPhilippe Mathieu-Daudé /* 177*8cc04fd9SPhilippe Mathieu-Daudé * Report total code size including the padding and TB structs; 178*8cc04fd9SPhilippe Mathieu-Daudé * otherwise users might think "-accel tcg,tb-size" is not honoured. 179*8cc04fd9SPhilippe Mathieu-Daudé * For avg host size we use the precise numbers from tb_tree_stats though. 180*8cc04fd9SPhilippe Mathieu-Daudé */ 181*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "gen code size %zu/%zu\n", 182*8cc04fd9SPhilippe Mathieu-Daudé tcg_code_size(), tcg_code_capacity()); 183*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB count %zu\n", nb_tbs); 184*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", 185*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? tst.target_size / nb_tbs : 0, 186*8cc04fd9SPhilippe Mathieu-Daudé tst.max_target_size); 187*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "TB avg host size %zu bytes " 188*8cc04fd9SPhilippe Mathieu-Daudé "(expansion ratio: %0.1f)\n", 189*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? tst.host_size / nb_tbs : 0, 190*8cc04fd9SPhilippe Mathieu-Daudé tst.target_size ? 191*8cc04fd9SPhilippe Mathieu-Daudé (double)tst.host_size / tst.target_size : 0); 192*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", 193*8cc04fd9SPhilippe Mathieu-Daudé tst.cross_page, 194*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); 195*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "direct jump count %zu (%zu%%) " 196*8cc04fd9SPhilippe Mathieu-Daudé "(2 jumps=%zu %zu%%)\n", 197*8cc04fd9SPhilippe Mathieu-Daudé tst.direct_jmp_count, 198*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, 199*8cc04fd9SPhilippe Mathieu-Daudé tst.direct_jmp2_count, 200*8cc04fd9SPhilippe Mathieu-Daudé nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); 201*8cc04fd9SPhilippe Mathieu-Daudé 202*8cc04fd9SPhilippe Mathieu-Daudé qht_statistics_init(&tb_ctx.htable, &hst); 203*8cc04fd9SPhilippe Mathieu-Daudé print_qht_statistics(hst, buf); 204*8cc04fd9SPhilippe Mathieu-Daudé qht_statistics_destroy(&hst); 205*8cc04fd9SPhilippe Mathieu-Daudé 206*8cc04fd9SPhilippe Mathieu-Daudé g_string_append_printf(buf, "\nStatistics:\n"); 207*8cc04fd9SPhilippe Mathieu-Daudé tcg_dump_flush_info(buf); 208*8cc04fd9SPhilippe Mathieu-Daudé } 209*8cc04fd9SPhilippe Mathieu-Daudé 210*8cc04fd9SPhilippe Mathieu-Daudé void tcg_dump_stats(GString *buf) 211*8cc04fd9SPhilippe Mathieu-Daudé { 212*8cc04fd9SPhilippe Mathieu-Daudé dump_accel_info(buf); 213*8cc04fd9SPhilippe Mathieu-Daudé dump_exec_info(buf); 214*8cc04fd9SPhilippe Mathieu-Daudé dump_drift_info(buf); 215*8cc04fd9SPhilippe Mathieu-Daudé } 216