1 /* 2 * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.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 <inttypes.h> 11 #include <string.h> 12 #include <unistd.h> 13 #include <stdio.h> 14 #include <glib.h> 15 16 #include <qemu-plugin.h> 17 18 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 19 20 static bool do_inline; 21 22 /* Plugins need to take care of their own locking */ 23 static GMutex lock; 24 static GHashTable *hotblocks; 25 static guint64 limit = 20; 26 27 /* 28 * Counting Structure 29 * 30 * The internals of the TCG are not exposed to plugins so we can only 31 * get the starting PC for each block. We cheat this slightly by 32 * xor'ing the number of instructions to the hash to help 33 * differentiate. 34 */ 35 typedef struct { 36 uint64_t start_addr; 37 uint64_t exec_count; 38 int trans_count; 39 unsigned long insns; 40 } ExecCount; 41 42 static gint cmp_exec_count(gconstpointer a, gconstpointer b) 43 { 44 ExecCount *ea = (ExecCount *) a; 45 ExecCount *eb = (ExecCount *) b; 46 return ea->exec_count > eb->exec_count ? -1 : 1; 47 } 48 49 static void plugin_exit(qemu_plugin_id_t id, void *p) 50 { 51 g_autoptr(GString) report = g_string_new("collected "); 52 GList *counts, *it; 53 int i; 54 55 g_mutex_lock(&lock); 56 g_string_append_printf(report, "%d entries in the hash table\n", 57 g_hash_table_size(hotblocks)); 58 counts = g_hash_table_get_values(hotblocks); 59 it = g_list_sort(counts, cmp_exec_count); 60 61 if (it) { 62 g_string_append_printf(report, "pc, tcount, icount, ecount\n"); 63 64 for (i = 0; i < limit && it->next; i++, it = it->next) { 65 ExecCount *rec = (ExecCount *) it->data; 66 g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", 67 rec->start_addr, rec->trans_count, 68 rec->insns, rec->exec_count); 69 } 70 71 g_list_free(it); 72 g_mutex_unlock(&lock); 73 } 74 75 qemu_plugin_outs(report->str); 76 } 77 78 static void plugin_init(void) 79 { 80 hotblocks = g_hash_table_new(NULL, g_direct_equal); 81 } 82 83 static void vcpu_tb_exec(unsigned int cpu_index, void *udata) 84 { 85 ExecCount *cnt; 86 uint64_t hash = (uint64_t) udata; 87 88 g_mutex_lock(&lock); 89 cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); 90 /* should always succeed */ 91 g_assert(cnt); 92 cnt->exec_count++; 93 g_mutex_unlock(&lock); 94 } 95 96 /* 97 * When do_inline we ask the plugin to increment the counter for us. 98 * Otherwise a helper is inserted which calls the vcpu_tb_exec 99 * callback. 100 */ 101 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 102 { 103 ExecCount *cnt; 104 uint64_t pc = qemu_plugin_tb_vaddr(tb); 105 size_t insns = qemu_plugin_tb_n_insns(tb); 106 uint64_t hash = pc ^ insns; 107 108 g_mutex_lock(&lock); 109 cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); 110 if (cnt) { 111 cnt->trans_count++; 112 } else { 113 cnt = g_new0(ExecCount, 1); 114 cnt->start_addr = pc; 115 cnt->trans_count = 1; 116 cnt->insns = insns; 117 g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); 118 } 119 120 g_mutex_unlock(&lock); 121 122 if (do_inline) { 123 qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, 124 &cnt->exec_count, 1); 125 } else { 126 qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, 127 QEMU_PLUGIN_CB_NO_REGS, 128 (void *)hash); 129 } 130 } 131 132 QEMU_PLUGIN_EXPORT 133 int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, 134 int argc, char **argv) 135 { 136 for (int i = 0; i < argc; i++) { 137 char *opt = argv[i]; 138 g_autofree char **tokens = g_strsplit(opt, "=", 2); 139 if (g_strcmp0(tokens[0], "inline") == 0) { 140 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { 141 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 142 return -1; 143 } 144 } else { 145 fprintf(stderr, "option parsing failed: %s\n", opt); 146 return -1; 147 } 148 } 149 150 plugin_init(); 151 152 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 153 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 154 return 0; 155 } 156