xref: /qemu/tests/tcg/plugins/mem.c (revision 7fd9ff76cccd5a5a701e462c8f2b487cba52d407)
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 
15671f760bSEmilio G. Cota #include <qemu-plugin.h>
16671f760bSEmilio G. Cota 
173fb356ccSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
183fb356ccSAlex Bennée 
194f8d8860SPierrick Bouvier typedef struct {
204f8d8860SPierrick Bouvier     uint64_t mem_count;
214f8d8860SPierrick Bouvier     uint64_t io_count;
224f8d8860SPierrick Bouvier } CPUCount;
234f8d8860SPierrick Bouvier 
24*7fd9ff76SPierrick Bouvier typedef struct {
25*7fd9ff76SPierrick Bouvier     uint64_t vaddr;
26*7fd9ff76SPierrick Bouvier     const char *sym;
27*7fd9ff76SPierrick Bouvier } InsnInfo;
28*7fd9ff76SPierrick Bouvier 
294f8d8860SPierrick Bouvier static struct qemu_plugin_scoreboard *counts;
304f8d8860SPierrick Bouvier static qemu_plugin_u64 mem_count;
314f8d8860SPierrick Bouvier static qemu_plugin_u64 io_count;
32*7fd9ff76SPierrick Bouvier static bool do_inline, do_callback, do_print_accesses;
33671f760bSEmilio G. Cota static bool do_haddr;
34671f760bSEmilio G. Cota static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
35671f760bSEmilio G. Cota 
36671f760bSEmilio G. Cota static void plugin_exit(qemu_plugin_id_t id, void *p)
37671f760bSEmilio G. Cota {
38671f760bSEmilio G. Cota     g_autoptr(GString) out = g_string_new("");
39671f760bSEmilio G. Cota 
404f8d8860SPierrick Bouvier     if (do_inline || do_callback) {
414f8d8860SPierrick Bouvier         g_string_printf(out, "mem accesses: %" PRIu64 "\n",
424f8d8860SPierrick Bouvier                         qemu_plugin_u64_sum(mem_count));
430eca92e2SAlex Bennée     }
44671f760bSEmilio G. Cota     if (do_haddr) {
454f8d8860SPierrick Bouvier         g_string_append_printf(out, "io accesses: %" PRIu64 "\n",
464f8d8860SPierrick Bouvier                                qemu_plugin_u64_sum(io_count));
47671f760bSEmilio G. Cota     }
48671f760bSEmilio G. Cota     qemu_plugin_outs(out->str);
494f8d8860SPierrick Bouvier     qemu_plugin_scoreboard_free(counts);
50671f760bSEmilio G. Cota }
51671f760bSEmilio G. Cota 
52671f760bSEmilio G. Cota static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
53671f760bSEmilio G. Cota                      uint64_t vaddr, void *udata)
54671f760bSEmilio G. Cota {
55671f760bSEmilio G. Cota     if (do_haddr) {
56671f760bSEmilio G. Cota         struct qemu_plugin_hwaddr *hwaddr;
57671f760bSEmilio G. Cota         hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr);
58671f760bSEmilio G. Cota         if (qemu_plugin_hwaddr_is_io(hwaddr)) {
594f8d8860SPierrick Bouvier             qemu_plugin_u64_add(io_count, cpu_index, 1);
60671f760bSEmilio G. Cota         } else {
614f8d8860SPierrick Bouvier             qemu_plugin_u64_add(mem_count, cpu_index, 1);
62671f760bSEmilio G. Cota         }
63671f760bSEmilio G. Cota     } else {
644f8d8860SPierrick Bouvier         qemu_plugin_u64_add(mem_count, cpu_index, 1);
65671f760bSEmilio G. Cota     }
66671f760bSEmilio G. Cota }
67671f760bSEmilio G. Cota 
68*7fd9ff76SPierrick Bouvier static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
69*7fd9ff76SPierrick Bouvier                          uint64_t vaddr, void *udata)
70*7fd9ff76SPierrick Bouvier {
71*7fd9ff76SPierrick Bouvier     InsnInfo *insn_info = udata;
72*7fd9ff76SPierrick Bouvier     unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo);
73*7fd9ff76SPierrick Bouvier     const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load";
74*7fd9ff76SPierrick Bouvier     qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo);
75*7fd9ff76SPierrick Bouvier     uint64_t hwaddr =
76*7fd9ff76SPierrick Bouvier         qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr));
77*7fd9ff76SPierrick Bouvier     g_autoptr(GString) out = g_string_new("");
78*7fd9ff76SPierrick Bouvier     g_string_printf(out,
79*7fd9ff76SPierrick Bouvier                     "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,",
80*7fd9ff76SPierrick Bouvier                     insn_info->vaddr, insn_info->sym,
81*7fd9ff76SPierrick Bouvier                     vaddr, hwaddr, size, type);
82*7fd9ff76SPierrick Bouvier     switch (value.type) {
83*7fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U8:
84*7fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%02"PRIx8, value.data.u8);
85*7fd9ff76SPierrick Bouvier         break;
86*7fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U16:
87*7fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%04"PRIx16, value.data.u16);
88*7fd9ff76SPierrick Bouvier         break;
89*7fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U32:
90*7fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%08"PRIx32, value.data.u32);
91*7fd9ff76SPierrick Bouvier         break;
92*7fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U64:
93*7fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%016"PRIx64, value.data.u64);
94*7fd9ff76SPierrick Bouvier         break;
95*7fd9ff76SPierrick Bouvier     case QEMU_PLUGIN_MEM_VALUE_U128:
96*7fd9ff76SPierrick Bouvier         g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64,
97*7fd9ff76SPierrick Bouvier                                value.data.u128.high, value.data.u128.low);
98*7fd9ff76SPierrick Bouvier         break;
99*7fd9ff76SPierrick Bouvier     default:
100*7fd9ff76SPierrick Bouvier         g_assert_not_reached();
101*7fd9ff76SPierrick Bouvier     }
102*7fd9ff76SPierrick Bouvier     g_string_append_printf(out, "\n");
103*7fd9ff76SPierrick Bouvier     qemu_plugin_outs(out->str);
104*7fd9ff76SPierrick Bouvier }
105*7fd9ff76SPierrick Bouvier 
106671f760bSEmilio G. Cota static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
107671f760bSEmilio G. Cota {
108671f760bSEmilio G. Cota     size_t n = qemu_plugin_tb_n_insns(tb);
109671f760bSEmilio G. Cota     size_t i;
110671f760bSEmilio G. Cota 
111671f760bSEmilio G. Cota     for (i = 0; i < n; i++) {
112671f760bSEmilio G. Cota         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
113671f760bSEmilio G. Cota 
114671f760bSEmilio G. Cota         if (do_inline) {
1154f8d8860SPierrick Bouvier             qemu_plugin_register_vcpu_mem_inline_per_vcpu(
1164f8d8860SPierrick Bouvier                 insn, rw,
117671f760bSEmilio G. Cota                 QEMU_PLUGIN_INLINE_ADD_U64,
1184f8d8860SPierrick Bouvier                 mem_count, 1);
1190eca92e2SAlex Bennée         }
1200eca92e2SAlex Bennée         if (do_callback) {
121671f760bSEmilio G. Cota             qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
122671f760bSEmilio G. Cota                                              QEMU_PLUGIN_CB_NO_REGS,
123671f760bSEmilio G. Cota                                              rw, NULL);
124671f760bSEmilio G. Cota         }
125*7fd9ff76SPierrick Bouvier         if (do_print_accesses) {
126*7fd9ff76SPierrick Bouvier             /* we leak this pointer, to avoid locking to keep track of it */
127*7fd9ff76SPierrick Bouvier             InsnInfo *insn_info = g_malloc(sizeof(InsnInfo));
128*7fd9ff76SPierrick Bouvier             const char *sym = qemu_plugin_insn_symbol(insn);
129*7fd9ff76SPierrick Bouvier             insn_info->sym = sym ? sym : "";
130*7fd9ff76SPierrick Bouvier             insn_info->vaddr = qemu_plugin_insn_vaddr(insn);
131*7fd9ff76SPierrick Bouvier             qemu_plugin_register_vcpu_mem_cb(insn, print_access,
132*7fd9ff76SPierrick Bouvier                                              QEMU_PLUGIN_CB_NO_REGS,
133*7fd9ff76SPierrick Bouvier                                              rw, (void *) insn_info);
134*7fd9ff76SPierrick Bouvier         }
135671f760bSEmilio G. Cota     }
136671f760bSEmilio G. Cota }
137671f760bSEmilio G. Cota 
138671f760bSEmilio G. Cota QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
139671f760bSEmilio G. Cota                                            const qemu_info_t *info,
140671f760bSEmilio G. Cota                                            int argc, char **argv)
141671f760bSEmilio G. Cota {
142671f760bSEmilio G. Cota 
1435ae589faSMahmoud Mandour     for (int i = 0; i < argc; i++) {
1445ae589faSMahmoud Mandour         char *opt = argv[i];
14540258741SAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
1465ae589faSMahmoud Mandour 
1475ae589faSMahmoud Mandour         if (g_strcmp0(tokens[0], "haddr") == 0) {
1485ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) {
1495ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
1505ae589faSMahmoud Mandour                 return -1;
1515ae589faSMahmoud Mandour             }
1525ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "track") == 0) {
1535ae589faSMahmoud Mandour             if (g_strcmp0(tokens[1], "r") == 0) {
154671f760bSEmilio G. Cota                 rw = QEMU_PLUGIN_MEM_R;
1555ae589faSMahmoud Mandour             } else if (g_strcmp0(tokens[1], "w") == 0) {
156671f760bSEmilio G. Cota                 rw = QEMU_PLUGIN_MEM_W;
1575ae589faSMahmoud Mandour             } else if (g_strcmp0(tokens[1], "rw") == 0) {
1585ae589faSMahmoud Mandour                 rw = QEMU_PLUGIN_MEM_RW;
1590eca92e2SAlex Bennée             } else {
16096420a30SMichael Tokarev                 fprintf(stderr, "invalid value for argument track: %s\n", opt);
1615ae589faSMahmoud Mandour                 return -1;
1625ae589faSMahmoud Mandour             }
1635ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "inline") == 0) {
1645ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
1655ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
1665ae589faSMahmoud Mandour                 return -1;
1675ae589faSMahmoud Mandour             }
1685ae589faSMahmoud Mandour         } else if (g_strcmp0(tokens[0], "callback") == 0) {
1695ae589faSMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) {
1705ae589faSMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
1715ae589faSMahmoud Mandour                 return -1;
1725ae589faSMahmoud Mandour             }
173*7fd9ff76SPierrick Bouvier         } else if (g_strcmp0(tokens[0], "print-accesses") == 0) {
174*7fd9ff76SPierrick Bouvier             if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
175*7fd9ff76SPierrick Bouvier                                         &do_print_accesses)) {
176*7fd9ff76SPierrick Bouvier                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
177*7fd9ff76SPierrick Bouvier                 return -1;
178*7fd9ff76SPierrick Bouvier             }
1795ae589faSMahmoud Mandour         } else {
1805ae589faSMahmoud Mandour             fprintf(stderr, "option parsing failed: %s\n", opt);
1815ae589faSMahmoud Mandour             return -1;
182671f760bSEmilio G. Cota         }
183671f760bSEmilio G. Cota     }
184671f760bSEmilio G. Cota 
1854f8d8860SPierrick Bouvier     if (do_inline && do_callback) {
1864f8d8860SPierrick Bouvier         fprintf(stderr,
1874f8d8860SPierrick Bouvier                 "can't enable inline and callback counting at the same time\n");
1884f8d8860SPierrick Bouvier         return -1;
1894f8d8860SPierrick Bouvier     }
1904f8d8860SPierrick Bouvier 
191*7fd9ff76SPierrick Bouvier     if (do_print_accesses) {
192*7fd9ff76SPierrick Bouvier         g_autoptr(GString) out = g_string_new("");
193*7fd9ff76SPierrick Bouvier         g_string_printf(out,
194*7fd9ff76SPierrick Bouvier                 "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr,"
195*7fd9ff76SPierrick Bouvier                 "access_size,access_type,mem_value\n");
196*7fd9ff76SPierrick Bouvier         qemu_plugin_outs(out->str);
197*7fd9ff76SPierrick Bouvier     }
198*7fd9ff76SPierrick Bouvier 
1994f8d8860SPierrick Bouvier     counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
2004f8d8860SPierrick Bouvier     mem_count = qemu_plugin_scoreboard_u64_in_struct(
2014f8d8860SPierrick Bouvier         counts, CPUCount, mem_count);
2024f8d8860SPierrick Bouvier     io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count);
203671f760bSEmilio G. Cota     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
204671f760bSEmilio G. Cota     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
205671f760bSEmilio G. Cota     return 0;
206671f760bSEmilio G. Cota }
207