2 * Copyright (C) 2021, Alexandre Iooss <erdnaxe@crans.org>
4 * Log instruction execution with memory access and register changes
6 * License: GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
16 #include <qemu-plugin.h>
19 struct qemu_plugin_register
*handle
;
26 /* Store last executed instruction on each vCPU as a GString */
28 /* Ptr array of Register */
32 QEMU_PLUGIN_EXPORT
int qemu_plugin_version
= QEMU_PLUGIN_VERSION
;
35 static GRWLock expand_array_lock
;
37 static GPtrArray
*imatches
;
38 static GArray
*amatches
;
39 static GPtrArray
*rmatches
;
40 static bool disas_assist
;
41 static GMutex add_reg_name_lock
;
42 static GPtrArray
*all_reg_names
;
44 static CPU
*get_cpu(int vcpu_index
)
47 g_rw_lock_reader_lock(&expand_array_lock
);
48 c
= &g_array_index(cpus
, CPU
, vcpu_index
);
49 g_rw_lock_reader_unlock(&expand_array_lock
);
55 * Add memory read or write information to current instruction log
57 static void vcpu_mem(unsigned int cpu_index
, qemu_plugin_meminfo_t info
,
58 uint64_t vaddr
, void *udata
)
60 CPU
*c
= get_cpu(cpu_index
);
61 GString
*s
= c
->last_exec
;
63 /* Find vCPU in array */
65 /* Indicate type of memory access */
66 if (qemu_plugin_mem_is_store(info
)) {
67 g_string_append(s
, ", store");
69 g_string_append(s
, ", load");
72 /* If full system emulation log physical address and device name */
73 struct qemu_plugin_hwaddr
*hwaddr
= qemu_plugin_get_hwaddr(info
, vaddr
);
75 uint64_t addr
= qemu_plugin_hwaddr_phys_addr(hwaddr
);
76 const char *name
= qemu_plugin_hwaddr_device_name(hwaddr
);
77 g_string_append_printf(s
, ", 0x%08"PRIx64
", %s", addr
, name
);
79 g_string_append_printf(s
, ", 0x%08"PRIx64
, vaddr
);
84 * Log instruction execution, outputting the last one.
86 * vcpu_insn_exec() is a copy and paste of vcpu_insn_exec_with_regs()
87 * without the checking of register values when we've attempted to
88 * optimise with disas_assist.
90 static void insn_check_regs(CPU
*cpu
)
92 for (int n
= 0; n
< cpu
->registers
->len
; n
++) {
93 Register
*reg
= cpu
->registers
->pdata
[n
];
96 g_byte_array_set_size(reg
->new, 0);
97 sz
= qemu_plugin_read_register(reg
->handle
, reg
->new);
98 g_assert(sz
== reg
->last
->len
);
100 if (memcmp(reg
->last
->data
, reg
->new->data
, sz
)) {
101 GByteArray
*temp
= reg
->last
;
102 g_string_append_printf(cpu
->last_exec
, ", %s -> 0x", reg
->name
);
103 /* TODO: handle BE properly */
104 for (int i
= sz
- 1; i
>= 0; i
--) {
105 g_string_append_printf(cpu
->last_exec
, "%02x",
108 reg
->last
= reg
->new;
114 /* Log last instruction while checking registers */
115 static void vcpu_insn_exec_with_regs(unsigned int cpu_index
, void *udata
)
117 CPU
*cpu
= get_cpu(cpu_index
);
119 /* Print previous instruction in cache */
120 if (cpu
->last_exec
->len
) {
121 if (cpu
->registers
) {
122 insn_check_regs(cpu
);
125 qemu_plugin_outs(cpu
->last_exec
->str
);
126 qemu_plugin_outs("\n");
129 /* Store new instruction in cache */
130 /* vcpu_mem will add memory access information to last_exec */
131 g_string_printf(cpu
->last_exec
, "%u, ", cpu_index
);
132 g_string_append(cpu
->last_exec
, (char *)udata
);
135 /* Log last instruction while checking registers, ignore next */
136 static void vcpu_insn_exec_only_regs(unsigned int cpu_index
, void *udata
)
138 CPU
*cpu
= get_cpu(cpu_index
);
140 /* Print previous instruction in cache */
141 if (cpu
->last_exec
->len
) {
142 if (cpu
->registers
) {
143 insn_check_regs(cpu
);
146 qemu_plugin_outs(cpu
->last_exec
->str
);
147 qemu_plugin_outs("\n");
151 cpu
->last_exec
->len
= 0;
154 /* Log last instruction without checking regs, setup next */
155 static void vcpu_insn_exec(unsigned int cpu_index
, void *udata
)
157 CPU
*cpu
= get_cpu(cpu_index
);
159 /* Print previous instruction in cache */
160 if (cpu
->last_exec
->len
) {
161 qemu_plugin_outs(cpu
->last_exec
->str
);
162 qemu_plugin_outs("\n");
165 /* Store new instruction in cache */
166 /* vcpu_mem will add memory access information to last_exec */
167 g_string_printf(cpu
->last_exec
, "%u, ", cpu_index
);
168 g_string_append(cpu
->last_exec
, (char *)udata
);
172 * On translation block new translation
174 * QEMU convert code by translation block (TB). By hooking here we can then hook
175 * a callback on each instruction and memory access.
177 static void vcpu_tb_trans(qemu_plugin_id_t id
, struct qemu_plugin_tb
*tb
)
179 struct qemu_plugin_insn
*insn
;
180 bool skip
= (imatches
|| amatches
);
181 bool check_regs_this
= rmatches
;
182 bool check_regs_next
= false;
184 size_t n_insns
= qemu_plugin_tb_n_insns(tb
);
185 for (size_t i
= 0; i
< n_insns
; i
++) {
190 * `insn` is shared between translations in QEMU, copy needed data here.
191 * `output` is never freed as it might be used multiple times during
192 * the emulation lifetime.
193 * We only consider the first 32 bits of the instruction, this may be
194 * a limitation for CISC architectures.
196 insn
= qemu_plugin_tb_get_insn(tb
, i
);
197 insn_disas
= qemu_plugin_insn_disas(insn
);
198 insn_vaddr
= qemu_plugin_insn_vaddr(insn
);
201 * If we are filtering we better check out if we have any
202 * hits. The skip "latches" so we can track memory accesses
203 * after the instruction we care about. Also enable register
204 * checking on the next instruction.
206 if (skip
&& imatches
) {
208 for (j
= 0; j
< imatches
->len
&& skip
; j
++) {
209 char *m
= g_ptr_array_index(imatches
, j
);
210 if (g_str_has_prefix(insn_disas
, m
)) {
212 check_regs_next
= rmatches
;
217 if (skip
&& amatches
) {
219 for (j
= 0; j
< amatches
->len
&& skip
; j
++) {
220 uint64_t v
= g_array_index(amatches
, uint64_t, j
);
221 if (v
== insn_vaddr
) {
228 * Check the disassembly to see if a register we care about
229 * will be affected by this instruction. This relies on the
230 * dissembler doing something sensible for the registers we
233 if (disas_assist
&& rmatches
) {
234 check_regs_next
= false;
235 gchar
*args
= g_strstr_len(insn_disas
, -1, " ");
236 for (int n
= 0; n
< all_reg_names
->len
; n
++) {
237 gchar
*reg
= g_ptr_array_index(all_reg_names
, n
);
238 if (g_strrstr(args
, reg
)) {
239 check_regs_next
= true;
246 * We now have 3 choices:
249 * - Log insn while checking registers
250 * - Don't log this insn but check if last insn changed registers
254 if (check_regs_this
) {
255 qemu_plugin_register_vcpu_insn_exec_cb(insn
,
256 vcpu_insn_exec_only_regs
,
257 QEMU_PLUGIN_CB_R_REGS
,
261 uint32_t insn_opcode
= 0;
262 qemu_plugin_insn_data(insn
, &insn_opcode
, sizeof(insn_opcode
));
264 char *output
= g_strdup_printf("0x%"PRIx64
", 0x%"PRIx32
", \"%s\"",
265 insn_vaddr
, insn_opcode
, insn_disas
);
267 /* Register callback on memory read or write */
268 qemu_plugin_register_vcpu_mem_cb(insn
, vcpu_mem
,
269 QEMU_PLUGIN_CB_NO_REGS
,
270 QEMU_PLUGIN_MEM_RW
, NULL
);
272 /* Register callback on instruction */
273 if (check_regs_this
) {
274 qemu_plugin_register_vcpu_insn_exec_cb(
275 insn
, vcpu_insn_exec_with_regs
,
276 QEMU_PLUGIN_CB_R_REGS
,
279 qemu_plugin_register_vcpu_insn_exec_cb(
280 insn
, vcpu_insn_exec
,
281 QEMU_PLUGIN_CB_NO_REGS
,
286 skip
= (imatches
|| amatches
);
289 /* set regs for next */
290 if (disas_assist
&& rmatches
) {
291 check_regs_this
= check_regs_next
;
298 static Register
*init_vcpu_register(qemu_plugin_reg_descriptor
*desc
)
300 Register
*reg
= g_new0(Register
, 1);
301 g_autofree gchar
*lower
= g_utf8_strdown(desc
->name
, -1);
304 reg
->handle
= desc
->handle
;
305 reg
->name
= g_intern_string(lower
);
306 reg
->last
= g_byte_array_new();
307 reg
->new = g_byte_array_new();
309 /* read the initial value */
310 r
= qemu_plugin_read_register(reg
->handle
, reg
->last
);
316 * g_pattern_match_string has been deprecated in Glib since 2.70 and
317 * will complain about it if you try to use it. Fortunately the
318 * signature of both functions is the same making it easy to work
322 gboolean
g_pattern_spec_match_string_qemu(GPatternSpec
*pspec
,
325 #if GLIB_CHECK_VERSION(2, 70, 0)
326 return g_pattern_spec_match_string(pspec
, string
);
328 return g_pattern_match_string(pspec
, string
);
331 #define g_pattern_spec_match_string(p, s) g_pattern_spec_match_string_qemu(p, s)
333 static GPtrArray
*registers_init(int vcpu_index
)
335 g_autoptr(GPtrArray
) registers
= g_ptr_array_new();
336 g_autoptr(GArray
) reg_list
= qemu_plugin_get_registers();
338 if (rmatches
&& reg_list
->len
) {
340 * Go through each register in the complete list and
341 * see if we want to track it.
343 for (int r
= 0; r
< reg_list
->len
; r
++) {
344 qemu_plugin_reg_descriptor
*rd
= &g_array_index(
345 reg_list
, qemu_plugin_reg_descriptor
, r
);
346 for (int p
= 0; p
< rmatches
->len
; p
++) {
347 g_autoptr(GPatternSpec
) pat
= g_pattern_spec_new(rmatches
->pdata
[p
]);
348 g_autofree gchar
*rd_lower
= g_utf8_strdown(rd
->name
, -1);
349 if (g_pattern_spec_match_string(pat
, rd
->name
) ||
350 g_pattern_spec_match_string(pat
, rd_lower
)) {
351 Register
*reg
= init_vcpu_register(rd
);
352 g_ptr_array_add(registers
, reg
);
354 /* we need a list of regnames at TB translation time */
356 g_mutex_lock(&add_reg_name_lock
);
357 if (!g_ptr_array_find(all_reg_names
, reg
->name
, NULL
)) {
358 g_ptr_array_add(all_reg_names
, (gpointer
)reg
->name
);
360 g_mutex_unlock(&add_reg_name_lock
);
367 return registers
->len
? g_steal_pointer(®isters
) : NULL
;
371 * Initialise a new vcpu/thread with:
372 * - last_exec tracking data
373 * - list of tracked registers
374 * - initial value of registers
376 * As we could have multiple threads trying to do this we need to
377 * serialise the expansion under a lock.
379 static void vcpu_init(qemu_plugin_id_t id
, unsigned int vcpu_index
)
383 g_rw_lock_writer_lock(&expand_array_lock
);
384 if (vcpu_index
>= cpus
->len
) {
385 g_array_set_size(cpus
, vcpu_index
+ 1);
387 g_rw_lock_writer_unlock(&expand_array_lock
);
389 c
= get_cpu(vcpu_index
);
390 c
->last_exec
= g_string_new(NULL
);
391 c
->registers
= registers_init(vcpu_index
);
395 * On plugin exit, print last instruction in cache
397 static void plugin_exit(qemu_plugin_id_t id
, void *p
)
400 g_rw_lock_reader_lock(&expand_array_lock
);
401 for (i
= 0; i
< cpus
->len
; i
++) {
403 if (c
->last_exec
&& c
->last_exec
->str
) {
404 qemu_plugin_outs(c
->last_exec
->str
);
405 qemu_plugin_outs("\n");
408 g_rw_lock_reader_unlock(&expand_array_lock
);
411 /* Add a match to the array of matches */
412 static void parse_insn_match(char *match
)
415 imatches
= g_ptr_array_new();
417 g_ptr_array_add(imatches
, g_strdup(match
));
420 static void parse_vaddr_match(char *match
)
422 uint64_t v
= g_ascii_strtoull(match
, NULL
, 16);
425 amatches
= g_array_new(false, true, sizeof(uint64_t));
427 g_array_append_val(amatches
, v
);
431 * We have to wait until vCPUs are started before we can check the
432 * patterns find anything.
434 static void add_regpat(char *regpat
)
437 rmatches
= g_ptr_array_new();
439 g_ptr_array_add(rmatches
, g_strdup(regpat
));
445 QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id
,
446 const qemu_info_t
*info
, int argc
,
450 * Initialize dynamic array to cache vCPU instruction. In user mode
451 * we don't know the size before emulation.
453 cpus
= g_array_sized_new(true, true, sizeof(CPU
),
454 info
->system_emulation
? info
->system
.max_vcpus
: 1);
456 for (int i
= 0; i
< argc
; i
++) {
458 g_auto(GStrv
) tokens
= g_strsplit(opt
, "=", 2);
459 if (g_strcmp0(tokens
[0], "ifilter") == 0) {
460 parse_insn_match(tokens
[1]);
461 } else if (g_strcmp0(tokens
[0], "afilter") == 0) {
462 parse_vaddr_match(tokens
[1]);
463 } else if (g_strcmp0(tokens
[0], "reg") == 0) {
464 add_regpat(tokens
[1]);
465 } else if (g_strcmp0(tokens
[0], "rdisas") == 0) {
466 if (!qemu_plugin_bool_parse(tokens
[0], tokens
[1], &disas_assist
)) {
467 fprintf(stderr
, "boolean argument parsing failed: %s\n", opt
);
470 all_reg_names
= g_ptr_array_new();
472 fprintf(stderr
, "option parsing failed: %s\n", opt
);
477 /* Register init, translation block and exit callbacks */
478 qemu_plugin_register_vcpu_init_cb(id
, vcpu_init
);
479 qemu_plugin_register_vcpu_tb_trans_cb(id
, vcpu_tb_trans
);
480 qemu_plugin_register_atexit_cb(id
, plugin_exit
, NULL
);