xref: /qemu/tests/plugin/syscall.c (revision b355f08a)
1 /*
2  * Copyright (C) 2020, Matthias Weckbecker <matthias@weckbecker.name>
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     int64_t num;
21     int64_t calls;
22     int64_t errors;
23 } SyscallStats;
24 
25 static GMutex lock;
26 static GHashTable *statistics;
27 
28 static SyscallStats *get_or_create_entry(int64_t num)
29 {
30     SyscallStats *entry =
31         (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num));
32 
33     if (!entry) {
34         entry = g_new0(SyscallStats, 1);
35         entry->num = num;
36         g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry);
37     }
38 
39     return entry;
40 }
41 
42 static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
43                          int64_t num, uint64_t a1, uint64_t a2,
44                          uint64_t a3, uint64_t a4, uint64_t a5,
45                          uint64_t a6, uint64_t a7, uint64_t a8)
46 {
47     if (statistics) {
48         SyscallStats *entry;
49         g_mutex_lock(&lock);
50         entry = get_or_create_entry(num);
51         entry->calls++;
52         g_mutex_unlock(&lock);
53     } else {
54         g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
55         qemu_plugin_outs(out);
56     }
57 }
58 
59 static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
60                              int64_t num, int64_t ret)
61 {
62     if (statistics) {
63         SyscallStats *entry;
64 
65         g_mutex_lock(&lock);
66         /* Should always return an existent entry. */
67         entry = get_or_create_entry(num);
68         if (ret < 0) {
69             entry->errors++;
70         }
71         g_mutex_unlock(&lock);
72     } else {
73         g_autofree gchar *out;
74         out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n",
75                 num, ret);
76         qemu_plugin_outs(out);
77     }
78 }
79 
80 static void print_entry(gpointer val, gpointer user_data)
81 {
82     g_autofree gchar *out;
83     SyscallStats *entry = (SyscallStats *) val;
84     int64_t syscall_num = entry->num;
85     out = g_strdup_printf(
86         "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
87         syscall_num, entry->calls, entry->errors);
88     qemu_plugin_outs(out);
89 }
90 
91 static gint comp_func(gconstpointer ea, gconstpointer eb)
92 {
93     SyscallStats *ent_a = (SyscallStats *) ea;
94     SyscallStats *ent_b = (SyscallStats *) eb;
95 
96     return ent_a->calls > ent_b->calls ? -1 : 1;
97 }
98 
99 /* ************************************************************************* */
100 static void plugin_exit(qemu_plugin_id_t id, void *p)
101 {
102     if (!statistics) {
103         return;
104     }
105 
106     g_mutex_lock(&lock);
107     GList *entries = g_hash_table_get_values(statistics);
108     entries = g_list_sort(entries, comp_func);
109     qemu_plugin_outs("syscall no.  calls  errors\n");
110 
111     g_list_foreach(entries, print_entry, NULL);
112 
113     g_list_free(entries);
114     g_hash_table_destroy(statistics);
115     g_mutex_unlock(&lock);
116 }
117 
118 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
119                                            const qemu_info_t *info,
120                                            int argc, char **argv)
121 {
122     bool do_print = false;
123 
124     for (int i = 0; i < argc; i++) {
125         char *opt = argv[i];
126         g_autofree char **tokens = g_strsplit(opt, "=", 2);
127 
128         if (g_strcmp0(tokens[0], "print") == 0) {
129             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) {
130                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
131             }
132         } else {
133             fprintf(stderr, "unsupported argument: %s\n", argv[i]);
134             return -1;
135         }
136     }
137 
138     if (!do_print) {
139         statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
140     }
141 
142     qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
143     qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
144     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
145     return 0;
146 }
147