xref: /qemu/tests/tcg/plugins/mem.c (revision 7fd9ff76cccd5a5a701e462c8f2b487cba52d407)
1 /*
2  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
3  *
4  * License: GNU GPL, version 2 or later.
5  *   See the COPYING file in the top-level directory.
6  */
7 #include <inttypes.h>
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <glib.h>
14 
15 #include <qemu-plugin.h>
16 
17 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
18 
19 typedef struct {
20     uint64_t mem_count;
21     uint64_t io_count;
22 } CPUCount;
23 
24 typedef struct {
25     uint64_t vaddr;
26     const char *sym;
27 } InsnInfo;
28 
29 static struct qemu_plugin_scoreboard *counts;
30 static qemu_plugin_u64 mem_count;
31 static qemu_plugin_u64 io_count;
32 static bool do_inline, do_callback, do_print_accesses;
33 static bool do_haddr;
34 static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
35 
36 static void plugin_exit(qemu_plugin_id_t id, void *p)
37 {
38     g_autoptr(GString) out = g_string_new("");
39 
40     if (do_inline || do_callback) {
41         g_string_printf(out, "mem accesses: %" PRIu64 "\n",
42                         qemu_plugin_u64_sum(mem_count));
43     }
44     if (do_haddr) {
45         g_string_append_printf(out, "io accesses: %" PRIu64 "\n",
46                                qemu_plugin_u64_sum(io_count));
47     }
48     qemu_plugin_outs(out->str);
49     qemu_plugin_scoreboard_free(counts);
50 }
51 
52 static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
53                      uint64_t vaddr, void *udata)
54 {
55     if (do_haddr) {
56         struct qemu_plugin_hwaddr *hwaddr;
57         hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr);
58         if (qemu_plugin_hwaddr_is_io(hwaddr)) {
59             qemu_plugin_u64_add(io_count, cpu_index, 1);
60         } else {
61             qemu_plugin_u64_add(mem_count, cpu_index, 1);
62         }
63     } else {
64         qemu_plugin_u64_add(mem_count, cpu_index, 1);
65     }
66 }
67 
68 static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
69                          uint64_t vaddr, void *udata)
70 {
71     InsnInfo *insn_info = udata;
72     unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo);
73     const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load";
74     qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo);
75     uint64_t hwaddr =
76         qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr));
77     g_autoptr(GString) out = g_string_new("");
78     g_string_printf(out,
79                     "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,",
80                     insn_info->vaddr, insn_info->sym,
81                     vaddr, hwaddr, size, type);
82     switch (value.type) {
83     case QEMU_PLUGIN_MEM_VALUE_U8:
84         g_string_append_printf(out, "0x%02"PRIx8, value.data.u8);
85         break;
86     case QEMU_PLUGIN_MEM_VALUE_U16:
87         g_string_append_printf(out, "0x%04"PRIx16, value.data.u16);
88         break;
89     case QEMU_PLUGIN_MEM_VALUE_U32:
90         g_string_append_printf(out, "0x%08"PRIx32, value.data.u32);
91         break;
92     case QEMU_PLUGIN_MEM_VALUE_U64:
93         g_string_append_printf(out, "0x%016"PRIx64, value.data.u64);
94         break;
95     case QEMU_PLUGIN_MEM_VALUE_U128:
96         g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64,
97                                value.data.u128.high, value.data.u128.low);
98         break;
99     default:
100         g_assert_not_reached();
101     }
102     g_string_append_printf(out, "\n");
103     qemu_plugin_outs(out->str);
104 }
105 
106 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
107 {
108     size_t n = qemu_plugin_tb_n_insns(tb);
109     size_t i;
110 
111     for (i = 0; i < n; i++) {
112         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
113 
114         if (do_inline) {
115             qemu_plugin_register_vcpu_mem_inline_per_vcpu(
116                 insn, rw,
117                 QEMU_PLUGIN_INLINE_ADD_U64,
118                 mem_count, 1);
119         }
120         if (do_callback) {
121             qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
122                                              QEMU_PLUGIN_CB_NO_REGS,
123                                              rw, NULL);
124         }
125         if (do_print_accesses) {
126             /* we leak this pointer, to avoid locking to keep track of it */
127             InsnInfo *insn_info = g_malloc(sizeof(InsnInfo));
128             const char *sym = qemu_plugin_insn_symbol(insn);
129             insn_info->sym = sym ? sym : "";
130             insn_info->vaddr = qemu_plugin_insn_vaddr(insn);
131             qemu_plugin_register_vcpu_mem_cb(insn, print_access,
132                                              QEMU_PLUGIN_CB_NO_REGS,
133                                              rw, (void *) insn_info);
134         }
135     }
136 }
137 
138 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
139                                            const qemu_info_t *info,
140                                            int argc, char **argv)
141 {
142 
143     for (int i = 0; i < argc; i++) {
144         char *opt = argv[i];
145         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
146 
147         if (g_strcmp0(tokens[0], "haddr") == 0) {
148             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) {
149                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
150                 return -1;
151             }
152         } else if (g_strcmp0(tokens[0], "track") == 0) {
153             if (g_strcmp0(tokens[1], "r") == 0) {
154                 rw = QEMU_PLUGIN_MEM_R;
155             } else if (g_strcmp0(tokens[1], "w") == 0) {
156                 rw = QEMU_PLUGIN_MEM_W;
157             } else if (g_strcmp0(tokens[1], "rw") == 0) {
158                 rw = QEMU_PLUGIN_MEM_RW;
159             } else {
160                 fprintf(stderr, "invalid value for argument track: %s\n", opt);
161                 return -1;
162             }
163         } else if (g_strcmp0(tokens[0], "inline") == 0) {
164             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
165                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
166                 return -1;
167             }
168         } else if (g_strcmp0(tokens[0], "callback") == 0) {
169             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) {
170                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
171                 return -1;
172             }
173         } else if (g_strcmp0(tokens[0], "print-accesses") == 0) {
174             if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
175                                         &do_print_accesses)) {
176                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
177                 return -1;
178             }
179         } else {
180             fprintf(stderr, "option parsing failed: %s\n", opt);
181             return -1;
182         }
183     }
184 
185     if (do_inline && do_callback) {
186         fprintf(stderr,
187                 "can't enable inline and callback counting at the same time\n");
188         return -1;
189     }
190 
191     if (do_print_accesses) {
192         g_autoptr(GString) out = g_string_new("");
193         g_string_printf(out,
194                 "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr,"
195                 "access_size,access_type,mem_value\n");
196         qemu_plugin_outs(out->str);
197     }
198 
199     counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
200     mem_count = qemu_plugin_scoreboard_u64_in_struct(
201         counts, CPUCount, mem_count);
202     io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count);
203     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
204     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
205     return 0;
206 }
207