xref: /openbmc/qemu/tests/plugin/syscall.c (revision 40258741)
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 
25a6851b49SMahmoud Mandour static GMutex lock;
26a6851b49SMahmoud Mandour static GHashTable *statistics;
27a6851b49SMahmoud Mandour 
get_or_create_entry(int64_t num)28a6851b49SMahmoud Mandour static SyscallStats *get_or_create_entry(int64_t num)
29a6851b49SMahmoud Mandour {
30a6851b49SMahmoud Mandour     SyscallStats *entry =
31a6851b49SMahmoud Mandour         (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num));
32a6851b49SMahmoud Mandour 
33a6851b49SMahmoud Mandour     if (!entry) {
34a6851b49SMahmoud Mandour         entry = g_new0(SyscallStats, 1);
35a6851b49SMahmoud Mandour         entry->num = num;
36a6851b49SMahmoud Mandour         g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry);
37a6851b49SMahmoud Mandour     }
38a6851b49SMahmoud Mandour 
39a6851b49SMahmoud Mandour     return entry;
40a6851b49SMahmoud Mandour }
41a6851b49SMahmoud Mandour 
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)42279d0a5bSMatthias Weckbecker static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
43279d0a5bSMatthias Weckbecker                          int64_t num, uint64_t a1, uint64_t a2,
44279d0a5bSMatthias Weckbecker                          uint64_t a3, uint64_t a4, uint64_t a5,
45279d0a5bSMatthias Weckbecker                          uint64_t a6, uint64_t a7, uint64_t a8)
46279d0a5bSMatthias Weckbecker {
47a6851b49SMahmoud Mandour     if (statistics) {
48a6851b49SMahmoud Mandour         SyscallStats *entry;
49a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
50a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
51a6851b49SMahmoud Mandour         entry->calls++;
52a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
53a6851b49SMahmoud Mandour     } else {
54279d0a5bSMatthias Weckbecker         g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
55279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
56279d0a5bSMatthias Weckbecker     }
57a6851b49SMahmoud Mandour }
58279d0a5bSMatthias Weckbecker 
vcpu_syscall_ret(qemu_plugin_id_t id,unsigned int vcpu_idx,int64_t num,int64_t ret)59279d0a5bSMatthias Weckbecker static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
60279d0a5bSMatthias Weckbecker                              int64_t num, int64_t ret)
61279d0a5bSMatthias Weckbecker {
62a6851b49SMahmoud Mandour     if (statistics) {
63a6851b49SMahmoud Mandour         SyscallStats *entry;
64a6851b49SMahmoud Mandour 
65a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
66a6851b49SMahmoud Mandour         /* Should always return an existent entry. */
67a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
68a6851b49SMahmoud Mandour         if (ret < 0) {
69a6851b49SMahmoud Mandour             entry->errors++;
70a6851b49SMahmoud Mandour         }
71a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
72a6851b49SMahmoud Mandour     } else {
73d5615bbfSJuro Bystricky         g_autofree gchar *out = g_strdup_printf(
74d5615bbfSJuro Bystricky              "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret);
75279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
76279d0a5bSMatthias Weckbecker     }
77a6851b49SMahmoud Mandour }
78a6851b49SMahmoud Mandour 
print_entry(gpointer val,gpointer user_data)79a6851b49SMahmoud Mandour static void print_entry(gpointer val, gpointer user_data)
80a6851b49SMahmoud Mandour {
81a6851b49SMahmoud Mandour     SyscallStats *entry = (SyscallStats *) val;
82a6851b49SMahmoud Mandour     int64_t syscall_num = entry->num;
83d5615bbfSJuro Bystricky     g_autofree gchar *out = g_strdup_printf(
84a6851b49SMahmoud Mandour         "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
85a6851b49SMahmoud Mandour         syscall_num, entry->calls, entry->errors);
86a6851b49SMahmoud Mandour     qemu_plugin_outs(out);
87a6851b49SMahmoud Mandour }
88a6851b49SMahmoud Mandour 
comp_func(gconstpointer ea,gconstpointer eb)89a6851b49SMahmoud Mandour static gint comp_func(gconstpointer ea, gconstpointer eb)
90a6851b49SMahmoud Mandour {
91a6851b49SMahmoud Mandour     SyscallStats *ent_a = (SyscallStats *) ea;
92a6851b49SMahmoud Mandour     SyscallStats *ent_b = (SyscallStats *) eb;
93a6851b49SMahmoud Mandour 
94a6851b49SMahmoud Mandour     return ent_a->calls > ent_b->calls ? -1 : 1;
95a6851b49SMahmoud Mandour }
96279d0a5bSMatthias Weckbecker 
97279d0a5bSMatthias Weckbecker /* ************************************************************************* */
plugin_exit(qemu_plugin_id_t id,void * p)98a6851b49SMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p)
99a6851b49SMahmoud Mandour {
100a6851b49SMahmoud Mandour     if (!statistics) {
101a6851b49SMahmoud Mandour         return;
102a6851b49SMahmoud Mandour     }
103279d0a5bSMatthias Weckbecker 
104a6851b49SMahmoud Mandour     g_mutex_lock(&lock);
105a6851b49SMahmoud Mandour     GList *entries = g_hash_table_get_values(statistics);
106a6851b49SMahmoud Mandour     entries = g_list_sort(entries, comp_func);
107a6851b49SMahmoud Mandour     qemu_plugin_outs("syscall no.  calls  errors\n");
108a6851b49SMahmoud Mandour 
109a6851b49SMahmoud Mandour     g_list_foreach(entries, print_entry, NULL);
110a6851b49SMahmoud Mandour 
111a6851b49SMahmoud Mandour     g_list_free(entries);
112a6851b49SMahmoud Mandour     g_hash_table_destroy(statistics);
113a6851b49SMahmoud Mandour     g_mutex_unlock(&lock);
114a6851b49SMahmoud Mandour }
115279d0a5bSMatthias Weckbecker 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)116279d0a5bSMatthias Weckbecker QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
117279d0a5bSMatthias Weckbecker                                            const qemu_info_t *info,
118279d0a5bSMatthias Weckbecker                                            int argc, char **argv)
119279d0a5bSMatthias Weckbecker {
120a694d739SMahmoud Mandour     bool do_print = false;
121a694d739SMahmoud Mandour 
122a6851b49SMahmoud Mandour     for (int i = 0; i < argc; i++) {
123a694d739SMahmoud Mandour         char *opt = argv[i];
124*40258741SAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
125a694d739SMahmoud Mandour 
126a694d739SMahmoud Mandour         if (g_strcmp0(tokens[0], "print") == 0) {
127a694d739SMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) {
128a694d739SMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
129a694d739SMahmoud Mandour             }
130a694d739SMahmoud Mandour         } else {
131a6851b49SMahmoud Mandour             fprintf(stderr, "unsupported argument: %s\n", argv[i]);
132a6851b49SMahmoud Mandour             return -1;
133a6851b49SMahmoud Mandour         }
134a6851b49SMahmoud Mandour     }
135a694d739SMahmoud Mandour 
136a694d739SMahmoud Mandour     if (!do_print) {
137a694d739SMahmoud Mandour         statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
138a6851b49SMahmoud Mandour     }
139a6851b49SMahmoud Mandour 
140279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
141279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
142279d0a5bSMatthias Weckbecker     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
143279d0a5bSMatthias Weckbecker     return 0;
144279d0a5bSMatthias Weckbecker }
145