1 /* 2 * Generate basic block vectors for use with the SimPoint analysis tool. 3 * SimPoint: https://cseweb.ucsd.edu/~calder/simpoint/ 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 */ 7 8 #include <stdio.h> 9 #include <glib.h> 10 11 #include <qemu-plugin.h> 12 13 typedef struct Bb { 14 uint64_t vaddr; 15 struct qemu_plugin_scoreboard *count; 16 unsigned int index; 17 } Bb; 18 19 typedef struct Vcpu { 20 uint64_t count; 21 FILE *file; 22 } Vcpu; 23 24 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 25 static GHashTable *bbs; 26 static GRWLock bbs_lock; 27 static char *filename; 28 static struct qemu_plugin_scoreboard *vcpus; 29 static uint64_t interval = 100000000; 30 31 static void plugin_exit(qemu_plugin_id_t id, void *p) 32 { 33 for (int i = 0; i < qemu_plugin_num_vcpus(); i++) { 34 fclose(((Vcpu *)qemu_plugin_scoreboard_find(vcpus, i))->file); 35 } 36 37 g_hash_table_unref(bbs); 38 g_free(filename); 39 qemu_plugin_scoreboard_free(vcpus); 40 } 41 42 static void free_bb(void *data) 43 { 44 qemu_plugin_scoreboard_free(((Bb *)data)->count); 45 g_free(data); 46 } 47 48 static qemu_plugin_u64 count_u64(void) 49 { 50 return qemu_plugin_scoreboard_u64_in_struct(vcpus, Vcpu, count); 51 } 52 53 static qemu_plugin_u64 bb_count_u64(Bb *bb) 54 { 55 return qemu_plugin_scoreboard_u64(bb->count); 56 } 57 58 static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) 59 { 60 g_autofree gchar *vcpu_filename = NULL; 61 Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); 62 63 vcpu_filename = g_strdup_printf("%s.%u.bb", filename, vcpu_index); 64 vcpu->file = fopen(vcpu_filename, "w"); 65 } 66 67 static void vcpu_interval_exec(unsigned int vcpu_index, void *udata) 68 { 69 Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); 70 GHashTableIter iter; 71 void *value; 72 73 if (!vcpu->file) { 74 return; 75 } 76 77 vcpu->count -= interval; 78 79 fputc('T', vcpu->file); 80 81 g_rw_lock_reader_lock(&bbs_lock); 82 g_hash_table_iter_init(&iter, bbs); 83 84 while (g_hash_table_iter_next(&iter, NULL, &value)) { 85 Bb *bb = value; 86 uint64_t bb_count = qemu_plugin_u64_get(bb_count_u64(bb), vcpu_index); 87 88 if (!bb_count) { 89 continue; 90 } 91 92 fprintf(vcpu->file, ":%u:%" PRIu64 " ", bb->index, bb_count); 93 qemu_plugin_u64_set(bb_count_u64(bb), vcpu_index, 0); 94 } 95 96 g_rw_lock_reader_unlock(&bbs_lock); 97 fputc('\n', vcpu->file); 98 } 99 100 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 101 { 102 uint64_t n_insns = qemu_plugin_tb_n_insns(tb); 103 uint64_t vaddr = qemu_plugin_tb_vaddr(tb); 104 Bb *bb; 105 106 g_rw_lock_writer_lock(&bbs_lock); 107 bb = g_hash_table_lookup(bbs, &vaddr); 108 if (!bb) { 109 bb = g_new(Bb, 1); 110 bb->vaddr = vaddr; 111 bb->count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); 112 bb->index = g_hash_table_size(bbs); 113 g_hash_table_replace(bbs, &bb->vaddr, bb); 114 } 115 g_rw_lock_writer_unlock(&bbs_lock); 116 117 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( 118 tb, QEMU_PLUGIN_INLINE_ADD_U64, count_u64(), n_insns); 119 120 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( 121 tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count_u64(bb), n_insns); 122 123 qemu_plugin_register_vcpu_tb_exec_cond_cb( 124 tb, vcpu_interval_exec, QEMU_PLUGIN_CB_NO_REGS, 125 QEMU_PLUGIN_COND_GE, count_u64(), interval, NULL); 126 } 127 128 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 129 const qemu_info_t *info, 130 int argc, char **argv) 131 { 132 for (int i = 0; i < argc; i++) { 133 char *opt = argv[i]; 134 g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); 135 if (g_strcmp0(tokens[0], "interval") == 0) { 136 interval = g_ascii_strtoull(tokens[1], NULL, 10); 137 } else if (g_strcmp0(tokens[0], "outfile") == 0) { 138 filename = tokens[1]; 139 tokens[1] = NULL; 140 } else { 141 fprintf(stderr, "option parsing failed: %s\n", opt); 142 return -1; 143 } 144 } 145 146 if (!filename) { 147 fputs("outfile unspecified\n", stderr); 148 return -1; 149 } 150 151 bbs = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, free_bb); 152 vcpus = qemu_plugin_scoreboard_new(sizeof(Vcpu)); 153 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 154 qemu_plugin_register_vcpu_init_cb(id, vcpu_init); 155 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 156 157 return 0; 158 } 159