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 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 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 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 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 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 /* ************************************************************************* */ 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 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