bb.c (4478B)
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 17QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 18 19typedef struct { 20 GMutex lock; 21 int index; 22 uint64_t bb_count; 23 uint64_t insn_count; 24} CPUCount; 25 26/* Used by the inline & linux-user counts */ 27static bool do_inline; 28static CPUCount inline_count; 29 30/* Dump running CPU total on idle? */ 31static bool idle_report; 32static GPtrArray *counts; 33static int max_cpus; 34 35static void gen_one_cpu_report(CPUCount *count, GString *report) 36{ 37 if (count->bb_count) { 38 g_string_append_printf(report, "CPU%d: " 39 "bb's: %" PRIu64", insns: %" PRIu64 "\n", 40 count->index, 41 count->bb_count, count->insn_count); 42 } 43} 44 45static void plugin_exit(qemu_plugin_id_t id, void *p) 46{ 47 g_autoptr(GString) report = g_string_new(""); 48 49 if (do_inline || !max_cpus) { 50 g_string_printf(report, "bb's: %" PRIu64", insns: %" PRIu64 "\n", 51 inline_count.bb_count, inline_count.insn_count); 52 } else { 53 g_ptr_array_foreach(counts, (GFunc) gen_one_cpu_report, report); 54 } 55 qemu_plugin_outs(report->str); 56} 57 58static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) 59{ 60 CPUCount *count = g_ptr_array_index(counts, cpu_index); 61 g_autoptr(GString) report = g_string_new(""); 62 gen_one_cpu_report(count, report); 63 64 if (report->len > 0) { 65 g_string_prepend(report, "Idling "); 66 qemu_plugin_outs(report->str); 67 } 68} 69 70static void vcpu_tb_exec(unsigned int cpu_index, void *udata) 71{ 72 CPUCount *count = max_cpus ? 73 g_ptr_array_index(counts, cpu_index) : &inline_count; 74 75 uintptr_t n_insns = (uintptr_t)udata; 76 g_mutex_lock(&count->lock); 77 count->insn_count += n_insns; 78 count->bb_count++; 79 g_mutex_unlock(&count->lock); 80} 81 82static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 83{ 84 size_t n_insns = qemu_plugin_tb_n_insns(tb); 85 86 if (do_inline) { 87 qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, 88 &inline_count.bb_count, 1); 89 qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, 90 &inline_count.insn_count, 91 n_insns); 92 } else { 93 qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, 94 QEMU_PLUGIN_CB_NO_REGS, 95 (void *)n_insns); 96 } 97} 98 99QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 100 const qemu_info_t *info, 101 int argc, char **argv) 102{ 103 int i; 104 105 for (i = 0; i < argc; i++) { 106 char *opt = argv[i]; 107 g_autofree char **tokens = g_strsplit(opt, "=", 2); 108 if (g_strcmp0(tokens[0], "inline") == 0) { 109 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { 110 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 111 return -1; 112 } 113 } else if (g_strcmp0(tokens[0], "idle") == 0) { 114 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &idle_report)) { 115 fprintf(stderr, "boolean argument parsing failed: %s\n", opt); 116 return -1; 117 } 118 } else { 119 fprintf(stderr, "option parsing failed: %s\n", opt); 120 return -1; 121 } 122 } 123 124 if (info->system_emulation && !do_inline) { 125 max_cpus = info->system.max_vcpus; 126 counts = g_ptr_array_new(); 127 for (i = 0; i < max_cpus; i++) { 128 CPUCount *count = g_new0(CPUCount, 1); 129 g_mutex_init(&count->lock); 130 count->index = i; 131 g_ptr_array_add(counts, count); 132 } 133 } else if (!do_inline) { 134 g_mutex_init(&inline_count.lock); 135 } 136 137 if (idle_report) { 138 qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle); 139 } 140 141 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 142 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 143 return 0; 144}