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