1 /* 2 * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> 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 19 typedef struct { 20 uint64_t mem_count; 21 uint64_t io_count; 22 } CPUCount; 23 24 typedef struct { 25 uint64_t vaddr; 26 const char *sym; 27 } InsnInfo; 28 29 static struct qemu_plugin_scoreboard *counts; 30 static qemu_plugin_u64 mem_count; 31 static qemu_plugin_u64 io_count; 32 static bool do_inline, do_callback, do_print_accesses; 33 static bool do_haddr; 34 static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; 35 36 static void plugin_exit(qemu_plugin_id_t id, void *p) 37 { 38 g_autoptr(GString) out = g_string_new(""); 39 40 if (do_inline || do_callback) { 41 g_string_printf(out, "mem accesses: %" PRIu64 "\n", 42 qemu_plugin_u64_sum(mem_count)); 43 } 44 if (do_haddr) { 45 g_string_append_printf(out, "io accesses: %" PRIu64 "\n", 46 qemu_plugin_u64_sum(io_count)); 47 } 48 qemu_plugin_outs(out->str); 49 qemu_plugin_scoreboard_free(counts); 50 } 51 52 static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, 53 uint64_t vaddr, void *udata) 54 { 55 if (do_haddr) { 56 struct qemu_plugin_hwaddr *hwaddr; 57 hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); 58 if (qemu_plugin_hwaddr_is_io(hwaddr)) { 59 qemu_plugin_u64_add(io_count, cpu_index, 1); 60 } else { 61 qemu_plugin_u64_add(mem_count, cpu_index, 1); 62 } 63 } else { 64 qemu_plugin_u64_add(mem_count, cpu_index, 1); 65 } 66 } 67 68 static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, 69 uint64_t vaddr, void *udata) 70 { 71 InsnInfo *insn_info = udata; 72 unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo); 73 const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load"; 74 qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); 75 uint64_t hwaddr = 76 qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr)); 77 g_autoptr(GString) out = g_string_new(""); 78 g_string_printf(out, 79 "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,", 80 insn_info->vaddr, insn_info->sym, 81 vaddr, hwaddr, size, type); 82 switch (value.type) { 83 case QEMU_PLUGIN_MEM_VALUE_U8: 84 g_string_append_printf(out, "0x%02"PRIx8, value.data.u8); 85 break; 86 case QEMU_PLUGIN_MEM_VALUE_U16: 87 g_string_append_printf(out, "0x%04"PRIx16, value.data.u16); 88 break; 89 case QEMU_PLUGIN_MEM_VALUE_U32: 90 g_string_append_printf(out, "0x%08"PRIx32, value.data.u32); 91 break; 92 case QEMU_PLUGIN_MEM_VALUE_U64: 93 g_string_append_printf(out, "0x%016"PRIx64, value.data.u64); 94 break; 95 case QEMU_PLUGIN_MEM_VALUE_U128: 96 g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64, 97 value.data.u128.high, value.data.u128.low); 98 break; 99 default: 100 g_assert_not_reached(); 101 } 102 g_string_append_printf(out, "\n"); 103 qemu_plugin_outs(out->str); 104 } 105 106 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 107 { 108 size_t n = qemu_plugin_tb_n_insns(tb); 109 size_t i; 110 111 for (i = 0; i < n; i++) { 112 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); 113 114 if (do_inline) { 115 qemu_plugin_register_vcpu_mem_inline_per_vcpu( 116 insn, rw, 117 QEMU_PLUGIN_INLINE_ADD_U64, 118 mem_count, 1); 119 } 120 if (do_callback) { 121 qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, 122 QEMU_PLUGIN_CB_NO_REGS, 123 rw, NULL); 124 } 125 if (do_print_accesses) { 126 /* we leak this pointer, to avoid locking to keep track of it */ 127 InsnInfo *insn_info = g_malloc(sizeof(InsnInfo)); 128 const char *sym = qemu_plugin_insn_symbol(insn); 129 insn_info->sym = sym ? sym : ""; 130 insn_info->vaddr = qemu_plugin_insn_vaddr(insn); 131 qemu_plugin_register_vcpu_mem_cb(insn, print_access, 132 QEMU_PLUGIN_CB_NO_REGS, 133 rw, (void *) insn_info); 134 } 135 } 136 } 137 138 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 139 const qemu_info_t *info, 140 int argc, char **argv) 141 { 142 143 for (int i = 0; i < argc; i++) { 144 char *opt = argv[i]; 145 g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); 146 147 if (g_strcmp0(tokens[0], "haddr") == 0) { 148 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) { 149 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 150 return -1; 151 } 152 } else if (g_strcmp0(tokens[0], "track") == 0) { 153 if (g_strcmp0(tokens[1], "r") == 0) { 154 rw = QEMU_PLUGIN_MEM_R; 155 } else if (g_strcmp0(tokens[1], "w") == 0) { 156 rw = QEMU_PLUGIN_MEM_W; 157 } else if (g_strcmp0(tokens[1], "rw") == 0) { 158 rw = QEMU_PLUGIN_MEM_RW; 159 } else { 160 fprintf(stderr, "invalid value for argument track: %s\n", opt); 161 return -1; 162 } 163 } else if (g_strcmp0(tokens[0], "inline") == 0) { 164 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { 165 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 166 return -1; 167 } 168 } else if (g_strcmp0(tokens[0], "callback") == 0) { 169 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) { 170 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 171 return -1; 172 } 173 } else if (g_strcmp0(tokens[0], "print-accesses") == 0) { 174 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], 175 &do_print_accesses)) { 176 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 177 return -1; 178 } 179 } else { 180 fprintf(stderr, "option parsing failed: %s\n", opt); 181 return -1; 182 } 183 } 184 185 if (do_inline && do_callback) { 186 fprintf(stderr, 187 "can't enable inline and callback counting at the same time\n"); 188 return -1; 189 } 190 191 if (do_print_accesses) { 192 g_autoptr(GString) out = g_string_new(""); 193 g_string_printf(out, 194 "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr," 195 "access_size,access_type,mem_value\n"); 196 qemu_plugin_outs(out->str); 197 } 198 199 counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); 200 mem_count = qemu_plugin_scoreboard_u64_in_struct( 201 counts, CPUCount, mem_count); 202 io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count); 203 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 204 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 205 return 0; 206 } 207