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 bb_count; 21 uint64_t insn_count; 22 } CPUCount; 23 24 static struct qemu_plugin_scoreboard *counts; 25 static qemu_plugin_u64 bb_count; 26 static qemu_plugin_u64 insn_count; 27 28 static bool do_inline; 29 /* Dump running CPU total on idle? */ 30 static bool idle_report; 31 32 static void gen_one_cpu_report(CPUCount *count, GString *report, 33 unsigned int cpu_index) 34 { 35 if (count->bb_count) { 36 g_string_append_printf(report, "CPU%d: " 37 "bb's: %" PRIu64", insns: %" PRIu64 "\n", 38 cpu_index, 39 count->bb_count, count->insn_count); 40 } 41 } 42 43 static void plugin_exit(qemu_plugin_id_t id, void *p) 44 { 45 g_autoptr(GString) report = g_string_new(""); 46 47 for (int i = 0; i < qemu_plugin_num_vcpus(); ++i) { 48 CPUCount *count = qemu_plugin_scoreboard_find(counts, i); 49 gen_one_cpu_report(count, report, i); 50 } 51 g_string_append_printf(report, "Total: " 52 "bb's: %" PRIu64", insns: %" PRIu64 "\n", 53 qemu_plugin_u64_sum(bb_count), 54 qemu_plugin_u64_sum(insn_count)); 55 qemu_plugin_outs(report->str); 56 qemu_plugin_scoreboard_free(counts); 57 } 58 59 static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) 60 { 61 CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); 62 g_autoptr(GString) report = g_string_new(""); 63 gen_one_cpu_report(count, report, cpu_index); 64 65 if (report->len > 0) { 66 g_string_prepend(report, "Idling "); 67 qemu_plugin_outs(report->str); 68 } 69 } 70 71 static void vcpu_tb_exec(unsigned int cpu_index, void *udata) 72 { 73 CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); 74 75 uintptr_t n_insns = (uintptr_t)udata; 76 count->insn_count += n_insns; 77 count->bb_count++; 78 } 79 80 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 81 { 82 size_t n_insns = qemu_plugin_tb_n_insns(tb); 83 84 if (do_inline) { 85 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( 86 tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count, 1); 87 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( 88 tb, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, n_insns); 89 } else { 90 qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, 91 QEMU_PLUGIN_CB_NO_REGS, 92 (void *)n_insns); 93 } 94 } 95 96 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 97 const qemu_info_t *info, 98 int argc, char **argv) 99 { 100 int i; 101 102 for (i = 0; i < argc; i++) { 103 char *opt = argv[i]; 104 g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); 105 if (g_strcmp0(tokens[0], "inline") == 0) { 106 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { 107 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 108 return -1; 109 } 110 } else if (g_strcmp0(tokens[0], "idle") == 0) { 111 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &idle_report)) { 112 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 113 return -1; 114 } 115 } else { 116 fprintf(stderr, "option parsing failed: %s\n", opt); 117 return -1; 118 } 119 } 120 121 counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); 122 bb_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, bb_count); 123 insn_count = qemu_plugin_scoreboard_u64_in_struct( 124 counts, CPUCount, insn_count); 125 126 if (idle_report) { 127 qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle); 128 } 129 130 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 131 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 132 return 0; 133 } 134