xref: /openbmc/qemu/contrib/plugins/bbv.c (revision 6fdc5bc1)
1 /*
2  * Generate basic block vectors for use with the SimPoint analysis tool.
3  * SimPoint: https://cseweb.ucsd.edu/~calder/simpoint/
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  */
7 
8 #include <stdio.h>
9 #include <glib.h>
10 
11 #include <qemu-plugin.h>
12 
13 typedef struct Bb {
14     uint64_t vaddr;
15     struct qemu_plugin_scoreboard *count;
16     unsigned int index;
17 } Bb;
18 
19 typedef struct Vcpu {
20     uint64_t count;
21     FILE *file;
22 } Vcpu;
23 
24 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
25 static GHashTable *bbs;
26 static GRWLock bbs_lock;
27 static char *filename;
28 static struct qemu_plugin_scoreboard *vcpus;
29 static uint64_t interval = 100000000;
30 
31 static void plugin_exit(qemu_plugin_id_t id, void *p)
32 {
33     for (int i = 0; i < qemu_plugin_num_vcpus(); i++) {
34         fclose(((Vcpu *)qemu_plugin_scoreboard_find(vcpus, i))->file);
35     }
36 
37     g_hash_table_unref(bbs);
38     g_free(filename);
39     qemu_plugin_scoreboard_free(vcpus);
40 }
41 
42 static void free_bb(void *data)
43 {
44     qemu_plugin_scoreboard_free(((Bb *)data)->count);
45     g_free(data);
46 }
47 
48 static qemu_plugin_u64 count_u64(void)
49 {
50     return qemu_plugin_scoreboard_u64_in_struct(vcpus, Vcpu, count);
51 }
52 
53 static qemu_plugin_u64 bb_count_u64(Bb *bb)
54 {
55     return qemu_plugin_scoreboard_u64(bb->count);
56 }
57 
58 static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
59 {
60     g_autofree gchar *vcpu_filename = NULL;
61     Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index);
62 
63     vcpu_filename = g_strdup_printf("%s.%u.bb", filename, vcpu_index);
64     vcpu->file = fopen(vcpu_filename, "w");
65 }
66 
67 static void vcpu_interval_exec(unsigned int vcpu_index, void *udata)
68 {
69     Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index);
70     GHashTableIter iter;
71     void *value;
72 
73     if (!vcpu->file) {
74         return;
75     }
76 
77     vcpu->count -= interval;
78 
79     fputc('T', vcpu->file);
80 
81     g_rw_lock_reader_lock(&bbs_lock);
82     g_hash_table_iter_init(&iter, bbs);
83 
84     while (g_hash_table_iter_next(&iter, NULL, &value)) {
85         Bb *bb = value;
86         uint64_t bb_count = qemu_plugin_u64_get(bb_count_u64(bb), vcpu_index);
87 
88         if (!bb_count) {
89             continue;
90         }
91 
92         fprintf(vcpu->file, ":%u:%" PRIu64 " ", bb->index, bb_count);
93         qemu_plugin_u64_set(bb_count_u64(bb), vcpu_index, 0);
94     }
95 
96     g_rw_lock_reader_unlock(&bbs_lock);
97     fputc('\n', vcpu->file);
98 }
99 
100 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
101 {
102     uint64_t n_insns = qemu_plugin_tb_n_insns(tb);
103     uint64_t vaddr = qemu_plugin_tb_vaddr(tb);
104     Bb *bb;
105 
106     g_rw_lock_writer_lock(&bbs_lock);
107     bb = g_hash_table_lookup(bbs, &vaddr);
108     if (!bb) {
109         bb = g_new(Bb, 1);
110         bb->vaddr = vaddr;
111         bb->count = qemu_plugin_scoreboard_new(sizeof(uint64_t));
112         bb->index = g_hash_table_size(bbs);
113         g_hash_table_replace(bbs, &bb->vaddr, bb);
114     }
115     g_rw_lock_writer_unlock(&bbs_lock);
116 
117     qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
118         tb, QEMU_PLUGIN_INLINE_ADD_U64, count_u64(), n_insns);
119 
120     qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
121         tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count_u64(bb), n_insns);
122 
123     qemu_plugin_register_vcpu_tb_exec_cond_cb(
124         tb, vcpu_interval_exec, QEMU_PLUGIN_CB_NO_REGS,
125         QEMU_PLUGIN_COND_GE, count_u64(), interval, NULL);
126 }
127 
128 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
129                                            const qemu_info_t *info,
130                                            int argc, char **argv)
131 {
132     for (int i = 0; i < argc; i++) {
133         char *opt = argv[i];
134         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
135         if (g_strcmp0(tokens[0], "interval") == 0) {
136             interval = g_ascii_strtoull(tokens[1], NULL, 10);
137         } else if (g_strcmp0(tokens[0], "outfile") == 0) {
138             filename = tokens[1];
139             tokens[1] = NULL;
140         } else {
141             fprintf(stderr, "option parsing failed: %s\n", opt);
142             return -1;
143         }
144     }
145 
146     if (!filename) {
147         fputs("outfile unspecified\n", stderr);
148         return -1;
149     }
150 
151     bbs = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, free_bb);
152     vcpus = qemu_plugin_scoreboard_new(sizeof(Vcpu));
153     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
154     qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
155     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
156 
157     return 0;
158 }
159