100c9a5c2SPhilippe Mathieu-Daudé /*
200c9a5c2SPhilippe Mathieu-Daudé * SPDX-License-Identifier: LGPL-2.1-or-later
300c9a5c2SPhilippe Mathieu-Daudé *
400c9a5c2SPhilippe Mathieu-Daudé * QEMU TCG monitor
500c9a5c2SPhilippe Mathieu-Daudé *
600c9a5c2SPhilippe Mathieu-Daudé * Copyright (c) 2003-2005 Fabrice Bellard
700c9a5c2SPhilippe Mathieu-Daudé */
800c9a5c2SPhilippe Mathieu-Daudé
900c9a5c2SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
10e726acd5SPeter Maydell #include "qemu/accel.h"
11*24a4d59aSRichard Henderson #include "qemu/qht.h"
1200c9a5c2SPhilippe Mathieu-Daudé #include "qapi/error.h"
1300c9a5c2SPhilippe Mathieu-Daudé #include "qapi/type-helpers.h"
1400c9a5c2SPhilippe Mathieu-Daudé #include "qapi/qapi-commands-machine.h"
1500c9a5c2SPhilippe Mathieu-Daudé #include "monitor/monitor.h"
1600c9a5c2SPhilippe Mathieu-Daudé #include "sysemu/cpus.h"
1700c9a5c2SPhilippe Mathieu-Daudé #include "sysemu/cpu-timers.h"
1800c9a5c2SPhilippe Mathieu-Daudé #include "sysemu/tcg.h"
19e5b49063SRichard Henderson #include "tcg/tcg.h"
205934660fSPhilippe Mathieu-Daudé #include "internal-common.h"
21*24a4d59aSRichard Henderson #include "tb-context.h"
2200c9a5c2SPhilippe Mathieu-Daudé
2300c9a5c2SPhilippe Mathieu-Daudé
dump_drift_info(GString * buf)2400c9a5c2SPhilippe Mathieu-Daudé static void dump_drift_info(GString *buf)
2500c9a5c2SPhilippe Mathieu-Daudé {
2600c9a5c2SPhilippe Mathieu-Daudé if (!icount_enabled()) {
2700c9a5c2SPhilippe Mathieu-Daudé return;
2800c9a5c2SPhilippe Mathieu-Daudé }
2900c9a5c2SPhilippe Mathieu-Daudé
3000c9a5c2SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
3100c9a5c2SPhilippe Mathieu-Daudé (cpu_get_clock() - icount_get()) / SCALE_MS);
3200c9a5c2SPhilippe Mathieu-Daudé if (icount_align_option) {
3300c9a5c2SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
3400c9a5c2SPhilippe Mathieu-Daudé -max_delay / SCALE_MS);
3500c9a5c2SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
3600c9a5c2SPhilippe Mathieu-Daudé max_advance / SCALE_MS);
3700c9a5c2SPhilippe Mathieu-Daudé } else {
3800c9a5c2SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest delay NA\n");
3900c9a5c2SPhilippe Mathieu-Daudé g_string_append_printf(buf, "Max guest advance NA\n");
4000c9a5c2SPhilippe Mathieu-Daudé }
4100c9a5c2SPhilippe Mathieu-Daudé }
4200c9a5c2SPhilippe Mathieu-Daudé
dump_accel_info(GString * buf)43e726acd5SPeter Maydell static void dump_accel_info(GString *buf)
44e726acd5SPeter Maydell {
45e726acd5SPeter Maydell AccelState *accel = current_accel();
46e726acd5SPeter Maydell bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
47e726acd5SPeter Maydell "one-insn-per-tb",
48e726acd5SPeter Maydell &error_fatal);
49e726acd5SPeter Maydell
50e726acd5SPeter Maydell g_string_append_printf(buf, "Accelerator settings:\n");
51e726acd5SPeter Maydell g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
52e726acd5SPeter Maydell one_insn_per_tb ? "on" : "off");
53e726acd5SPeter Maydell }
54e726acd5SPeter Maydell
print_qht_statistics(struct qht_stats hst,GString * buf)55*24a4d59aSRichard Henderson static void print_qht_statistics(struct qht_stats hst, GString *buf)
56*24a4d59aSRichard Henderson {
57*24a4d59aSRichard Henderson uint32_t hgram_opts;
58*24a4d59aSRichard Henderson size_t hgram_bins;
59*24a4d59aSRichard Henderson char *hgram;
60*24a4d59aSRichard Henderson
61*24a4d59aSRichard Henderson if (!hst.head_buckets) {
62*24a4d59aSRichard Henderson return;
63*24a4d59aSRichard Henderson }
64*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB hash buckets %zu/%zu "
65*24a4d59aSRichard Henderson "(%0.2f%% head buckets used)\n",
66*24a4d59aSRichard Henderson hst.used_head_buckets, hst.head_buckets,
67*24a4d59aSRichard Henderson (double)hst.used_head_buckets /
68*24a4d59aSRichard Henderson hst.head_buckets * 100);
69*24a4d59aSRichard Henderson
70*24a4d59aSRichard Henderson hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
71*24a4d59aSRichard Henderson hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
72*24a4d59aSRichard Henderson if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
73*24a4d59aSRichard Henderson hgram_opts |= QDIST_PR_NODECIMAL;
74*24a4d59aSRichard Henderson }
75*24a4d59aSRichard Henderson hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
76*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. "
77*24a4d59aSRichard Henderson "Histogram: %s\n",
78*24a4d59aSRichard Henderson qdist_avg(&hst.occupancy) * 100, hgram);
79*24a4d59aSRichard Henderson g_free(hgram);
80*24a4d59aSRichard Henderson
81*24a4d59aSRichard Henderson hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
82*24a4d59aSRichard Henderson hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
83*24a4d59aSRichard Henderson if (hgram_bins > 10) {
84*24a4d59aSRichard Henderson hgram_bins = 10;
85*24a4d59aSRichard Henderson } else {
86*24a4d59aSRichard Henderson hgram_bins = 0;
87*24a4d59aSRichard Henderson hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
88*24a4d59aSRichard Henderson }
89*24a4d59aSRichard Henderson hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
90*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
91*24a4d59aSRichard Henderson "Histogram: %s\n",
92*24a4d59aSRichard Henderson qdist_avg(&hst.chain), hgram);
93*24a4d59aSRichard Henderson g_free(hgram);
94*24a4d59aSRichard Henderson }
95*24a4d59aSRichard Henderson
96*24a4d59aSRichard Henderson struct tb_tree_stats {
97*24a4d59aSRichard Henderson size_t nb_tbs;
98*24a4d59aSRichard Henderson size_t host_size;
99*24a4d59aSRichard Henderson size_t target_size;
100*24a4d59aSRichard Henderson size_t max_target_size;
101*24a4d59aSRichard Henderson size_t direct_jmp_count;
102*24a4d59aSRichard Henderson size_t direct_jmp2_count;
103*24a4d59aSRichard Henderson size_t cross_page;
104*24a4d59aSRichard Henderson };
105*24a4d59aSRichard Henderson
tb_tree_stats_iter(gpointer key,gpointer value,gpointer data)106*24a4d59aSRichard Henderson static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
107*24a4d59aSRichard Henderson {
108*24a4d59aSRichard Henderson const TranslationBlock *tb = value;
109*24a4d59aSRichard Henderson struct tb_tree_stats *tst = data;
110*24a4d59aSRichard Henderson
111*24a4d59aSRichard Henderson tst->nb_tbs++;
112*24a4d59aSRichard Henderson tst->host_size += tb->tc.size;
113*24a4d59aSRichard Henderson tst->target_size += tb->size;
114*24a4d59aSRichard Henderson if (tb->size > tst->max_target_size) {
115*24a4d59aSRichard Henderson tst->max_target_size = tb->size;
116*24a4d59aSRichard Henderson }
117*24a4d59aSRichard Henderson if (tb->page_addr[1] != -1) {
118*24a4d59aSRichard Henderson tst->cross_page++;
119*24a4d59aSRichard Henderson }
120*24a4d59aSRichard Henderson if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
121*24a4d59aSRichard Henderson tst->direct_jmp_count++;
122*24a4d59aSRichard Henderson if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
123*24a4d59aSRichard Henderson tst->direct_jmp2_count++;
124*24a4d59aSRichard Henderson }
125*24a4d59aSRichard Henderson }
126*24a4d59aSRichard Henderson return false;
127*24a4d59aSRichard Henderson }
128*24a4d59aSRichard Henderson
tlb_flush_counts(size_t * pfull,size_t * ppart,size_t * pelide)129*24a4d59aSRichard Henderson static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
130*24a4d59aSRichard Henderson {
131*24a4d59aSRichard Henderson CPUState *cpu;
132*24a4d59aSRichard Henderson size_t full = 0, part = 0, elide = 0;
133*24a4d59aSRichard Henderson
134*24a4d59aSRichard Henderson CPU_FOREACH(cpu) {
135*24a4d59aSRichard Henderson full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
136*24a4d59aSRichard Henderson part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
137*24a4d59aSRichard Henderson elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
138*24a4d59aSRichard Henderson }
139*24a4d59aSRichard Henderson *pfull = full;
140*24a4d59aSRichard Henderson *ppart = part;
141*24a4d59aSRichard Henderson *pelide = elide;
142*24a4d59aSRichard Henderson }
143*24a4d59aSRichard Henderson
tcg_dump_info(GString * buf)144*24a4d59aSRichard Henderson static void tcg_dump_info(GString *buf)
145*24a4d59aSRichard Henderson {
146*24a4d59aSRichard Henderson g_string_append_printf(buf, "[TCG profiler not compiled]\n");
147*24a4d59aSRichard Henderson }
148*24a4d59aSRichard Henderson
dump_exec_info(GString * buf)149*24a4d59aSRichard Henderson static void dump_exec_info(GString *buf)
150*24a4d59aSRichard Henderson {
151*24a4d59aSRichard Henderson struct tb_tree_stats tst = {};
152*24a4d59aSRichard Henderson struct qht_stats hst;
153*24a4d59aSRichard Henderson size_t nb_tbs, flush_full, flush_part, flush_elide;
154*24a4d59aSRichard Henderson
155*24a4d59aSRichard Henderson tcg_tb_foreach(tb_tree_stats_iter, &tst);
156*24a4d59aSRichard Henderson nb_tbs = tst.nb_tbs;
157*24a4d59aSRichard Henderson /* XXX: avoid using doubles ? */
158*24a4d59aSRichard Henderson g_string_append_printf(buf, "Translation buffer state:\n");
159*24a4d59aSRichard Henderson /*
160*24a4d59aSRichard Henderson * Report total code size including the padding and TB structs;
161*24a4d59aSRichard Henderson * otherwise users might think "-accel tcg,tb-size" is not honoured.
162*24a4d59aSRichard Henderson * For avg host size we use the precise numbers from tb_tree_stats though.
163*24a4d59aSRichard Henderson */
164*24a4d59aSRichard Henderson g_string_append_printf(buf, "gen code size %zu/%zu\n",
165*24a4d59aSRichard Henderson tcg_code_size(), tcg_code_capacity());
166*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
167*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
168*24a4d59aSRichard Henderson nb_tbs ? tst.target_size / nb_tbs : 0,
169*24a4d59aSRichard Henderson tst.max_target_size);
170*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB avg host size %zu bytes "
171*24a4d59aSRichard Henderson "(expansion ratio: %0.1f)\n",
172*24a4d59aSRichard Henderson nb_tbs ? tst.host_size / nb_tbs : 0,
173*24a4d59aSRichard Henderson tst.target_size ?
174*24a4d59aSRichard Henderson (double)tst.host_size / tst.target_size : 0);
175*24a4d59aSRichard Henderson g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
176*24a4d59aSRichard Henderson tst.cross_page,
177*24a4d59aSRichard Henderson nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
178*24a4d59aSRichard Henderson g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
179*24a4d59aSRichard Henderson "(2 jumps=%zu %zu%%)\n",
180*24a4d59aSRichard Henderson tst.direct_jmp_count,
181*24a4d59aSRichard Henderson nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
182*24a4d59aSRichard Henderson tst.direct_jmp2_count,
183*24a4d59aSRichard Henderson nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
184*24a4d59aSRichard Henderson
185*24a4d59aSRichard Henderson qht_statistics_init(&tb_ctx.htable, &hst);
186*24a4d59aSRichard Henderson print_qht_statistics(hst, buf);
187*24a4d59aSRichard Henderson qht_statistics_destroy(&hst);
188*24a4d59aSRichard Henderson
189*24a4d59aSRichard Henderson g_string_append_printf(buf, "\nStatistics:\n");
190*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB flush count %u\n",
191*24a4d59aSRichard Henderson qatomic_read(&tb_ctx.tb_flush_count));
192*24a4d59aSRichard Henderson g_string_append_printf(buf, "TB invalidate count %u\n",
193*24a4d59aSRichard Henderson qatomic_read(&tb_ctx.tb_phys_invalidate_count));
194*24a4d59aSRichard Henderson
195*24a4d59aSRichard Henderson tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
196*24a4d59aSRichard Henderson g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
197*24a4d59aSRichard Henderson g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
198*24a4d59aSRichard Henderson g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
199*24a4d59aSRichard Henderson tcg_dump_info(buf);
200*24a4d59aSRichard Henderson }
201*24a4d59aSRichard Henderson
qmp_x_query_jit(Error ** errp)20200c9a5c2SPhilippe Mathieu-Daudé HumanReadableText *qmp_x_query_jit(Error **errp)
20300c9a5c2SPhilippe Mathieu-Daudé {
20400c9a5c2SPhilippe Mathieu-Daudé g_autoptr(GString) buf = g_string_new("");
20500c9a5c2SPhilippe Mathieu-Daudé
20600c9a5c2SPhilippe Mathieu-Daudé if (!tcg_enabled()) {
20700c9a5c2SPhilippe Mathieu-Daudé error_setg(errp, "JIT information is only available with accel=tcg");
20800c9a5c2SPhilippe Mathieu-Daudé return NULL;
20900c9a5c2SPhilippe Mathieu-Daudé }
21000c9a5c2SPhilippe Mathieu-Daudé
211e726acd5SPeter Maydell dump_accel_info(buf);
21200c9a5c2SPhilippe Mathieu-Daudé dump_exec_info(buf);
21300c9a5c2SPhilippe Mathieu-Daudé dump_drift_info(buf);
21400c9a5c2SPhilippe Mathieu-Daudé
21500c9a5c2SPhilippe Mathieu-Daudé return human_readable_text_from_str(buf);
21600c9a5c2SPhilippe Mathieu-Daudé }
21700c9a5c2SPhilippe Mathieu-Daudé
tcg_dump_op_count(GString * buf)218*24a4d59aSRichard Henderson static void tcg_dump_op_count(GString *buf)
219*24a4d59aSRichard Henderson {
220*24a4d59aSRichard Henderson g_string_append_printf(buf, "[TCG profiler not compiled]\n");
221*24a4d59aSRichard Henderson }
222*24a4d59aSRichard Henderson
qmp_x_query_opcount(Error ** errp)22300c9a5c2SPhilippe Mathieu-Daudé HumanReadableText *qmp_x_query_opcount(Error **errp)
22400c9a5c2SPhilippe Mathieu-Daudé {
22500c9a5c2SPhilippe Mathieu-Daudé g_autoptr(GString) buf = g_string_new("");
22600c9a5c2SPhilippe Mathieu-Daudé
22700c9a5c2SPhilippe Mathieu-Daudé if (!tcg_enabled()) {
22800c9a5c2SPhilippe Mathieu-Daudé error_setg(errp,
22900c9a5c2SPhilippe Mathieu-Daudé "Opcode count information is only available with accel=tcg");
23000c9a5c2SPhilippe Mathieu-Daudé return NULL;
23100c9a5c2SPhilippe Mathieu-Daudé }
23200c9a5c2SPhilippe Mathieu-Daudé
23300c9a5c2SPhilippe Mathieu-Daudé tcg_dump_op_count(buf);
23400c9a5c2SPhilippe Mathieu-Daudé
23500c9a5c2SPhilippe Mathieu-Daudé return human_readable_text_from_str(buf);
23600c9a5c2SPhilippe Mathieu-Daudé }
23700c9a5c2SPhilippe Mathieu-Daudé
hmp_tcg_register(void)23800c9a5c2SPhilippe Mathieu-Daudé static void hmp_tcg_register(void)
23900c9a5c2SPhilippe Mathieu-Daudé {
24000c9a5c2SPhilippe Mathieu-Daudé monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
24100c9a5c2SPhilippe Mathieu-Daudé monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount);
24200c9a5c2SPhilippe Mathieu-Daudé }
24300c9a5c2SPhilippe Mathieu-Daudé
24400c9a5c2SPhilippe Mathieu-Daudé type_init(hmp_tcg_register);
245