| /* | |
| * Generate basic block vectors for use with the SimPoint analysis tool. | |
| * SimPoint: https://cseweb.ucsd.edu/~calder/simpoint/ | |
| * | |
| * SPDX-License-Identifier: GPL-2.0-or-later | |
| */ | |
| #include <stdio.h> | |
| #include <glib.h> | |
| #include <qemu-plugin.h> | |
| typedef struct Bb { | |
| uint64_t vaddr; | |
| struct qemu_plugin_scoreboard *count; | |
| unsigned int index; | |
| } Bb; | |
| typedef struct Vcpu { | |
| uint64_t count; | |
| FILE *file; | |
| } Vcpu; | |
| QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; | |
| static GHashTable *bbs; | |
| static GRWLock bbs_lock; | |
| static char *filename; | |
| static struct qemu_plugin_scoreboard *vcpus; | |
| static uint64_t interval = 100000000; | |
| static void plugin_exit(void *p) | |
| { | |
| Vcpu *vcpu; | |
| for (int i = 0; i < qemu_plugin_num_vcpus(); i++) { | |
| vcpu = qemu_plugin_scoreboard_find(vcpus, i); | |
| if (vcpu->file) { | |
| fclose(vcpu->file); | |
| } | |
| } | |
| g_hash_table_unref(bbs); | |
| g_free(filename); | |
| qemu_plugin_scoreboard_free(vcpus); | |
| } | |
| static void free_bb(void *data) | |
| { | |
| qemu_plugin_scoreboard_free(((Bb *)data)->count); | |
| g_free(data); | |
| } | |
| static qemu_plugin_u64 count_u64(void) | |
| { | |
| return qemu_plugin_scoreboard_u64_in_struct(vcpus, Vcpu, count); | |
| } | |
| static qemu_plugin_u64 bb_count_u64(Bb *bb) | |
| { | |
| return qemu_plugin_scoreboard_u64(bb->count); | |
| } | |
| static void vcpu_init(unsigned int vcpu_index, void *userdata) | |
| { | |
| g_autofree gchar *vcpu_filename = NULL; | |
| Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); | |
| vcpu_filename = g_strdup_printf("%s.%u.bb", filename, vcpu_index); | |
| vcpu->file = fopen(vcpu_filename, "w"); | |
| } | |
| static void vcpu_interval_exec(unsigned int vcpu_index, void *udata) | |
| { | |
| Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); | |
| GHashTableIter iter; | |
| void *value; | |
| if (!vcpu->file) { | |
| return; | |
| } | |
| vcpu->count -= interval; | |
| fputc('T', vcpu->file); | |
| g_rw_lock_reader_lock(&bbs_lock); | |
| g_hash_table_iter_init(&iter, bbs); | |
| while (g_hash_table_iter_next(&iter, NULL, &value)) { | |
| Bb *bb = value; | |
| uint64_t bb_count = qemu_plugin_u64_get(bb_count_u64(bb), vcpu_index); | |
| if (!bb_count) { | |
| continue; | |
| } | |
| fprintf(vcpu->file, ":%u:%" PRIu64 " ", bb->index, bb_count); | |
| qemu_plugin_u64_set(bb_count_u64(bb), vcpu_index, 0); | |
| } | |
| g_rw_lock_reader_unlock(&bbs_lock); | |
| fputc('\n', vcpu->file); | |
| } | |
| static void vcpu_tb_trans(struct qemu_plugin_tb *tb, void *userdata) | |
| { | |
| uint64_t n_insns = qemu_plugin_tb_n_insns(tb); | |
| uint64_t vaddr = qemu_plugin_tb_vaddr(tb); | |
| Bb *bb; | |
| g_rw_lock_writer_lock(&bbs_lock); | |
| bb = g_hash_table_lookup(bbs, &vaddr); | |
| if (!bb) { | |
| bb = g_new(Bb, 1); | |
| bb->vaddr = vaddr; | |
| bb->count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); | |
| bb->index = g_hash_table_size(bbs) + 1; | |
| g_hash_table_replace(bbs, &bb->vaddr, bb); | |
| } | |
| g_rw_lock_writer_unlock(&bbs_lock); | |
| qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( | |
| tb, QEMU_PLUGIN_INLINE_ADD_U64, count_u64(), n_insns); | |
| qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( | |
| tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count_u64(bb), n_insns); | |
| qemu_plugin_register_vcpu_tb_exec_cond_cb( | |
| tb, vcpu_interval_exec, QEMU_PLUGIN_CB_NO_REGS, | |
| QEMU_PLUGIN_COND_GE, count_u64(), interval, NULL); | |
| } | |
| QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, | |
| const qemu_info_t *info, | |
| int argc, char **argv) | |
| { | |
| for (int i = 0; i < argc; i++) { | |
| char *opt = argv[i]; | |
| g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); | |
| if (g_strcmp0(tokens[0], "interval") == 0) { | |
| interval = g_ascii_strtoull(tokens[1], NULL, 10); | |
| } else if (g_strcmp0(tokens[0], "outfile") == 0) { | |
| filename = tokens[1]; | |
| tokens[1] = NULL; | |
| } else { | |
| fprintf(stderr, "option parsing failed: %s\n", opt); | |
| return -1; | |
| } | |
| } | |
| if (!filename) { | |
| fputs("outfile unspecified\n", stderr); | |
| return -1; | |
| } | |
| bbs = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, free_bb); | |
| vcpus = qemu_plugin_scoreboard_new(sizeof(Vcpu)); | |
| qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); | |
| qemu_plugin_register_vcpu_init_cb(id, vcpu_init, NULL); | |
| qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL); | |
| return 0; | |
| } |