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 25f2505260SRowan Hart struct SyscallInfo { 26f2505260SRowan Hart const char *name; 27f2505260SRowan Hart int64_t write_sysno; 28f2505260SRowan Hart }; 29f2505260SRowan Hart 30f2505260SRowan Hart static const struct SyscallInfo arch_syscall_info[] = { 31f2505260SRowan Hart { "aarch64", 64 }, 32f2505260SRowan Hart { "aarch64_be", 64 }, 33f2505260SRowan Hart { "alpha", 4 }, 34f2505260SRowan Hart { "arm", 4 }, 35f2505260SRowan Hart { "armeb", 4 }, 36f2505260SRowan Hart { "avr", -1 }, 37f2505260SRowan Hart { "hexagon", 64 }, 38f2505260SRowan Hart { "hppa", -1 }, 39f2505260SRowan Hart { "i386", 4 }, 40f2505260SRowan Hart { "loongarch64", -1 }, 41f2505260SRowan Hart { "m68k", 4 }, 42f2505260SRowan Hart { "microblaze", 4 }, 43f2505260SRowan Hart { "microblazeel", 4 }, 44f2505260SRowan Hart { "mips", 1 }, 45f2505260SRowan Hart { "mips64", 1 }, 46f2505260SRowan Hart { "mips64el", 1 }, 47f2505260SRowan Hart { "mipsel", 1 }, 48f2505260SRowan Hart { "mipsn32", 1 }, 49f2505260SRowan Hart { "mipsn32el", 1 }, 50f2505260SRowan Hart { "or1k", -1 }, 51f2505260SRowan Hart { "ppc", 4 }, 52f2505260SRowan Hart { "ppc64", 4 }, 53f2505260SRowan Hart { "ppc64le", 4 }, 54f2505260SRowan Hart { "riscv32", 64 }, 55f2505260SRowan Hart { "riscv64", 64 }, 56f2505260SRowan Hart { "rx", -1 }, 57f2505260SRowan Hart { "s390x", -1 }, 58f2505260SRowan Hart { "sh4", -1 }, 59f2505260SRowan Hart { "sh4eb", -1 }, 60f2505260SRowan Hart { "sparc", 4 }, 61f2505260SRowan Hart { "sparc32plus", 4 }, 62f2505260SRowan Hart { "sparc64", 4 }, 63f2505260SRowan Hart { "tricore", -1 }, 64f2505260SRowan Hart { "x86_64", 1 }, 65f2505260SRowan Hart { "xtensa", 13 }, 66f2505260SRowan Hart { "xtensaeb", 13 }, 67f2505260SRowan Hart { NULL, -1 }, 68f2505260SRowan Hart }; 69f2505260SRowan Hart 70a6851b49SMahmoud Mandour static GMutex lock; 71a6851b49SMahmoud Mandour static GHashTable *statistics; 72f2505260SRowan Hart static GByteArray *memory_buffer; 73f2505260SRowan Hart static bool do_log_writes; 74f2505260SRowan Hart static int64_t write_sysno = -1; 75a6851b49SMahmoud Mandour 76a6851b49SMahmoud Mandour static SyscallStats *get_or_create_entry(int64_t num) 77a6851b49SMahmoud Mandour { 78a6851b49SMahmoud Mandour SyscallStats *entry = 79*b2a3ebb7SPierrick Bouvier (SyscallStats *) g_hash_table_lookup(statistics, &num); 80a6851b49SMahmoud Mandour 81a6851b49SMahmoud Mandour if (!entry) { 82a6851b49SMahmoud Mandour entry = g_new0(SyscallStats, 1); 83a6851b49SMahmoud Mandour entry->num = num; 84*b2a3ebb7SPierrick Bouvier g_hash_table_insert(statistics, &entry->num, entry); 85a6851b49SMahmoud Mandour } 86a6851b49SMahmoud Mandour 87a6851b49SMahmoud Mandour return entry; 88a6851b49SMahmoud Mandour } 89a6851b49SMahmoud Mandour 90f2505260SRowan Hart /* 91f2505260SRowan Hart * Hex-dump a GByteArray to the QEMU plugin output in the format: 92f2505260SRowan Hart * 61 63 63 65 6c 09 09 20 20 20 66 70 75 09 09 09 | accel.....fpu... 93f2505260SRowan Hart * 20 6d 6f 64 75 6c 65 2d 63 6f 6d 6d 6f 6e 2e 63 | .module-common.c 94f2505260SRowan Hart */ 95f2505260SRowan Hart static void hexdump(const GByteArray *data) 96f2505260SRowan Hart { 97f2505260SRowan Hart g_autoptr(GString) out = g_string_new(""); 98f2505260SRowan Hart 99f2505260SRowan Hart for (guint index = 0; index < data->len; index += 16) { 100f2505260SRowan Hart for (guint col = 0; col < 16; col++) { 101f2505260SRowan Hart if (index + col < data->len) { 102f2505260SRowan Hart g_string_append_printf(out, "%02x ", data->data[index + col]); 103f2505260SRowan Hart } else { 104f2505260SRowan Hart g_string_append(out, " "); 105f2505260SRowan Hart } 106f2505260SRowan Hart } 107f2505260SRowan Hart 108f2505260SRowan Hart g_string_append(out, " | "); 109f2505260SRowan Hart 110f2505260SRowan Hart for (guint col = 0; col < 16; col++) { 111f2505260SRowan Hart if (index + col >= data->len) { 112f2505260SRowan Hart break; 113f2505260SRowan Hart } 114f2505260SRowan Hart 115f2505260SRowan Hart if (g_ascii_isgraph(data->data[index + col])) { 116f2505260SRowan Hart g_string_append_printf(out, "%c", data->data[index + col]); 117f2505260SRowan Hart } else { 118f2505260SRowan Hart g_string_append(out, "."); 119f2505260SRowan Hart } 120f2505260SRowan Hart } 121f2505260SRowan Hart 122f2505260SRowan Hart g_string_append(out, "\n"); 123f2505260SRowan Hart } 124f2505260SRowan Hart 125f2505260SRowan Hart qemu_plugin_outs(out->str); 126f2505260SRowan Hart } 127f2505260SRowan Hart 128279d0a5bSMatthias Weckbecker static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, 129279d0a5bSMatthias Weckbecker int64_t num, uint64_t a1, uint64_t a2, 130279d0a5bSMatthias Weckbecker uint64_t a3, uint64_t a4, uint64_t a5, 131279d0a5bSMatthias Weckbecker uint64_t a6, uint64_t a7, uint64_t a8) 132279d0a5bSMatthias Weckbecker { 133a6851b49SMahmoud Mandour if (statistics) { 134a6851b49SMahmoud Mandour SyscallStats *entry; 135a6851b49SMahmoud Mandour g_mutex_lock(&lock); 136a6851b49SMahmoud Mandour entry = get_or_create_entry(num); 137a6851b49SMahmoud Mandour entry->calls++; 138a6851b49SMahmoud Mandour g_mutex_unlock(&lock); 139a6851b49SMahmoud Mandour } else { 140279d0a5bSMatthias Weckbecker g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num); 141279d0a5bSMatthias Weckbecker qemu_plugin_outs(out); 142279d0a5bSMatthias Weckbecker } 143f2505260SRowan Hart 144f2505260SRowan Hart if (do_log_writes && num == write_sysno) { 145f2505260SRowan Hart if (qemu_plugin_read_memory_vaddr(a2, memory_buffer, a3)) { 146f2505260SRowan Hart hexdump(memory_buffer); 147f2505260SRowan Hart } else { 148f2505260SRowan Hart fprintf(stderr, "Error reading memory from vaddr %"PRIu64"\n", a2); 149f2505260SRowan Hart } 150f2505260SRowan Hart } 151a6851b49SMahmoud Mandour } 152279d0a5bSMatthias Weckbecker 153279d0a5bSMatthias Weckbecker static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, 154279d0a5bSMatthias Weckbecker int64_t num, int64_t ret) 155279d0a5bSMatthias Weckbecker { 156a6851b49SMahmoud Mandour if (statistics) { 157a6851b49SMahmoud Mandour SyscallStats *entry; 158a6851b49SMahmoud Mandour 159a6851b49SMahmoud Mandour g_mutex_lock(&lock); 160a6851b49SMahmoud Mandour /* Should always return an existent entry. */ 161a6851b49SMahmoud Mandour entry = get_or_create_entry(num); 162a6851b49SMahmoud Mandour if (ret < 0) { 163a6851b49SMahmoud Mandour entry->errors++; 164a6851b49SMahmoud Mandour } 165a6851b49SMahmoud Mandour g_mutex_unlock(&lock); 166a6851b49SMahmoud Mandour } else { 167d5615bbfSJuro Bystricky g_autofree gchar *out = g_strdup_printf( 168d5615bbfSJuro Bystricky "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret); 169279d0a5bSMatthias Weckbecker qemu_plugin_outs(out); 170279d0a5bSMatthias Weckbecker } 171a6851b49SMahmoud Mandour } 172a6851b49SMahmoud Mandour 173a6851b49SMahmoud Mandour static void print_entry(gpointer val, gpointer user_data) 174a6851b49SMahmoud Mandour { 175a6851b49SMahmoud Mandour SyscallStats *entry = (SyscallStats *) val; 176a6851b49SMahmoud Mandour int64_t syscall_num = entry->num; 177d5615bbfSJuro Bystricky g_autofree gchar *out = g_strdup_printf( 178a6851b49SMahmoud Mandour "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n", 179a6851b49SMahmoud Mandour syscall_num, entry->calls, entry->errors); 180a6851b49SMahmoud Mandour qemu_plugin_outs(out); 181a6851b49SMahmoud Mandour } 182a6851b49SMahmoud Mandour 183a6851b49SMahmoud Mandour static gint comp_func(gconstpointer ea, gconstpointer eb) 184a6851b49SMahmoud Mandour { 185a6851b49SMahmoud Mandour SyscallStats *ent_a = (SyscallStats *) ea; 186a6851b49SMahmoud Mandour SyscallStats *ent_b = (SyscallStats *) eb; 187a6851b49SMahmoud Mandour 188a6851b49SMahmoud Mandour return ent_a->calls > ent_b->calls ? -1 : 1; 189a6851b49SMahmoud Mandour } 190279d0a5bSMatthias Weckbecker 191279d0a5bSMatthias Weckbecker /* ************************************************************************* */ 192a6851b49SMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p) 193a6851b49SMahmoud Mandour { 194a6851b49SMahmoud Mandour if (!statistics) { 195a6851b49SMahmoud Mandour return; 196a6851b49SMahmoud Mandour } 197279d0a5bSMatthias Weckbecker 198a6851b49SMahmoud Mandour g_mutex_lock(&lock); 199a6851b49SMahmoud Mandour GList *entries = g_hash_table_get_values(statistics); 200a6851b49SMahmoud Mandour entries = g_list_sort(entries, comp_func); 201a6851b49SMahmoud Mandour qemu_plugin_outs("syscall no. calls errors\n"); 202a6851b49SMahmoud Mandour 203a6851b49SMahmoud Mandour g_list_foreach(entries, print_entry, NULL); 204a6851b49SMahmoud Mandour 205a6851b49SMahmoud Mandour g_list_free(entries); 206a6851b49SMahmoud Mandour g_hash_table_destroy(statistics); 207a6851b49SMahmoud Mandour g_mutex_unlock(&lock); 208a6851b49SMahmoud Mandour } 209279d0a5bSMatthias Weckbecker 210279d0a5bSMatthias Weckbecker QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 211279d0a5bSMatthias Weckbecker const qemu_info_t *info, 212279d0a5bSMatthias Weckbecker int argc, char **argv) 213279d0a5bSMatthias Weckbecker { 214a694d739SMahmoud Mandour bool do_print = false; 215a694d739SMahmoud Mandour 216a6851b49SMahmoud Mandour for (int i = 0; i < argc; i++) { 217a694d739SMahmoud Mandour char *opt = argv[i]; 21840258741SAlex Bennée g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); 219a694d739SMahmoud Mandour 220a694d739SMahmoud Mandour if (g_strcmp0(tokens[0], "print") == 0) { 221a694d739SMahmoud Mandour if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { 222a694d739SMahmoud Mandour fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 223a694d739SMahmoud Mandour } 224f2505260SRowan Hart } else if (g_strcmp0(tokens[0], "log_writes") == 0) { 225f2505260SRowan Hart if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_log_writes)) { 226f2505260SRowan Hart fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 227f2505260SRowan Hart } 228a694d739SMahmoud Mandour } else { 229a6851b49SMahmoud Mandour fprintf(stderr, "unsupported argument: %s\n", argv[i]); 230a6851b49SMahmoud Mandour return -1; 231a6851b49SMahmoud Mandour } 232a6851b49SMahmoud Mandour } 233a694d739SMahmoud Mandour 234a694d739SMahmoud Mandour if (!do_print) { 235*b2a3ebb7SPierrick Bouvier statistics = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); 236a6851b49SMahmoud Mandour } 237a6851b49SMahmoud Mandour 238f2505260SRowan Hart if (do_log_writes) { 239f2505260SRowan Hart for (const struct SyscallInfo *syscall_info = arch_syscall_info; 240f2505260SRowan Hart syscall_info->name != NULL; syscall_info++) { 241f2505260SRowan Hart 242f2505260SRowan Hart if (g_strcmp0(syscall_info->name, info->target_name) == 0) { 243f2505260SRowan Hart write_sysno = syscall_info->write_sysno; 244f2505260SRowan Hart break; 245f2505260SRowan Hart } 246f2505260SRowan Hart } 247f2505260SRowan Hart 248f2505260SRowan Hart if (write_sysno == -1) { 249f2505260SRowan Hart fprintf(stderr, "write syscall number not found\n"); 250f2505260SRowan Hart return -1; 251f2505260SRowan Hart } 252f2505260SRowan Hart 253f2505260SRowan Hart memory_buffer = g_byte_array_new(); 254f2505260SRowan Hart } 255f2505260SRowan Hart 256279d0a5bSMatthias Weckbecker qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); 257279d0a5bSMatthias Weckbecker qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); 258279d0a5bSMatthias Weckbecker qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 259279d0a5bSMatthias Weckbecker return 0; 260279d0a5bSMatthias Weckbecker } 261