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
plugin_exit(qemu_plugin_id_t id,void * p)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
free_bb(void * data)42 static void free_bb(void *data)
43 {
44 qemu_plugin_scoreboard_free(((Bb *)data)->count);
45 g_free(data);
46 }
47
count_u64(void)48 static qemu_plugin_u64 count_u64(void)
49 {
50 return qemu_plugin_scoreboard_u64_in_struct(vcpus, Vcpu, count);
51 }
52
bb_count_u64(Bb * bb)53 static qemu_plugin_u64 bb_count_u64(Bb *bb)
54 {
55 return qemu_plugin_scoreboard_u64(bb->count);
56 }
57
vcpu_init(qemu_plugin_id_t id,unsigned int vcpu_index)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
vcpu_interval_exec(unsigned int vcpu_index,void * udata)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
vcpu_tb_trans(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)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
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)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