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