xref: /qemu/tests/plugin/bb.c (revision b49f4755)
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     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 */
27 static bool do_inline;
28 static CPUCount inline_count;
29 
30 /* Dump running CPU total on idle? */
31 static bool idle_report;
32 static GPtrArray *counts;
33 static int max_cpus;
34 
35 static 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 
45 static 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 
58 static 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 
70 static 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 
82 static 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 
99 QEMU_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_auto(GStrv) 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 }
145