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