Skip to content

Commit b54babe

Browse files
committed
test: add qemu trace profiling support
1 parent 6d15ce6 commit b54babe

3 files changed

Lines changed: 189 additions & 0 deletions

File tree

app/trace_sched.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ static void worker_b(void)
146146

147147
static void verifier(void)
148148
{
149+
uint32_t trace_count;
150+
uint32_t trace_overwrites;
151+
uint32_t total_events;
152+
149153
while (!scenario_done)
150154
mo_task_wfi();
151155

@@ -210,6 +214,14 @@ static void verifier(void)
210214
printf("Tests passed: %d\n", tests_passed);
211215
printf("Tests failed: %d\n", tests_failed);
212216
printf("Overall: %s\n", tests_failed == 0 ? "PASS" : "FAIL");
217+
if (tests_failed == 0) {
218+
trace_count = debug_trace_count();
219+
trace_overwrites = debug_trace_overwrites();
220+
total_events = trace_count + trace_overwrites;
221+
222+
printf("Trace totals: count=%u overwrites=%u total_events=%u\n",
223+
trace_count, trace_overwrites, total_events);
224+
}
213225
if (tests_failed != 0)
214226
debug_dump_events();
215227

tools/qemu/Makefile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
PLUGIN := debug_trace_profiler.so
2+
SRC := debug_trace_profiler.c
3+
4+
QEMU_PLUGIN_INC ?=
5+
CC ?= cc
6+
PKG_CONFIG ?= pkg-config
7+
8+
GLIB_CFLAGS := $(shell $(PKG_CONFIG) --cflags glib-2.0 2>/dev/null)
9+
10+
CFLAGS ?= -O2
11+
CFLAGS += -fPIC -shared -fvisibility=hidden
12+
ifneq ($(strip $(QEMU_PLUGIN_INC)),)
13+
CFLAGS += -I$(QEMU_PLUGIN_INC)
14+
endif
15+
CFLAGS += $(GLIB_CFLAGS)
16+
17+
.PHONY: all clean
18+
19+
all: $(PLUGIN)
20+
21+
$(PLUGIN): $(SRC)
22+
$(CC) $(CFLAGS) -o $@ $<
23+
24+
clean:
25+
rm -f $(PLUGIN)

tools/qemu/debug_trace_profiler.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include <ctype.h>
2+
#include <errno.h>
3+
#include <inttypes.h>
4+
#include <stdbool.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
9+
#include <qemu-plugin.h>
10+
11+
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
12+
13+
typedef struct {
14+
/* Instrument the half-open guest PC range [start, end).
15+
* For example, if:
16+
* nm -S --defined-only build/image.elf
17+
* prints:
18+
* 80004a54 000000f0 T debug_trace_event
19+
* then:
20+
* start = 0x80004a54
21+
* size = 0x000000f0
22+
* end = start + size = 0x80004b44
23+
*/
24+
uint64_t start;
25+
uint64_t end;
26+
uint64_t insn_count;
27+
uint64_t store_count;
28+
} profiler_state_t;
29+
30+
static profiler_state_t g_state;
31+
32+
static bool parse_u64_arg(const char *value, uint64_t *out)
33+
{
34+
char *end = NULL;
35+
size_t len;
36+
37+
if (!value || value[0] == '\0' || value[0] == '+' || value[0] == '-') {
38+
return false;
39+
}
40+
41+
len = strlen(value);
42+
if (isspace((unsigned char) value[0]) ||
43+
isspace((unsigned char) value[len - 1])) {
44+
return false;
45+
}
46+
47+
errno = 0;
48+
uint64_t parsed = strtoull(value, &end, 0);
49+
50+
if (errno == ERANGE || !end || end == value || *end != '\0') {
51+
return false;
52+
}
53+
54+
*out = parsed;
55+
return true;
56+
}
57+
58+
static void insn_exec_cb(unsigned int vcpu_index, void *userdata)
59+
{
60+
(void) vcpu_index;
61+
(void) userdata;
62+
__atomic_fetch_add(&g_state.insn_count, 1, __ATOMIC_RELAXED);
63+
}
64+
65+
static void mem_exec_cb(unsigned int vcpu_index,
66+
qemu_plugin_meminfo_t info,
67+
uint64_t vaddr,
68+
void *userdata)
69+
{
70+
(void) vcpu_index;
71+
(void) vaddr;
72+
(void) userdata;
73+
74+
if (qemu_plugin_mem_is_store(info)) {
75+
__atomic_fetch_add(&g_state.store_count, 1, __ATOMIC_RELAXED);
76+
}
77+
}
78+
79+
static void plugin_exit_cb(qemu_plugin_id_t id, void *userdata)
80+
{
81+
(void) id;
82+
(void) userdata;
83+
84+
fprintf(stderr,
85+
"debug_trace_profiler: start=0x%" PRIx64 " end=0x%" PRIx64
86+
" insn_count=%" PRIu64 " store_count=%" PRIu64 "\n",
87+
g_state.start, g_state.end,
88+
__atomic_load_n(&g_state.insn_count, __ATOMIC_RELAXED),
89+
__atomic_load_n(&g_state.store_count, __ATOMIC_RELAXED));
90+
}
91+
92+
static void tb_trans_cb(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
93+
{
94+
size_t n_insns = qemu_plugin_tb_n_insns(tb);
95+
96+
for (size_t i = 0; i < n_insns; ++i) {
97+
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
98+
uint64_t insn_pc = qemu_plugin_insn_vaddr(insn);
99+
100+
if (insn_pc < g_state.start || insn_pc >= g_state.end) {
101+
continue;
102+
}
103+
104+
qemu_plugin_register_vcpu_insn_exec_cb(insn, insn_exec_cb,
105+
QEMU_PLUGIN_CB_NO_REGS, NULL);
106+
qemu_plugin_register_vcpu_mem_cb(
107+
insn, mem_exec_cb, QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_MEM_W, NULL);
108+
}
109+
}
110+
111+
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
112+
const qemu_info_t *info,
113+
int argc,
114+
char **argv)
115+
{
116+
bool have_start = false;
117+
bool have_end = false;
118+
119+
(void) info;
120+
121+
for (int i = 0; i < argc; ++i) {
122+
const char *arg = argv[i];
123+
const char *eq = arg ? strchr(arg, '=') : NULL;
124+
size_t name_len;
125+
126+
if (!eq) {
127+
continue;
128+
}
129+
130+
name_len = (size_t) (eq - arg);
131+
if (name_len == 5 && strncmp(arg, "start", name_len) == 0) {
132+
have_start = parse_u64_arg(eq + 1, &g_state.start);
133+
} else if (name_len == 3 && strncmp(arg, "end", name_len) == 0) {
134+
have_end = parse_u64_arg(eq + 1, &g_state.end);
135+
}
136+
}
137+
138+
if (!have_start || !have_end || g_state.start >= g_state.end) {
139+
fprintf(stderr,
140+
"debug_trace_profiler: expected start=0x... and end=0x...\n");
141+
return -1;
142+
}
143+
144+
/* start/end are resolved outside the plugin from the target ELF symbol
145+
* table. end should be computed as start + size from `nm -S` output, so
146+
* the plugin can filter instructions with start <= PC < end.
147+
*/
148+
149+
qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_cb);
150+
qemu_plugin_register_atexit_cb(id, plugin_exit_cb, NULL);
151+
return 0;
152+
}

0 commit comments

Comments
 (0)