1671f760bSEmilio G. Cota /* 2671f760bSEmilio G. Cota * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> 3671f760bSEmilio G. Cota * 4671f760bSEmilio G. Cota * License: GNU GPL, version 2 or later. 5671f760bSEmilio G. Cota * See the COPYING file in the top-level directory. 6671f760bSEmilio G. Cota */ 7671f760bSEmilio G. Cota #include <inttypes.h> 8671f760bSEmilio G. Cota #include <assert.h> 9671f760bSEmilio G. Cota #include <stdlib.h> 10671f760bSEmilio G. Cota #include <string.h> 11671f760bSEmilio G. Cota #include <unistd.h> 12671f760bSEmilio G. Cota #include <stdio.h> 13671f760bSEmilio G. Cota #include <glib.h> 14671f760bSEmilio G. Cota 15671f760bSEmilio G. Cota #include <qemu-plugin.h> 16671f760bSEmilio G. Cota 173fb356ccSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 183fb356ccSAlex Bennée 194f8d8860SPierrick Bouvier typedef struct { 204f8d8860SPierrick Bouvier uint64_t mem_count; 214f8d8860SPierrick Bouvier uint64_t io_count; 224f8d8860SPierrick Bouvier } CPUCount; 234f8d8860SPierrick Bouvier 24*7fd9ff76SPierrick Bouvier typedef struct { 25*7fd9ff76SPierrick Bouvier uint64_t vaddr; 26*7fd9ff76SPierrick Bouvier const char *sym; 27*7fd9ff76SPierrick Bouvier } InsnInfo; 28*7fd9ff76SPierrick Bouvier 294f8d8860SPierrick Bouvier static struct qemu_plugin_scoreboard *counts; 304f8d8860SPierrick Bouvier static qemu_plugin_u64 mem_count; 314f8d8860SPierrick Bouvier static qemu_plugin_u64 io_count; 32*7fd9ff76SPierrick Bouvier static bool do_inline, do_callback, do_print_accesses; 33671f760bSEmilio G. Cota static bool do_haddr; 34671f760bSEmilio G. Cota static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; 35671f760bSEmilio G. Cota 36671f760bSEmilio G. Cota static void plugin_exit(qemu_plugin_id_t id, void *p) 37671f760bSEmilio G. Cota { 38671f760bSEmilio G. Cota g_autoptr(GString) out = g_string_new(""); 39671f760bSEmilio G. Cota 404f8d8860SPierrick Bouvier if (do_inline || do_callback) { 414f8d8860SPierrick Bouvier g_string_printf(out, "mem accesses: %" PRIu64 "\n", 424f8d8860SPierrick Bouvier qemu_plugin_u64_sum(mem_count)); 430eca92e2SAlex Bennée } 44671f760bSEmilio G. Cota if (do_haddr) { 454f8d8860SPierrick Bouvier g_string_append_printf(out, "io accesses: %" PRIu64 "\n", 464f8d8860SPierrick Bouvier qemu_plugin_u64_sum(io_count)); 47671f760bSEmilio G. Cota } 48671f760bSEmilio G. Cota qemu_plugin_outs(out->str); 494f8d8860SPierrick Bouvier qemu_plugin_scoreboard_free(counts); 50671f760bSEmilio G. Cota } 51671f760bSEmilio G. Cota 52671f760bSEmilio G. Cota static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, 53671f760bSEmilio G. Cota uint64_t vaddr, void *udata) 54671f760bSEmilio G. Cota { 55671f760bSEmilio G. Cota if (do_haddr) { 56671f760bSEmilio G. Cota struct qemu_plugin_hwaddr *hwaddr; 57671f760bSEmilio G. Cota hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); 58671f760bSEmilio G. Cota if (qemu_plugin_hwaddr_is_io(hwaddr)) { 594f8d8860SPierrick Bouvier qemu_plugin_u64_add(io_count, cpu_index, 1); 60671f760bSEmilio G. Cota } else { 614f8d8860SPierrick Bouvier qemu_plugin_u64_add(mem_count, cpu_index, 1); 62671f760bSEmilio G. Cota } 63671f760bSEmilio G. Cota } else { 644f8d8860SPierrick Bouvier qemu_plugin_u64_add(mem_count, cpu_index, 1); 65671f760bSEmilio G. Cota } 66671f760bSEmilio G. Cota } 67671f760bSEmilio G. Cota 68*7fd9ff76SPierrick Bouvier static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, 69*7fd9ff76SPierrick Bouvier uint64_t vaddr, void *udata) 70*7fd9ff76SPierrick Bouvier { 71*7fd9ff76SPierrick Bouvier InsnInfo *insn_info = udata; 72*7fd9ff76SPierrick Bouvier unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo); 73*7fd9ff76SPierrick Bouvier const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load"; 74*7fd9ff76SPierrick Bouvier qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); 75*7fd9ff76SPierrick Bouvier uint64_t hwaddr = 76*7fd9ff76SPierrick Bouvier qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr)); 77*7fd9ff76SPierrick Bouvier g_autoptr(GString) out = g_string_new(""); 78*7fd9ff76SPierrick Bouvier g_string_printf(out, 79*7fd9ff76SPierrick Bouvier "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,", 80*7fd9ff76SPierrick Bouvier insn_info->vaddr, insn_info->sym, 81*7fd9ff76SPierrick Bouvier vaddr, hwaddr, size, type); 82*7fd9ff76SPierrick Bouvier switch (value.type) { 83*7fd9ff76SPierrick Bouvier case QEMU_PLUGIN_MEM_VALUE_U8: 84*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "0x%02"PRIx8, value.data.u8); 85*7fd9ff76SPierrick Bouvier break; 86*7fd9ff76SPierrick Bouvier case QEMU_PLUGIN_MEM_VALUE_U16: 87*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "0x%04"PRIx16, value.data.u16); 88*7fd9ff76SPierrick Bouvier break; 89*7fd9ff76SPierrick Bouvier case QEMU_PLUGIN_MEM_VALUE_U32: 90*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "0x%08"PRIx32, value.data.u32); 91*7fd9ff76SPierrick Bouvier break; 92*7fd9ff76SPierrick Bouvier case QEMU_PLUGIN_MEM_VALUE_U64: 93*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "0x%016"PRIx64, value.data.u64); 94*7fd9ff76SPierrick Bouvier break; 95*7fd9ff76SPierrick Bouvier case QEMU_PLUGIN_MEM_VALUE_U128: 96*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64, 97*7fd9ff76SPierrick Bouvier value.data.u128.high, value.data.u128.low); 98*7fd9ff76SPierrick Bouvier break; 99*7fd9ff76SPierrick Bouvier default: 100*7fd9ff76SPierrick Bouvier g_assert_not_reached(); 101*7fd9ff76SPierrick Bouvier } 102*7fd9ff76SPierrick Bouvier g_string_append_printf(out, "\n"); 103*7fd9ff76SPierrick Bouvier qemu_plugin_outs(out->str); 104*7fd9ff76SPierrick Bouvier } 105*7fd9ff76SPierrick Bouvier 106671f760bSEmilio G. Cota static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 107671f760bSEmilio G. Cota { 108671f760bSEmilio G. Cota size_t n = qemu_plugin_tb_n_insns(tb); 109671f760bSEmilio G. Cota size_t i; 110671f760bSEmilio G. Cota 111671f760bSEmilio G. Cota for (i = 0; i < n; i++) { 112671f760bSEmilio G. Cota struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); 113671f760bSEmilio G. Cota 114671f760bSEmilio G. Cota if (do_inline) { 1154f8d8860SPierrick Bouvier qemu_plugin_register_vcpu_mem_inline_per_vcpu( 1164f8d8860SPierrick Bouvier insn, rw, 117671f760bSEmilio G. Cota QEMU_PLUGIN_INLINE_ADD_U64, 1184f8d8860SPierrick Bouvier mem_count, 1); 1190eca92e2SAlex Bennée } 1200eca92e2SAlex Bennée if (do_callback) { 121671f760bSEmilio G. Cota qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, 122671f760bSEmilio G. Cota QEMU_PLUGIN_CB_NO_REGS, 123671f760bSEmilio G. Cota rw, NULL); 124671f760bSEmilio G. Cota } 125*7fd9ff76SPierrick Bouvier if (do_print_accesses) { 126*7fd9ff76SPierrick Bouvier /* we leak this pointer, to avoid locking to keep track of it */ 127*7fd9ff76SPierrick Bouvier InsnInfo *insn_info = g_malloc(sizeof(InsnInfo)); 128*7fd9ff76SPierrick Bouvier const char *sym = qemu_plugin_insn_symbol(insn); 129*7fd9ff76SPierrick Bouvier insn_info->sym = sym ? sym : ""; 130*7fd9ff76SPierrick Bouvier insn_info->vaddr = qemu_plugin_insn_vaddr(insn); 131*7fd9ff76SPierrick Bouvier qemu_plugin_register_vcpu_mem_cb(insn, print_access, 132*7fd9ff76SPierrick Bouvier QEMU_PLUGIN_CB_NO_REGS, 133*7fd9ff76SPierrick Bouvier rw, (void *) insn_info); 134*7fd9ff76SPierrick Bouvier } 135671f760bSEmilio G. Cota } 136671f760bSEmilio G. Cota } 137671f760bSEmilio G. Cota 138671f760bSEmilio G. Cota QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 139671f760bSEmilio G. Cota const qemu_info_t *info, 140671f760bSEmilio G. Cota int argc, char **argv) 141671f760bSEmilio G. Cota { 142671f760bSEmilio G. Cota 1435ae589faSMahmoud Mandour for (int i = 0; i < argc; i++) { 1445ae589faSMahmoud Mandour char *opt = argv[i]; 14540258741SAlex Bennée g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); 1465ae589faSMahmoud Mandour 1475ae589faSMahmoud Mandour if (g_strcmp0(tokens[0], "haddr") == 0) { 1485ae589faSMahmoud Mandour if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) { 1495ae589faSMahmoud Mandour fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 1505ae589faSMahmoud Mandour return -1; 1515ae589faSMahmoud Mandour } 1525ae589faSMahmoud Mandour } else if (g_strcmp0(tokens[0], "track") == 0) { 1535ae589faSMahmoud Mandour if (g_strcmp0(tokens[1], "r") == 0) { 154671f760bSEmilio G. Cota rw = QEMU_PLUGIN_MEM_R; 1555ae589faSMahmoud Mandour } else if (g_strcmp0(tokens[1], "w") == 0) { 156671f760bSEmilio G. Cota rw = QEMU_PLUGIN_MEM_W; 1575ae589faSMahmoud Mandour } else if (g_strcmp0(tokens[1], "rw") == 0) { 1585ae589faSMahmoud Mandour rw = QEMU_PLUGIN_MEM_RW; 1590eca92e2SAlex Bennée } else { 16096420a30SMichael Tokarev fprintf(stderr, "invalid value for argument track: %s\n", opt); 1615ae589faSMahmoud Mandour return -1; 1625ae589faSMahmoud Mandour } 1635ae589faSMahmoud Mandour } else if (g_strcmp0(tokens[0], "inline") == 0) { 1645ae589faSMahmoud Mandour if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { 1655ae589faSMahmoud Mandour fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 1665ae589faSMahmoud Mandour return -1; 1675ae589faSMahmoud Mandour } 1685ae589faSMahmoud Mandour } else if (g_strcmp0(tokens[0], "callback") == 0) { 1695ae589faSMahmoud Mandour if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) { 1705ae589faSMahmoud Mandour fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 1715ae589faSMahmoud Mandour return -1; 1725ae589faSMahmoud Mandour } 173*7fd9ff76SPierrick Bouvier } else if (g_strcmp0(tokens[0], "print-accesses") == 0) { 174*7fd9ff76SPierrick Bouvier if (!qemu_plugin_bool_parse(tokens[0], tokens[1], 175*7fd9ff76SPierrick Bouvier &do_print_accesses)) { 176*7fd9ff76SPierrick Bouvier fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 177*7fd9ff76SPierrick Bouvier return -1; 178*7fd9ff76SPierrick Bouvier } 1795ae589faSMahmoud Mandour } else { 1805ae589faSMahmoud Mandour fprintf(stderr, "option parsing failed: %s\n", opt); 1815ae589faSMahmoud Mandour return -1; 182671f760bSEmilio G. Cota } 183671f760bSEmilio G. Cota } 184671f760bSEmilio G. Cota 1854f8d8860SPierrick Bouvier if (do_inline && do_callback) { 1864f8d8860SPierrick Bouvier fprintf(stderr, 1874f8d8860SPierrick Bouvier "can't enable inline and callback counting at the same time\n"); 1884f8d8860SPierrick Bouvier return -1; 1894f8d8860SPierrick Bouvier } 1904f8d8860SPierrick Bouvier 191*7fd9ff76SPierrick Bouvier if (do_print_accesses) { 192*7fd9ff76SPierrick Bouvier g_autoptr(GString) out = g_string_new(""); 193*7fd9ff76SPierrick Bouvier g_string_printf(out, 194*7fd9ff76SPierrick Bouvier "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr," 195*7fd9ff76SPierrick Bouvier "access_size,access_type,mem_value\n"); 196*7fd9ff76SPierrick Bouvier qemu_plugin_outs(out->str); 197*7fd9ff76SPierrick Bouvier } 198*7fd9ff76SPierrick Bouvier 1994f8d8860SPierrick Bouvier counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); 2004f8d8860SPierrick Bouvier mem_count = qemu_plugin_scoreboard_u64_in_struct( 2014f8d8860SPierrick Bouvier counts, CPUCount, mem_count); 2024f8d8860SPierrick Bouvier io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count); 203671f760bSEmilio G. Cota qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 204671f760bSEmilio G. Cota qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 205671f760bSEmilio G. Cota return 0; 206671f760bSEmilio G. Cota } 207