xref: /openbmc/qemu/tests/tcg/plugins/insn.c (revision 1b4c136b)
1*1b4c136bSAlex Bennée /*
2*1b4c136bSAlex Bennée  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
3*1b4c136bSAlex Bennée  *
4*1b4c136bSAlex Bennée  * License: GNU GPL, version 2 or later.
5*1b4c136bSAlex Bennée  *   See the COPYING file in the top-level directory.
6*1b4c136bSAlex Bennée  */
7*1b4c136bSAlex Bennée #include <inttypes.h>
8*1b4c136bSAlex Bennée #include <assert.h>
9*1b4c136bSAlex Bennée #include <stdlib.h>
10*1b4c136bSAlex Bennée #include <string.h>
11*1b4c136bSAlex Bennée #include <unistd.h>
12*1b4c136bSAlex Bennée #include <stdio.h>
13*1b4c136bSAlex Bennée #include <glib.h>
14*1b4c136bSAlex Bennée 
15*1b4c136bSAlex Bennée #include <qemu-plugin.h>
16*1b4c136bSAlex Bennée 
17*1b4c136bSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
18*1b4c136bSAlex Bennée 
19*1b4c136bSAlex Bennée static qemu_plugin_u64 insn_count;
20*1b4c136bSAlex Bennée 
21*1b4c136bSAlex Bennée static bool do_inline;
22*1b4c136bSAlex Bennée static bool do_size;
23*1b4c136bSAlex Bennée static bool do_trace;
24*1b4c136bSAlex Bennée static GArray *sizes;
25*1b4c136bSAlex Bennée 
26*1b4c136bSAlex Bennée typedef struct {
27*1b4c136bSAlex Bennée     uint64_t hits;
28*1b4c136bSAlex Bennée     uint64_t last_hit;
29*1b4c136bSAlex Bennée     uint64_t total_delta;
30*1b4c136bSAlex Bennée } MatchCount;
31*1b4c136bSAlex Bennée 
32*1b4c136bSAlex Bennée typedef struct {
33*1b4c136bSAlex Bennée     char *match_string;
34*1b4c136bSAlex Bennée     struct qemu_plugin_scoreboard *counts; /* MatchCount */
35*1b4c136bSAlex Bennée } Match;
36*1b4c136bSAlex Bennée 
37*1b4c136bSAlex Bennée static GArray *matches;
38*1b4c136bSAlex Bennée 
39*1b4c136bSAlex Bennée typedef struct {
40*1b4c136bSAlex Bennée     Match *match;
41*1b4c136bSAlex Bennée     uint64_t vaddr;
42*1b4c136bSAlex Bennée     uint64_t hits;
43*1b4c136bSAlex Bennée     char *disas;
44*1b4c136bSAlex Bennée } Instruction;
45*1b4c136bSAlex Bennée 
46*1b4c136bSAlex Bennée /* A hash table to hold matched instructions */
47*1b4c136bSAlex Bennée static GHashTable *match_insn_records;
48*1b4c136bSAlex Bennée static GMutex match_hash_lock;
49*1b4c136bSAlex Bennée 
50*1b4c136bSAlex Bennée 
get_insn_record(const char * disas,uint64_t vaddr,Match * m)51*1b4c136bSAlex Bennée static Instruction * get_insn_record(const char *disas, uint64_t vaddr, Match *m)
52*1b4c136bSAlex Bennée {
53*1b4c136bSAlex Bennée     g_autofree char *str_hash = g_strdup_printf("%"PRIx64" %s", vaddr, disas);
54*1b4c136bSAlex Bennée     Instruction *record;
55*1b4c136bSAlex Bennée 
56*1b4c136bSAlex Bennée     g_mutex_lock(&match_hash_lock);
57*1b4c136bSAlex Bennée 
58*1b4c136bSAlex Bennée     if (!match_insn_records) {
59*1b4c136bSAlex Bennée         match_insn_records = g_hash_table_new(g_str_hash, g_str_equal);
60*1b4c136bSAlex Bennée     }
61*1b4c136bSAlex Bennée 
62*1b4c136bSAlex Bennée     record = g_hash_table_lookup(match_insn_records, str_hash);
63*1b4c136bSAlex Bennée 
64*1b4c136bSAlex Bennée     if (!record) {
65*1b4c136bSAlex Bennée         g_autoptr(GString) ts = g_string_new(str_hash);
66*1b4c136bSAlex Bennée 
67*1b4c136bSAlex Bennée         record = g_new0(Instruction, 1);
68*1b4c136bSAlex Bennée         record->disas = g_strdup(disas);
69*1b4c136bSAlex Bennée         record->vaddr = vaddr;
70*1b4c136bSAlex Bennée         record->match = m;
71*1b4c136bSAlex Bennée 
72*1b4c136bSAlex Bennée         g_hash_table_insert(match_insn_records, str_hash, record);
73*1b4c136bSAlex Bennée 
74*1b4c136bSAlex Bennée         g_string_prepend(ts, "Created record for: ");
75*1b4c136bSAlex Bennée         g_string_append(ts, "\n");
76*1b4c136bSAlex Bennée         qemu_plugin_outs(ts->str);
77*1b4c136bSAlex Bennée     }
78*1b4c136bSAlex Bennée 
79*1b4c136bSAlex Bennée     g_mutex_unlock(&match_hash_lock);
80*1b4c136bSAlex Bennée 
81*1b4c136bSAlex Bennée     return record;
82*1b4c136bSAlex Bennée }
83*1b4c136bSAlex Bennée 
84*1b4c136bSAlex Bennée /*
85*1b4c136bSAlex Bennée  * Initialise a new vcpu with reading the register list
86*1b4c136bSAlex Bennée  */
vcpu_init(qemu_plugin_id_t id,unsigned int vcpu_index)87*1b4c136bSAlex Bennée static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
88*1b4c136bSAlex Bennée {
89*1b4c136bSAlex Bennée     g_autoptr(GArray) reg_list = qemu_plugin_get_registers();
90*1b4c136bSAlex Bennée     g_autoptr(GByteArray) reg_value = g_byte_array_new();
91*1b4c136bSAlex Bennée 
92*1b4c136bSAlex Bennée     if (reg_list) {
93*1b4c136bSAlex Bennée         for (int i = 0; i < reg_list->len; i++) {
94*1b4c136bSAlex Bennée             qemu_plugin_reg_descriptor *rd = &g_array_index(
95*1b4c136bSAlex Bennée                 reg_list, qemu_plugin_reg_descriptor, i);
96*1b4c136bSAlex Bennée             int count = qemu_plugin_read_register(rd->handle, reg_value);
97*1b4c136bSAlex Bennée             g_assert(count > 0);
98*1b4c136bSAlex Bennée         }
99*1b4c136bSAlex Bennée     }
100*1b4c136bSAlex Bennée }
101*1b4c136bSAlex Bennée 
102*1b4c136bSAlex Bennée 
vcpu_insn_exec_before(unsigned int cpu_index,void * udata)103*1b4c136bSAlex Bennée static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
104*1b4c136bSAlex Bennée {
105*1b4c136bSAlex Bennée     qemu_plugin_u64_add(insn_count, cpu_index, 1);
106*1b4c136bSAlex Bennée }
107*1b4c136bSAlex Bennée 
vcpu_insn_matched_exec_before(unsigned int cpu_index,void * udata)108*1b4c136bSAlex Bennée static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata)
109*1b4c136bSAlex Bennée {
110*1b4c136bSAlex Bennée     Instruction *insn = (Instruction *) udata;
111*1b4c136bSAlex Bennée     Match *insn_match = insn->match;
112*1b4c136bSAlex Bennée     MatchCount *match = qemu_plugin_scoreboard_find(insn_match->counts,
113*1b4c136bSAlex Bennée                                                     cpu_index);
114*1b4c136bSAlex Bennée 
115*1b4c136bSAlex Bennée     insn->hits++;
116*1b4c136bSAlex Bennée 
117*1b4c136bSAlex Bennée     uint64_t icount = qemu_plugin_u64_get(insn_count, cpu_index);
118*1b4c136bSAlex Bennée     uint64_t delta = icount - match->last_hit;
119*1b4c136bSAlex Bennée 
120*1b4c136bSAlex Bennée     match->hits++;
121*1b4c136bSAlex Bennée     match->total_delta += delta;
122*1b4c136bSAlex Bennée     match->last_hit = icount;
123*1b4c136bSAlex Bennée 
124*1b4c136bSAlex Bennée     if (do_trace) {
125*1b4c136bSAlex Bennée         g_autoptr(GString) ts = g_string_new("");
126*1b4c136bSAlex Bennée         g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits",
127*1b4c136bSAlex Bennée                                insn->vaddr, insn->disas, insn->hits);
128*1b4c136bSAlex Bennée         g_string_append_printf(ts,
129*1b4c136bSAlex Bennée                                " , cpu %u,"
130*1b4c136bSAlex Bennée                                " %"PRId64" match hits,"
131*1b4c136bSAlex Bennée                                " Δ+%"PRId64 " since last match,"
132*1b4c136bSAlex Bennée                                " %"PRId64 " avg insns/match\n",
133*1b4c136bSAlex Bennée                                cpu_index,
134*1b4c136bSAlex Bennée                                match->hits, delta,
135*1b4c136bSAlex Bennée                                match->total_delta / match->hits);
136*1b4c136bSAlex Bennée 
137*1b4c136bSAlex Bennée         qemu_plugin_outs(ts->str);
138*1b4c136bSAlex Bennée     }
139*1b4c136bSAlex Bennée }
140*1b4c136bSAlex Bennée 
vcpu_tb_trans(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)141*1b4c136bSAlex Bennée static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
142*1b4c136bSAlex Bennée {
143*1b4c136bSAlex Bennée     size_t n = qemu_plugin_tb_n_insns(tb);
144*1b4c136bSAlex Bennée     size_t i;
145*1b4c136bSAlex Bennée 
146*1b4c136bSAlex Bennée     for (i = 0; i < n; i++) {
147*1b4c136bSAlex Bennée         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
148*1b4c136bSAlex Bennée 
149*1b4c136bSAlex Bennée         if (do_inline) {
150*1b4c136bSAlex Bennée             qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
151*1b4c136bSAlex Bennée                 insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1);
152*1b4c136bSAlex Bennée         } else {
153*1b4c136bSAlex Bennée             uint64_t vaddr = qemu_plugin_insn_vaddr(insn);
154*1b4c136bSAlex Bennée             qemu_plugin_register_vcpu_insn_exec_cb(
155*1b4c136bSAlex Bennée                 insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS,
156*1b4c136bSAlex Bennée                 GUINT_TO_POINTER(vaddr));
157*1b4c136bSAlex Bennée         }
158*1b4c136bSAlex Bennée 
159*1b4c136bSAlex Bennée         if (do_size) {
160*1b4c136bSAlex Bennée             size_t sz = qemu_plugin_insn_size(insn);
161*1b4c136bSAlex Bennée             if (sz > sizes->len) {
162*1b4c136bSAlex Bennée                 g_array_set_size(sizes, sz);
163*1b4c136bSAlex Bennée             }
164*1b4c136bSAlex Bennée             unsigned long *cnt = &g_array_index(sizes, unsigned long, sz);
165*1b4c136bSAlex Bennée             (*cnt)++;
166*1b4c136bSAlex Bennée         }
167*1b4c136bSAlex Bennée 
168*1b4c136bSAlex Bennée         /*
169*1b4c136bSAlex Bennée          * If we are tracking certain instructions we will need more
170*1b4c136bSAlex Bennée          * information about the instruction which we also need to
171*1b4c136bSAlex Bennée          * save if there is a hit.
172*1b4c136bSAlex Bennée          *
173*1b4c136bSAlex Bennée          * We only want one record for each occurrence of the matched
174*1b4c136bSAlex Bennée          * instruction.
175*1b4c136bSAlex Bennée          */
176*1b4c136bSAlex Bennée         if (matches->len) {
177*1b4c136bSAlex Bennée             char *insn_disas = qemu_plugin_insn_disas(insn);
178*1b4c136bSAlex Bennée             for (int j = 0; j < matches->len; j++) {
179*1b4c136bSAlex Bennée                 Match *m = &g_array_index(matches, Match, j);
180*1b4c136bSAlex Bennée                 if (g_str_has_prefix(insn_disas, m->match_string)) {
181*1b4c136bSAlex Bennée                     Instruction *rec = get_insn_record(insn_disas,
182*1b4c136bSAlex Bennée                                                        qemu_plugin_insn_vaddr(insn),
183*1b4c136bSAlex Bennée                                                        m);
184*1b4c136bSAlex Bennée 
185*1b4c136bSAlex Bennée                     qemu_plugin_register_vcpu_insn_exec_cb(
186*1b4c136bSAlex Bennée                         insn, vcpu_insn_matched_exec_before,
187*1b4c136bSAlex Bennée                         QEMU_PLUGIN_CB_NO_REGS, rec);
188*1b4c136bSAlex Bennée                 }
189*1b4c136bSAlex Bennée             }
190*1b4c136bSAlex Bennée             g_free(insn_disas);
191*1b4c136bSAlex Bennée         }
192*1b4c136bSAlex Bennée     }
193*1b4c136bSAlex Bennée }
194*1b4c136bSAlex Bennée 
plugin_exit(qemu_plugin_id_t id,void * p)195*1b4c136bSAlex Bennée static void plugin_exit(qemu_plugin_id_t id, void *p)
196*1b4c136bSAlex Bennée {
197*1b4c136bSAlex Bennée     g_autoptr(GString) out = g_string_new(NULL);
198*1b4c136bSAlex Bennée     int i;
199*1b4c136bSAlex Bennée 
200*1b4c136bSAlex Bennée     if (do_size) {
201*1b4c136bSAlex Bennée         for (i = 0; i <= sizes->len; i++) {
202*1b4c136bSAlex Bennée             unsigned long *cnt = &g_array_index(sizes, unsigned long, i);
203*1b4c136bSAlex Bennée             if (*cnt) {
204*1b4c136bSAlex Bennée                 g_string_append_printf(out,
205*1b4c136bSAlex Bennée                                        "len %d bytes: %ld insns\n", i, *cnt);
206*1b4c136bSAlex Bennée             }
207*1b4c136bSAlex Bennée         }
208*1b4c136bSAlex Bennée     } else {
209*1b4c136bSAlex Bennée         for (i = 0; i < qemu_plugin_num_vcpus(); i++) {
210*1b4c136bSAlex Bennée             g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n",
211*1b4c136bSAlex Bennée                                    i, qemu_plugin_u64_get(insn_count, i));
212*1b4c136bSAlex Bennée         }
213*1b4c136bSAlex Bennée         g_string_append_printf(out, "total insns: %" PRIu64 "\n",
214*1b4c136bSAlex Bennée                                qemu_plugin_u64_sum(insn_count));
215*1b4c136bSAlex Bennée     }
216*1b4c136bSAlex Bennée     qemu_plugin_outs(out->str);
217*1b4c136bSAlex Bennée     qemu_plugin_scoreboard_free(insn_count.score);
218*1b4c136bSAlex Bennée 
219*1b4c136bSAlex Bennée     g_mutex_lock(&match_hash_lock);
220*1b4c136bSAlex Bennée 
221*1b4c136bSAlex Bennée     for (i = 0; i < matches->len; ++i) {
222*1b4c136bSAlex Bennée         Match *m = &g_array_index(matches, Match, i);
223*1b4c136bSAlex Bennée         GHashTableIter iter;
224*1b4c136bSAlex Bennée         Instruction *record;
225*1b4c136bSAlex Bennée         qemu_plugin_u64 hit_e = qemu_plugin_scoreboard_u64_in_struct(m->counts, MatchCount, hits);
226*1b4c136bSAlex Bennée         uint64_t hits = qemu_plugin_u64_sum(hit_e);
227*1b4c136bSAlex Bennée 
228*1b4c136bSAlex Bennée         g_string_printf(out, "Match: %s, hits %"PRId64"\n", m->match_string, hits);
229*1b4c136bSAlex Bennée         qemu_plugin_outs(out->str);
230*1b4c136bSAlex Bennée 
231*1b4c136bSAlex Bennée         g_hash_table_iter_init(&iter, match_insn_records);
232*1b4c136bSAlex Bennée         while (g_hash_table_iter_next(&iter, NULL, (void **)&record)) {
233*1b4c136bSAlex Bennée             if (record->match == m) {
234*1b4c136bSAlex Bennée                 g_string_printf(out,
235*1b4c136bSAlex Bennée                                 "  %"PRIx64": %s (hits %"PRId64")\n",
236*1b4c136bSAlex Bennée                                 record->vaddr,
237*1b4c136bSAlex Bennée                                 record->disas,
238*1b4c136bSAlex Bennée                                 record->hits);
239*1b4c136bSAlex Bennée                 qemu_plugin_outs(out->str);
240*1b4c136bSAlex Bennée             }
241*1b4c136bSAlex Bennée         }
242*1b4c136bSAlex Bennée 
243*1b4c136bSAlex Bennée         g_free(m->match_string);
244*1b4c136bSAlex Bennée         qemu_plugin_scoreboard_free(m->counts);
245*1b4c136bSAlex Bennée     }
246*1b4c136bSAlex Bennée 
247*1b4c136bSAlex Bennée     g_mutex_unlock(&match_hash_lock);
248*1b4c136bSAlex Bennée 
249*1b4c136bSAlex Bennée     g_array_free(matches, TRUE);
250*1b4c136bSAlex Bennée     g_array_free(sizes, TRUE);
251*1b4c136bSAlex Bennée }
252*1b4c136bSAlex Bennée 
253*1b4c136bSAlex Bennée 
254*1b4c136bSAlex Bennée /* Add a match to the array of matches */
parse_match(char * match)255*1b4c136bSAlex Bennée static void parse_match(char *match)
256*1b4c136bSAlex Bennée {
257*1b4c136bSAlex Bennée     Match new_match = {
258*1b4c136bSAlex Bennée         .match_string = g_strdup(match),
259*1b4c136bSAlex Bennée         .counts = qemu_plugin_scoreboard_new(sizeof(MatchCount)) };
260*1b4c136bSAlex Bennée     g_array_append_val(matches, new_match);
261*1b4c136bSAlex Bennée }
262*1b4c136bSAlex Bennée 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)263*1b4c136bSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
264*1b4c136bSAlex Bennée                                            const qemu_info_t *info,
265*1b4c136bSAlex Bennée                                            int argc, char **argv)
266*1b4c136bSAlex Bennée {
267*1b4c136bSAlex Bennée     matches = g_array_new(false, true, sizeof(Match));
268*1b4c136bSAlex Bennée     /* null terminated so 0 is not a special case */
269*1b4c136bSAlex Bennée     sizes = g_array_new(true, true, sizeof(unsigned long));
270*1b4c136bSAlex Bennée 
271*1b4c136bSAlex Bennée     for (int i = 0; i < argc; i++) {
272*1b4c136bSAlex Bennée         char *opt = argv[i];
273*1b4c136bSAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
274*1b4c136bSAlex Bennée         if (g_strcmp0(tokens[0], "inline") == 0) {
275*1b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
276*1b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
277*1b4c136bSAlex Bennée                 return -1;
278*1b4c136bSAlex Bennée             }
279*1b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "sizes") == 0) {
280*1b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_size)) {
281*1b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
282*1b4c136bSAlex Bennée                 return -1;
283*1b4c136bSAlex Bennée             }
284*1b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "match") == 0) {
285*1b4c136bSAlex Bennée             parse_match(tokens[1]);
286*1b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "trace") == 0) {
287*1b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_trace)) {
288*1b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
289*1b4c136bSAlex Bennée                 return -1;
290*1b4c136bSAlex Bennée             }
291*1b4c136bSAlex Bennée         } else {
292*1b4c136bSAlex Bennée             fprintf(stderr, "option parsing failed: %s\n", opt);
293*1b4c136bSAlex Bennée             return -1;
294*1b4c136bSAlex Bennée         }
295*1b4c136bSAlex Bennée     }
296*1b4c136bSAlex Bennée 
297*1b4c136bSAlex Bennée     insn_count = qemu_plugin_scoreboard_u64(
298*1b4c136bSAlex Bennée         qemu_plugin_scoreboard_new(sizeof(uint64_t)));
299*1b4c136bSAlex Bennée 
300*1b4c136bSAlex Bennée     /* Register init, translation block and exit callbacks */
301*1b4c136bSAlex Bennée     qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
302*1b4c136bSAlex Bennée     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
303*1b4c136bSAlex Bennée     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
304*1b4c136bSAlex Bennée     return 0;
305*1b4c136bSAlex Bennée }
306