xref: /qemu/tests/tcg/plugins/inline.c (revision a1c9bf2514d82b757288ee61584e667ddcb19a4f)
1b480f7a6SPierrick Bouvier /*
2b480f7a6SPierrick Bouvier  * Copyright (C) 2023, Pierrick Bouvier <pierrick.bouvier@linaro.org>
3b480f7a6SPierrick Bouvier  *
4b480f7a6SPierrick Bouvier  * Demonstrates and tests usage of inline ops.
5b480f7a6SPierrick Bouvier  *
6b480f7a6SPierrick Bouvier  * License: GNU GPL, version 2 or later.
7b480f7a6SPierrick Bouvier  *   See the COPYING file in the top-level directory.
8b480f7a6SPierrick Bouvier  */
9b480f7a6SPierrick Bouvier 
10b480f7a6SPierrick Bouvier #include <glib.h>
11b480f7a6SPierrick Bouvier #include <stdint.h>
12b480f7a6SPierrick Bouvier #include <stdio.h>
13b480f7a6SPierrick Bouvier 
14b480f7a6SPierrick Bouvier #include <qemu-plugin.h>
15b480f7a6SPierrick Bouvier 
16b480f7a6SPierrick Bouvier typedef struct {
17b480f7a6SPierrick Bouvier     uint64_t count_tb;
18b480f7a6SPierrick Bouvier     uint64_t count_tb_inline;
19b480f7a6SPierrick Bouvier     uint64_t count_insn;
20b480f7a6SPierrick Bouvier     uint64_t count_insn_inline;
21b480f7a6SPierrick Bouvier     uint64_t count_mem;
22b480f7a6SPierrick Bouvier     uint64_t count_mem_inline;
23b480f7a6SPierrick Bouvier } CPUCount;
24b480f7a6SPierrick Bouvier 
25*a1c9bf25SPierrick Bouvier typedef struct {
26*a1c9bf25SPierrick Bouvier     uint64_t data_insn;
27*a1c9bf25SPierrick Bouvier     uint64_t data_tb;
28*a1c9bf25SPierrick Bouvier     uint64_t data_mem;
29*a1c9bf25SPierrick Bouvier } CPUData;
30*a1c9bf25SPierrick Bouvier 
31b480f7a6SPierrick Bouvier static struct qemu_plugin_scoreboard *counts;
32b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_tb;
33b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_tb_inline;
34b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_insn;
35b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_insn_inline;
36b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_mem;
37b480f7a6SPierrick Bouvier static qemu_plugin_u64 count_mem_inline;
38*a1c9bf25SPierrick Bouvier static struct qemu_plugin_scoreboard *data;
39*a1c9bf25SPierrick Bouvier static qemu_plugin_u64 data_insn;
40*a1c9bf25SPierrick Bouvier static qemu_plugin_u64 data_tb;
41*a1c9bf25SPierrick Bouvier static qemu_plugin_u64 data_mem;
42b480f7a6SPierrick Bouvier 
43b480f7a6SPierrick Bouvier static uint64_t global_count_tb;
44b480f7a6SPierrick Bouvier static uint64_t global_count_insn;
45b480f7a6SPierrick Bouvier static uint64_t global_count_mem;
46b480f7a6SPierrick Bouvier static unsigned int max_cpu_index;
47b480f7a6SPierrick Bouvier static GMutex tb_lock;
48b480f7a6SPierrick Bouvier static GMutex insn_lock;
49b480f7a6SPierrick Bouvier static GMutex mem_lock;
50b480f7a6SPierrick Bouvier 
51b480f7a6SPierrick Bouvier QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
52b480f7a6SPierrick Bouvier 
53b480f7a6SPierrick Bouvier static void stats_insn(void)
54b480f7a6SPierrick Bouvier {
55b480f7a6SPierrick Bouvier     const uint64_t expected = global_count_insn;
56b480f7a6SPierrick Bouvier     const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
57b480f7a6SPierrick Bouvier     const uint64_t inl_per_vcpu =
58b480f7a6SPierrick Bouvier         qemu_plugin_u64_sum(count_insn_inline);
59b480f7a6SPierrick Bouvier     printf("insn: %" PRIu64 "\n", expected);
60b480f7a6SPierrick Bouvier     printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
61b480f7a6SPierrick Bouvier     printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
62b480f7a6SPierrick Bouvier     g_assert(expected > 0);
63b480f7a6SPierrick Bouvier     g_assert(per_vcpu == expected);
64b480f7a6SPierrick Bouvier     g_assert(inl_per_vcpu == expected);
65b480f7a6SPierrick Bouvier }
66b480f7a6SPierrick Bouvier 
67b480f7a6SPierrick Bouvier static void stats_tb(void)
68b480f7a6SPierrick Bouvier {
69b480f7a6SPierrick Bouvier     const uint64_t expected = global_count_tb;
70b480f7a6SPierrick Bouvier     const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
71b480f7a6SPierrick Bouvier     const uint64_t inl_per_vcpu =
72b480f7a6SPierrick Bouvier         qemu_plugin_u64_sum(count_tb_inline);
73b480f7a6SPierrick Bouvier     printf("tb: %" PRIu64 "\n", expected);
74b480f7a6SPierrick Bouvier     printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
75b480f7a6SPierrick Bouvier     printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
76b480f7a6SPierrick Bouvier     g_assert(expected > 0);
77b480f7a6SPierrick Bouvier     g_assert(per_vcpu == expected);
78b480f7a6SPierrick Bouvier     g_assert(inl_per_vcpu == expected);
79b480f7a6SPierrick Bouvier }
80b480f7a6SPierrick Bouvier 
81b480f7a6SPierrick Bouvier static void stats_mem(void)
82b480f7a6SPierrick Bouvier {
83b480f7a6SPierrick Bouvier     const uint64_t expected = global_count_mem;
84b480f7a6SPierrick Bouvier     const uint64_t per_vcpu = qemu_plugin_u64_sum(count_mem);
85b480f7a6SPierrick Bouvier     const uint64_t inl_per_vcpu =
86b480f7a6SPierrick Bouvier         qemu_plugin_u64_sum(count_mem_inline);
87b480f7a6SPierrick Bouvier     printf("mem: %" PRIu64 "\n", expected);
88b480f7a6SPierrick Bouvier     printf("mem: %" PRIu64 " (per vcpu)\n", per_vcpu);
89b480f7a6SPierrick Bouvier     printf("mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
90b480f7a6SPierrick Bouvier     g_assert(expected > 0);
91b480f7a6SPierrick Bouvier     g_assert(per_vcpu == expected);
92b480f7a6SPierrick Bouvier     g_assert(inl_per_vcpu == expected);
93b480f7a6SPierrick Bouvier }
94b480f7a6SPierrick Bouvier 
95b480f7a6SPierrick Bouvier static void plugin_exit(qemu_plugin_id_t id, void *udata)
96b480f7a6SPierrick Bouvier {
97b480f7a6SPierrick Bouvier     const unsigned int num_cpus = qemu_plugin_num_vcpus();
98b480f7a6SPierrick Bouvier     g_assert(num_cpus == max_cpu_index + 1);
99b480f7a6SPierrick Bouvier 
100b480f7a6SPierrick Bouvier     for (int i = 0; i < num_cpus ; ++i) {
101b480f7a6SPierrick Bouvier         const uint64_t tb = qemu_plugin_u64_get(count_tb, i);
102b480f7a6SPierrick Bouvier         const uint64_t tb_inline = qemu_plugin_u64_get(count_tb_inline, i);
103b480f7a6SPierrick Bouvier         const uint64_t insn = qemu_plugin_u64_get(count_insn, i);
104b480f7a6SPierrick Bouvier         const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
105b480f7a6SPierrick Bouvier         const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
106b480f7a6SPierrick Bouvier         const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
107b480f7a6SPierrick Bouvier         printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
108b480f7a6SPierrick Bouvier                "insn (%" PRIu64 ", %" PRIu64 ") | "
109b480f7a6SPierrick Bouvier                "mem (%" PRIu64 ", %" PRIu64 ")"
110b480f7a6SPierrick Bouvier                "\n",
111b480f7a6SPierrick Bouvier                i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
112b480f7a6SPierrick Bouvier         g_assert(tb == tb_inline);
113b480f7a6SPierrick Bouvier         g_assert(insn == insn_inline);
114b480f7a6SPierrick Bouvier         g_assert(mem == mem_inline);
115b480f7a6SPierrick Bouvier     }
116b480f7a6SPierrick Bouvier 
117b480f7a6SPierrick Bouvier     stats_tb();
118b480f7a6SPierrick Bouvier     stats_insn();
119b480f7a6SPierrick Bouvier     stats_mem();
120b480f7a6SPierrick Bouvier 
121b480f7a6SPierrick Bouvier     qemu_plugin_scoreboard_free(counts);
122*a1c9bf25SPierrick Bouvier     qemu_plugin_scoreboard_free(data);
123b480f7a6SPierrick Bouvier }
124b480f7a6SPierrick Bouvier 
125b480f7a6SPierrick Bouvier static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
126b480f7a6SPierrick Bouvier {
127b480f7a6SPierrick Bouvier     qemu_plugin_u64_add(count_tb, cpu_index, 1);
128*a1c9bf25SPierrick Bouvier     g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
129b480f7a6SPierrick Bouvier     g_mutex_lock(&tb_lock);
130b480f7a6SPierrick Bouvier     max_cpu_index = MAX(max_cpu_index, cpu_index);
131b480f7a6SPierrick Bouvier     global_count_tb++;
132b480f7a6SPierrick Bouvier     g_mutex_unlock(&tb_lock);
133b480f7a6SPierrick Bouvier }
134b480f7a6SPierrick Bouvier 
135b480f7a6SPierrick Bouvier static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
136b480f7a6SPierrick Bouvier {
137b480f7a6SPierrick Bouvier     qemu_plugin_u64_add(count_insn, cpu_index, 1);
138*a1c9bf25SPierrick Bouvier     g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
139b480f7a6SPierrick Bouvier     g_mutex_lock(&insn_lock);
140b480f7a6SPierrick Bouvier     global_count_insn++;
141b480f7a6SPierrick Bouvier     g_mutex_unlock(&insn_lock);
142b480f7a6SPierrick Bouvier }
143b480f7a6SPierrick Bouvier 
144b480f7a6SPierrick Bouvier static void vcpu_mem_access(unsigned int cpu_index,
145b480f7a6SPierrick Bouvier                             qemu_plugin_meminfo_t info,
146b480f7a6SPierrick Bouvier                             uint64_t vaddr,
147*a1c9bf25SPierrick Bouvier                             void *udata)
148b480f7a6SPierrick Bouvier {
149b480f7a6SPierrick Bouvier     qemu_plugin_u64_add(count_mem, cpu_index, 1);
150*a1c9bf25SPierrick Bouvier     g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
151b480f7a6SPierrick Bouvier     g_mutex_lock(&mem_lock);
152b480f7a6SPierrick Bouvier     global_count_mem++;
153b480f7a6SPierrick Bouvier     g_mutex_unlock(&mem_lock);
154b480f7a6SPierrick Bouvier }
155b480f7a6SPierrick Bouvier 
156b480f7a6SPierrick Bouvier static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
157b480f7a6SPierrick Bouvier {
158*a1c9bf25SPierrick Bouvier     void *tb_store = tb;
159*a1c9bf25SPierrick Bouvier     qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
160*a1c9bf25SPierrick Bouvier         tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
161b480f7a6SPierrick Bouvier     qemu_plugin_register_vcpu_tb_exec_cb(
162*a1c9bf25SPierrick Bouvier         tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
163b480f7a6SPierrick Bouvier     qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
164b480f7a6SPierrick Bouvier         tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
165b480f7a6SPierrick Bouvier 
166b480f7a6SPierrick Bouvier     for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
167b480f7a6SPierrick Bouvier         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
168*a1c9bf25SPierrick Bouvier         void *insn_store = insn;
169*a1c9bf25SPierrick Bouvier         void *mem_store = (char *)insn_store + 0xff;
170*a1c9bf25SPierrick Bouvier 
171*a1c9bf25SPierrick Bouvier         qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
172*a1c9bf25SPierrick Bouvier             insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
173*a1c9bf25SPierrick Bouvier             (uintptr_t) insn_store);
174b480f7a6SPierrick Bouvier         qemu_plugin_register_vcpu_insn_exec_cb(
175*a1c9bf25SPierrick Bouvier             insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
176b480f7a6SPierrick Bouvier         qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
177b480f7a6SPierrick Bouvier             insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
178*a1c9bf25SPierrick Bouvier 
179*a1c9bf25SPierrick Bouvier         qemu_plugin_register_vcpu_mem_inline_per_vcpu(
180*a1c9bf25SPierrick Bouvier             insn, QEMU_PLUGIN_MEM_RW,
181*a1c9bf25SPierrick Bouvier             QEMU_PLUGIN_INLINE_STORE_U64,
182*a1c9bf25SPierrick Bouvier             data_mem, (uintptr_t) mem_store);
183b480f7a6SPierrick Bouvier         qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access,
184b480f7a6SPierrick Bouvier                                          QEMU_PLUGIN_CB_NO_REGS,
185*a1c9bf25SPierrick Bouvier                                          QEMU_PLUGIN_MEM_RW, mem_store);
186b480f7a6SPierrick Bouvier         qemu_plugin_register_vcpu_mem_inline_per_vcpu(
187b480f7a6SPierrick Bouvier             insn, QEMU_PLUGIN_MEM_RW,
188b480f7a6SPierrick Bouvier             QEMU_PLUGIN_INLINE_ADD_U64,
189b480f7a6SPierrick Bouvier             count_mem_inline, 1);
190b480f7a6SPierrick Bouvier     }
191b480f7a6SPierrick Bouvier }
192b480f7a6SPierrick Bouvier 
193b480f7a6SPierrick Bouvier QEMU_PLUGIN_EXPORT
194b480f7a6SPierrick Bouvier int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
195b480f7a6SPierrick Bouvier                         int argc, char **argv)
196b480f7a6SPierrick Bouvier {
197b480f7a6SPierrick Bouvier     counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
198b480f7a6SPierrick Bouvier     count_tb = qemu_plugin_scoreboard_u64_in_struct(
199b480f7a6SPierrick Bouvier         counts, CPUCount, count_tb);
200b480f7a6SPierrick Bouvier     count_insn = qemu_plugin_scoreboard_u64_in_struct(
201b480f7a6SPierrick Bouvier         counts, CPUCount, count_insn);
202b480f7a6SPierrick Bouvier     count_mem = qemu_plugin_scoreboard_u64_in_struct(
203b480f7a6SPierrick Bouvier         counts, CPUCount, count_mem);
204b480f7a6SPierrick Bouvier     count_tb_inline = qemu_plugin_scoreboard_u64_in_struct(
205b480f7a6SPierrick Bouvier         counts, CPUCount, count_tb_inline);
206b480f7a6SPierrick Bouvier     count_insn_inline = qemu_plugin_scoreboard_u64_in_struct(
207b480f7a6SPierrick Bouvier         counts, CPUCount, count_insn_inline);
208b480f7a6SPierrick Bouvier     count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
209b480f7a6SPierrick Bouvier         counts, CPUCount, count_mem_inline);
210*a1c9bf25SPierrick Bouvier     data = qemu_plugin_scoreboard_new(sizeof(CPUData));
211*a1c9bf25SPierrick Bouvier     data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
212*a1c9bf25SPierrick Bouvier     data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
213*a1c9bf25SPierrick Bouvier     data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
214*a1c9bf25SPierrick Bouvier 
215b480f7a6SPierrick Bouvier     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
216b480f7a6SPierrick Bouvier     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
217b480f7a6SPierrick Bouvier 
218b480f7a6SPierrick Bouvier     return 0;
219b480f7a6SPierrick Bouvier }
220