2 * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
4 * License: GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
15 #include <qemu-plugin.h>
17 QEMU_PLUGIN_EXPORT
int qemu_plugin_version
= QEMU_PLUGIN_VERSION
;
19 static qemu_plugin_u64 insn_count
;
21 static bool do_inline
;
33 struct qemu_plugin_scoreboard
*counts
; /* MatchCount */
36 static GArray
*matches
;
46 * Initialise a new vcpu with reading the register list
48 static void vcpu_init(qemu_plugin_id_t id
, unsigned int vcpu_index
)
50 g_autoptr(GArray
) reg_list
= qemu_plugin_get_registers();
51 g_autoptr(GByteArray
) reg_value
= g_byte_array_new();
54 for (int i
= 0; i
< reg_list
->len
; i
++) {
55 qemu_plugin_reg_descriptor
*rd
= &g_array_index(
56 reg_list
, qemu_plugin_reg_descriptor
, i
);
57 int count
= qemu_plugin_read_register(rd
->handle
, reg_value
);
64 static void vcpu_insn_exec_before(unsigned int cpu_index
, void *udata
)
66 qemu_plugin_u64_add(insn_count
, cpu_index
, 1);
69 static void vcpu_insn_matched_exec_before(unsigned int cpu_index
, void *udata
)
71 Instruction
*insn
= (Instruction
*) udata
;
72 Match
*insn_match
= insn
->match
;
73 MatchCount
*match
= qemu_plugin_scoreboard_find(insn_match
->counts
,
76 g_autoptr(GString
) ts
= g_string_new("");
79 g_string_append_printf(ts
, "0x%" PRIx64
", '%s', %"PRId64
" hits",
80 insn
->vaddr
, insn
->disas
, insn
->hits
);
82 uint64_t icount
= qemu_plugin_u64_get(insn_count
, cpu_index
);
83 uint64_t delta
= icount
- match
->last_hit
;
86 match
->total_delta
+= delta
;
88 g_string_append_printf(ts
,
90 " %"PRId64
" match hits,"
91 " Δ+%"PRId64
" since last match,"
92 " %"PRId64
" avg insns/match\n",
95 match
->total_delta
/ match
->hits
);
97 match
->last_hit
= icount
;
99 qemu_plugin_outs(ts
->str
);
102 static void vcpu_tb_trans(qemu_plugin_id_t id
, struct qemu_plugin_tb
*tb
)
104 size_t n
= qemu_plugin_tb_n_insns(tb
);
107 for (i
= 0; i
< n
; i
++) {
108 struct qemu_plugin_insn
*insn
= qemu_plugin_tb_get_insn(tb
, i
);
111 qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
112 insn
, QEMU_PLUGIN_INLINE_ADD_U64
, insn_count
, 1);
114 uint64_t vaddr
= qemu_plugin_insn_vaddr(insn
);
115 qemu_plugin_register_vcpu_insn_exec_cb(
116 insn
, vcpu_insn_exec_before
, QEMU_PLUGIN_CB_NO_REGS
,
117 GUINT_TO_POINTER(vaddr
));
121 size_t sz
= qemu_plugin_insn_size(insn
);
122 if (sz
> sizes
->len
) {
123 g_array_set_size(sizes
, sz
);
125 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, sz
);
130 * If we are tracking certain instructions we will need more
131 * information about the instruction which we also need to
132 * save if there is a hit.
135 char *insn_disas
= qemu_plugin_insn_disas(insn
);
136 for (int j
= 0; j
< matches
->len
; j
++) {
137 Match
*m
= &g_array_index(matches
, Match
, j
);
138 if (g_str_has_prefix(insn_disas
, m
->match_string
)) {
139 Instruction
*rec
= g_new0(Instruction
, 1);
140 rec
->disas
= g_strdup(insn_disas
);
141 rec
->vaddr
= qemu_plugin_insn_vaddr(insn
);
143 qemu_plugin_register_vcpu_insn_exec_cb(
144 insn
, vcpu_insn_matched_exec_before
,
145 QEMU_PLUGIN_CB_NO_REGS
, rec
);
153 static void plugin_exit(qemu_plugin_id_t id
, void *p
)
155 g_autoptr(GString
) out
= g_string_new(NULL
);
159 for (i
= 0; i
<= sizes
->len
; i
++) {
160 unsigned long *cnt
= &g_array_index(sizes
, unsigned long, i
);
162 g_string_append_printf(out
,
163 "len %d bytes: %ld insns\n", i
, *cnt
);
167 for (i
= 0; i
< qemu_plugin_num_vcpus(); i
++) {
168 g_string_append_printf(out
, "cpu %d insns: %" PRIu64
"\n",
169 i
, qemu_plugin_u64_get(insn_count
, i
));
171 g_string_append_printf(out
, "total insns: %" PRIu64
"\n",
172 qemu_plugin_u64_sum(insn_count
));
174 qemu_plugin_outs(out
->str
);
176 qemu_plugin_scoreboard_free(insn_count
.score
);
177 for (i
= 0; i
< matches
->len
; ++i
) {
178 Match
*m
= &g_array_index(matches
, Match
, i
);
179 g_free(m
->match_string
);
180 qemu_plugin_scoreboard_free(m
->counts
);
182 g_array_free(matches
, TRUE
);
183 g_array_free(sizes
, TRUE
);
187 /* Add a match to the array of matches */
188 static void parse_match(char *match
)
191 .match_string
= g_strdup(match
),
192 .counts
= qemu_plugin_scoreboard_new(sizeof(MatchCount
)) };
193 g_array_append_val(matches
, new_match
);
196 QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id
,
197 const qemu_info_t
*info
,
198 int argc
, char **argv
)
200 matches
= g_array_new(false, true, sizeof(Match
));
201 /* null terminated so 0 is not a special case */
202 sizes
= g_array_new(true, true, sizeof(unsigned long));
204 for (int i
= 0; i
< argc
; i
++) {
206 g_auto(GStrv
) tokens
= g_strsplit(opt
, "=", 2);
207 if (g_strcmp0(tokens
[0], "inline") == 0) {
208 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_inline
)) {
209 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
212 } else if (g_strcmp0(tokens
[0], "sizes") == 0) {
213 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &do_size
)) {
214 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
217 } else if (g_strcmp0(tokens
[0], "match") == 0) {
218 parse_match(tokens
[1]);
220 fprintf(stderr
, "option parsing failed: %s\n", opt
);
225 insn_count
= qemu_plugin_scoreboard_u64(
226 qemu_plugin_scoreboard_new(sizeof(uint64_t)));
228 /* Register init, translation block and exit callbacks */
229 qemu_plugin_register_vcpu_init_cb(id
, vcpu_init
);
230 qemu_plugin_register_vcpu_tb_trans_cb(id
, vcpu_tb_trans
);
231 qemu_plugin_register_atexit_cb(id
, plugin_exit
, NULL
);