xref: /qemu/tests/tcg/plugins/syscall.c (revision 01499add2ae6529589002860e1880ff193a6578a)
1279d0a5bSMatthias Weckbecker /*
2279d0a5bSMatthias Weckbecker  * Copyright (C) 2020, Matthias Weckbecker <matthias@weckbecker.name>
3279d0a5bSMatthias Weckbecker  *
4279d0a5bSMatthias Weckbecker  * License: GNU GPL, version 2 or later.
5279d0a5bSMatthias Weckbecker  *   See the COPYING file in the top-level directory.
6279d0a5bSMatthias Weckbecker  */
7279d0a5bSMatthias Weckbecker #include <inttypes.h>
8279d0a5bSMatthias Weckbecker #include <assert.h>
9279d0a5bSMatthias Weckbecker #include <stdlib.h>
10279d0a5bSMatthias Weckbecker #include <string.h>
11279d0a5bSMatthias Weckbecker #include <unistd.h>
12279d0a5bSMatthias Weckbecker #include <stdio.h>
13279d0a5bSMatthias Weckbecker #include <glib.h>
14279d0a5bSMatthias Weckbecker 
15279d0a5bSMatthias Weckbecker #include <qemu-plugin.h>
16279d0a5bSMatthias Weckbecker 
17279d0a5bSMatthias Weckbecker QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
18279d0a5bSMatthias Weckbecker 
19a6851b49SMahmoud Mandour typedef struct {
20a6851b49SMahmoud Mandour     int64_t num;
21a6851b49SMahmoud Mandour     int64_t calls;
22a6851b49SMahmoud Mandour     int64_t errors;
23a6851b49SMahmoud Mandour } SyscallStats;
24a6851b49SMahmoud Mandour 
25f2505260SRowan Hart struct SyscallInfo {
26f2505260SRowan Hart     const char *name;
27f2505260SRowan Hart     int64_t write_sysno;
28f2505260SRowan Hart };
29f2505260SRowan Hart 
30f2505260SRowan Hart static const struct SyscallInfo arch_syscall_info[] = {
31f2505260SRowan Hart     { "aarch64", 64 },
32f2505260SRowan Hart     { "aarch64_be", 64 },
33f2505260SRowan Hart     { "alpha", 4 },
34f2505260SRowan Hart     { "arm", 4 },
35f2505260SRowan Hart     { "armeb", 4 },
36f2505260SRowan Hart     { "avr", -1 },
37f2505260SRowan Hart     { "hexagon", 64 },
38f2505260SRowan Hart     { "hppa", -1 },
39f2505260SRowan Hart     { "i386", 4 },
40f2505260SRowan Hart     { "loongarch64", -1 },
41f2505260SRowan Hart     { "m68k", 4 },
42f2505260SRowan Hart     { "microblaze", 4 },
43f2505260SRowan Hart     { "microblazeel", 4 },
44f2505260SRowan Hart     { "mips", 1 },
45f2505260SRowan Hart     { "mips64", 1 },
46f2505260SRowan Hart     { "mips64el", 1 },
47f2505260SRowan Hart     { "mipsel", 1 },
48f2505260SRowan Hart     { "mipsn32", 1 },
49f2505260SRowan Hart     { "mipsn32el", 1 },
50f2505260SRowan Hart     { "or1k", -1 },
51f2505260SRowan Hart     { "ppc", 4 },
52f2505260SRowan Hart     { "ppc64", 4 },
53f2505260SRowan Hart     { "ppc64le", 4 },
54f2505260SRowan Hart     { "riscv32", 64 },
55f2505260SRowan Hart     { "riscv64", 64 },
56f2505260SRowan Hart     { "rx", -1 },
57f2505260SRowan Hart     { "s390x", -1 },
58f2505260SRowan Hart     { "sh4", -1 },
59f2505260SRowan Hart     { "sh4eb", -1 },
60f2505260SRowan Hart     { "sparc", 4 },
61f2505260SRowan Hart     { "sparc32plus", 4 },
62f2505260SRowan Hart     { "sparc64", 4 },
63f2505260SRowan Hart     { "tricore", -1 },
64f2505260SRowan Hart     { "x86_64", 1 },
65f2505260SRowan Hart     { "xtensa", 13 },
66f2505260SRowan Hart     { "xtensaeb", 13 },
67f2505260SRowan Hart     { NULL, -1 },
68f2505260SRowan Hart };
69f2505260SRowan Hart 
70a6851b49SMahmoud Mandour static GMutex lock;
71a6851b49SMahmoud Mandour static GHashTable *statistics;
72f2505260SRowan Hart static GByteArray *memory_buffer;
73f2505260SRowan Hart static bool do_log_writes;
74f2505260SRowan Hart static int64_t write_sysno = -1;
75a6851b49SMahmoud Mandour 
get_or_create_entry(int64_t num)76a6851b49SMahmoud Mandour static SyscallStats *get_or_create_entry(int64_t num)
77a6851b49SMahmoud Mandour {
78a6851b49SMahmoud Mandour     SyscallStats *entry =
79b2a3ebb7SPierrick Bouvier         (SyscallStats *) g_hash_table_lookup(statistics, &num);
80a6851b49SMahmoud Mandour 
81a6851b49SMahmoud Mandour     if (!entry) {
82a6851b49SMahmoud Mandour         entry = g_new0(SyscallStats, 1);
83a6851b49SMahmoud Mandour         entry->num = num;
84b2a3ebb7SPierrick Bouvier         g_hash_table_insert(statistics, &entry->num, entry);
85a6851b49SMahmoud Mandour     }
86a6851b49SMahmoud Mandour 
87a6851b49SMahmoud Mandour     return entry;
88a6851b49SMahmoud Mandour }
89a6851b49SMahmoud Mandour 
90f2505260SRowan Hart /*
91f2505260SRowan Hart  * Hex-dump a GByteArray to the QEMU plugin output in the format:
92f2505260SRowan Hart  * 61 63 63 65 6c 09 09 20 20 20 66 70 75 09 09 09  | accel.....fpu...
93f2505260SRowan Hart  * 20 6d 6f 64 75 6c 65 2d 63 6f 6d 6d 6f 6e 2e 63  | .module-common.c
94f2505260SRowan Hart  */
hexdump(const GByteArray * data)95f2505260SRowan Hart static void hexdump(const GByteArray *data)
96f2505260SRowan Hart {
97f2505260SRowan Hart     g_autoptr(GString) out = g_string_new("");
98f2505260SRowan Hart 
99f2505260SRowan Hart     for (guint index = 0; index < data->len; index += 16) {
100f2505260SRowan Hart         for (guint col = 0; col < 16; col++) {
101f2505260SRowan Hart             if (index + col < data->len) {
102f2505260SRowan Hart                 g_string_append_printf(out, "%02x ", data->data[index + col]);
103f2505260SRowan Hart             } else {
104f2505260SRowan Hart                 g_string_append(out, "   ");
105f2505260SRowan Hart             }
106f2505260SRowan Hart         }
107f2505260SRowan Hart 
108f2505260SRowan Hart         g_string_append(out, " | ");
109f2505260SRowan Hart 
110f2505260SRowan Hart         for (guint col = 0; col < 16; col++) {
111f2505260SRowan Hart             if (index + col >= data->len) {
112f2505260SRowan Hart                 break;
113f2505260SRowan Hart             }
114f2505260SRowan Hart 
115f2505260SRowan Hart             if (g_ascii_isgraph(data->data[index + col])) {
116f2505260SRowan Hart                 g_string_append_printf(out, "%c", data->data[index + col]);
117f2505260SRowan Hart             } else {
118f2505260SRowan Hart                 g_string_append(out, ".");
119f2505260SRowan Hart             }
120f2505260SRowan Hart         }
121f2505260SRowan Hart 
122f2505260SRowan Hart         g_string_append(out, "\n");
123f2505260SRowan Hart     }
124f2505260SRowan Hart 
125f2505260SRowan Hart     qemu_plugin_outs(out->str);
126f2505260SRowan Hart }
127f2505260SRowan Hart 
vcpu_syscall(qemu_plugin_id_t id,unsigned int vcpu_index,int64_t num,uint64_t a1,uint64_t a2,uint64_t a3,uint64_t a4,uint64_t a5,uint64_t a6,uint64_t a7,uint64_t a8)128279d0a5bSMatthias Weckbecker static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
129279d0a5bSMatthias Weckbecker                          int64_t num, uint64_t a1, uint64_t a2,
130279d0a5bSMatthias Weckbecker                          uint64_t a3, uint64_t a4, uint64_t a5,
131279d0a5bSMatthias Weckbecker                          uint64_t a6, uint64_t a7, uint64_t a8)
132279d0a5bSMatthias Weckbecker {
133a6851b49SMahmoud Mandour     if (statistics) {
134a6851b49SMahmoud Mandour         SyscallStats *entry;
135a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
136a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
137a6851b49SMahmoud Mandour         entry->calls++;
138a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
139a6851b49SMahmoud Mandour     } else {
140279d0a5bSMatthias Weckbecker         g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
141279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
142279d0a5bSMatthias Weckbecker     }
143f2505260SRowan Hart 
144f2505260SRowan Hart     if (do_log_writes && num == write_sysno) {
145f2505260SRowan Hart         if (qemu_plugin_read_memory_vaddr(a2, memory_buffer, a3)) {
146f2505260SRowan Hart             hexdump(memory_buffer);
147f2505260SRowan Hart         } else {
148f2505260SRowan Hart             fprintf(stderr, "Error reading memory from vaddr %"PRIu64"\n", a2);
149f2505260SRowan Hart         }
150f2505260SRowan Hart     }
151a6851b49SMahmoud Mandour }
152279d0a5bSMatthias Weckbecker 
vcpu_syscall_ret(qemu_plugin_id_t id,unsigned int vcpu_idx,int64_t num,int64_t ret)153279d0a5bSMatthias Weckbecker static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
154279d0a5bSMatthias Weckbecker                              int64_t num, int64_t ret)
155279d0a5bSMatthias Weckbecker {
156a6851b49SMahmoud Mandour     if (statistics) {
157a6851b49SMahmoud Mandour         SyscallStats *entry;
158a6851b49SMahmoud Mandour 
159a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
160a6851b49SMahmoud Mandour         /* Should always return an existent entry. */
161a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
162a6851b49SMahmoud Mandour         if (ret < 0) {
163a6851b49SMahmoud Mandour             entry->errors++;
164a6851b49SMahmoud Mandour         }
165a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
166a6851b49SMahmoud Mandour     } else {
167d5615bbfSJuro Bystricky         g_autofree gchar *out = g_strdup_printf(
168d5615bbfSJuro Bystricky              "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret);
169279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
170279d0a5bSMatthias Weckbecker     }
171a6851b49SMahmoud Mandour }
172a6851b49SMahmoud Mandour 
print_entry(gpointer val,gpointer user_data)173a6851b49SMahmoud Mandour static void print_entry(gpointer val, gpointer user_data)
174a6851b49SMahmoud Mandour {
175a6851b49SMahmoud Mandour     SyscallStats *entry = (SyscallStats *) val;
176a6851b49SMahmoud Mandour     int64_t syscall_num = entry->num;
177d5615bbfSJuro Bystricky     g_autofree gchar *out = g_strdup_printf(
178a6851b49SMahmoud Mandour         "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
179a6851b49SMahmoud Mandour         syscall_num, entry->calls, entry->errors);
180a6851b49SMahmoud Mandour     qemu_plugin_outs(out);
181a6851b49SMahmoud Mandour }
182a6851b49SMahmoud Mandour 
comp_func(gconstpointer ea,gconstpointer eb,gpointer d)183*01499addSKohei Tokunaga static gint comp_func(gconstpointer ea, gconstpointer eb, gpointer d)
184a6851b49SMahmoud Mandour {
185a6851b49SMahmoud Mandour     SyscallStats *ent_a = (SyscallStats *) ea;
186a6851b49SMahmoud Mandour     SyscallStats *ent_b = (SyscallStats *) eb;
187a6851b49SMahmoud Mandour 
188a6851b49SMahmoud Mandour     return ent_a->calls > ent_b->calls ? -1 : 1;
189a6851b49SMahmoud Mandour }
190279d0a5bSMatthias Weckbecker 
191279d0a5bSMatthias Weckbecker /* ************************************************************************* */
plugin_exit(qemu_plugin_id_t id,void * p)192a6851b49SMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p)
193a6851b49SMahmoud Mandour {
194a6851b49SMahmoud Mandour     if (!statistics) {
195a6851b49SMahmoud Mandour         return;
196a6851b49SMahmoud Mandour     }
197279d0a5bSMatthias Weckbecker 
198a6851b49SMahmoud Mandour     g_mutex_lock(&lock);
199a6851b49SMahmoud Mandour     GList *entries = g_hash_table_get_values(statistics);
200*01499addSKohei Tokunaga     entries = g_list_sort_with_data(entries, comp_func, NULL);
201a6851b49SMahmoud Mandour     qemu_plugin_outs("syscall no.  calls  errors\n");
202a6851b49SMahmoud Mandour 
203a6851b49SMahmoud Mandour     g_list_foreach(entries, print_entry, NULL);
204a6851b49SMahmoud Mandour 
205a6851b49SMahmoud Mandour     g_list_free(entries);
206a6851b49SMahmoud Mandour     g_hash_table_destroy(statistics);
207a6851b49SMahmoud Mandour     g_mutex_unlock(&lock);
208a6851b49SMahmoud Mandour }
209279d0a5bSMatthias Weckbecker 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)210279d0a5bSMatthias Weckbecker QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
211279d0a5bSMatthias Weckbecker                                            const qemu_info_t *info,
212279d0a5bSMatthias Weckbecker                                            int argc, char **argv)
213279d0a5bSMatthias Weckbecker {
214a694d739SMahmoud Mandour     bool do_print = false;
215a694d739SMahmoud Mandour 
216a6851b49SMahmoud Mandour     for (int i = 0; i < argc; i++) {
217a694d739SMahmoud Mandour         char *opt = argv[i];
21840258741SAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
219a694d739SMahmoud Mandour 
220a694d739SMahmoud Mandour         if (g_strcmp0(tokens[0], "print") == 0) {
221a694d739SMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) {
222a694d739SMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
223a694d739SMahmoud Mandour             }
224f2505260SRowan Hart         } else if (g_strcmp0(tokens[0], "log_writes") == 0) {
225f2505260SRowan Hart             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_log_writes)) {
226f2505260SRowan Hart                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
227f2505260SRowan Hart             }
228a694d739SMahmoud Mandour         } else {
229a6851b49SMahmoud Mandour             fprintf(stderr, "unsupported argument: %s\n", argv[i]);
230a6851b49SMahmoud Mandour             return -1;
231a6851b49SMahmoud Mandour         }
232a6851b49SMahmoud Mandour     }
233a694d739SMahmoud Mandour 
234a694d739SMahmoud Mandour     if (!do_print) {
235b2a3ebb7SPierrick Bouvier         statistics = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free);
236a6851b49SMahmoud Mandour     }
237a6851b49SMahmoud Mandour 
238f2505260SRowan Hart     if (do_log_writes) {
239f2505260SRowan Hart         for (const struct SyscallInfo *syscall_info = arch_syscall_info;
240f2505260SRowan Hart             syscall_info->name != NULL; syscall_info++) {
241f2505260SRowan Hart 
242f2505260SRowan Hart             if (g_strcmp0(syscall_info->name, info->target_name) == 0) {
243f2505260SRowan Hart                 write_sysno = syscall_info->write_sysno;
244f2505260SRowan Hart                 break;
245f2505260SRowan Hart             }
246f2505260SRowan Hart         }
247f2505260SRowan Hart 
248f2505260SRowan Hart         if (write_sysno == -1) {
249f2505260SRowan Hart             fprintf(stderr, "write syscall number not found\n");
250f2505260SRowan Hart             return -1;
251f2505260SRowan Hart         }
252f2505260SRowan Hart 
253f2505260SRowan Hart         memory_buffer = g_byte_array_new();
254f2505260SRowan Hart     }
255f2505260SRowan Hart 
256279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
257279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
258279d0a5bSMatthias Weckbecker     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
259279d0a5bSMatthias Weckbecker     return 0;
260279d0a5bSMatthias Weckbecker }
261