xref: /qemu/tests/tcg/plugins/mem.c (revision 01499add2ae6529589002860e1880ff193a6578a)
1671f760bSEmilio G. Cota /*
2671f760bSEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
3671f760bSEmilio G. Cota  *
4671f760bSEmilio G. Cota  * License: GNU GPL, version 2 or later.
5671f760bSEmilio G. Cota  *   See the COPYING file in the top-level directory.
6671f760bSEmilio G. Cota  */
7671f760bSEmilio G. Cota #include <inttypes.h>
8671f760bSEmilio G. Cota #include <assert.h>
9671f760bSEmilio G. Cota #include <stdlib.h>
10671f760bSEmilio G. Cota #include <string.h>
11671f760bSEmilio G. Cota #include <unistd.h>
12671f760bSEmilio G. Cota #include <stdio.h>
13671f760bSEmilio G. Cota #include <glib.h>
14671f760bSEmilio G. Cota 
15ecbcc9eaSAlex Bennée /*
16ecbcc9eaSAlex Bennée  * plugins should not include anything from QEMU aside from the
17ecbcc9eaSAlex Bennée  * API header. However as this is a test plugin to exercise the
18ecbcc9eaSAlex Bennée  * internals of QEMU and we want to avoid needless code duplication we
19ecbcc9eaSAlex Bennée  * do so here. bswap.h is pretty self-contained although it needs a
20ecbcc9eaSAlex Bennée  * few things provided by compiler.h.
21ecbcc9eaSAlex Bennée  */
22ecbcc9eaSAlex Bennée #include <compiler.h>
23ecbcc9eaSAlex Bennée #include <bswap.h>
24671f760bSEmilio G. Cota #include <qemu-plugin.h>
25671f760bSEmilio G. Cota 
263fb356ccSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
273fb356ccSAlex Bennée 
284f8d8860SPierrick Bouvier typedef struct {
294f8d8860SPierrick Bouvier     uint64_t mem_count;
304f8d8860SPierrick Bouvier     uint64_t io_count;
314f8d8860SPierrick Bouvier } CPUCount;
324f8d8860SPierrick Bouvier 
337fd9ff76SPierrick Bouvier typedef struct {
347fd9ff76SPierrick Bouvier     uint64_t vaddr;
357fd9ff76SPierrick Bouvier     const char *sym;
367fd9ff76SPierrick Bouvier } InsnInfo;
377fd9ff76SPierrick Bouvier 
38ecbcc9eaSAlex Bennée /*
39ecbcc9eaSAlex Bennée  * For the "memory" system test we need to track accesses to
40ecbcc9eaSAlex Bennée  * individual regions. We mirror the data written to the region and
41ecbcc9eaSAlex Bennée  * then check when it is read that it matches up.
42ecbcc9eaSAlex Bennée  *
43ecbcc9eaSAlex Bennée  * We do this as regions rather than pages to save on complications
44ecbcc9eaSAlex Bennée  * with page crossing and the fact the test only cares about the
45ecbcc9eaSAlex Bennée  * test_data region.
46ecbcc9eaSAlex Bennée  */
47ecbcc9eaSAlex Bennée static uint64_t region_size = 4096 * 4;
48ecbcc9eaSAlex Bennée static uint64_t region_mask;
49ecbcc9eaSAlex Bennée 
50ecbcc9eaSAlex Bennée typedef struct {
51ecbcc9eaSAlex Bennée     uint64_t region_address;
52ecbcc9eaSAlex Bennée     uint64_t reads;
53ecbcc9eaSAlex Bennée     uint64_t writes;
54ecbcc9eaSAlex Bennée     uint8_t *data;
55ecbcc9eaSAlex Bennée     /* Did we see every write and read with correct values? */
56ecbcc9eaSAlex Bennée     bool     seen_all;
57ecbcc9eaSAlex Bennée } RegionInfo;
58ecbcc9eaSAlex Bennée 
594f8d8860SPierrick Bouvier static struct qemu_plugin_scoreboard *counts;
604f8d8860SPierrick Bouvier static qemu_plugin_u64 mem_count;
614f8d8860SPierrick Bouvier static qemu_plugin_u64 io_count;
62ecbcc9eaSAlex Bennée static bool do_inline, do_callback, do_print_accesses, do_region_summary;
63671f760bSEmilio G. Cota static bool do_haddr;
64671f760bSEmilio G. Cota static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
65671f760bSEmilio G. Cota 
66ecbcc9eaSAlex Bennée 
67ecbcc9eaSAlex Bennée static GMutex lock;
68ecbcc9eaSAlex Bennée static GHashTable *regions;
69ecbcc9eaSAlex Bennée 
addr_order(gconstpointer a,gconstpointer b,gpointer d)70*01499addSKohei Tokunaga static gint addr_order(gconstpointer a, gconstpointer b, gpointer d)
71ecbcc9eaSAlex Bennée {
72ecbcc9eaSAlex Bennée     RegionInfo *na = (RegionInfo *) a;
73ecbcc9eaSAlex Bennée     RegionInfo *nb = (RegionInfo *) b;
74ecbcc9eaSAlex Bennée 
75ecbcc9eaSAlex Bennée     return na->region_address > nb->region_address ? 1 : -1;
76ecbcc9eaSAlex Bennée }
77ecbcc9eaSAlex Bennée 
78ecbcc9eaSAlex Bennée 
plugin_exit(qemu_plugin_id_t id,void * p)79671f760bSEmilio G. Cota static void plugin_exit(qemu_plugin_id_t id, void *p)
80671f760bSEmilio G. Cota {
81671f760bSEmilio G. Cota     g_autoptr(GString) out = g_string_new("");
82671f760bSEmilio G. Cota 
834f8d8860SPierrick Bouvier     if (do_inline || do_callback) {
844f8d8860SPierrick Bouvier         g_string_printf(out, "mem accesses: %" PRIu64 "\n",
854f8d8860SPierrick Bouvier                         qemu_plugin_u64_sum(mem_count));
860eca92e2SAlex Bennée     }
87671f760bSEmilio G. Cota     if (do_haddr) {
884f8d8860SPierrick Bouvier         g_string_append_printf(out, "io accesses: %" PRIu64 "\n",
894f8d8860SPierrick Bouvier                                qemu_plugin_u64_sum(io_count));
90671f760bSEmilio G. Cota     }
91671f760bSEmilio G. Cota     qemu_plugin_outs(out->str);
92ecbcc9eaSAlex Bennée 
93ecbcc9eaSAlex Bennée 
94ecbcc9eaSAlex Bennée     if (do_region_summary) {
95ecbcc9eaSAlex Bennée         GList *counts = g_hash_table_get_values(regions);
96ecbcc9eaSAlex Bennée 
97*01499addSKohei Tokunaga         counts = g_list_sort_with_data(counts, addr_order, NULL);
98ecbcc9eaSAlex Bennée 
99ecbcc9eaSAlex Bennée         g_string_printf(out, "Region Base, Reads, Writes, Seen all\n");
100ecbcc9eaSAlex Bennée 
101ecbcc9eaSAlex Bennée         if (counts && g_list_next(counts)) {
102ecbcc9eaSAlex Bennée             for (/* counts */; counts; counts = counts->next) {
103ecbcc9eaSAlex Bennée                 RegionInfo *ri = (RegionInfo *) counts->data;
104ecbcc9eaSAlex Bennée 
105ecbcc9eaSAlex Bennée                 g_string_append_printf(out,
106ecbcc9eaSAlex Bennée                                        "0x%016"PRIx64", "
107ecbcc9eaSAlex Bennée                                        "%"PRId64", %"PRId64", %s\n",
108ecbcc9eaSAlex Bennée                                        ri->region_address,
109ecbcc9eaSAlex Bennée                                        ri->reads,
110ecbcc9eaSAlex Bennée                                        ri->writes,
111ecbcc9eaSAlex Bennée                                        ri->seen_all ? "true" : "false");
112ecbcc9eaSAlex Bennée             }
113ecbcc9eaSAlex Bennée         }
114ecbcc9eaSAlex Bennée         qemu_plugin_outs(out->str);
115ecbcc9eaSAlex Bennée     }
116ecbcc9eaSAlex Bennée 
1174f8d8860SPierrick Bouvier     qemu_plugin_scoreboard_free(counts);
118671f760bSEmilio G. Cota }
119671f760bSEmilio G. Cota 
120ecbcc9eaSAlex Bennée /*
121ecbcc9eaSAlex Bennée  * Update the region tracking info for the access. We split up accesses
122ecbcc9eaSAlex Bennée  * that span regions even though the plugin infrastructure will deliver
123ecbcc9eaSAlex Bennée  * it as a single access.
124ecbcc9eaSAlex Bennée  */
update_region_info(uint64_t region,uint64_t offset,qemu_plugin_meminfo_t meminfo,qemu_plugin_mem_value value,unsigned size)125ecbcc9eaSAlex Bennée static void update_region_info(uint64_t region, uint64_t offset,
126ecbcc9eaSAlex Bennée                                qemu_plugin_meminfo_t meminfo,
127ecbcc9eaSAlex Bennée                                qemu_plugin_mem_value value,
128ecbcc9eaSAlex Bennée                                unsigned size)
129ecbcc9eaSAlex Bennée {
130ecbcc9eaSAlex Bennée     bool be = qemu_plugin_mem_is_big_endian(meminfo);
131ecbcc9eaSAlex Bennée     bool is_store = qemu_plugin_mem_is_store(meminfo);
132ecbcc9eaSAlex Bennée     RegionInfo *ri;
133ecbcc9eaSAlex Bennée     bool unseen_data = false;
134ecbcc9eaSAlex Bennée 
135ecbcc9eaSAlex Bennée     g_assert(offset + size <= region_size);
136ecbcc9eaSAlex Bennée 
137ecbcc9eaSAlex Bennée     g_mutex_lock(&lock);
138376bc151SPierrick Bouvier     ri = (RegionInfo *) g_hash_table_lookup(regions, &region);
139ecbcc9eaSAlex Bennée 
140ecbcc9eaSAlex Bennée     if (!ri) {
141ecbcc9eaSAlex Bennée         ri = g_new0(RegionInfo, 1);
142ecbcc9eaSAlex Bennée         ri->region_address = region;
143ecbcc9eaSAlex Bennée         ri->data = g_malloc0(region_size);
144ecbcc9eaSAlex Bennée         ri->seen_all = true;
145376bc151SPierrick Bouvier         g_hash_table_insert(regions, &ri->region_address, ri);
146ecbcc9eaSAlex Bennée     }
147ecbcc9eaSAlex Bennée 
148ecbcc9eaSAlex Bennée     if (is_store) {
149ecbcc9eaSAlex Bennée         ri->writes++;
150ecbcc9eaSAlex Bennée     } else {
151ecbcc9eaSAlex Bennée         ri->reads++;
152ecbcc9eaSAlex Bennée     }
153ecbcc9eaSAlex Bennée 
154ecbcc9eaSAlex Bennée     switch (value.type) {
155ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U8:
156ecbcc9eaSAlex Bennée         if (is_store) {
157ecbcc9eaSAlex Bennée             ri->data[offset] = value.data.u8;
158ecbcc9eaSAlex Bennée         } else if (ri->data[offset] != value.data.u8) {
159ecbcc9eaSAlex Bennée             unseen_data = true;
160ecbcc9eaSAlex Bennée         }
161ecbcc9eaSAlex Bennée         break;
162ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U16:
163ecbcc9eaSAlex Bennée     {
164ecbcc9eaSAlex Bennée         uint16_t *p = (uint16_t *) &ri->data[offset];
165ecbcc9eaSAlex Bennée         if (is_store) {
166ecbcc9eaSAlex Bennée             if (be) {
167ecbcc9eaSAlex Bennée                 stw_be_p(p, value.data.u16);
168ecbcc9eaSAlex Bennée             } else {
169ecbcc9eaSAlex Bennée                 stw_le_p(p, value.data.u16);
170ecbcc9eaSAlex Bennée             }
171ecbcc9eaSAlex Bennée         } else {
172ecbcc9eaSAlex Bennée             uint16_t val = be ? lduw_be_p(p) : lduw_le_p(p);
173ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u16;
174ecbcc9eaSAlex Bennée         }
175ecbcc9eaSAlex Bennée         break;
176ecbcc9eaSAlex Bennée     }
177ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U32:
178ecbcc9eaSAlex Bennée     {
179ecbcc9eaSAlex Bennée         uint32_t *p = (uint32_t *) &ri->data[offset];
180ecbcc9eaSAlex Bennée         if (is_store) {
181ecbcc9eaSAlex Bennée             if (be) {
182ecbcc9eaSAlex Bennée                 stl_be_p(p, value.data.u32);
183ecbcc9eaSAlex Bennée             } else {
184ecbcc9eaSAlex Bennée                 stl_le_p(p, value.data.u32);
185ecbcc9eaSAlex Bennée             }
186ecbcc9eaSAlex Bennée         } else {
187ecbcc9eaSAlex Bennée             uint32_t val = be ? ldl_be_p(p) : ldl_le_p(p);
188ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u32;
189ecbcc9eaSAlex Bennée         }
190ecbcc9eaSAlex Bennée         break;
191ecbcc9eaSAlex Bennée     }
192ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U64:
193ecbcc9eaSAlex Bennée     {
194ecbcc9eaSAlex Bennée         uint64_t *p = (uint64_t *) &ri->data[offset];
195ecbcc9eaSAlex Bennée         if (is_store) {
196ecbcc9eaSAlex Bennée             if (be) {
197ecbcc9eaSAlex Bennée                 stq_be_p(p, value.data.u64);
198ecbcc9eaSAlex Bennée             } else {
199ecbcc9eaSAlex Bennée                 stq_le_p(p, value.data.u64);
200ecbcc9eaSAlex Bennée             }
201ecbcc9eaSAlex Bennée         } else {
202ecbcc9eaSAlex Bennée             uint64_t val = be ? ldq_be_p(p) : ldq_le_p(p);
203ecbcc9eaSAlex Bennée             unseen_data = val != value.data.u64;
204ecbcc9eaSAlex Bennée         }
205ecbcc9eaSAlex Bennée         break;
206ecbcc9eaSAlex Bennée     }
207ecbcc9eaSAlex Bennée     case QEMU_PLUGIN_MEM_VALUE_U128:
208ecbcc9eaSAlex Bennée         /* non in test so skip */
209ecbcc9eaSAlex Bennée         break;
210ecbcc9eaSAlex Bennée     default:
211ecbcc9eaSAlex Bennée         g_assert_not_reached();
212ecbcc9eaSAlex Bennée     }
213ecbcc9eaSAlex Bennée 
214ecbcc9eaSAlex Bennée     /*
215ecbcc9eaSAlex Bennée      * This is expected for regions initialised by QEMU (.text etc) but we
216ecbcc9eaSAlex Bennée      * expect to see all data read and written to the test_data region
217ecbcc9eaSAlex Bennée      * of the memory test.
218ecbcc9eaSAlex Bennée      */
219ecbcc9eaSAlex Bennée     if (unseen_data && ri->seen_all) {
220ecbcc9eaSAlex Bennée         g_autoptr(GString) error = g_string_new("Warning: ");
221ecbcc9eaSAlex Bennée         g_string_append_printf(error, "0x%016"PRIx64":%"PRId64
222ecbcc9eaSAlex Bennée                                " read an un-instrumented value\n",
223ecbcc9eaSAlex Bennée                                region, offset);
224ecbcc9eaSAlex Bennée         qemu_plugin_outs(error->str);
225ecbcc9eaSAlex Bennée         ri->seen_all = false;
226ecbcc9eaSAlex Bennée     }
227ecbcc9eaSAlex Bennée 
228ecbcc9eaSAlex Bennée     g_mutex_unlock(&lock);
229ecbcc9eaSAlex Bennée }
230ecbcc9eaSAlex Bennée 
vcpu_mem(unsigned int cpu_index,qemu_plugin_meminfo_t meminfo,uint64_t vaddr,void * udata)231671f760bSEmilio G. Cota static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
232671f760bSEmilio G. Cota                      uint64_t vaddr, void *udata)
233671f760bSEmilio G. Cota {
234671f760bSEmilio G. Cota     if (do_haddr) {
235671f760bSEmilio G. Cota         struct qemu_plugin_hwaddr *hwaddr;
236671f760bSEmilio G. Cota         hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr);
237671f760bSEmilio G. Cota         if (qemu_plugin_hwaddr_is_io(hwaddr)) {
2384f8d8860SPierrick Bouvier             qemu_plugin_u64_add(io_count, cpu_index, 1);
239671f760bSEmilio G. Cota         } else {
2404f8d8860SPierrick Bouvier             qemu_plugin_u64_add(mem_count, cpu_index, 1);
241671f760bSEmilio G. Cota         }
242671f760bSEmilio G. Cota     } else {
2434f8d8860SPierrick Bouvier         qemu_plugin_u64_add(mem_count, cpu_index, 1);
244671f760bSEmilio G. Cota     }
245ecbcc9eaSAlex Bennée 
246ecbcc9eaSAlex Bennée     if (do_region_summary) {
247ecbcc9eaSAlex Bennée         uint64_t region = vaddr & ~region_mask;
248ecbcc9eaSAlex Bennée         uint64_t offset = vaddr & region_mask;
249ecbcc9eaSAlex Bennée         qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo);
250ecbcc9eaSAlex Bennée         unsigned size = 1 << qemu_plugin_mem_size_shift(meminfo);
251ecbcc9eaSAlex Bennée 
252ecbcc9eaSAlex Bennée         update_region_info(region, offset, meminfo, value, size);
253ecbcc9eaSAlex Bennée     }
254671f760bSEmilio G. Cota }
255671f760bSEmilio G. Cota 
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)294671f760bSEmilio G. Cota static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
295671f760bSEmilio G. Cota {
296671f760bSEmilio G. Cota     size_t n = qemu_plugin_tb_n_insns(tb);
297671f760bSEmilio G. Cota     size_t i;
298671f760bSEmilio G. Cota 
299671f760bSEmilio G. Cota     for (i = 0; i < n; i++) {
300671f760bSEmilio G. Cota         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
301671f760bSEmilio G. Cota 
302671f760bSEmilio G. Cota         if (do_inline) {
3034f8d8860SPierrick Bouvier             qemu_plugin_register_vcpu_mem_inline_per_vcpu(
3044f8d8860SPierrick Bouvier                 insn, rw,
305671f760bSEmilio G. Cota                 QEMU_PLUGIN_INLINE_ADD_U64,
3064f8d8860SPierrick Bouvier                 mem_count, 1);
3070eca92e2SAlex Bennée         }
308ecbcc9eaSAlex Bennée         if (do_callback || do_region_summary) {
309671f760bSEmilio G. Cota             qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
310671f760bSEmilio G. Cota                                              QEMU_PLUGIN_CB_NO_REGS,
311671f760bSEmilio G. Cota                                              rw, NULL);
312671f760bSEmilio G. Cota         }
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         }
323671f760bSEmilio G. Cota     }
324671f760bSEmilio G. Cota }
325671f760bSEmilio G. Cota 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)326671f760bSEmilio G. Cota QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
327671f760bSEmilio G. Cota                                            const qemu_info_t *info,
328671f760bSEmilio G. Cota                                            int argc, char **argv)
329671f760bSEmilio G. Cota {
330671f760bSEmilio G. Cota 
3315ae589faSMahmoud Mandour     for (int i = 0; i < argc; i++) {
3325ae589faSMahmoud Mandour         char *opt = argv[i];
33340258741SAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
3345ae589faSMahmoud Mandour 
3355ae589faSMahmoud Mandour         if (g_strcmp0(tokens[0], "haddr") == 0) {
3365ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) {
3375ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3385ae589faSMahmoud Mandour                 return -1;
3395ae589faSMahmoud Mandour             }
3405ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "track") == 0) {
3415ae589faSMahmoud Mandour             if (g_strcmp0(tokens[1], "r") == 0) {
342671f760bSEmilio G. Cota                 rw = QEMU_PLUGIN_MEM_R;
3435ae589faSMahmoud Mandour             } else if (g_strcmp0(tokens[1], "w") == 0) {
344671f760bSEmilio G. Cota                 rw = QEMU_PLUGIN_MEM_W;
3455ae589faSMahmoud Mandour             } else if (g_strcmp0(tokens[1], "rw") == 0) {
3465ae589faSMahmoud Mandour                 rw = QEMU_PLUGIN_MEM_RW;
3470eca92e2SAlex Bennée             } else {
34896420a30SMichael Tokarev                 fprintf(stderr, "invalid value for argument track: %s\n", opt);
3495ae589faSMahmoud Mandour                 return -1;
3505ae589faSMahmoud Mandour             }
3515ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "inline") == 0) {
3525ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
3535ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3545ae589faSMahmoud Mandour                 return -1;
3555ae589faSMahmoud Mandour             }
3565ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "callback") == 0) {
3575ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) {
3585ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
3595ae589faSMahmoud Mandour                 return -1;
3605ae589faSMahmoud Mandour             }
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             }
367ecbcc9eaSAlex Bennée         } else if (g_strcmp0(tokens[0], "region-summary") == 0) {
368ecbcc9eaSAlex Bennée             if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
369ecbcc9eaSAlex Bennée                                         &do_region_summary)) {
370ecbcc9eaSAlex Bennée                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
371ecbcc9eaSAlex Bennée                 return -1;
372ecbcc9eaSAlex Bennée             }
3735ae589faSMahmoud Mandour         } else {
3745ae589faSMahmoud Mandour             fprintf(stderr, "option parsing failed: %s\n", opt);
3755ae589faSMahmoud Mandour             return -1;
376671f760bSEmilio G. Cota         }
377671f760bSEmilio G. Cota     }
378671f760bSEmilio G. Cota 
3794f8d8860SPierrick Bouvier     if (do_inline && do_callback) {
3804f8d8860SPierrick Bouvier         fprintf(stderr,
3814f8d8860SPierrick Bouvier                 "can't enable inline and callback counting at the same time\n");
3824f8d8860SPierrick Bouvier         return -1;
3834f8d8860SPierrick Bouvier     }
3844f8d8860SPierrick Bouvier 
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 
393ecbcc9eaSAlex Bennée     if (do_region_summary) {
394ecbcc9eaSAlex Bennée         region_mask = (region_size - 1);
395376bc151SPierrick Bouvier         regions = g_hash_table_new(g_int64_hash, g_int64_equal);
396ecbcc9eaSAlex Bennée     }
397ecbcc9eaSAlex Bennée 
3984f8d8860SPierrick Bouvier     counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
3994f8d8860SPierrick Bouvier     mem_count = qemu_plugin_scoreboard_u64_in_struct(
4004f8d8860SPierrick Bouvier         counts, CPUCount, mem_count);
4014f8d8860SPierrick Bouvier     io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count);
402671f760bSEmilio G. Cota     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
403671f760bSEmilio G. Cota     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
404671f760bSEmilio G. Cota     return 0;
405671f760bSEmilio G. Cota }
406