master
c 1,000 lines 28.6 KB
Raw
1 /*
2 * Copyright (C) 2025, Pierrick Bouvier <pierrick.bouvier@linaro.org>
3 *
4 * Generates a trace compatible with uftrace (similar to uftrace record).
5 * https://github.com/namhyung/uftrace
6 *
7 * See docs/about/emulation.rst|Uftrace for details and examples.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include <qemu-plugin.h>
13 #include <glib.h>
14 #include <stdio.h>
15 #include <sys/stat.h>
16 #include <sys/time.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 #define MiB (INT64_C(1) << 20)
21 #define NANOSECONDS_PER_SECOND 1000000000LL
22 #define TRACE_FLUSH_SIZE (32 * MiB)
23 #define TRACE_ID_SCALE 100
24
25 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
26
27 typedef struct {
28 GArray *s;
29 } Callstack;
30
31 typedef struct {
32 uint64_t pc;
33 uint64_t frame_pointer;
34 } CallstackEntry;
35
36 typedef struct {
37 GArray *t;
38 GString *path;
39 GString *name;
40 uint32_t id;
41 } Trace;
42
43 typedef struct Cpu Cpu;
44
45 typedef struct {
46 void (*init)(Cpu *cpu);
47 void (*end)(Cpu *cpu);
48 uint64_t (*get_frame_pointer)(Cpu *cpu);
49 uint64_t (*get_next_frame_pointer)(Cpu *cpu, uint64_t fp);
50 uint64_t (*get_next_return_address)(Cpu *cpu, uint64_t fp);
51 uint8_t (*get_privilege_level)(Cpu *cpu);
52 uint8_t (*num_privilege_levels)(void);
53 const char *(*get_privilege_level_name)(uint8_t pl);
54 bool (*does_insn_modify_frame_pointer)(const char *disas);
55 } CpuOps;
56
57 typedef struct Cpu {
58 Trace *trace;
59 Callstack *cs;
60 uint8_t privilege_level;
61 GArray *traces; /* Trace *traces [] */
62 GByteArray *buf;
63 CpuOps ops;
64 void *arch;
65 } Cpu;
66
67 typedef enum {
68 AARCH64_EL0_SECURE,
69 AARCH64_EL0_NONSECURE,
70 AARCH64_EL0_REALM,
71 AARCH64_EL1_SECURE,
72 AARCH64_EL1_NONSECURE,
73 AARCH64_EL1_REALM,
74 AARCH64_EL2_SECURE,
75 AARCH64_EL2_NONSECURE,
76 AARCH64_EL2_REALM,
77 AARCH64_EL3,
78 AARCH64_PRIVILEGE_LEVEL_MAX,
79 } Aarch64PrivilegeLevel;
80
81 typedef struct {
82 struct qemu_plugin_register *reg_fp;
83 struct qemu_plugin_register *reg_cpsr;
84 struct qemu_plugin_register *reg_scr_el3;
85 } Aarch64Cpu;
86
87 typedef enum {
88 X64_RING0,
89 X64_RING1,
90 X64_RING2,
91 X64_RING3,
92 X64_REAL_MODE,
93 X64_PRIVILEGE_LEVEL_MAX,
94 } X64PrivilegeLevel;
95
96 typedef struct {
97 struct qemu_plugin_register *reg_rbp;
98 struct qemu_plugin_register *reg_cs;
99 struct qemu_plugin_register *reg_cr0;
100 } X64Cpu;
101
102 typedef struct {
103 struct qemu_plugin_register *reg_fp;
104 struct qemu_plugin_register *reg_priv;
105 } Riscv64Cpu;
106
107 typedef enum {
108 RISCV64_USER,
109 RISCV64_SUPERVISOR,
110 RISCV64_RESERVED,
111 RISCV64_MACHINE,
112 RISCV64_VUSER,
113 RISCV64_VSUPERVISOR,
114 RISCV64_PRIVILEGE_LEVEL_MAX,
115 } Riscv64PrivilegeLevel;
116
117 typedef struct {
118 uint64_t timestamp;
119 uint64_t data;
120 } UftraceEntry;
121
122 typedef enum {
123 UFTRACE_ENTRY,
124 UFTRACE_EXIT,
125 UFTRACE_LOST,
126 UFTRACE_EVENT,
127 } UftraceRecordType;
128
129 static struct qemu_plugin_scoreboard *score;
130 static bool trace_privilege_level;
131 static CpuOps arch_ops;
132
133 static uint64_t gettime_ns(void)
134 {
135 #ifdef _WIN32
136 /*
137 * On Windows, timespec_get is available only with UCRT, but not with
138 * MinGW64 environment. Simplify by using only gettimeofday on this
139 * platform. This may result in a precision loss.
140 */
141 struct timeval tv;
142 gettimeofday(&tv, NULL);
143 uint64_t now_ns = tv.tv_sec * NANOSECONDS_PER_SECOND + tv.tv_usec * 1000;
144 #else
145 /* We need nanosecond precision for short lived functions. */
146 struct timespec ts;
147 timespec_get(&ts, TIME_UTC);
148 uint64_t now_ns = ts.tv_sec * NANOSECONDS_PER_SECOND + ts.tv_nsec;
149 #endif
150 return now_ns;
151 }
152
153 static void uftrace_write_map(bool system_emulation)
154 {
155 const char *path = "./uftrace.data/sid-0.map";
156
157 if (system_emulation && access(path, F_OK) == 0) {
158 /*
159 * do not erase existing map in system emulation, as a custom one might
160 * already have been generated by uftrace_symbols.py
161 */
162 return;
163 }
164
165 FILE *sid_map = fopen(path, "w");
166 g_assert(sid_map);
167
168 if (system_emulation) {
169 fprintf(sid_map,
170 "# map stack on highest address possible, to prevent uftrace\n"
171 "# from considering any kernel address\n");
172 fprintf(sid_map,
173 "ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]\n");
174 } else {
175 /* in user mode, copy /proc/self/maps instead */
176 FILE *self_map = fopen("/proc/self/maps", "r");
177 g_assert(self_map);
178 for (;;) {
179 int c = fgetc(self_map);
180 if (c == EOF) {
181 break;
182 }
183 fputc(c, sid_map);
184 }
185 fclose(self_map);
186 }
187 fclose(sid_map);
188 }
189
190 static void uftrace_write_task(const GArray *traces)
191 {
192 FILE *task = fopen("./uftrace.data/task.txt", "w");
193 g_assert(task);
194 for (int i = 0; i < traces->len; ++i) {
195 Trace *t = g_array_index(traces, Trace*, i);
196 fprintf(task, "SESS timestamp=0.0 pid=%"PRIu32" sid=0 exename=\"%s\"\n",
197 t->id, t->name->str);
198 fprintf(task, "TASK timestamp=0.0 tid=%"PRIu32" pid=%"PRIu32"\n",
199 t->id, t->id);
200 }
201 fclose(task);
202 }
203
204 static void uftrace_write_info(const GArray *traces)
205 {
206 g_autoptr(GString) taskinfo_tids = g_string_new("taskinfo:tids=");
207 for (int i = 0; i < traces->len; ++i) {
208 Trace *t = g_array_index(traces, Trace*, i);
209 const char *delim = i > 0 ? "," : "";
210 g_string_append_printf(taskinfo_tids, "%s%"PRIu32, delim, t->id);
211 }
212
213 g_autoptr(GString) taskinfo_nr_tid = g_string_new("taskinfo:nr_tid=");
214 g_string_append_printf(taskinfo_nr_tid, "%d", traces->len);
215
216 FILE *info = fopen("./uftrace.data/info", "w");
217 g_assert(info);
218 /*
219 * $ uftrace dump --debug
220 * uftrace file header: magic = 4674726163652100
221 * uftrace file header: version = 4
222 * uftrace file header: header size = 40
223 * uftrace file header: endian = 1 (little)
224 * uftrace file header: class = 2 (64 bit)
225 * uftrace file header: features = 0x1263 (PLTHOOK | ...
226 * uftrace file header: info = 0x7bff (EXE_NAME | ...
227 * <0000000000000000>: 46 74 72 61 63 65 21 00 04 00 00 00 28 00 01 02
228 * <0000000000000010>: 63 12 00 00 00 00 00 00 ff 7b 00 00 00 00 00 00
229 * <0000000000000020>: 00 04 00 00 00 00 00 00
230 */
231 const uint8_t header[] = {0x46, 0x74, 0x72, 0x61, 0x63, 0x65, 0x21, 0x00,
232 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x02,
233 0x63, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
236 size_t wrote = fwrite(header, sizeof(header), 1, info);
237 g_assert(wrote == 1);
238 const char *info_data[] = {
239 "exename:",
240 "build_id:0000000000000000000000000000000000000000",
241 "exit_status:",
242 "cmdline:",
243 "cpuinfo:lines=2",
244 "cpuinfo:nr_cpus=",
245 "cpuinfo:desc=",
246 "meminfo:",
247 "osinfo:lines=3",
248 "osinfo:kernel=",
249 "osinfo:hostname=",
250 "osinfo:distro=",
251 "taskinfo:lines=2",
252 taskinfo_nr_tid->str,
253 taskinfo_tids->str,
254 "usageinfo:lines=6",
255 "usageinfo:systime=",
256 "usageinfo:usrtime=",
257 "usageinfo:ctxsw=",
258 "usageinfo:maxrss=",
259 "usageinfo:pagefault=",
260 "usageinfo:iops=",
261 "loadinfo:",
262 "record_date:",
263 "elapsed_time:",
264 "pattern_type:regex",
265 "uftrace_version:",
266 "utc_offset:",
267 0};
268 const char **info_data_it = info_data;
269 while (*(info_data_it)) {
270 fprintf(info, "%s\n", *info_data_it);
271 ++info_data_it;
272 }
273 fclose(info);
274 }
275
276 static Callstack *callstack_new(void)
277 {
278 Callstack *cs = g_new0(Callstack, 1);
279 cs->s = g_array_new(false, false, sizeof(CallstackEntry));
280 return cs;
281 }
282
283 static void callstack_free(Callstack *cs)
284 {
285 g_array_free(cs->s, true);
286 cs->s = NULL;
287 g_free(cs);
288 }
289
290 static size_t callstack_depth(const Callstack *cs)
291 {
292 return cs->s->len;
293 }
294
295 static size_t callstack_empty(const Callstack *cs)
296 {
297 return callstack_depth(cs) == 0;
298 }
299
300 static void callstack_clear(Callstack *cs)
301 {
302 g_array_set_size(cs->s, 0);
303 }
304
305 static const CallstackEntry *callstack_at(const Callstack *cs, size_t depth)
306 {
307 g_assert(depth > 0);
308 g_assert(depth <= callstack_depth(cs));
309 return &g_array_index(cs->s, CallstackEntry, depth - 1);
310 }
311
312 static CallstackEntry callstack_top(const Callstack *cs)
313 {
314 if (callstack_depth(cs) >= 1) {
315 return *callstack_at(cs, callstack_depth(cs));
316 }
317 return (CallstackEntry){};
318 }
319
320 static CallstackEntry callstack_caller(const Callstack *cs)
321 {
322 if (callstack_depth(cs) >= 2) {
323 return *callstack_at(cs, callstack_depth(cs) - 1);
324 }
325 return (CallstackEntry){};
326 }
327
328 static void callstack_push(Callstack *cs, CallstackEntry e)
329 {
330 g_array_append_val(cs->s, e);
331 }
332
333 static CallstackEntry callstack_pop(Callstack *cs)
334 {
335 g_assert(!callstack_empty(cs));
336 CallstackEntry e = callstack_top(cs);
337 g_array_set_size(cs->s, callstack_depth(cs) - 1);
338 return e;
339 }
340
341 static Trace *trace_new(uint32_t id, GString *name)
342 {
343 Trace *t = g_new0(Trace, 1);
344 t->t = g_array_new(false, false, sizeof(UftraceEntry));
345 t->path = g_string_new(NULL);
346 g_string_append_printf(t->path, "./uftrace.data/%"PRIu32".dat", id);
347 t->name = g_string_new(name->str);
348 t->id = id;
349 return t;
350 }
351
352 static void trace_free(Trace *t)
353 {
354 g_assert(t->t->len == 0);
355 g_array_free(t->t, true);
356 t->t = NULL;
357 g_string_free(t->path, true);
358 t->path = NULL;
359 g_string_free(t->name, true);
360 t->name = NULL;
361 g_free(t);
362 }
363
364 static void trace_flush(Trace *t, bool append)
365 {
366 int create_dir = g_mkdir_with_parents("./uftrace.data",
367 S_IRWXU | S_IRWXG | S_IRWXO);
368 g_assert(create_dir == 0);
369 FILE *dat = fopen(t->path->str, append ? "a" : "w");
370 g_assert(dat);
371 GArray *data = t->t;
372 if (data->len) {
373 size_t wrote = fwrite(data->data, sizeof(UftraceEntry), data->len, dat);
374 g_assert(wrote == data->len);
375 }
376 fclose(dat);
377 g_array_set_size(data, 0);
378 }
379
380 static void trace_add_entry(Trace *t, uint64_t timestamp, uint64_t pc,
381 size_t depth, UftraceRecordType type)
382 {
383 /* https://github.com/namhyung/uftrace/blob/v0.18/libmcount/record.c#L909 */
384 const uint64_t record_magic = 0x5;
385 uint64_t data = type | (record_magic << 3);
386 data += depth << 6;
387 data += pc << 16;
388 UftraceEntry e = {.timestamp = timestamp, .data = data};
389 g_array_append_val(t->t, e);
390 if (t->t->len * sizeof(UftraceEntry) > TRACE_FLUSH_SIZE) {
391 trace_flush(t, true);
392 }
393 }
394
395 static void trace_enter_function(Trace *t, uint64_t timestamp,
396 uint64_t pc, size_t depth)
397 {
398 trace_add_entry(t, timestamp, pc, depth, UFTRACE_ENTRY);
399 }
400
401 static void trace_exit_function(Trace *t, uint64_t timestamp,
402 uint64_t pc, size_t depth)
403 {
404 trace_add_entry(t, timestamp, pc, depth, UFTRACE_EXIT);
405 }
406
407 static void trace_enter_stack(Trace *t, Callstack *cs, uint64_t timestamp)
408 {
409 for (size_t depth = 1; depth <= callstack_depth(cs); ++depth) {
410 trace_enter_function(t, timestamp, callstack_at(cs, depth)->pc, depth);
411 }
412 }
413
414 static void trace_exit_stack(Trace *t, Callstack *cs, uint64_t timestamp)
415 {
416 for (size_t depth = callstack_depth(cs); depth > 0; --depth) {
417 trace_exit_function(t, timestamp, callstack_at(cs, depth)->pc, depth);
418 }
419 }
420
421 static uint64_t cpu_read_register64(Cpu *cpu, struct qemu_plugin_register *reg)
422 {
423 GByteArray *buf = cpu->buf;
424 g_byte_array_set_size(buf, 0);
425 bool success = qemu_plugin_read_register(reg, buf);
426 g_assert(success);
427 g_assert(buf->len == 8);
428 return *((uint64_t *) buf->data);
429 }
430
431 static uint32_t cpu_read_register32(Cpu *cpu, struct qemu_plugin_register *reg)
432 {
433 GByteArray *buf = cpu->buf;
434 g_byte_array_set_size(buf, 0);
435 bool success = qemu_plugin_read_register(reg, buf);
436 g_assert(success);
437 g_assert(buf->len == 4);
438 return *((uint32_t *) buf->data);
439 }
440
441 static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr)
442 {
443 if (!addr) {
444 return 0;
445 }
446 GByteArray *buf = cpu->buf;
447 g_byte_array_set_size(buf, 0);
448 bool read = qemu_plugin_read_memory_vaddr(addr, buf, 8);
449 if (!read) {
450 return 0;
451 }
452 g_assert(buf->len == 8);
453 return *((uint64_t *) buf->data);
454 }
455
456 static void cpu_unwind_stack(Cpu *cpu, uint64_t frame_pointer, uint64_t pc)
457 {
458 g_assert(callstack_empty(cpu->cs));
459
460 #define UNWIND_STACK_MAX_DEPTH 1024
461 CallstackEntry unwind[UNWIND_STACK_MAX_DEPTH];
462 size_t depth = 0;
463 do {
464 /* check we don't have an infinite stack */
465 for (size_t i = 0; i < depth; ++i) {
466 if (frame_pointer == unwind[i].frame_pointer) {
467 goto after_unwind;
468 }
469 }
470 CallstackEntry e = {.frame_pointer = frame_pointer, .pc = pc};
471 unwind[depth] = e;
472 depth++;
473 frame_pointer = cpu->ops.get_next_frame_pointer(cpu, frame_pointer);
474 pc = cpu->ops.get_next_return_address(cpu, frame_pointer);
475 } while (frame_pointer && pc && depth < UNWIND_STACK_MAX_DEPTH);
476 #undef UNWIND_STACK_MAX_DEPTH
477
478 after_unwind:
479 /* push it from bottom to top */
480 while (depth) {
481 callstack_push(cpu->cs, unwind[depth - 1]);
482 --depth;
483 }
484 }
485
486 static struct qemu_plugin_register *plugin_find_register(const char *name)
487 {
488 g_autoptr(GArray) regs = qemu_plugin_get_registers();
489 for (int i = 0; i < regs->len; ++i) {
490 qemu_plugin_reg_descriptor *reg;
491 reg = &g_array_index(regs, qemu_plugin_reg_descriptor, i);
492 if (!strcmp(reg->name, name)) {
493 return reg->handle;
494 }
495 }
496 return NULL;
497 }
498
499 static uint8_t aarch64_num_privilege_levels(void)
500 {
501 return AARCH64_PRIVILEGE_LEVEL_MAX;
502 }
503
504 static const char *aarch64_get_privilege_level_name(uint8_t pl)
505 {
506 switch (pl) {
507 case AARCH64_EL0_SECURE: return "S-EL0";
508 case AARCH64_EL0_NONSECURE: return "NS-EL0";
509 case AARCH64_EL0_REALM: return "R-EL0";
510 case AARCH64_EL1_SECURE: return "S-EL1";
511 case AARCH64_EL1_NONSECURE: return "NS-EL1";
512 case AARCH64_EL1_REALM: return "R-EL1";
513 case AARCH64_EL2_SECURE: return "S-EL2";
514 case AARCH64_EL2_NONSECURE: return "NS-EL2";
515 case AARCH64_EL2_REALM: return "R-EL2";
516 case AARCH64_EL3: return "EL3";
517 default:
518 g_assert_not_reached();
519 }
520 }
521
522 static uint8_t aarch64_get_privilege_level(Cpu *cpu_)
523 {
524 Aarch64Cpu *cpu = cpu_->arch;
525 /*
526 * QEMU gdbstub does not provide access to CurrentEL,
527 * so we use CPSR instead.
528 */
529 uint8_t el = cpu_read_register32(cpu_, cpu->reg_cpsr) >> 2 & 0b11;
530
531 if (el == 3) {
532 return AARCH64_EL3;
533 }
534
535 uint8_t ss = AARCH64_EL0_SECURE;
536 if (!cpu->reg_scr_el3) {
537 ss = AARCH64_EL0_NONSECURE;
538 }
539 uint64_t scr_el3 = cpu_read_register64(cpu_, cpu->reg_scr_el3);
540 uint64_t ns = (scr_el3 >> 0) & 0b1;
541 uint64_t nse = (scr_el3 >> 62) & 0b1;
542 switch (nse << 1 | ns) {
543 case 0b00:
544 ss = AARCH64_EL0_SECURE;
545 break;
546 case 0b01:
547 ss = AARCH64_EL0_NONSECURE;
548 break;
549 case 0b11:
550 ss = AARCH64_EL0_REALM;
551 break;
552 default:
553 g_assert_not_reached();
554 }
555
556 const uint8_t num_ss = 3;
557 Aarch64PrivilegeLevel pl = el * num_ss + ss;
558 return pl;
559 }
560
561 static uint64_t aarch64_get_frame_pointer(Cpu *cpu_)
562 {
563 Aarch64Cpu *cpu = cpu_->arch;
564 return cpu_read_register64(cpu_, cpu->reg_fp);
565 }
566
567 static uint64_t aarch64_get_next_frame_pointer(Cpu *cpu_, uint64_t fp)
568 {
569 return cpu_read_memory64(cpu_, fp);
570 }
571
572 static uint64_t aarch64_get_next_return_address(Cpu *cpu_, uint64_t fp)
573 {
574 return cpu_read_memory64(cpu_, fp + 8);
575 }
576
577 static void aarch64_init(Cpu *cpu_)
578 {
579 Aarch64Cpu *cpu = g_new0(Aarch64Cpu, 1);
580 cpu_->arch = cpu;
581 cpu->reg_fp = plugin_find_register("x29");
582 if (!cpu->reg_fp) {
583 fprintf(stderr, "uftrace plugin: frame pointer register (x29) is not "
584 "available. Please use an AArch64 cpu (or -cpu max).\n");
585 g_abort();
586 }
587 cpu->reg_cpsr = plugin_find_register("cpsr");
588 g_assert(cpu->reg_cpsr);
589 cpu->reg_scr_el3 = plugin_find_register("SCR_EL3");
590 /* scr_el3 is optional */
591 }
592
593 static void aarch64_end(Cpu *cpu)
594 {
595 g_free(cpu->arch);
596 }
597
598 static bool aarch64_does_insn_modify_frame_pointer(const char *disas)
599 {
600 /*
601 * Check if current instruction concerns fp register "x29".
602 * We add a prefix space to make sure we don't match addresses dump
603 * in disassembly.
604 */
605 return strstr(disas, " x29");
606 }
607
608 static CpuOps aarch64_ops = {
609 .init = aarch64_init,
610 .end = aarch64_end,
611 .get_frame_pointer = aarch64_get_frame_pointer,
612 .get_next_frame_pointer = aarch64_get_next_frame_pointer,
613 .get_next_return_address = aarch64_get_next_return_address,
614 .get_privilege_level = aarch64_get_privilege_level,
615 .num_privilege_levels = aarch64_num_privilege_levels,
616 .get_privilege_level_name = aarch64_get_privilege_level_name,
617 .does_insn_modify_frame_pointer = aarch64_does_insn_modify_frame_pointer,
618 };
619
620 static uint8_t x64_num_privilege_levels(void)
621 {
622 return X64_PRIVILEGE_LEVEL_MAX;
623 }
624
625 static const char *x64_get_privilege_level_name(uint8_t pl)
626 {
627 switch (pl) {
628 case X64_RING0: return "Ring0";
629 case X64_RING1: return "Ring1";
630 case X64_RING2: return "Ring2";
631 case X64_RING3: return "Ring3";
632 case X64_REAL_MODE: return "RealMode";
633 default:
634 g_assert_not_reached();
635 }
636 }
637
638 static uint8_t x64_get_privilege_level(Cpu *cpu_)
639 {
640 X64Cpu *cpu = cpu_->arch;
641 uint64_t cr0 = cpu_read_register64(cpu_, cpu->reg_cr0);
642 uint64_t protected_mode = (cr0 >> 0) & 0b1;
643 if (!protected_mode) {
644 return X64_REAL_MODE;
645 }
646 uint32_t cs = cpu_read_register32(cpu_, cpu->reg_cs);
647 uint32_t ring_level = (cs >> 0) & 0b11;
648 return ring_level;
649 }
650
651 static uint64_t x64_get_frame_pointer(Cpu *cpu_)
652 {
653 X64Cpu *cpu = cpu_->arch;
654 return cpu_read_register64(cpu_, cpu->reg_rbp);
655 }
656
657 static uint64_t x64_get_next_frame_pointer(Cpu *cpu_, uint64_t fp)
658 {
659 return cpu_read_memory64(cpu_, fp);
660 }
661
662 static uint64_t x64_get_next_return_address(Cpu *cpu_, uint64_t fp)
663 {
664 return cpu_read_memory64(cpu_, fp + 8);
665 }
666
667 static void x64_init(Cpu *cpu_)
668 {
669 X64Cpu *cpu = g_new0(X64Cpu, 1);
670 cpu_->arch = cpu;
671 cpu->reg_rbp = plugin_find_register("rbp");
672 g_assert(cpu->reg_rbp);
673 cpu->reg_cs = plugin_find_register("cs");
674 g_assert(cpu->reg_cs);
675 cpu->reg_cr0 = plugin_find_register("cr0");
676 g_assert(cpu->reg_cr0);
677 }
678
679 static void x64_end(Cpu *cpu)
680 {
681 g_free(cpu->arch);
682 }
683
684 static bool x64_does_insn_modify_frame_pointer(const char *disas)
685 {
686 return strstr(disas, "rbp");
687 }
688
689 static CpuOps x64_ops = {
690 .init = x64_init,
691 .end = x64_end,
692 .get_frame_pointer = x64_get_frame_pointer,
693 .get_next_frame_pointer = x64_get_next_frame_pointer,
694 .get_next_return_address = x64_get_next_return_address,
695 .get_privilege_level = x64_get_privilege_level,
696 .num_privilege_levels = x64_num_privilege_levels,
697 .get_privilege_level_name = x64_get_privilege_level_name,
698 .does_insn_modify_frame_pointer = x64_does_insn_modify_frame_pointer,
699 };
700
701 static uint8_t riscv64_num_privilege_levels(void)
702 {
703 return RISCV64_PRIVILEGE_LEVEL_MAX;
704 }
705
706 static const char *riscv64_get_privilege_level_name(uint8_t pl)
707 {
708 switch (pl) {
709 case RISCV64_USER: return "User";
710 case RISCV64_SUPERVISOR: return "Supervisor";
711 case RISCV64_RESERVED: return "Unknown";
712 case RISCV64_MACHINE: return "Machine";
713 case RISCV64_VUSER: return "VUser";
714 case RISCV64_VSUPERVISOR: return "VSupervisor";
715 default:
716 g_assert_not_reached();
717 }
718 }
719
720 static uint8_t riscv64_get_privilege_level(Cpu *cpu_)
721 {
722 Riscv64Cpu *cpu = cpu_->arch;
723 return cpu_read_register64(cpu_, cpu->reg_priv);
724 }
725
726 static uint64_t riscv64_get_frame_pointer(Cpu *cpu_)
727 {
728 Riscv64Cpu *cpu = cpu_->arch;
729 return cpu_read_register64(cpu_, cpu->reg_fp);
730 }
731
732 static uint64_t riscv64_get_next_frame_pointer(Cpu *cpu_, uint64_t fp)
733 {
734 return cpu_read_memory64(cpu_, fp - 16);
735 }
736
737 static uint64_t riscv64_get_next_return_address(Cpu *cpu_, uint64_t fp)
738 {
739 return cpu_read_memory64(cpu_, fp - 8);
740 }
741
742 static void riscv64_init(Cpu *cpu_)
743 {
744 Riscv64Cpu *cpu = g_new0(Riscv64Cpu, 1);
745 cpu_->arch = cpu;
746 cpu->reg_fp = plugin_find_register("fp");
747 g_assert(cpu->reg_fp);
748 cpu->reg_priv = plugin_find_register("priv");
749 g_assert(cpu->reg_priv);
750 }
751
752 static void riscv64_end(Cpu *cpu)
753 {
754 g_free(cpu->arch);
755 }
756
757 static bool riscv64_does_insn_modify_frame_pointer(const char *disas)
758 {
759 /* fp is s0 in disassembly */
760 return strstr(disas, "s0");
761 }
762
763 static CpuOps riscv64_ops = {
764 .init = riscv64_init,
765 .end = riscv64_end,
766 .get_frame_pointer = riscv64_get_frame_pointer,
767 .get_next_frame_pointer = riscv64_get_next_frame_pointer,
768 .get_next_return_address = riscv64_get_next_return_address,
769 .get_privilege_level = riscv64_get_privilege_level,
770 .num_privilege_levels = riscv64_num_privilege_levels,
771 .get_privilege_level_name = riscv64_get_privilege_level_name,
772 .does_insn_modify_frame_pointer = riscv64_does_insn_modify_frame_pointer,
773 };
774
775 static void track_privilege_change(unsigned int cpu_index, void *udata)
776 {
777 Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index);
778 uint8_t new_pl = cpu->ops.get_privilege_level(cpu);
779
780 if (new_pl == cpu->privilege_level) {
781 return;
782 }
783
784 uint64_t pc = (uintptr_t) udata;
785 uint64_t timestamp = gettime_ns();
786
787 trace_exit_stack(cpu->trace, cpu->cs, timestamp);
788 callstack_clear(cpu->cs);
789
790 cpu->privilege_level = new_pl;
791 cpu->trace = g_array_index(cpu->traces, Trace*, new_pl);
792
793 cpu_unwind_stack(cpu, cpu->ops.get_frame_pointer(cpu), pc);
794 trace_enter_stack(cpu->trace, cpu->cs, timestamp);
795 }
796
797 static void track_callstack(unsigned int cpu_index, void *udata)
798 {
799 uint64_t pc = (uintptr_t) udata;
800 Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index);
801 uint64_t timestamp = gettime_ns();
802 Callstack *cs = cpu->cs;
803 Trace *t = cpu->trace;
804
805 uint64_t fp = cpu->ops.get_frame_pointer(cpu);
806 if (!fp && callstack_empty(cs)) {
807 /*
808 * We simply push current pc. Note that we won't detect symbol change as
809 * long as a proper call does not happen.
810 */
811 callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc});
812 trace_enter_function(t, timestamp, pc, callstack_depth(cs));
813 return;
814 }
815
816 CallstackEntry top = callstack_top(cs);
817 if (fp == top.frame_pointer) {
818 /* same function */
819 return;
820 }
821
822 CallstackEntry caller = callstack_caller(cs);
823 if (fp == caller.frame_pointer) {
824 /* return */
825 CallstackEntry e = callstack_pop(cs);
826 /* uftrace convention is to use same depth as entry */
827 trace_exit_function(t, timestamp, e.pc, callstack_depth(cs) + 1);
828 return;
829 }
830
831 uint64_t caller_fp = cpu->ops.get_next_frame_pointer(cpu, fp);
832 if (caller_fp == top.frame_pointer) {
833 /* call */
834 callstack_push(cs, (CallstackEntry){.frame_pointer = fp, .pc = pc});
835 trace_enter_function(t, timestamp, pc, callstack_depth(cs));
836 return;
837 }
838
839 /* discontinuity, exit current stack and unwind new one */
840 trace_exit_stack(t, cs, timestamp);
841 callstack_clear(cs);
842
843 cpu_unwind_stack(cpu, fp, pc);
844 trace_enter_stack(t, cs, timestamp);
845 }
846
847 static void vcpu_tb_trans(struct qemu_plugin_tb *tb, void *userdata)
848 {
849 size_t n_insns = qemu_plugin_tb_n_insns(tb);
850 uintptr_t tb_pc = qemu_plugin_tb_vaddr(tb);
851
852 if (trace_privilege_level) {
853 qemu_plugin_register_vcpu_tb_exec_cb(tb, track_privilege_change,
854 QEMU_PLUGIN_CB_R_REGS,
855 (void *) tb_pc);
856 }
857
858 /*
859 * Callbacks and inline instrumentation are inserted before an instruction.
860 * Thus, to see instruction effect, we need to wait for next one.
861 * Potentially, the last instruction of a block could modify the frame
862 * pointer. Thus, we need to always instrument first instruction in a tb.
863 */
864 bool instrument_insn = true;
865 for (size_t i = 0; i < n_insns; i++) {
866 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
867
868 if (instrument_insn) {
869 uintptr_t pc = qemu_plugin_insn_vaddr(insn);
870 qemu_plugin_register_vcpu_insn_exec_cb(insn, track_callstack,
871 QEMU_PLUGIN_CB_R_REGS,
872 (void *) pc);
873 instrument_insn = false;
874 }
875
876 char *disas = qemu_plugin_insn_disas(insn);
877 if (arch_ops.does_insn_modify_frame_pointer(disas)) {
878 instrument_insn = true;
879 }
880 }
881 }
882
883 static void vcpu_init(unsigned int vcpu_index, void *userdata)
884 {
885 Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index);
886 cpu->ops = arch_ops;
887
888 cpu->ops.init(cpu);
889 cpu->buf = g_byte_array_new();
890 cpu->traces = g_array_new(0, 0, sizeof(Trace *));
891
892 g_assert(vcpu_index < UINT32_MAX / TRACE_ID_SCALE);
893 g_assert(cpu->ops.num_privilege_levels() < TRACE_ID_SCALE);
894 /* trace_id is: cpu_number * TRACE_ID_SCALE + privilege_level */
895 uint32_t trace_id = (vcpu_index + 1) * TRACE_ID_SCALE;
896
897 if (trace_privilege_level) {
898 for (uint8_t pl = 0; pl < cpu->ops.num_privilege_levels(); ++pl) {
899 g_autoptr(GString) trace_name = g_string_new(NULL);
900 g_string_append_printf(trace_name, "cpu%u %s", vcpu_index,
901 cpu->ops.get_privilege_level_name(pl));
902 Trace *t = trace_new(trace_id + pl, trace_name);
903 g_array_append_val(cpu->traces, t);
904 }
905 } else {
906 g_autoptr(GString) trace_name = g_string_new(NULL);
907 g_string_append_printf(trace_name, "cpu%u", vcpu_index);
908 Trace *t = trace_new(trace_id, trace_name);
909 g_array_append_val(cpu->traces, t);
910 }
911
912 for (size_t i = 0; i < cpu->traces->len; ++i) {
913 /* create/truncate trace files */
914 Trace *t = g_array_index(cpu->traces, Trace*, i);
915 trace_flush(t, false);
916 }
917
918 cpu->cs = callstack_new();
919 cpu->trace = g_array_index(cpu->traces, Trace*, cpu->privilege_level);
920 }
921
922 static void vcpu_end(unsigned int vcpu_index)
923 {
924 Cpu *cpu = qemu_plugin_scoreboard_find(score, vcpu_index);
925 g_byte_array_free(cpu->buf, true);
926
927 for (size_t i = 0; i < cpu->traces->len; ++i) {
928 Trace *t = g_array_index(cpu->traces, Trace*, i);
929 trace_free(t);
930 }
931
932 g_array_free(cpu->traces, true);
933 callstack_free(cpu->cs);
934 memset(cpu, 0, sizeof(Cpu));
935 }
936
937 static void at_exit(void *data)
938 {
939 bool system_emulation = (bool) data;
940 g_autoptr(GArray) traces = g_array_new(0, 0, sizeof(Trace *));
941
942 for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) {
943 Cpu *cpu = qemu_plugin_scoreboard_find(score, i);
944 for (size_t j = 0; j < cpu->traces->len; ++j) {
945 Trace *t = g_array_index(cpu->traces, Trace*, j);
946 trace_flush(t, true);
947 g_array_append_val(traces, t);
948 }
949 }
950
951 uftrace_write_map(system_emulation);
952 uftrace_write_info(traces);
953 uftrace_write_task(traces);
954
955 for (size_t i = 0; i < qemu_plugin_num_vcpus(); ++i) {
956 vcpu_end(i);
957 }
958
959 qemu_plugin_scoreboard_free(score);
960 }
961
962 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
963 const qemu_info_t *info,
964 int argc, char **argv)
965 {
966 for (int i = 0; i < argc; i++) {
967 char *opt = argv[i];
968 g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
969 if (g_strcmp0(tokens[0], "trace-privilege-level") == 0) {
970 if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
971 &trace_privilege_level)) {
972 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
973 return -1;
974 }
975 } else {
976 fprintf(stderr, "option parsing failed: %s\n", opt);
977 return -1;
978 }
979 }
980
981 if (!strcmp(info->target_name, "aarch64")) {
982 arch_ops = aarch64_ops;
983 } else if (!strcmp(info->target_name, "x86_64")) {
984 arch_ops = x64_ops;
985 } else if (!strcmp(info->target_name, "riscv64")) {
986 arch_ops = riscv64_ops;
987 } else {
988 fprintf(stderr, "plugin uftrace: %s target is not supported\n",
989 info->target_name);
990 return 1;
991 }
992
993 score = qemu_plugin_scoreboard_new(sizeof(Cpu));
994 qemu_plugin_register_vcpu_init_cb(id, vcpu_init, NULL);
995 qemu_plugin_register_atexit_cb(id, at_exit,
996 (void *) info->system_emulation);
997 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL);
998
999 return 0;
1000 }