xref: /openbmc/qemu/tests/plugin/syscall.c (revision a6851b49)
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 
19*a6851b49SMahmoud Mandour typedef struct {
20*a6851b49SMahmoud Mandour     int64_t num;
21*a6851b49SMahmoud Mandour     int64_t calls;
22*a6851b49SMahmoud Mandour     int64_t errors;
23*a6851b49SMahmoud Mandour } SyscallStats;
24*a6851b49SMahmoud Mandour 
25*a6851b49SMahmoud Mandour static GMutex lock;
26*a6851b49SMahmoud Mandour static GHashTable *statistics;
27*a6851b49SMahmoud Mandour 
28*a6851b49SMahmoud Mandour static SyscallStats *get_or_create_entry(int64_t num)
29*a6851b49SMahmoud Mandour {
30*a6851b49SMahmoud Mandour     SyscallStats *entry =
31*a6851b49SMahmoud Mandour         (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num));
32*a6851b49SMahmoud Mandour 
33*a6851b49SMahmoud Mandour     if (!entry) {
34*a6851b49SMahmoud Mandour         entry = g_new0(SyscallStats, 1);
35*a6851b49SMahmoud Mandour         entry->num = num;
36*a6851b49SMahmoud Mandour         g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry);
37*a6851b49SMahmoud Mandour     }
38*a6851b49SMahmoud Mandour 
39*a6851b49SMahmoud Mandour     return entry;
40*a6851b49SMahmoud Mandour }
41*a6851b49SMahmoud Mandour 
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 {
47*a6851b49SMahmoud Mandour     if (statistics) {
48*a6851b49SMahmoud Mandour         SyscallStats *entry;
49*a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
50*a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
51*a6851b49SMahmoud Mandour         entry->calls++;
52*a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
53*a6851b49SMahmoud Mandour     } else {
54279d0a5bSMatthias Weckbecker         g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
55279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
56279d0a5bSMatthias Weckbecker     }
57*a6851b49SMahmoud Mandour }
58279d0a5bSMatthias Weckbecker 
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 {
62*a6851b49SMahmoud Mandour     if (statistics) {
63*a6851b49SMahmoud Mandour         SyscallStats *entry;
64*a6851b49SMahmoud Mandour 
65*a6851b49SMahmoud Mandour         g_mutex_lock(&lock);
66*a6851b49SMahmoud Mandour         /* Should always return an existent entry. */
67*a6851b49SMahmoud Mandour         entry = get_or_create_entry(num);
68*a6851b49SMahmoud Mandour         if (ret < 0) {
69*a6851b49SMahmoud Mandour             entry->errors++;
70*a6851b49SMahmoud Mandour         }
71*a6851b49SMahmoud Mandour         g_mutex_unlock(&lock);
72*a6851b49SMahmoud Mandour     } else {
73279d0a5bSMatthias Weckbecker         g_autofree gchar *out;
74279d0a5bSMatthias Weckbecker         out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n",
75279d0a5bSMatthias Weckbecker                 num, ret);
76279d0a5bSMatthias Weckbecker         qemu_plugin_outs(out);
77279d0a5bSMatthias Weckbecker     }
78*a6851b49SMahmoud Mandour }
79*a6851b49SMahmoud Mandour 
80*a6851b49SMahmoud Mandour static void print_entry(gpointer val, gpointer user_data)
81*a6851b49SMahmoud Mandour {
82*a6851b49SMahmoud Mandour     g_autofree gchar *out;
83*a6851b49SMahmoud Mandour     SyscallStats *entry = (SyscallStats *) val;
84*a6851b49SMahmoud Mandour     int64_t syscall_num = entry->num;
85*a6851b49SMahmoud Mandour     out = g_strdup_printf(
86*a6851b49SMahmoud Mandour         "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
87*a6851b49SMahmoud Mandour         syscall_num, entry->calls, entry->errors);
88*a6851b49SMahmoud Mandour     qemu_plugin_outs(out);
89*a6851b49SMahmoud Mandour }
90*a6851b49SMahmoud Mandour 
91*a6851b49SMahmoud Mandour static gint comp_func(gconstpointer ea, gconstpointer eb)
92*a6851b49SMahmoud Mandour {
93*a6851b49SMahmoud Mandour     SyscallStats *ent_a = (SyscallStats *) ea;
94*a6851b49SMahmoud Mandour     SyscallStats *ent_b = (SyscallStats *) eb;
95*a6851b49SMahmoud Mandour 
96*a6851b49SMahmoud Mandour     return ent_a->calls > ent_b->calls ? -1 : 1;
97*a6851b49SMahmoud Mandour }
98279d0a5bSMatthias Weckbecker 
99279d0a5bSMatthias Weckbecker /* ************************************************************************* */
100*a6851b49SMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p)
101*a6851b49SMahmoud Mandour {
102*a6851b49SMahmoud Mandour     if (!statistics) {
103*a6851b49SMahmoud Mandour         return;
104*a6851b49SMahmoud Mandour     }
105279d0a5bSMatthias Weckbecker 
106*a6851b49SMahmoud Mandour     g_mutex_lock(&lock);
107*a6851b49SMahmoud Mandour     GList *entries = g_hash_table_get_values(statistics);
108*a6851b49SMahmoud Mandour     entries = g_list_sort(entries, comp_func);
109*a6851b49SMahmoud Mandour     qemu_plugin_outs("syscall no.  calls  errors\n");
110*a6851b49SMahmoud Mandour 
111*a6851b49SMahmoud Mandour     g_list_foreach(entries, print_entry, NULL);
112*a6851b49SMahmoud Mandour 
113*a6851b49SMahmoud Mandour     g_list_free(entries);
114*a6851b49SMahmoud Mandour     g_hash_table_destroy(statistics);
115*a6851b49SMahmoud Mandour     g_mutex_unlock(&lock);
116*a6851b49SMahmoud Mandour }
117279d0a5bSMatthias Weckbecker 
118279d0a5bSMatthias Weckbecker QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
119279d0a5bSMatthias Weckbecker                                            const qemu_info_t *info,
120279d0a5bSMatthias Weckbecker                                            int argc, char **argv)
121279d0a5bSMatthias Weckbecker {
122*a6851b49SMahmoud Mandour     if (argc == 0) {
123*a6851b49SMahmoud Mandour         statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
124*a6851b49SMahmoud Mandour     } else {
125*a6851b49SMahmoud Mandour         for (int i = 0; i < argc; i++) {
126*a6851b49SMahmoud Mandour             if (g_strcmp0(argv[i], "print") != 0) {
127*a6851b49SMahmoud Mandour                 fprintf(stderr, "unsupported argument: %s\n", argv[i]);
128*a6851b49SMahmoud Mandour                 return -1;
129*a6851b49SMahmoud Mandour             }
130*a6851b49SMahmoud Mandour         }
131*a6851b49SMahmoud Mandour     }
132*a6851b49SMahmoud Mandour 
133279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
134279d0a5bSMatthias Weckbecker     qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
135279d0a5bSMatthias Weckbecker     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
136279d0a5bSMatthias Weckbecker     return 0;
137279d0a5bSMatthias Weckbecker }
138