xref: /openbmc/qemu/tests/tcg/plugins/mem.c (revision a5dd9ee060b0ad65239889a62e93a33276055981)
11b4c136bSAlex Bennée /*
21b4c136bSAlex Bennée  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
31b4c136bSAlex Bennée  *
41b4c136bSAlex Bennée  * License: GNU GPL, version 2 or later.
51b4c136bSAlex Bennée  *   See the COPYING file in the top-level directory.
61b4c136bSAlex Bennée  */
71b4c136bSAlex Bennée #include <inttypes.h>
81b4c136bSAlex Bennée #include <assert.h>
91b4c136bSAlex Bennée #include <stdlib.h>
101b4c136bSAlex Bennée #include <string.h>
111b4c136bSAlex Bennée #include <unistd.h>
121b4c136bSAlex Bennée #include <stdio.h>
131b4c136bSAlex Bennée #include <glib.h>
141b4c136bSAlex Bennée 
15*ecbcc9eaSAlex Bennée /*
16*ecbcc9eaSAlex Bennée  * plugins should not include anything from QEMU aside from the
17*ecbcc9eaSAlex Bennée  * API header. However as this is a test plugin to exercise the
18*ecbcc9eaSAlex Bennée  * internals of QEMU and we want to avoid needless code duplication we
19*ecbcc9eaSAlex Bennée  * do so here. bswap.h is pretty self-contained although it needs a
20*ecbcc9eaSAlex Bennée  * few things provided by compiler.h.
21*ecbcc9eaSAlex Bennée  */
22*ecbcc9eaSAlex Bennée #include <compiler.h>
23*ecbcc9eaSAlex Bennée #include <bswap.h>
241b4c136bSAlex Bennée #include <qemu-plugin.h>
251b4c136bSAlex Bennée 
261b4c136bSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
271b4c136bSAlex Bennée 
281b4c136bSAlex Bennée typedef struct {
291b4c136bSAlex Bennée     uint64_t mem_count;
301b4c136bSAlex Bennée     uint64_t io_count;
311b4c136bSAlex Bennée } CPUCount;
321b4c136bSAlex Bennée 
337fd9ff76SPierrick Bouvier typedef struct {
347fd9ff76SPierrick Bouvier     uint64_t vaddr;
357fd9ff76SPierrick Bouvier     const char *sym;
367fd9ff76SPierrick Bouvier } InsnInfo;
377fd9ff76SPierrick Bouvier 
38*ecbcc9eaSAlex Bennée /*
39*ecbcc9eaSAlex Bennée  * For the "memory" system test we need to track accesses to
40*ecbcc9eaSAlex Bennée  * individual regions. We mirror the data written to the region and
41*ecbcc9eaSAlex Bennée  * then check when it is read that it matches up.
42*ecbcc9eaSAlex Bennée  *
43*ecbcc9eaSAlex Bennée  * We do this as regions rather than pages to save on complications
44*ecbcc9eaSAlex Bennée  * with page crossing and the fact the test only cares about the
45*ecbcc9eaSAlex Bennée  * test_data region.
46*ecbcc9eaSAlex Bennée  */
47*ecbcc9eaSAlex Bennée static uint64_t region_size = 4096 * 4;
48*ecbcc9eaSAlex Bennée static uint64_t region_mask;
49*ecbcc9eaSAlex Bennée 
50*ecbcc9eaSAlex Bennée typedef struct {
51*ecbcc9eaSAlex Bennée     uint64_t region_address;
52*ecbcc9eaSAlex Bennée     uint64_t reads;
53*ecbcc9eaSAlex Bennée     uint64_t writes;
54*ecbcc9eaSAlex Bennée     uint8_t *data;
55*ecbcc9eaSAlex Bennée     /* Did we see every write and read with correct values? */
56*ecbcc9eaSAlex Bennée     bool     seen_all;
57*ecbcc9eaSAlex Bennée } RegionInfo;
58*ecbcc9eaSAlex Bennée 
591b4c136bSAlex Bennée static struct qemu_plugin_scoreboard *counts;
601b4c136bSAlex Bennée static qemu_plugin_u64 mem_count;
611b4c136bSAlex Bennée static qemu_plugin_u64 io_count;
62*ecbcc9eaSAlex Bennée static bool do_inline, do_callback, do_print_accesses, do_region_summary;
631b4c136bSAlex Bennée static bool do_haddr;
641b4c136bSAlex Bennée static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
651b4c136bSAlex Bennée 
66*ecbcc9eaSAlex Bennée 
67*ecbcc9eaSAlex Bennée static GMutex lock;
68*ecbcc9eaSAlex Bennée static GHashTable *regions;
69*ecbcc9eaSAlex Bennée 
addr_order(gconstpointer a,gconstpointer b)70*ecbcc9eaSAlex Bennée static gint addr_order(gconstpointer a, gconstpointer b)
71*ecbcc9eaSAlex Bennée {
72*ecbcc9eaSAlex Bennée     RegionInfo *na = (RegionInfo *) a;
73*ecbcc9eaSAlex Bennée     RegionInfo *nb = (RegionInfo *) b;
74*ecbcc9eaSAlex Bennée 
75*ecbcc9eaSAlex Bennée     return na->region_address > nb->region_address ? 1 : -1;
76*ecbcc9eaSAlex Bennée }
77*ecbcc9eaSAlex Bennée 
78*ecbcc9eaSAlex Bennée 
plugin_exit(qemu_plugin_id_t id,void * p)791b4c136bSAlex Bennée static void plugin_exit(qemu_plugin_id_t id, void *p)
801b4c136bSAlex Bennée {
811b4c136bSAlex Bennée     g_autoptr(GString) out = g_string_new("");
821b4c136bSAlex Bennée 
831b4c136bSAlex Bennée     if (do_inline || do_callback) {
841b4c136bSAlex Bennée         g_string_printf(out, "mem accesses: %" PRIu64 "\n",
851b4c136bSAlex Bennée                         qemu_plugin_u64_sum(mem_count));
861b4c136bSAlex Bennée     }
871b4c136bSAlex Bennée     if (do_haddr) {
881b4c136bSAlex Bennée         g_string_append_printf(out, "io accesses: %" PRIu64 "\n",
891b4c136bSAlex Bennée                                qemu_plugin_u64_sum(io_count));
901b4c136bSAlex Bennée     }
911b4c136bSAlex Bennée     qemu_plugin_outs(out->str);
92*ecbcc9eaSAlex Bennée 
93*ecbcc9eaSAlex Bennée 
94*ecbcc9eaSAlex Bennée     if (do_region_summary) {
95*ecbcc9eaSAlex Bennée         GList *counts = g_hash_table_get_values(regions);
96*ecbcc9eaSAlex Bennée 
97*ecbcc9eaSAlex Bennée         counts = g_list_sort(counts, addr_order);
98*ecbcc9eaSAlex Bennée 
99*ecbcc9eaSAlex Bennée         g_string_printf(out, "Region Base, Reads, Writes, Seen all\n");
100*ecbcc9eaSAlex Bennée 
101*ecbcc9eaSAlex Bennée         if (counts && g_list_next(counts)) {
102*ecbcc9eaSAlex Bennée             for (/* counts */; counts; counts = counts->next) {
103*ecbcc9eaSAlex Bennée                 RegionInfo *ri = (RegionInfo *) counts->data;
104*ecbcc9eaSAlex Bennée 
105*ecbcc9eaSAlex Bennée                 g_string_append_printf(out,
106*ecbcc9eaSAlex Bennée                                        "0x%016"PRIx64", "
107*ecbcc9eaSAlex Bennée                                        "%"PRId64", %"PRId64", %s\n",
108*ecbcc9eaSAlex Bennée                                        ri->region_address,
109*ecbcc9eaSAlex Bennée                                        ri->reads,
110*ecbcc9eaSAlex Bennée                                        ri->writes,
111*ecbcc9eaSAlex Bennée                                        ri->seen_all ? "true" : "false");
112*ecbcc9eaSAlex Bennée             }
113*ecbcc9eaSAlex Bennée         }
114*ecbcc9eaSAlex Bennée         qemu_plugin_outs(out->str);
115*ecbcc9eaSAlex Bennée     }
116*ecbcc9eaSAlex Bennée 
1171b4c136bSAlex Bennée     qemu_plugin_scoreboard_free(counts);
1181b4c136bSAlex Bennée }
1191b4c136bSAlex Bennée 
120*ecbcc9eaSAlex Bennée /*
121*ecbcc9eaSAlex Bennée  * Update the region tracking info for the access. We split up accesses
122*ecbcc9eaSAlex Bennée  * that span regions even though the plugin infrastructure will deliver
123*ecbcc9eaSAlex Bennée  * it as a single access.
124*ecbcc9eaSAlex Bennée  */
update_region_info(uint64_t region,uint64_t offset,qemu_plugin_meminfo_t meminfo,qemu_plugin_mem_value value,unsigned size)125*ecbcc9eaSAlex Bennée static void update_region_info(uint64_t region, uint64_t offset,
126*ecbcc9eaSAlex Bennée                                qemu_plugin_meminfo_t meminfo,
127*ecbcc9eaSAlex Bennée                                qemu_plugin_mem_value value,
128*ecbcc9eaSAlex Bennée                                unsigned size)
129*ecbcc9eaSAlex Bennée {
130*ecbcc9eaSAlex Bennée     bool be = qemu_plugin_mem_is_big_endian(meminfo);
131*ecbcc9eaSAlex Bennée     bool is_store = qemu_plugin_mem_is_store(meminfo);
132*ecbcc9eaSAlex Bennée     RegionInfo *ri;
133*ecbcc9eaSAlex Bennée     bool unseen_data = false;
134*ecbcc9eaSAlex Bennée 
135*ecbcc9eaSAlex Bennée     g_assert(offset + size <= region_size);
136*ecbcc9eaSAlex Bennée 
137*ecbcc9eaSAlex Bennée     g_mutex_lock(&lock);
138*ecbcc9eaSAlex Bennée     ri = (RegionInfo *) g_hash_table_lookup(regions, GUINT_TO_POINTER(region));
139*ecbcc9eaSAlex Bennée 
140*ecbcc9eaSAlex Bennée     if (!ri) {
141*ecbcc9eaSAlex Bennée         ri = g_new0(RegionInfo, 1);
142*ecbcc9eaSAlex Bennée         ri->region_address = region;
143*ecbcc9eaSAlex Bennée         ri->data = g_malloc0(region_size);
144*ecbcc9eaSAlex Bennée         ri->seen_all = true;
145*ecbcc9eaSAlex Bennée         g_hash_table_insert(regions, GUINT_TO_POINTER(region), (gpointer) ri);
146*ecbcc9eaSAlex Bennée     }
147*ecbcc9eaSAlex Bennée 
148*ecbcc9eaSAlex Bennée     if (is_store) {
149*ecbcc9eaSAlex Bennée         ri->writes++;
150*ecbcc9eaSAlex Bennée     } else {
151*ecbcc9eaSAlex Bennée         ri->reads++;
152*ecbcc9eaSAlex Bennée     }
153*ecbcc9eaSAlex Bennée 
154*ecbcc9eaSAlex Bennée     switch (value.type) {
155*ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U8:
156*ecbcc9eaSAlex Bennée         if (is_store) {
157*ecbcc9eaSAlex Bennée             ri->data[offset] = value.data.u8;
158*ecbcc9eaSAlex Bennée         } else if (ri->data[offset] != value.data.u8) {
159*ecbcc9eaSAlex Bennée             unseen_data = true;
160*ecbcc9eaSAlex Bennée         }
161*ecbcc9eaSAlex Bennée         break;
162*ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U16:
163*ecbcc9eaSAlex Bennée     {
164*ecbcc9eaSAlex Bennée         uint16_t *p = (uint16_t *) &ri->data[offset];
165*ecbcc9eaSAlex Bennée         if (is_store) {
166*ecbcc9eaSAlex Bennée             if (be) {
167*ecbcc9eaSAlex Bennée                 stw_be_p(p, value.data.u16);
168*ecbcc9eaSAlex Bennée             } else {
169*ecbcc9eaSAlex Bennée                 stw_le_p(p, value.data.u16);
170*ecbcc9eaSAlex Bennée             }
171*ecbcc9eaSAlex Bennée         } else {
172*ecbcc9eaSAlex Bennée             uint16_t val = be ? lduw_be_p(p) : lduw_le_p(p);
173*ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u16;
174*ecbcc9eaSAlex Bennée         }
175*ecbcc9eaSAlex Bennée         break;
176*ecbcc9eaSAlex Bennée     }
177*ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U32:
178*ecbcc9eaSAlex Bennée     {
179*ecbcc9eaSAlex Bennée         uint32_t *p = (uint32_t *) &ri->data[offset];
180*ecbcc9eaSAlex Bennée         if (is_store) {
181*ecbcc9eaSAlex Bennée             if (be) {
182*ecbcc9eaSAlex Bennée                 stl_be_p(p, value.data.u32);
183*ecbcc9eaSAlex Bennée             } else {
184*ecbcc9eaSAlex Bennée                 stl_le_p(p, value.data.u32);
185*ecbcc9eaSAlex Bennée             }
186*ecbcc9eaSAlex Bennée         } else {
187*ecbcc9eaSAlex Bennée             uint32_t val = be ? ldl_be_p(p) : ldl_le_p(p);
188*ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u32;
189*ecbcc9eaSAlex Bennée         }
190*ecbcc9eaSAlex Bennée         break;
191*ecbcc9eaSAlex Bennée     }
192*ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U64:
193*ecbcc9eaSAlex Bennée     {
194*ecbcc9eaSAlex Bennée         uint64_t *p = (uint64_t *) &ri->data[offset];
195*ecbcc9eaSAlex Bennée         if (is_store) {
196*ecbcc9eaSAlex Bennée             if (be) {
197*ecbcc9eaSAlex Bennée                 stq_be_p(p, value.data.u64);
198*ecbcc9eaSAlex Bennée             } else {
199*ecbcc9eaSAlex Bennée                 stq_le_p(p, value.data.u64);
200*ecbcc9eaSAlex Bennée             }
201*ecbcc9eaSAlex Bennée         } else {
202*ecbcc9eaSAlex Bennée             uint64_t val = be ? ldq_be_p(p) : ldq_le_p(p);
203*ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u64;
204*ecbcc9eaSAlex Bennée         }
205*ecbcc9eaSAlex Bennée         break;
206*ecbcc9eaSAlex Bennée     }
207*ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U128:
208*ecbcc9eaSAlex Bennée         /* non in test so skip */
209*ecbcc9eaSAlex Bennée         break;
210*ecbcc9eaSAlex Bennée     default:
211*ecbcc9eaSAlex Bennée         g_assert_not_reached();
212*ecbcc9eaSAlex Bennée     }
213*ecbcc9eaSAlex Bennée 
214*ecbcc9eaSAlex Bennée     /*
215*ecbcc9eaSAlex Bennée      * This is expected for regions initialised by QEMU (.text etc) but we
216*ecbcc9eaSAlex Bennée      * expect to see all data read and written to the test_data region
217*ecbcc9eaSAlex Bennée      * of the memory test.
218*ecbcc9eaSAlex Bennée      */
219*ecbcc9eaSAlex Bennée     if (unseen_data && ri->seen_all) {
220*ecbcc9eaSAlex Bennée         g_autoptr(GString) error = g_string_new("Warning: ");
221*ecbcc9eaSAlex Bennée         g_string_append_printf(error, "0x%016"PRIx64":%"PRId64
222*ecbcc9eaSAlex Bennée                                " read an un-instrumented value\n",
223*ecbcc9eaSAlex Bennée                                region, offset);
224*ecbcc9eaSAlex Bennée         qemu_plugin_outs(error->str);
225*ecbcc9eaSAlex Bennée         ri->seen_all = false;
226*ecbcc9eaSAlex Bennée     }
227*ecbcc9eaSAlex Bennée 
228*ecbcc9eaSAlex Bennée     g_mutex_unlock(&lock);
229*ecbcc9eaSAlex Bennée }
230*ecbcc9eaSAlex Bennée 
vcpu_mem(unsigned int cpu_index,qemu_plugin_meminfo_t meminfo,uint64_t vaddr,void * udata)2311b4c136bSAlex Bennée static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
2321b4c136bSAlex Bennée                      uint64_t vaddr, void *udata)
2331b4c136bSAlex Bennée {
2341b4c136bSAlex Bennée     if (do_haddr) {
2351b4c136bSAlex Bennée         struct qemu_plugin_hwaddr *hwaddr;
2361b4c136bSAlex Bennée         hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr);
2371b4c136bSAlex Bennée         if (qemu_plugin_hwaddr_is_io(hwaddr)) {
2381b4c136bSAlex Bennée             qemu_plugin_u64_add(io_count, cpu_index, 1);
2391b4c136bSAlex Bennée         } else {
2401b4c136bSAlex Bennée             qemu_plugin_u64_add(mem_count, cpu_index, 1);
2411b4c136bSAlex Bennée         }
2421b4c136bSAlex Bennée     } else {
2431b4c136bSAlex Bennée         qemu_plugin_u64_add(mem_count, cpu_index, 1);
2441b4c136bSAlex Bennée     }
245*ecbcc9eaSAlex Bennée 
246*ecbcc9eaSAlex Bennée     if (do_region_summary) {
247*ecbcc9eaSAlex Bennée         uint64_t region = vaddr & ~region_mask;
248*ecbcc9eaSAlex Bennée         uint64_t offset = vaddr & region_mask;
249*ecbcc9eaSAlex Bennée         qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo);
250*ecbcc9eaSAlex Bennée         unsigned size = 1 << qemu_plugin_mem_size_shift(meminfo);
251*ecbcc9eaSAlex Bennée 
252*ecbcc9eaSAlex Bennée         update_region_info(region, offset, meminfo, value, size);
253*ecbcc9eaSAlex Bennée     }
2541b4c136bSAlex Bennée }
2551b4c136bSAlex Bennée 
print_access(unsigned int cpu_index,qemu_plugin_meminfo_t meminfo,uint64_t vaddr,void * udata)2567fd9ff76SPierrick Bouvier static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
2577fd9ff76SPierrick Bouvier                          uint64_t vaddr, void *udata)
2587fd9ff76SPierrick Bouvier {
2597fd9ff76SPierrick Bouvier     InsnInfo *insn_info = udata;
2607fd9ff76SPierrick Bouvier     unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo);
2617fd9ff76SPierrick Bouvier     const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load";
2627fd9ff76SPierrick Bouvier     qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo);
2637fd9ff76SPierrick Bouvier     uint64_t hwaddr =
2647fd9ff76SPierrick Bouvier         qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr));
2657fd9ff76SPierrick Bouvier     g_autoptr(GString) out = g_string_new("");
2667fd9ff76SPierrick Bouvier     g_string_printf(out,
2677fd9ff76SPierrick Bouvier                     "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,",
2687fd9ff76SPierrick Bouvier                     insn_info->vaddr, insn_info->sym,
2697fd9ff76SPierrick Bouvier                     vaddr, hwaddr, size, type);
2707fd9ff76SPierrick Bouvier     switch (value.type) {
2717fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U8:
2727fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%02"PRIx8, value.data.u8);
2737fd9ff76SPierrick Bouvier         break;
2747fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U16:
2757fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%04"PRIx16, value.data.u16);
2767fd9ff76SPierrick Bouvier         break;
2777fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U32:
2787fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%08"PRIx32, value.data.u32);
2797fd9ff76SPierrick Bouvier         break;
2807fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U64:
2817fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%016"PRIx64, value.data.u64);
2827fd9ff76SPierrick Bouvier         break;
2837fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U128:
2847fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64,
2857fd9ff76SPierrick Bouvier                                value.data.u128.high, value.data.u128.low);
2867fd9ff76SPierrick Bouvier         break;
2877fd9ff76SPierrick Bouvier     default:
2887fd9ff76SPierrick Bouvier         g_assert_not_reached();
2897fd9ff76SPierrick Bouvier     }
2907fd9ff76SPierrick Bouvier     g_string_append_printf(out, "\n");
2917fd9ff76SPierrick Bouvier     qemu_plugin_outs(out->str);
2927fd9ff76SPierrick Bouvier }
2937fd9ff76SPierrick Bouvier 
vcpu_tb_trans(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)2941b4c136bSAlex Bennée static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
2951b4c136bSAlex Bennée {
2961b4c136bSAlex Bennée     size_t n = qemu_plugin_tb_n_insns(tb);
2971b4c136bSAlex Bennée     size_t i;
2981b4c136bSAlex Bennée 
2991b4c136bSAlex Bennée     for (i = 0; i < n; i++) {
3001b4c136bSAlex Bennée         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
3011b4c136bSAlex Bennée 
3021b4c136bSAlex Bennée         if (do_inline) {
3031b4c136bSAlex Bennée             qemu_plugin_register_vcpu_mem_inline_per_vcpu(
3041b4c136bSAlex Bennée                 insn, rw,
3051b4c136bSAlex Bennée                 QEMU_PLUGIN_INLINE_ADD_U64,
3061b4c136bSAlex Bennée                 mem_count, 1);
3071b4c136bSAlex Bennée         }
308*ecbcc9eaSAlex Bennée         if (do_callback || do_region_summary) {
3091b4c136bSAlex Bennée             qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
3101b4c136bSAlex Bennée                                              QEMU_PLUGIN_CB_NO_REGS,
3111b4c136bSAlex Bennée                                              rw, NULL);
3121b4c136bSAlex Bennée         }
3137fd9ff76SPierrick Bouvier         if (do_print_accesses) {
3147fd9ff76SPierrick Bouvier             /* we leak this pointer, to avoid locking to keep track of it */
3157fd9ff76SPierrick Bouvier             InsnInfo *insn_info = g_malloc(sizeof(InsnInfo));
3167fd9ff76SPierrick Bouvier             const char *sym = qemu_plugin_insn_symbol(insn);
3177fd9ff76SPierrick Bouvier             insn_info->sym = sym ? sym : "";
3187fd9ff76SPierrick Bouvier             insn_info->vaddr = qemu_plugin_insn_vaddr(insn);
3197fd9ff76SPierrick Bouvier             qemu_plugin_register_vcpu_mem_cb(insn, print_access,
3207fd9ff76SPierrick Bouvier                                              QEMU_PLUGIN_CB_NO_REGS,
3217fd9ff76SPierrick Bouvier                                              rw, (void *) insn_info);
3227fd9ff76SPierrick Bouvier         }
3231b4c136bSAlex Bennée     }
3241b4c136bSAlex Bennée }
3251b4c136bSAlex Bennée 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)3261b4c136bSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
3271b4c136bSAlex Bennée                                            const qemu_info_t *info,
3281b4c136bSAlex Bennée                                            int argc, char **argv)
3291b4c136bSAlex Bennée {
3301b4c136bSAlex Bennée 
3311b4c136bSAlex Bennée     for (int i = 0; i < argc; i++) {
3321b4c136bSAlex Bennée         char *opt = argv[i];
3331b4c136bSAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
3341b4c136bSAlex Bennée 
3351b4c136bSAlex Bennée         if (g_strcmp0(tokens[0], "haddr") == 0) {
3361b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) {
3371b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3381b4c136bSAlex Bennée                 return -1;
3391b4c136bSAlex Bennée             }
3401b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "track") == 0) {
3411b4c136bSAlex Bennée             if (g_strcmp0(tokens[1], "r") == 0) {
3421b4c136bSAlex Bennée                 rw = QEMU_PLUGIN_MEM_R;
3431b4c136bSAlex Bennée             } else if (g_strcmp0(tokens[1], "w") == 0) {
3441b4c136bSAlex Bennée                 rw = QEMU_PLUGIN_MEM_W;
3451b4c136bSAlex Bennée             } else if (g_strcmp0(tokens[1], "rw") == 0) {
3461b4c136bSAlex Bennée                 rw = QEMU_PLUGIN_MEM_RW;
3471b4c136bSAlex Bennée             } else {
3481b4c136bSAlex Bennée                 fprintf(stderr, "invalid value for argument track: %s\n", opt);
3491b4c136bSAlex Bennée                 return -1;
3501b4c136bSAlex Bennée             }
3511b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "inline") == 0) {
3521b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
3531b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3541b4c136bSAlex Bennée                 return -1;
3551b4c136bSAlex Bennée             }
3561b4c136bSAlex Bennée         } else if (g_strcmp0(tokens[0], "callback") == 0) {
3571b4c136bSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) {
3581b4c136bSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3591b4c136bSAlex Bennée                 return -1;
3601b4c136bSAlex Bennée             }
3617fd9ff76SPierrick Bouvier         } else if (g_strcmp0(tokens[0], "print-accesses") == 0) {
3627fd9ff76SPierrick Bouvier             if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
3637fd9ff76SPierrick Bouvier                                         &do_print_accesses)) {
3647fd9ff76SPierrick Bouvier                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3657fd9ff76SPierrick Bouvier                 return -1;
3667fd9ff76SPierrick Bouvier             }
367*ecbcc9eaSAlex Bennée         } else if (g_strcmp0(tokens[0], "region-summary") == 0) {
368*ecbcc9eaSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
369*ecbcc9eaSAlex Bennée                                         &do_region_summary)) {
370*ecbcc9eaSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
371*ecbcc9eaSAlex Bennée                 return -1;
372*ecbcc9eaSAlex Bennée             }
3731b4c136bSAlex Bennée         } else {
3741b4c136bSAlex Bennée             fprintf(stderr, "option parsing failed: %s\n", opt);
3751b4c136bSAlex Bennée             return -1;
3761b4c136bSAlex Bennée         }
3771b4c136bSAlex Bennée     }
3781b4c136bSAlex Bennée 
3791b4c136bSAlex Bennée     if (do_inline && do_callback) {
3801b4c136bSAlex Bennée         fprintf(stderr,
3811b4c136bSAlex Bennée                 "can't enable inline and callback counting at the same time\n");
3821b4c136bSAlex Bennée         return -1;
3831b4c136bSAlex Bennée     }
3841b4c136bSAlex Bennée 
3857fd9ff76SPierrick Bouvier     if (do_print_accesses) {
3867fd9ff76SPierrick Bouvier         g_autoptr(GString) out = g_string_new("");
3877fd9ff76SPierrick Bouvier         g_string_printf(out,
3887fd9ff76SPierrick Bouvier                 "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr,"
3897fd9ff76SPierrick Bouvier                 "access_size,access_type,mem_value\n");
3907fd9ff76SPierrick Bouvier         qemu_plugin_outs(out->str);
3917fd9ff76SPierrick Bouvier     }
3927fd9ff76SPierrick Bouvier 
393*ecbcc9eaSAlex Bennée     if (do_region_summary) {
394*ecbcc9eaSAlex Bennée         region_mask = (region_size - 1);
395*ecbcc9eaSAlex Bennée         regions = g_hash_table_new(NULL, g_direct_equal);
396*ecbcc9eaSAlex Bennée     }
397*ecbcc9eaSAlex Bennée 
3981b4c136bSAlex Bennée     counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
3991b4c136bSAlex Bennée     mem_count = qemu_plugin_scoreboard_u64_in_struct(
4001b4c136bSAlex Bennée         counts, CPUCount, mem_count);
4011b4c136bSAlex Bennée     io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count);
4021b4c136bSAlex Bennée     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
4031b4c136bSAlex Bennée     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
4041b4c136bSAlex Bennée     return 0;
4051b4c136bSAlex Bennée }
406