/* * green-eye TCG trace plugin - full instruction/memory activity recording. * * This is the "complete map" upgrade from stop-based observation: QEMU calls * the plugin for every translated instruction and every memory access. Output * is JSONL (one object per line), ready to be folded into a green-eye session: * * {"t":"exec","vcpu":0,"pc":"0x80000194"} * {"t":"write","vcpu":0,"addr":"0x80000850","size":8} * {"t":"read","vcpu":0,"addr":"0x10000005","size":1} * {"t":"summary","insns":12345,"mem":678} * * Arguments (all optional): * path=FILE output file (default greeneye-trace.jsonl) * exec=on|off record instruction execution (default off; heavy) * start=0xADDR only instrument PCs >= start * end=0xADDR only instrument PCs <= end * * Build: make -C qemu (needs qemu-plugin.h, vendored here) * Run: qemu-system-riscv64 ... \ * -plugin qemu/greeneye-trace.so,path=/tmp/ge-trace.jsonl,exec=on * * License: GPL-2.0-or-later (matches QEMU's plugin API header). */ #include #include #include #include #include #include QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; static FILE *trace_fp; static bool trace_exec; static uint64_t start_pc; static uint64_t end_pc; static uint64_t insn_count; static uint64_t mem_count; static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) { uint64_t pc = (uint64_t)(uintptr_t)userdata; insn_count++; fprintf(trace_fp, "{\"t\":\"exec\",\"vcpu\":%u,\"pc\":\"0x%" PRIx64 "\"}\n", vcpu_index, pc); } static void vcpu_mem(unsigned int vcpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *userdata) { bool is_store = qemu_plugin_mem_is_store(info); unsigned int shift = qemu_plugin_mem_size_shift(info); (void)userdata; mem_count++; fprintf(trace_fp, "{\"t\":\"%s\",\"vcpu\":%u,\"addr\":\"0x%" PRIx64 "\",\"size\":%u}\n", is_store ? "write" : "read", vcpu_index, vaddr, 1u << shift); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n = qemu_plugin_tb_n_insns(tb); (void)id; for (size_t i = 0; i < n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); uint64_t pc = qemu_plugin_insn_vaddr(insn); if (start_pc && pc < start_pc) { continue; } if (end_pc && pc > end_pc) { continue; } if (trace_exec) { qemu_plugin_register_vcpu_insn_exec_cb( insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, (void *)(uintptr_t)pc); } qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_MEM_RW, NULL); } } static void plugin_exit(qemu_plugin_id_t id, void *userdata) { (void)id; (void)userdata; if (trace_fp) { fprintf(trace_fp, "{\"t\":\"summary\",\"insns\":%" PRIu64 ",\"mem\":%" PRIu64 "}\n", insn_count, mem_count); fclose(trace_fp); trace_fp = NULL; } qemu_plugin_outs("green-eye trace complete\n"); } QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { const char *path = "greeneye-trace.jsonl"; (void)info; for (int i = 0; i < argc; i++) { if (strncmp(argv[i], "path=", 5) == 0) { path = argv[i] + 5; } else if (strncmp(argv[i], "exec=", 5) == 0) { trace_exec = strcmp(argv[i] + 5, "on") == 0 || strcmp(argv[i] + 5, "true") == 0; } else if (strncmp(argv[i], "start=", 6) == 0) { start_pc = strtoull(argv[i] + 6, NULL, 0); } else if (strncmp(argv[i], "end=", 4) == 0) { end_pc = strtoull(argv[i] + 4, NULL, 0); } } trace_fp = fopen(path, "w"); if (!trace_fp) { qemu_plugin_outs("green-eye: cannot open trace output\n"); return -1; } qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); qemu_plugin_outs("green-eye trace plugin armed\n"); return 0; }