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