]>
Commit | Line | Data |
---|---|---|
279d0a5b MW |
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 | ||
a6851b49 MM |
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 | ||
279d0a5b MW |
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 | { | |
a6851b49 MM |
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 | } | |
279d0a5b MW |
57 | } |
58 | ||
59 | static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, | |
60 | int64_t num, int64_t ret) | |
a6851b49 MM |
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 { | |
d5615bbf JB |
73 | g_autofree gchar *out = g_strdup_printf( |
74 | "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret); | |
a6851b49 MM |
75 | qemu_plugin_outs(out); |
76 | } | |
77 | } | |
78 | ||
79 | static void print_entry(gpointer val, gpointer user_data) | |
279d0a5b | 80 | { |
a6851b49 MM |
81 | SyscallStats *entry = (SyscallStats *) val; |
82 | int64_t syscall_num = entry->num; | |
d5615bbf | 83 | g_autofree gchar *out = g_strdup_printf( |
a6851b49 MM |
84 | "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n", |
85 | syscall_num, entry->calls, entry->errors); | |
279d0a5b MW |
86 | qemu_plugin_outs(out); |
87 | } | |
88 | ||
a6851b49 MM |
89 | static gint comp_func(gconstpointer ea, gconstpointer eb) |
90 | { | |
91 | SyscallStats *ent_a = (SyscallStats *) ea; | |
92 | SyscallStats *ent_b = (SyscallStats *) eb; | |
93 | ||
94 | return ent_a->calls > ent_b->calls ? -1 : 1; | |
95 | } | |
96 | ||
279d0a5b | 97 | /* ************************************************************************* */ |
a6851b49 MM |
98 | static void plugin_exit(qemu_plugin_id_t id, void *p) |
99 | { | |
100 | if (!statistics) { | |
101 | return; | |
102 | } | |
103 | ||
104 | g_mutex_lock(&lock); | |
105 | GList *entries = g_hash_table_get_values(statistics); | |
106 | entries = g_list_sort(entries, comp_func); | |
107 | qemu_plugin_outs("syscall no. calls errors\n"); | |
279d0a5b | 108 | |
a6851b49 MM |
109 | g_list_foreach(entries, print_entry, NULL); |
110 | ||
111 | g_list_free(entries); | |
112 | g_hash_table_destroy(statistics); | |
113 | g_mutex_unlock(&lock); | |
114 | } | |
279d0a5b MW |
115 | |
116 | QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, | |
117 | const qemu_info_t *info, | |
118 | int argc, char **argv) | |
119 | { | |
a694d739 MM |
120 | bool do_print = false; |
121 | ||
122 | for (int i = 0; i < argc; i++) { | |
123 | char *opt = argv[i]; | |
40258741 | 124 | g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); |
a694d739 MM |
125 | |
126 | if (g_strcmp0(tokens[0], "print") == 0) { | |
127 | if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { | |
128 | fprintf(stderr, "boolean argument parsing failed: %s\n", opt); | |
a6851b49 | 129 | } |
a694d739 MM |
130 | } else { |
131 | fprintf(stderr, "unsupported argument: %s\n", argv[i]); | |
132 | return -1; | |
a6851b49 MM |
133 | } |
134 | } | |
135 | ||
a694d739 MM |
136 | if (!do_print) { |
137 | statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); | |
138 | } | |
139 | ||
279d0a5b MW |
140 | qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); |
141 | qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); | |
142 | qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); | |
143 | return 0; | |
144 | } |