From 991624161ef291529f097eb45469ef081f650885 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 20 Jun 2025 11:42:24 -0500 Subject: [PATCH 01/59] Add skeleton implementation of tracing plugin. --- contrib/plugins/execlog_bap.c | 74 +++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 contrib/plugins/execlog_bap.c diff --git a/contrib/plugins/execlog_bap.c b/contrib/plugins/execlog_bap.c new file mode 100644 index 0000000000000..1a60259adc909 --- /dev/null +++ b/contrib/plugins/execlog_bap.c @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct { + // Current instruction related things. +} VCPU; + +typedef struct { + GRWLock vcpus_array_lock; + GArray *vcpus; + + GRWLock frame_buffer_lock; + GPtrArray *frame_buffer; +} TraceState; + +static TraceState state; + +static VCPU *get_vcpu(TraceState *state, int vcpu_index) { + VCPU *c; + g_rw_lock_reader_lock(&state->vcpus_array_lock); + c = &g_array_index(state->vcpus, VCPU, vcpu_index); + g_rw_lock_reader_unlock(&state->vcpus_array_lock); + + return c; +} + +static void log_insn_frame(unsigned int cpu_index, void *udata) { + // VCPU *vcpu = get_vcpu(state, cpu_index); + + // Add change to previous frame + // Finish previous frame + // Check if buffer should be dumped to file. + // Open new one. + return; +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { + // Add new vcpu +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) { + // Dump rest of frames to file. +} + +static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { + // Add a callback for each instruction in every translated block. + struct qemu_plugin_insn *insn; + size_t n_insns = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < n_insns; i++) { + insn = qemu_plugin_tb_get_insn(tb, i); + qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_frame, + QEMU_PLUGIN_CB_R_REGS, &state); + } +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) { + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_vcpu_tb_trans_cb(id, cb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + // Get reg names + // qemu_plugin_get_registers + // + // Logging + // qemu_plugin_outs + + return 0; +} From a0a98ed56f6389a1cdecfbda03386291a5fbe6d7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 20 Jun 2025 11:49:58 -0500 Subject: [PATCH 02/59] Remove lockdown workflow --- .github/workflows/lockdown.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 .github/workflows/lockdown.yml diff --git a/.github/workflows/lockdown.yml b/.github/workflows/lockdown.yml deleted file mode 100644 index d5e1265cffb39..0000000000000 --- a/.github/workflows/lockdown.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown - -name: 'Repo Lockdown' - -on: - pull_request_target: - types: opened - -permissions: - pull-requests: write - -jobs: - action: - runs-on: ubuntu-latest - steps: - - uses: dessant/repo-lockdown@v2 - with: - pr-comment: | - Thank you for your interest in the QEMU project. - - This repository is a read-only mirror of the project's repostories hosted - on https://gitlab.com/qemu-project/qemu.git. - The project does not process merge requests filed on GitHub. - - QEMU welcomes contributions of code (either fixing bugs or adding new - functionality). However, we get a lot of patches, and so we have some - guidelines about contributing on the project website: - https://www.qemu.org/contribute/ - lock-pr: true - close-pr: true From dbafb0cbdb9bd16b3cda990eeb635eaeb7436ca7 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 20 Jun 2025 12:32:13 -0500 Subject: [PATCH 03/59] Move plugin into own dir. --- .gitmodules | 3 +++ contrib/plugins/bap-tracing/bap-frames | 1 + contrib/plugins/{execlog_bap.c => bap-tracing/tracing.c} | 0 3 files changed, 4 insertions(+) create mode 160000 contrib/plugins/bap-tracing/bap-frames rename contrib/plugins/{execlog_bap.c => bap-tracing/tracing.c} (100%) diff --git a/.gitmodules b/.gitmodules index 73cae4cd4da00..439b531cfbdda 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "tests/lcitool/libvirt-ci"] path = tests/lcitool/libvirt-ci url = https://gitlab.com/libvirt/libvirt-ci.git +[submodule "contrib/plugins/bap-tracing/bap-frames"] + path = contrib/plugins/bap-tracing/bap-frames + url = git@github.com:BinaryAnalysisPlatform/bap-frames.git diff --git a/contrib/plugins/bap-tracing/bap-frames b/contrib/plugins/bap-tracing/bap-frames new file mode 160000 index 0000000000000..71368108ee63d --- /dev/null +++ b/contrib/plugins/bap-tracing/bap-frames @@ -0,0 +1 @@ +Subproject commit 71368108ee63d3bc557657e81fb58d7a6235b920 diff --git a/contrib/plugins/execlog_bap.c b/contrib/plugins/bap-tracing/tracing.c similarity index 100% rename from contrib/plugins/execlog_bap.c rename to contrib/plugins/bap-tracing/tracing.c From abf60400fd5271ec4050c61592b7f468bd595602 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 20 Jun 2025 12:43:49 -0500 Subject: [PATCH 04/59] Re-add tracewrap code from tracewrap-8.1-hexagon --- contrib/plugins/bap-tracing/trace_consts.h | 13 + contrib/plugins/bap-tracing/tracewrap.c | 360 +++++++++++++++++++++ contrib/plugins/bap-tracing/tracewrap.h | 65 ++++ 3 files changed, 438 insertions(+) create mode 100644 contrib/plugins/bap-tracing/trace_consts.h create mode 100644 contrib/plugins/bap-tracing/tracewrap.c create mode 100644 contrib/plugins/bap-tracing/tracewrap.h diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h new file mode 100644 index 0000000000000..8010118eca96b --- /dev/null +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -0,0 +1,13 @@ +#pragma once + +#include "trace_info.h" + +const uint64_t magic_number = 7456879624156307493LL; +const uint64_t magic_number_offset = 0LL; +const uint64_t trace_version_offset = 8LL; +const uint64_t bfd_arch_offset = 16LL; +const uint64_t bfd_machine_offset = 24LL; +const uint64_t num_trace_frames_offset = 32LL; +const uint64_t toc_offset_offset = 40LL; +const uint64_t first_frame_offset = 48LL; +const uint64_t out_trace_version = 2LL; diff --git a/contrib/plugins/bap-tracing/tracewrap.c b/contrib/plugins/bap-tracing/tracewrap.c new file mode 100644 index 0000000000000..f2afcb5bb221e --- /dev/null +++ b/contrib/plugins/bap-tracing/tracewrap.c @@ -0,0 +1,360 @@ +#include "tracewrap.h" +#include "trace_consts.h" +#include "exec/cpu_ldst.h" + +#include +#include +#include "qemu/log.h" + +#include +#include +#include + +#include +#include + + +char tracer_name[] = "qemu"; +char tracer_version[] = "2.0.0/tracewrap"; + +static Frame * g_frame; +static uint64_t frames_per_toc_entry = 64LL; +static uint32_t open_frame = 0; +static FILE *file = NULL; + +/* don't use the following data directly! + use toc_init, toc_update and toc_write functions instead */ +static uint64_t *toc = NULL; +static int toc_entries = 0; +static int toc_capacity = 0; +static uint64_t toc_num_frames = 0; + +#define MD5LEN 16 +static guchar target_md5[MD5LEN]; +static char target_path[PATH_MAX] = "unknown"; + + +#define WRITE(x) do { \ + if (!file) \ + err(1, "qemu_trace is not initialized"); \ + if (fwrite(&(x), sizeof(x),1,file) != 1) \ + err(1, "fwrite failed"); \ + } while(0) + +#define WRITE_BUF(x,n) do { \ + if (!file) \ + err(1, "qemu_trace is not initialized"); \ + if (fwrite((x),1,(n),file) != n) \ + err(1, "fwrite failed"); \ + } while(0) + +#define SEEK(off) do { \ + if (fseek(file,(off), SEEK_SET) < 0) \ + err(1, "stream not seekable"); \ + } while(0) + + +static void toc_init(void) { + if (toc_entries != 0) + err(1, "qemu_trace was initialized twice"); + toc = g_new(uint64_t, 1024); + toc_capacity = 1024; + toc_entries = 0; +} + +static void toc_append(uint64_t entry) { + if (toc_capacity <= toc_entries) { + toc = g_renew(uint64_t, toc, toc_capacity * 2); + toc_capacity *= 2; + } + toc[toc_entries++] = entry; +} + +static void toc_write(void) { + int64_t toc_offset = ftell(file); + if (toc_offset > 0) { + int i = 0; + WRITE(frames_per_toc_entry); + for (i = 0; i < toc_entries; i++) + WRITE(toc[i]); + SEEK(num_trace_frames_offset); + WRITE(toc_num_frames); + SEEK(toc_offset_offset); + WRITE(toc_offset); + } +} + +static void toc_update(void) { + toc_num_frames++; + if (toc_num_frames % frames_per_toc_entry == 0) { + int64_t off = ftell(file); + if (off >= 0) toc_append(off); + } +} + +static void write_header(void) { + uint64_t toc_off = 0L; + WRITE(magic_number); + WRITE(out_trace_version); + WRITE(frame_arch); + WRITE(frame_mach); + WRITE(toc_num_frames); + WRITE(toc_off); +} + +static int list_length(char **list) { + int n=0; + if (list) { + char **p = list; + for (;*p;p++,n++); + } + return n; +} + +static void compute_target_md5(void) { + const GChecksumType md5 = G_CHECKSUM_MD5; + GChecksum *cs = g_checksum_new(md5); + FILE *target = fopen(target_path, "r"); + guchar buf[BUFSIZ]; + gsize expected_length = MD5LEN; + + if (!cs) err(1, "failed to create a checksum"); + if (!target) err(1, "failed to open target binary"); + if (g_checksum_type_get_length(md5) != expected_length) abort(); + + while (!feof(target)) { + size_t len = fread(buf,1,BUFSIZ,target); + if (ferror(target)) + err(1, "failed to read target binary"); + g_checksum_update(cs, buf, len); + } + + g_checksum_get_digest(cs, target_md5, &expected_length); + fclose(target); +} + +static void store_to_trace(ProtobufCBuffer *self, size_t len, const uint8_t *data) { + WRITE_BUF(data,len); +} + +static void init_tracer(Tracer *tracer, char **argv, char **envp) { + tracer__init(tracer); + tracer->name = tracer_name; + tracer->n_args = list_length(argv); + tracer->args = argv; + tracer->n_envp = list_length(envp); + tracer->envp = envp; + tracer->version = tracer_version; +} + +static void init_target(Target *target, char **argv, char **envp) { + compute_target_md5(); + + target__init(target); + target->path = target_path; + target->n_args = list_length(argv); + target->args = argv; + target->n_envp = list_length(envp); + target->envp = envp; + target->md5sum.len = MD5LEN; + target->md5sum.data = target_md5; +} + +#ifdef G_OS_UNIX +static void unix_fill_fstats(Fstats *fstats, char *path) { + struct stat stats; + if (stat(path, &stats) < 0) + err(1, "failed to obtain file stats"); + + fstats->size = stats.st_size; + fstats->atime = stats.st_atime; + fstats->mtime = stats.st_mtime; + fstats->ctime = stats.st_ctime; +} +#endif + + +static void init_fstats(Fstats *fstats) { + fstats__init(fstats); +#ifdef G_OS_UNIX + unix_fill_fstats(fstats, target_path); +#endif +} + + +static void write_meta( + char **tracer_argv, + char **tracer_envp, + char **target_argv, + char **target_envp) +{ + MetaFrame meta; + Tracer tracer; + Target target; + Fstats fstats; + ProtobufCBuffer buffer; + + buffer.append = store_to_trace; + + + meta_frame__init(&meta); + init_tracer(&tracer, tracer_argv, tracer_envp); + init_target(&target, target_argv, target_envp); + init_fstats(&fstats); + + meta.tracer = &tracer; + meta.target = ⌖ + meta.fstats = &fstats; + meta.time = time(NULL); + char *user = g_strdup(g_get_real_name()); + meta.user = user; + + char *host = g_strdup(g_get_host_name()); + meta.host = host; + + uint64_t size = meta_frame__get_packed_size(&meta); + WRITE(size); + + meta_frame__pack_to_buffer(&meta, &buffer); + + free(user); + free(host); +} + + +void qemu_trace_init(const char *filename, + const char *targetname, + char **argv, char **envp, + char **target_argv, + char **target_envp) { + qemu_log("Initializing tracer\n"); + if (realpath(targetname,target_path) == NULL) + err(1, "can't get target path"); + + + char *name = filename + ? g_strdup(filename) + : g_strdup_printf("%s.frames", basename(target_path)); + file = fopen(name, "wb"); + if (file == NULL) + err(1, "tracewrap: can't open trace file %s", name); + write_header(); + write_meta(argv, envp, target_argv, target_envp); + toc_init(); + g_free(name); +} + + +void qemu_trace_newframe(target_ulong addr, int __unused/*thread_id*/ ) { + int thread_id = 1; + if (open_frame) { + qemu_log("frame is still open"); + qemu_trace_endframe(NULL, 0, 0); + } + + open_frame = 1; + g_frame = g_new(Frame,1); + frame__init(g_frame); + + StdFrame *sframe = g_new(StdFrame, 1); + std_frame__init(sframe); + g_frame->std_frame = sframe; + + sframe->address = addr; + sframe->thread_id = thread_id; + + OperandValueList *ol_in = g_new(OperandValueList,1); + operand_value_list__init(ol_in); + ol_in->n_elem = 0; + sframe->operand_pre_list = ol_in; + + OperandValueList *ol_out = g_new(OperandValueList,1); + operand_value_list__init(ol_out); + ol_out->n_elem = 0; + sframe->operand_post_list = ol_out; +} + +static inline void free_operand(OperandInfo *oi) { + OperandInfoSpecific *ois = oi->operand_info_specific; + + //Free reg-operand + RegOperand *ro = ois->reg_operand; + if (ro && ro->name) + g_free(ro->name); + g_free(ro); + + //Free mem-operand + MemOperand *mo = ois->mem_operand; + g_free(mo); + g_free(oi->value.data); + g_free(oi->taint_info); + g_free(ois); + g_free(oi->operand_usage); + g_free(oi); +} + +void qemu_trace_add_operand(OperandInfo *oi, int inout) { + if (!open_frame) { + if (oi) + free_operand(oi); + return; + } + OperandValueList *ol; + if (inout & 0x1) { + ol = g_frame->std_frame->operand_pre_list; + } else { + ol = g_frame->std_frame->operand_post_list; + } + + oi->taint_info = g_new(TaintInfo, 1); + taint_info__init(oi->taint_info); + oi->taint_info->no_taint = 1; + oi->taint_info->has_no_taint = 1; + + ol->n_elem += 1; + ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); + ol->elem[ol->n_elem - 1] = oi; +} + +void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size) { + int i = 0; + StdFrame *sframe = g_frame->std_frame; + + if (!open_frame) return; + + sframe->rawbytes.len = size; + sframe->rawbytes.data = g_malloc(size); + for (i = 0; i < size; i++) { + sframe->rawbytes.data[i] = cpu_ldub_code(env, pc+i); + } + + size_t msg_size = frame__get_packed_size(g_frame); + uint8_t *packed_buffer = g_alloca(msg_size); + uint64_t packed_size = frame__pack(g_frame, packed_buffer); + WRITE(packed_size); + WRITE_BUF(packed_buffer, packed_size); + toc_update(); + + //counting num_frames in newframe does not work by far ... + //how comes? disas_arm_insn might not always return at the end? + for (i = 0; i < sframe->operand_pre_list->n_elem; i++) + free_operand(sframe->operand_pre_list->elem[i]); + g_free(sframe->operand_pre_list->elem); + g_free(sframe->operand_pre_list); + + for (i = 0; i < sframe->operand_post_list->n_elem; i++) + free_operand(sframe->operand_post_list->elem[i]); + g_free(sframe->operand_post_list->elem); + g_free(sframe->operand_post_list); + + g_free(sframe->rawbytes.data); + g_free(sframe); + g_free(g_frame); + open_frame = 0; +} + +void qemu_trace_finish(uint32_t exit_code) { + toc_write(); + if (fclose(file) != 0) + err(1,"failed to write trace file, the file maybe corrupted"); +} diff --git a/contrib/plugins/bap-tracing/tracewrap.h b/contrib/plugins/bap-tracing/tracewrap.h new file mode 100644 index 0000000000000..1539cd118d952 --- /dev/null +++ b/contrib/plugins/bap-tracing/tracewrap.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include "qemu/osdep.h" +#include "cpu.h" + +#include "frame.piqi.pb-c.h" + + +/** initializes trace subsystem. + + All pointers are owned by the caller. + + @param filename a name of filesystem entry where trace will be dumpled, + if NULL then the name is basename(argv[0]).frames + + @param targetname a path to the executable, must be non NULL + + + @param argv a full list of arguments passed to the tracer, NULL terminated. + Can be NULL or empty (i.e., contain only a NULL element). + The list may include target arguments. + + @param envp a null terminated list of environment parameters, + can be NULL or empty. + + @param target_argv a null terminated list of target arguments, + can be NULL or empty. + + @param target_envp a null terminated list of target environment, + can be NULL or empty. + */ +void qemu_trace_init(const char *filename, const char *targetname, + char **argv, char **envp, + char **target_argv, + char **target_envp); +void qemu_trace_newframe(target_ulong addr, int tread_id); +void qemu_trace_add_operand(OperandInfo *oi, int inout); +void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size); +void qemu_trace_finish(uint32_t exit_code); + +OperandInfo * load_store_reg(target_ulong reg, target_ulong val, int ls); +OperandInfo * load_store_mem(target_ulong addr, target_ulong val, int ls, int len); + +#define REG_EFLAGS 66 +#define REG_LO 33 +#define REG_HI 34 + +#define REG_CPSR 64 +#define REG_APSR 65 +#define REG_SP 13 +#define REG_LR 14 +#define REG_PC 15 + +#define REG_NF 94 +#define REG_ZF 95 +#define REG_CF 96 +#define REG_VF 97 +#define REG_QF 98 +#define REG_GE 99 + +#define SEG_BIT 8 From b96ce6ac2fe5f7460052113f84f0715007da7e5b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 20 Jun 2025 12:47:26 -0500 Subject: [PATCH 05/59] Add preliminary README --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..e510f80126157 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# BAP emulation trace generator + +This QEMU fork implements the TCG plugin to generate execution traces in the +[bap-frame](https://github.com/BinaryAnalysisPlatform/bap-frames) format. + +Previous traces were generated with a patched QEMU. +You can find these in the branches tracewrap-6.2.0 for ARM and x86 and tracewrap-8.1 for Hexagon. + +## Building + +```bash +mkdir build +cd build +# See `../configure --help` for a list of targets. +../configure --enable-plugins --target-list= +make +``` From a040e4199113c690cb122dc8f60fe3585b13a4e8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 22 Jun 2025 12:07:59 -0500 Subject: [PATCH 06/59] Enable bap-tracing plugin build with meson. --- README.md | 6 + contrib/plugins/bap-tracing/fix_proto_src.py | 22 + contrib/plugins/bap-tracing/meson.build | 48 ++ contrib/plugins/bap-tracing/trace_consts.h | 2 +- contrib/plugins/bap-tracing/tracewrap.c | 571 ++++++++++--------- contrib/plugins/bap-tracing/tracewrap.h | 15 +- contrib/plugins/bap-tracing/tracing.c | 3 + contrib/plugins/meson.build | 2 + 8 files changed, 373 insertions(+), 296 deletions(-) create mode 100755 contrib/plugins/bap-tracing/fix_proto_src.py create mode 100644 contrib/plugins/bap-tracing/meson.build diff --git a/README.md b/README.md index e510f80126157..70e36ef9b6091 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ This QEMU fork implements the TCG plugin to generate execution traces in the Previous traces were generated with a patched QEMU. You can find these in the branches tracewrap-6.2.0 for ARM and x86 and tracewrap-8.1 for Hexagon. +## Dependencies + +1. Install [piqi](https://piqi.org/downloads/) so you have the `piqi` binary in `PATH`. +2. Install the developer package of `protobuf-c`. E.g. `protobuf-c-devel` (Fedora), `libprotobuf-c-dev` (Debian). +3. QEMU dependencies (see [QEMU docs](https://www.qemu.org/docs/master/devel/build-environment.html)). + ## Building ```bash diff --git a/contrib/plugins/bap-tracing/fix_proto_src.py b/contrib/plugins/bap-tracing/fix_proto_src.py new file mode 100755 index 0000000000000..15efa5fc68938 --- /dev/null +++ b/contrib/plugins/bap-tracing/fix_proto_src.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +""" +This just does: +sed -i 's/->base/->__base/g' +sed -i 's/ProtobufCMessage base;/ProtobufCMessage __base;/g' +""" + +import sys +import re + +if len(sys.argv) != 6 or sys.argv[3] != "-o": + print("usage: fix_proto_src.py frame.piqi.pb-c.c frame.piqi.pb-c.h -o frame.piqi.pb-c-fixed.c frame.piqi.pb-c-fixed.h") + exit(1) + +for (in_file, out_file) in zip(sys.argv[1:3], sys.argv[4:6]): + with open(in_file, "r") as i: + contents = i.read() + contents = contents.replace("->base", "->__base") + contents = contents.replace("ProtobufCMessage base;", "ProtobufCMessage __base;") + contents = re.sub(r'".*frame.piqi.pb-c.h"', '"frame.piqi.pb-c-patched.h"', contents) + with open(out_file, "w") as o: + o.write(contents) diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build new file mode 100644 index 0000000000000..0835eb7670563 --- /dev/null +++ b/contrib/plugins/bap-tracing/meson.build @@ -0,0 +1,48 @@ +piqi = find_program('piqi') +protoc_c = find_program('protoc-c') + +# piqi -> protobuf file. +piqi_src = custom_target('piqi', + input: 'bap-frames/piqi/frame.piqi', + output: 'frame.piqi.proto', + command: [piqi, 'to-proto', '@INPUT@', '-o', '@OUTPUT@']) + +# protobuf file -> C code +proto_src_raw = custom_target('proto', + input: piqi_src, + output: ['frame.piqi.pb-c.c', 'frame.piqi.pb-c.h'], + command: [protoc_c, '--c_out=.', '@INPUT@'], + depends: piqi_src) + +# Patch protobuf header: base -> __base. +# Necessary for the C build. +# See fix_proto_src.py +proto_src = custom_target( + input: proto_src_raw, + output: ['frame.piqi.pb-c-patched.c', 'frame.piqi.pb-c-patched.h'], + command: [files('fix_proto_src.py'), '@INPUT@', '-o', '@OUTPUT@'], + depends: proto_src_raw +) + +libprotobuf = static_library('protobuf', [proto_src], pic: true) +dep_libprotobuf = declare_dependency( + sources : proto_src, + link_with : [libprotobuf], +) + +bap_tracing_src = files( + 'tracing.c', + 'tracewrap.c', +) + +if host_os == 'windows' + plugin_modules += shared_module('bap_tracing', bap_tracing_src, + include_directories: '../../../include/qemu', + link_depends: [win32_qemu_plugin_api_lib], + link_args: win32_qemu_plugin_api_link_flags, + dependencies: [glib, dep_libprotobuf]) +else + plugin_modules += shared_module('bap_tracing', bap_tracing_src, + include_directories: '../../../include/qemu', + dependencies: [glib, dep_libprotobuf]) +endif diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h index 8010118eca96b..22dde6dfdb947 100644 --- a/contrib/plugins/bap-tracing/trace_consts.h +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -1,6 +1,6 @@ #pragma once -#include "trace_info.h" +// #include "trace_info.h" const uint64_t magic_number = 7456879624156307493LL; const uint64_t magic_number_offset = 0LL; diff --git a/contrib/plugins/bap-tracing/tracewrap.c b/contrib/plugins/bap-tracing/tracewrap.c index f2afcb5bb221e..e1bf8afbb0a2e 100644 --- a/contrib/plugins/bap-tracing/tracewrap.c +++ b/contrib/plugins/bap-tracing/tracewrap.c @@ -1,10 +1,8 @@ #include "tracewrap.h" #include "trace_consts.h" -#include "exec/cpu_ldst.h" #include #include -#include "qemu/log.h" #include #include @@ -14,13 +12,15 @@ #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" char tracer_name[] = "qemu"; char tracer_version[] = "2.0.0/tracewrap"; static Frame * g_frame; static uint64_t frames_per_toc_entry = 64LL; static uint32_t open_frame = 0; -static FILE *file = NULL; +// static FILE *file = NULL; /* don't use the following data directly! use toc_init, toc_update and toc_write functions instead */ @@ -32,6 +32,7 @@ static uint64_t toc_num_frames = 0; #define MD5LEN 16 static guchar target_md5[MD5LEN]; static char target_path[PATH_MAX] = "unknown"; +#pragma GCC diagnostic pop #define WRITE(x) do { \ @@ -54,172 +55,172 @@ static char target_path[PATH_MAX] = "unknown"; } while(0) -static void toc_init(void) { - if (toc_entries != 0) - err(1, "qemu_trace was initialized twice"); - toc = g_new(uint64_t, 1024); - toc_capacity = 1024; - toc_entries = 0; -} - -static void toc_append(uint64_t entry) { - if (toc_capacity <= toc_entries) { - toc = g_renew(uint64_t, toc, toc_capacity * 2); - toc_capacity *= 2; - } - toc[toc_entries++] = entry; -} - -static void toc_write(void) { - int64_t toc_offset = ftell(file); - if (toc_offset > 0) { - int i = 0; - WRITE(frames_per_toc_entry); - for (i = 0; i < toc_entries; i++) - WRITE(toc[i]); - SEEK(num_trace_frames_offset); - WRITE(toc_num_frames); - SEEK(toc_offset_offset); - WRITE(toc_offset); - } -} - -static void toc_update(void) { - toc_num_frames++; - if (toc_num_frames % frames_per_toc_entry == 0) { - int64_t off = ftell(file); - if (off >= 0) toc_append(off); - } -} - -static void write_header(void) { - uint64_t toc_off = 0L; - WRITE(magic_number); - WRITE(out_trace_version); - WRITE(frame_arch); - WRITE(frame_mach); - WRITE(toc_num_frames); - WRITE(toc_off); -} - -static int list_length(char **list) { - int n=0; - if (list) { - char **p = list; - for (;*p;p++,n++); - } - return n; -} - -static void compute_target_md5(void) { - const GChecksumType md5 = G_CHECKSUM_MD5; - GChecksum *cs = g_checksum_new(md5); - FILE *target = fopen(target_path, "r"); - guchar buf[BUFSIZ]; - gsize expected_length = MD5LEN; - - if (!cs) err(1, "failed to create a checksum"); - if (!target) err(1, "failed to open target binary"); - if (g_checksum_type_get_length(md5) != expected_length) abort(); - - while (!feof(target)) { - size_t len = fread(buf,1,BUFSIZ,target); - if (ferror(target)) - err(1, "failed to read target binary"); - g_checksum_update(cs, buf, len); - } - - g_checksum_get_digest(cs, target_md5, &expected_length); - fclose(target); -} - -static void store_to_trace(ProtobufCBuffer *self, size_t len, const uint8_t *data) { - WRITE_BUF(data,len); -} - -static void init_tracer(Tracer *tracer, char **argv, char **envp) { - tracer__init(tracer); - tracer->name = tracer_name; - tracer->n_args = list_length(argv); - tracer->args = argv; - tracer->n_envp = list_length(envp); - tracer->envp = envp; - tracer->version = tracer_version; -} - -static void init_target(Target *target, char **argv, char **envp) { - compute_target_md5(); - - target__init(target); - target->path = target_path; - target->n_args = list_length(argv); - target->args = argv; - target->n_envp = list_length(envp); - target->envp = envp; - target->md5sum.len = MD5LEN; - target->md5sum.data = target_md5; -} - -#ifdef G_OS_UNIX -static void unix_fill_fstats(Fstats *fstats, char *path) { - struct stat stats; - if (stat(path, &stats) < 0) - err(1, "failed to obtain file stats"); - - fstats->size = stats.st_size; - fstats->atime = stats.st_atime; - fstats->mtime = stats.st_mtime; - fstats->ctime = stats.st_ctime; -} -#endif - - -static void init_fstats(Fstats *fstats) { - fstats__init(fstats); -#ifdef G_OS_UNIX - unix_fill_fstats(fstats, target_path); -#endif -} - - -static void write_meta( - char **tracer_argv, - char **tracer_envp, - char **target_argv, - char **target_envp) -{ - MetaFrame meta; - Tracer tracer; - Target target; - Fstats fstats; - ProtobufCBuffer buffer; - - buffer.append = store_to_trace; - - - meta_frame__init(&meta); - init_tracer(&tracer, tracer_argv, tracer_envp); - init_target(&target, target_argv, target_envp); - init_fstats(&fstats); - - meta.tracer = &tracer; - meta.target = ⌖ - meta.fstats = &fstats; - meta.time = time(NULL); - char *user = g_strdup(g_get_real_name()); - meta.user = user; - - char *host = g_strdup(g_get_host_name()); - meta.host = host; - - uint64_t size = meta_frame__get_packed_size(&meta); - WRITE(size); - - meta_frame__pack_to_buffer(&meta, &buffer); - - free(user); - free(host); -} +// static void toc_init(void) { +// if (toc_entries != 0) +// err(1, "qemu_trace was initialized twice"); +// toc = g_new(uint64_t, 1024); +// toc_capacity = 1024; +// toc_entries = 0; +// } + +// static void toc_append(uint64_t entry) { +// if (toc_capacity <= toc_entries) { +// toc = g_renew(uint64_t, toc, toc_capacity * 2); +// toc_capacity *= 2; +// } +// toc[toc_entries++] = entry; +// } + +// static void toc_write(void) { +// int64_t toc_offset = ftell(file); +// if (toc_offset > 0) { +// int i = 0; +// WRITE(frames_per_toc_entry); +// for (i = 0; i < toc_entries; i++) +// WRITE(toc[i]); +// SEEK(num_trace_frames_offset); +// WRITE(toc_num_frames); +// SEEK(toc_offset_offset); +// WRITE(toc_offset); +// } +// } + +// static void toc_update(void) { +// toc_num_frames++; +// if (toc_num_frames % frames_per_toc_entry == 0) { +// int64_t off = ftell(file); +// if (off >= 0) toc_append(off); +// } +// } + +// static void write_header(void) { +// uint64_t toc_off = 0L; +// WRITE(magic_number); +// WRITE(out_trace_version); +// WRITE(frame_arch); +// WRITE(frame_mach); +// WRITE(toc_num_frames); +// WRITE(toc_off); +// } + +// static int list_length(char **list) { +// int n=0; +// if (list) { +// char **p = list; +// for (;*p;p++,n++); +// } +// return n; +// } + +// static void compute_target_md5(void) { +// const GChecksumType md5 = G_CHECKSUM_MD5; +// GChecksum *cs = g_checksum_new(md5); +// FILE *target = fopen(target_path, "r"); +// guchar buf[BUFSIZ]; +// gsize expected_length = MD5LEN; + +// if (!cs) err(1, "failed to create a checksum"); +// if (!target) err(1, "failed to open target binary"); +// if (g_checksum_type_get_length(md5) != expected_length) abort(); + +// while (!feof(target)) { +// size_t len = fread(buf,1,BUFSIZ,target); +// if (ferror(target)) +// err(1, "failed to read target binary"); +// g_checksum_update(cs, buf, len); +// } + +// g_checksum_get_digest(cs, target_md5, &expected_length); +// fclose(target); +// } + +// static void store_to_trace(ProtobufCBuffer *self, size_t len, const uint8_t *data) { +// WRITE_BUF(data,len); +// } + +// static void init_tracer(Tracer *tracer, char **argv, char **envp) { +// tracer__init(tracer); +// tracer->name = tracer_name; +// tracer->n_args = list_length(argv); +// tracer->args = argv; +// tracer->n_envp = list_length(envp); +// tracer->envp = envp; +// tracer->version = tracer_version; +// } + +// static void init_target(Target *target, char **argv, char **envp) { +// compute_target_md5(); + +// target__init(target); +// target->path = target_path; +// target->n_args = list_length(argv); +// target->args = argv; +// target->n_envp = list_length(envp); +// target->envp = envp; +// target->md5sum.len = MD5LEN; +// target->md5sum.data = target_md5; +// } + +// #ifdef G_OS_UNIX +// static void unix_fill_fstats(Fstats *fstats, char *path) { +// struct stat stats; +// if (stat(path, &stats) < 0) +// err(1, "failed to obtain file stats"); + +// fstats->size = stats.st_size; +// fstats->atime = stats.st_atime; +// fstats->mtime = stats.st_mtime; +// fstats->ctime = stats.st_ctime; +// } +// #endif + + +// static void init_fstats(Fstats *fstats) { +// fstats__init(fstats); +// #ifdef G_OS_UNIX +// unix_fill_fstats(fstats, target_path); +// #endif +// } + + +// static void write_meta( +// char **tracer_argv, +// char **tracer_envp, +// char **target_argv, +// char **target_envp) +// { +// MetaFrame meta; +// Tracer tracer; +// Target target; +// Fstats fstats; +// ProtobufCBuffer buffer; + +// buffer.append = store_to_trace; + + +// meta_frame__init(&meta); +// init_tracer(&tracer, tracer_argv, tracer_envp); +// init_target(&target, target_argv, target_envp); +// init_fstats(&fstats); + +// meta.tracer = &tracer; +// meta.target = ⌖ +// meta.fstats = &fstats; +// meta.time = time(NULL); +// char *user = g_strdup(g_get_real_name()); +// meta.user = user; + +// char *host = g_strdup(g_get_host_name()); +// meta.host = host; + +// uint64_t size = meta_frame__get_packed_size(&meta); +// WRITE(size); + +// meta_frame__pack_to_buffer(&meta, &buffer); + +// free(user); +// free(host); +// } void qemu_trace_init(const char *filename, @@ -227,134 +228,134 @@ void qemu_trace_init(const char *filename, char **argv, char **envp, char **target_argv, char **target_envp) { - qemu_log("Initializing tracer\n"); - if (realpath(targetname,target_path) == NULL) - err(1, "can't get target path"); - - - char *name = filename - ? g_strdup(filename) - : g_strdup_printf("%s.frames", basename(target_path)); - file = fopen(name, "wb"); - if (file == NULL) - err(1, "tracewrap: can't open trace file %s", name); - write_header(); - write_meta(argv, envp, target_argv, target_envp); - toc_init(); - g_free(name); + // qemu_log("Initializing tracer\n"); + // if (realpath(targetname,target_path) == NULL) + // err(1, "can't get target path"); + + + // char *name = filename + // ? g_strdup(filename) + // : g_strdup_printf("%s.frames", basename(target_path)); + // file = fopen(name, "wb"); + // if (file == NULL) + // err(1, "tracewrap: can't open trace file %s", name); + // write_header(); + // write_meta(argv, envp, target_argv, target_envp); + // toc_init(); + // g_free(name); } -void qemu_trace_newframe(target_ulong addr, int __unused/*thread_id*/ ) { - int thread_id = 1; - if (open_frame) { - qemu_log("frame is still open"); - qemu_trace_endframe(NULL, 0, 0); - } +void qemu_trace_newframe(uint64_t addr, int __unused/*thread_id*/ ) { +// int thread_id = 1; +// if (open_frame) { +// qemu_log("frame is still open"); +// qemu_trace_endframe(NULL, 0, 0); +// } - open_frame = 1; - g_frame = g_new(Frame,1); - frame__init(g_frame); +// open_frame = 1; +// g_frame = g_new(Frame,1); +// frame__init(g_frame); - StdFrame *sframe = g_new(StdFrame, 1); - std_frame__init(sframe); - g_frame->std_frame = sframe; +// StdFrame *sframe = g_new(StdFrame, 1); +// std_frame__init(sframe); +// g_frame->std_frame = sframe; - sframe->address = addr; - sframe->thread_id = thread_id; +// sframe->address = addr; +// sframe->thread_id = thread_id; - OperandValueList *ol_in = g_new(OperandValueList,1); - operand_value_list__init(ol_in); - ol_in->n_elem = 0; - sframe->operand_pre_list = ol_in; +// OperandValueList *ol_in = g_new(OperandValueList,1); +// operand_value_list__init(ol_in); +// ol_in->n_elem = 0; +// sframe->operand_pre_list = ol_in; - OperandValueList *ol_out = g_new(OperandValueList,1); - operand_value_list__init(ol_out); - ol_out->n_elem = 0; - sframe->operand_post_list = ol_out; +// OperandValueList *ol_out = g_new(OperandValueList,1); +// operand_value_list__init(ol_out); +// ol_out->n_elem = 0; +// sframe->operand_post_list = ol_out; } static inline void free_operand(OperandInfo *oi) { - OperandInfoSpecific *ois = oi->operand_info_specific; - - //Free reg-operand - RegOperand *ro = ois->reg_operand; - if (ro && ro->name) - g_free(ro->name); - g_free(ro); - - //Free mem-operand - MemOperand *mo = ois->mem_operand; - g_free(mo); - g_free(oi->value.data); - g_free(oi->taint_info); - g_free(ois); - g_free(oi->operand_usage); - g_free(oi); -} - -void qemu_trace_add_operand(OperandInfo *oi, int inout) { - if (!open_frame) { - if (oi) - free_operand(oi); - return; - } - OperandValueList *ol; - if (inout & 0x1) { - ol = g_frame->std_frame->operand_pre_list; - } else { - ol = g_frame->std_frame->operand_post_list; - } - - oi->taint_info = g_new(TaintInfo, 1); - taint_info__init(oi->taint_info); - oi->taint_info->no_taint = 1; - oi->taint_info->has_no_taint = 1; - - ol->n_elem += 1; - ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); - ol->elem[ol->n_elem - 1] = oi; -} - -void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size) { - int i = 0; - StdFrame *sframe = g_frame->std_frame; - - if (!open_frame) return; - - sframe->rawbytes.len = size; - sframe->rawbytes.data = g_malloc(size); - for (i = 0; i < size; i++) { - sframe->rawbytes.data[i] = cpu_ldub_code(env, pc+i); - } - - size_t msg_size = frame__get_packed_size(g_frame); - uint8_t *packed_buffer = g_alloca(msg_size); - uint64_t packed_size = frame__pack(g_frame, packed_buffer); - WRITE(packed_size); - WRITE_BUF(packed_buffer, packed_size); - toc_update(); - - //counting num_frames in newframe does not work by far ... - //how comes? disas_arm_insn might not always return at the end? - for (i = 0; i < sframe->operand_pre_list->n_elem; i++) - free_operand(sframe->operand_pre_list->elem[i]); - g_free(sframe->operand_pre_list->elem); - g_free(sframe->operand_pre_list); - - for (i = 0; i < sframe->operand_post_list->n_elem; i++) - free_operand(sframe->operand_post_list->elem[i]); - g_free(sframe->operand_post_list->elem); - g_free(sframe->operand_post_list); - - g_free(sframe->rawbytes.data); - g_free(sframe); - g_free(g_frame); - open_frame = 0; +// OperandInfoSpecific *ois = oi->operand_info_specific; + +// //Free reg-operand +// RegOperand *ro = ois->reg_operand; +// if (ro && ro->name) +// g_free(ro->name); +// g_free(ro); + +// //Free mem-operand +// MemOperand *mo = ois->mem_operand; +// g_free(mo); +// g_free(oi->value.data); +// g_free(oi->taint_info); +// g_free(ois); +// g_free(oi->operand_usage); +// g_free(oi); +// } + +// void qemu_trace_add_operand(OperandInfo *oi, int inout) { +// if (!open_frame) { +// if (oi) +// free_operand(oi); +// return; +// } +// OperandValueList *ol; +// if (inout & 0x1) { +// ol = g_frame->std_frame->operand_pre_list; +// } else { +// ol = g_frame->std_frame->operand_post_list; +// } + +// oi->taint_info = g_new(TaintInfo, 1); +// taint_info__init(oi->taint_info); +// oi->taint_info->no_taint = 1; +// oi->taint_info->has_no_taint = 1; + +// ol->n_elem += 1; +// ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); +// ol->elem[ol->n_elem - 1] = oi; +// } + +// void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size) { +// int i = 0; +// StdFrame *sframe = g_frame->std_frame; + +// if (!open_frame) return; + +// sframe->rawbytes.len = size; +// sframe->rawbytes.data = g_malloc(size); +// for (i = 0; i < size; i++) { +// sframe->rawbytes.data[i] = cpu_ldub_code(env, pc+i); +// } + +// size_t msg_size = frame__get_packed_size(g_frame); +// uint8_t *packed_buffer = g_alloca(msg_size); +// uint64_t packed_size = frame__pack(g_frame, packed_buffer); +// WRITE(packed_size); +// WRITE_BUF(packed_buffer, packed_size); +// toc_update(); + +// //counting num_frames in newframe does not work by far ... +// //how comes? disas_arm_insn might not always return at the end? +// for (i = 0; i < sframe->operand_pre_list->n_elem; i++) +// free_operand(sframe->operand_pre_list->elem[i]); +// g_free(sframe->operand_pre_list->elem); +// g_free(sframe->operand_pre_list); + +// for (i = 0; i < sframe->operand_post_list->n_elem; i++) +// free_operand(sframe->operand_post_list->elem[i]); +// g_free(sframe->operand_post_list->elem); +// g_free(sframe->operand_post_list); + +// g_free(sframe->rawbytes.data); +// g_free(sframe); +// g_free(g_frame); +// open_frame = 0; } void qemu_trace_finish(uint32_t exit_code) { - toc_write(); - if (fclose(file) != 0) - err(1,"failed to write trace file, the file maybe corrupted"); + // toc_write(); + // if (fclose(file) != 0) + // err(1,"failed to write trace file, the file maybe corrupted"); } diff --git a/contrib/plugins/bap-tracing/tracewrap.h b/contrib/plugins/bap-tracing/tracewrap.h index 1539cd118d952..c52d17bbf04df 100644 --- a/contrib/plugins/bap-tracing/tracewrap.h +++ b/contrib/plugins/bap-tracing/tracewrap.h @@ -1,13 +1,8 @@ #pragma once #include -#include -#include -#include "qemu/osdep.h" -#include "cpu.h" - -#include "frame.piqi.pb-c.h" +#include "frame.piqi.pb-c-patched.h" /** initializes trace subsystem. @@ -37,13 +32,13 @@ void qemu_trace_init(const char *filename, const char *targetname, char **argv, char **envp, char **target_argv, char **target_envp); -void qemu_trace_newframe(target_ulong addr, int tread_id); +void qemu_trace_newframe(uint64_t addr, int tread_id); void qemu_trace_add_operand(OperandInfo *oi, int inout); -void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size); +void qemu_trace_endframe(void *env, uint64_t pc, uint64_t size); void qemu_trace_finish(uint32_t exit_code); -OperandInfo * load_store_reg(target_ulong reg, target_ulong val, int ls); -OperandInfo * load_store_mem(target_ulong addr, target_ulong val, int ls, int len); +OperandInfo * load_store_reg(uint64_t reg, uint64_t val, int ls); +OperandInfo * load_store_mem(uint64_t addr, uint64_t val, int ls, int len); #define REG_EFLAGS 66 #define REG_LO 33 diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 1a60259adc909..1285545fa325c 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -19,6 +19,8 @@ typedef struct { static TraceState state; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" static VCPU *get_vcpu(TraceState *state, int vcpu_index) { VCPU *c; g_rw_lock_reader_lock(&state->vcpus_array_lock); @@ -27,6 +29,7 @@ static VCPU *get_vcpu(TraceState *state, int vcpu_index) { return c; } +#pragma GCC diagnostic pop static void log_insn_frame(unsigned int cpu_index, void *udata) { // VCPU *vcpu = get_vcpu(state, cpu_index); diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index fa8a426c8b594..3001f1d8f0e35 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -7,6 +7,8 @@ endif t = [] if get_option('plugins') + subdir('bap-tracing') + foreach i : contrib_plugins if host_os == 'windows' t += shared_module(i, files(i + '.c') + 'win32_linker.c', From 48e0f40b9814df0aa6017334986e1f6fef722545 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 22 Jun 2025 12:18:00 -0500 Subject: [PATCH 07/59] Add workflow to test the plugin build. --- .github/workflows/build.yaml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000000000..374e16efcdf13 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,36 @@ +name: Build + +on: [pull_request, workflow_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.x + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install deps + run: | + sudo apt-get -y update + sudo apt-get install -y git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build autoconf libtool protobuf-c-compiler libprotobuf-c-dev + - name: Install piqi + run: | + curl -OL https://raw.github.com/alavrik/piqi-binary/master/Linux-x86_64/piqi + chmod +x piqi + sudo mv piqi /usr/local/bin + piqi --version + - name: Checkout qemu + uses: actions/checkout@v4 + with: + repository: BinaryAnalysisPlatform/qemu + path: qemu + submodules: true + - name: Build for Targets + run: | + cd qemu + mkdir build + cd build + ../configure --enable-plugins --target-list=sparc-linux-user,sparc64-linux-user + ninja From e3a5dc898664b24f9def6e81473cea2aea5fb03f Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 07:52:21 -0500 Subject: [PATCH 08/59] Move defs into header file and add Frame struct. --- contrib/plugins/bap-tracing/frame.c | 24 +++++++++++ contrib/plugins/bap-tracing/tracing.c | 16 +------- contrib/plugins/bap-tracing/tracing.h | 58 +++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 contrib/plugins/bap-tracing/frame.c create mode 100644 contrib/plugins/bap-tracing/tracing.h diff --git a/contrib/plugins/bap-tracing/frame.c b/contrib/plugins/bap-tracing/frame.c new file mode 100644 index 0000000000000..bebf6f90e0be3 --- /dev/null +++ b/contrib/plugins/bap-tracing/frame.c @@ -0,0 +1,24 @@ +#include "tracing.h" + +Frame *frame_new_std(uint64_t addr, int vcpu_id) { + Frame *frame = g_new(Frame, 1); + frame__init(frame); + + StdFrame *sframe = g_new(StdFrame, 1); + std_frame__init(sframe); + frame->std_frame = sframe; + + sframe->address = addr; + sframe->thread_id = vcpu_id; + + OperandValueList *ol_in = g_new(OperandValueList, 1); + operand_value_list__init(ol_in); + ol_in->n_elem = 0; + sframe->operand_pre_list = ol_in; + + OperandValueList *ol_out = g_new(OperandValueList, 1); + operand_value_list__init(ol_out); + ol_out->n_elem = 0; + sframe->operand_post_list = ol_out; + return frame; +} diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 1285545fa325c..c0f295d80e732 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -1,21 +1,9 @@ // SPDX-FileCopyrightText: 2025 Rot127 // SPDX-License-Identifier: GPL-2.0-only -#include +#include -QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; - -typedef struct { - // Current instruction related things. -} VCPU; - -typedef struct { - GRWLock vcpus_array_lock; - GArray *vcpus; - - GRWLock frame_buffer_lock; - GPtrArray *frame_buffer; -} TraceState; +#include "tracing.h" static TraceState state; diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h new file mode 100644 index 0000000000000..3a78a756b086c --- /dev/null +++ b/contrib/plugins/bap-tracing/tracing.h @@ -0,0 +1,58 @@ +#ifndef BAP_TRACING_H +#define BAP_TRACING_H + +#include +#include + +#include "tracewrap.h" + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +#define FRAME_BUFFER_SIZE 1024 + +typedef struct { + Frame **fbuf; + size_t len; +} FrameBuffer; + +typedef struct { + // Current instruction related things. +} VCPU; + +typedef struct { + GRWLock vcpus_array_lock; + GArray *vcpus; + + GRWLock frame_buffer_lock; + FrameBuffer *frame_buffer; + + GRWLock file_lock; + FILE *file; +} TraceState; + +VCPU *vcpu_new(void); + +/** + * \brief Initializes a frame buffer with space for \p size frames. + * Returns the buffer or NULL in case of failure. + */ +FrameBuffer *frame_buffer_init(size_t size); + +/** + * \brief Push a frame into the buffer. + * Returns true on success. False otherwise. + */ +bool frame_buffer_push(FrameBuffer *buf, Frame *frame); + +/** + * \brief Flusehs the buffer and returns it's content. + * The size of the returned buffer is written to \p fbuf_size. + */ +Frame **frame_buffer_flush(FrameBuffer *buf, size_t *fbuf_size); + +/** + * \brief Create new std frame + */ +Frame *frame_new_std(uint64_t addr, int vcpu_id); + +#endif From 7054ef80b5401ebb189689576353528c21617f08 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 07:53:45 -0500 Subject: [PATCH 09/59] Imlpement plugin setup, vcpu init and locks for insn logging. --- contrib/plugins/bap-tracing/tracing.c | 44 +++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c0f295d80e732..c52cbf773b63c 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -7,30 +7,26 @@ static TraceState state; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -static VCPU *get_vcpu(TraceState *state, int vcpu_index) { - VCPU *c; - g_rw_lock_reader_lock(&state->vcpus_array_lock); - c = &g_array_index(state->vcpus, VCPU, vcpu_index); - g_rw_lock_reader_unlock(&state->vcpus_array_lock); - - return c; -} -#pragma GCC diagnostic pop - -static void log_insn_frame(unsigned int cpu_index, void *udata) { - // VCPU *vcpu = get_vcpu(state, cpu_index); +static void log_insn_exec(unsigned int vcpu_index, void *udata) { + g_rw_lock_reader_lock(&state.vcpus_array_lock); + // VCPU *c = &g_array_index(state.vcpus, VCPU, vcpu_index); + g_rw_lock_writer_lock(&state.frame_buffer_lock); // Add change to previous frame // Finish previous frame // Check if buffer should be dumped to file. // Open new one. + g_rw_lock_writer_unlock(&state.frame_buffer_lock); + g_rw_lock_reader_unlock(&state.vcpus_array_lock); + return; } static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { - // Add new vcpu + g_rw_lock_writer_lock(&state.vcpus_array_lock); + VCPU *vcpu = calloc(sizeof(VCPU), 1); + g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); + g_rw_lock_writer_unlock(&state.vcpus_array_lock); } static void plugin_exit(qemu_plugin_id_t id, void *udata) { @@ -43,23 +39,25 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n_insns = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < n_insns; i++) { insn = qemu_plugin_tb_get_insn(tb, i); - qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_frame, - QEMU_PLUGIN_CB_R_REGS, &state); + qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_exec, + QEMU_PLUGIN_CB_R_REGS, NULL); } } QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + const char *target_path = "/tmp/test.trace"; + state.frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE); + state.vcpus = g_array_new(false, true, sizeof(VCPU)); + state.file = fopen(target_path, "r"); + if (!(state.frame_buffer || state.vcpus || state.file)) { + return 1; + } + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_vcpu_tb_trans_cb(id, cb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); - // Get reg names - // qemu_plugin_get_registers - // - // Logging - // qemu_plugin_outs - return 0; } From b820d3845a88407c6f9e2af38f89a7ab8d0ccba3 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 10:51:27 -0500 Subject: [PATCH 10/59] Init registers for VCPU --- contrib/plugins/bap-tracing/tracing.c | 38 +++++++++++++++++++++++++-- contrib/plugins/bap-tracing/tracing.h | 10 +++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c52cbf773b63c..c31a2a9213794 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -7,7 +7,7 @@ static TraceState state; -static void log_insn_exec(unsigned int vcpu_index, void *udata) { +static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); // VCPU *c = &g_array_index(state.vcpus, VCPU, vcpu_index); @@ -22,9 +22,43 @@ static void log_insn_exec(unsigned int vcpu_index, void *udata) { return; } +static Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) +{ + Register *reg = g_new0(Register, 1); + g_autofree gchar *lower = g_utf8_strdown(desc->name, -1); + int r; + + reg->handle = desc->handle; + reg->name = g_intern_string(lower); + reg->content = g_byte_array_new(); + + /* read the initial value */ + r = qemu_plugin_read_register(reg->handle, reg->content); + g_assert(r > 0); + return reg; +} + +static GPtrArray *registers_init(int vcpu_index) { + g_autoptr(GPtrArray) registers = g_ptr_array_new(); + g_autoptr(GArray) reg_list = qemu_plugin_get_registers(); + + if (!reg_list->len) { + return NULL; + } + for (int r = 0; r < reg_list->len; r++) { + qemu_plugin_reg_descriptor *rd = + &g_array_index(reg_list, qemu_plugin_reg_descriptor, r); + Register *reg = init_vcpu_register(rd); + g_ptr_array_add(registers, reg); + } + + return registers->len ? g_steal_pointer(®isters) : NULL; +} + static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.vcpus_array_lock); VCPU *vcpu = calloc(sizeof(VCPU), 1); + vcpu->registers = registers_init(vcpu_index); g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); g_rw_lock_writer_unlock(&state.vcpus_array_lock); } @@ -39,7 +73,7 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n_insns = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < n_insns; i++) { insn = qemu_plugin_tb_get_insn(tb, i); - qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_exec, + qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_reg_access, QEMU_PLUGIN_CB_R_REGS, NULL); } } diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 3a78a756b086c..400b791fe64e8 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -16,12 +16,18 @@ typedef struct { } FrameBuffer; typedef struct { - // Current instruction related things. + struct qemu_plugin_register *handle; ///< Passed to qemu API. + GByteArray *content; + const char *name; +} Register; + +typedef struct { + GPtrArray /**/ *registers; } VCPU; typedef struct { GRWLock vcpus_array_lock; - GArray *vcpus; + GArray /**/ *vcpus; GRWLock frame_buffer_lock; FrameBuffer *frame_buffer; From 531c8eb695058abab3c6c0d6d085fdb3046b9a29 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 10:52:11 -0500 Subject: [PATCH 11/59] Add mem access callback --- contrib/plugins/bap-tracing/tracing.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c31a2a9213794..40c898d8338a3 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -7,6 +7,10 @@ static TraceState state; +static void log_insn_mem_access(unsigned int vcpu_index, + qemu_plugin_meminfo_t info, uint64_t vaddr, + void *userdata) {} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); // VCPU *c = &g_array_index(state.vcpus, VCPU, vcpu_index); @@ -75,6 +79,9 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { insn = qemu_plugin_tb_get_insn(tb, i); qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_reg_access, QEMU_PLUGIN_CB_R_REGS, NULL); + qemu_plugin_register_vcpu_mem_cb(insn, log_insn_mem_access, + QEMU_PLUGIN_CB_R_REGS, QEMU_PLUGIN_MEM_R, + NULL); } } From 036ef5e79218d371c1e6e8579f3e0436283d09da Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 10:52:25 -0500 Subject: [PATCH 12/59] Rename buffer size constant --- contrib/plugins/bap-tracing/tracing.c | 2 +- contrib/plugins/bap-tracing/tracing.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 40c898d8338a3..68930b41c0bc9 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -89,7 +89,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { const char *target_path = "/tmp/test.trace"; - state.frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE); + state.frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE_DEFAULT); state.vcpus = g_array_new(false, true, sizeof(VCPU)); state.file = fopen(target_path, "r"); if (!(state.frame_buffer || state.vcpus || state.file)) { diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 400b791fe64e8..d7ce56e0dd43b 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -8,7 +8,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -#define FRAME_BUFFER_SIZE 1024 +#define FRAME_BUFFER_SIZE_DEFAULT 1024 typedef struct { Frame **fbuf; From 4b3a908700cdbfd84882213674ba1389486b7fe6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 10:52:54 -0500 Subject: [PATCH 13/59] Add operand append function for frames. --- contrib/plugins/bap-tracing/frame.c | 18 ++++++++++++++++++ contrib/plugins/bap-tracing/tracing.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/contrib/plugins/bap-tracing/frame.c b/contrib/plugins/bap-tracing/frame.c index bebf6f90e0be3..ff1ba105dcfc1 100644 --- a/contrib/plugins/bap-tracing/frame.c +++ b/contrib/plugins/bap-tracing/frame.c @@ -22,3 +22,21 @@ Frame *frame_new_std(uint64_t addr, int vcpu_id) { sframe->operand_post_list = ol_out; return frame; } + +void frame_add_operand(Frame *frame, OperandInfo *oi, bool is_post) { + OperandValueList *ol; + if (is_post) { + ol = frame->std_frame->operand_post_list; + } else { + ol = frame->std_frame->operand_pre_list; + } + + oi->taint_info = g_new(TaintInfo, 1); + taint_info__init(oi->taint_info); + oi->taint_info->no_taint = 1; + oi->taint_info->has_no_taint = 1; + + ol->n_elem += 1; + ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); + ol->elem[ol->n_elem - 1] = oi; +} diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index d7ce56e0dd43b..367b222eed712 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -61,4 +61,6 @@ Frame **frame_buffer_flush(FrameBuffer *buf, size_t *fbuf_size); */ Frame *frame_new_std(uint64_t addr, int vcpu_id); +void frame_add_operand(Frame *frame, OperandInfo *oi, bool is_out); + #endif From e1f7bdc3189c70a4c2a9f19da769423c1e6073d4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 12:31:26 -0500 Subject: [PATCH 14/59] Add an instruction object --- contrib/plugins/bap-tracing/tracing.c | 52 ++++++++++++++++----------- contrib/plugins/bap-tracing/tracing.h | 20 +++++++++-- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 68930b41c0bc9..c23a847202dc2 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -12,10 +12,14 @@ static void log_insn_mem_access(unsigned int vcpu_index, void *userdata) {} static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { + Instruction *insn = udata; g_rw_lock_reader_lock(&state.vcpus_array_lock); - // VCPU *c = &g_array_index(state.vcpus, VCPU, vcpu_index); - g_rw_lock_writer_lock(&state.frame_buffer_lock); + + VCPU *vcpu = &g_array_index(state.vcpus, VCPU, vcpu_index); + GArray *current_regs = qemu_plugin_get_registers(); + g_assert(current_regs->len == vcpu->registers->len); + // Add change to previous frame // Finish previous frame // Check if buffer should be dumped to file. @@ -26,20 +30,19 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { return; } -static Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) -{ - Register *reg = g_new0(Register, 1); - g_autofree gchar *lower = g_utf8_strdown(desc->name, -1); - int r; +Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) { + Register *reg = g_new0(Register, 1); + g_autofree gchar *lower = g_utf8_strdown(desc->name, -1); + int r; - reg->handle = desc->handle; - reg->name = g_intern_string(lower); - reg->content = g_byte_array_new(); + reg->handle = desc->handle; + reg->name = g_intern_string(lower); + reg->content = g_byte_array_new(); - /* read the initial value */ - r = qemu_plugin_read_register(reg->handle, reg->content); - g_assert(r > 0); - return reg; + /* read the initial value */ + r = qemu_plugin_read_register(reg->handle, reg->content); + g_assert(r > 0); + return reg; } static GPtrArray *registers_init(int vcpu_index) { @@ -61,7 +64,7 @@ static GPtrArray *registers_init(int vcpu_index) { static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.vcpus_array_lock); - VCPU *vcpu = calloc(sizeof(VCPU), 1); + VCPU *vcpu = g_malloc0(sizeof(VCPU)); vcpu->registers = registers_init(vcpu_index); g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); g_rw_lock_writer_unlock(&state.vcpus_array_lock); @@ -71,15 +74,24 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { // Dump rest of frames to file. } +Instruction *init_insn(struct qemu_plugin_insn *tb_insn) { + Instruction *insn = g_malloc0(sizeof(Instruction)); + qemu_plugin_insn_data(tb_insn, &insn->bytes, sizeof(insn->bytes)); + insn->size = qemu_plugin_insn_size(tb_insn); + insn->vaddr = qemu_plugin_insn_vaddr(tb_insn); + return insn; +} + static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { // Add a callback for each instruction in every translated block. - struct qemu_plugin_insn *insn; + struct qemu_plugin_insn *tb_insn; size_t n_insns = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < n_insns; i++) { - insn = qemu_plugin_tb_get_insn(tb, i); - qemu_plugin_register_vcpu_insn_exec_cb(insn, log_insn_reg_access, - QEMU_PLUGIN_CB_R_REGS, NULL); - qemu_plugin_register_vcpu_mem_cb(insn, log_insn_mem_access, + tb_insn = qemu_plugin_tb_get_insn(tb, i); + Instruction *insn_data = init_insn(tb_insn); + qemu_plugin_register_vcpu_insn_exec_cb(tb_insn, log_insn_reg_access, + QEMU_PLUGIN_CB_R_REGS, insn_data); + qemu_plugin_register_vcpu_mem_cb(tb_insn, log_insn_mem_access, QEMU_PLUGIN_CB_R_REGS, QEMU_PLUGIN_MEM_R, NULL); } diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 367b222eed712..872214ba50772 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -10,15 +10,26 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; #define FRAME_BUFFER_SIZE_DEFAULT 1024 +/** + * \brief VLIW architecture have instructions longer than 4 or 8bytes. + */ +#define MAX_INSTRUCTION_SIZE 64 + typedef struct { Frame **fbuf; size_t len; } FrameBuffer; typedef struct { - struct qemu_plugin_register *handle; ///< Passed to qemu API. - GByteArray *content; - const char *name; + uint8_t bytes[MAX_INSTRUCTION_SIZE]; ///< Instruction bytes. + size_t size; ///< Len of instruction in bytes. + uint64_t vaddr; +} Instruction; + +typedef struct { + struct qemu_plugin_register *handle; ///< Passed to qemu API. + GByteArray *content; + const char *name; } Register; typedef struct { @@ -63,4 +74,7 @@ Frame *frame_new_std(uint64_t addr, int vcpu_id); void frame_add_operand(Frame *frame, OperandInfo *oi, bool is_out); +Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc); +Instruction *init_insn(struct qemu_plugin_insn *insn); + #endif From 0c058f3da05444576aa47f65019bf43271c4090b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 23 Jun 2025 13:17:03 -0500 Subject: [PATCH 15/59] Implement update of register operands. --- contrib/plugins/bap-tracing/frame.c | 4 +- contrib/plugins/bap-tracing/tracing.c | 71 ++++++++++++++++++++++++--- contrib/plugins/bap-tracing/tracing.h | 19 +++++-- 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame.c b/contrib/plugins/bap-tracing/frame.c index ff1ba105dcfc1..37dea8d146af4 100644 --- a/contrib/plugins/bap-tracing/frame.c +++ b/contrib/plugins/bap-tracing/frame.c @@ -23,9 +23,9 @@ Frame *frame_new_std(uint64_t addr, int vcpu_id) { return frame; } -void frame_add_operand(Frame *frame, OperandInfo *oi, bool is_post) { +void frame_add_operand(Frame *frame, OperandInfo *oi) { OperandValueList *ol; - if (is_post) { + if (oi->operand_usage->written) { ol = frame->std_frame->operand_post_list; } else { ol = frame->std_frame->operand_pre_list; diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c23a847202dc2..e740e27a3fcfb 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -11,20 +11,43 @@ static void log_insn_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *userdata) {} +static void add_post_state_regs(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs) { + GByteArray *rtmp = g_byte_array_new(); + for (size_t i = 0; i < current_regs->len; ++i) { + Register *prev_reg = vcpu->registers->pdata[i]; + + qemu_plugin_reg_descriptor *reg = + &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); + int s = qemu_plugin_read_register(reg->handle, rtmp); + assert(s == prev_reg->content->len); + if (!memcmp(rtmp->data, prev_reg->content->data, s)) { + // No change + continue; + } + + OperandInfo *rinfo = init_reg_operand_info(prev_reg->name, rtmp->data, + rtmp->len, OperandRead); + g_assert(rinfo); + + g_rw_lock_writer_lock(&state.frame_buffer_lock); + FrameBuffer *fb = g_ptr_array_index(state.frame_buffer, vcpu_index); + frame_buffer_append_op_info(fb, rinfo); + g_rw_lock_writer_unlock(&state.frame_buffer_lock); + } +} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { - Instruction *insn = udata; g_rw_lock_reader_lock(&state.vcpus_array_lock); - g_rw_lock_writer_lock(&state.frame_buffer_lock); VCPU *vcpu = &g_array_index(state.vcpus, VCPU, vcpu_index); GArray *current_regs = qemu_plugin_get_registers(); g_assert(current_regs->len == vcpu->registers->len); - // Add change to previous frame - // Finish previous frame + add_post_state_regs(vcpu, vcpu_index, current_regs); // Check if buffer should be dumped to file. + // Open new one. - g_rw_lock_writer_unlock(&state.frame_buffer_lock); + Instruction *insn = udata; g_rw_lock_reader_unlock(&state.vcpus_array_lock); return; @@ -64,14 +87,42 @@ static GPtrArray *registers_init(int vcpu_index) { static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.vcpus_array_lock); + g_rw_lock_writer_lock(&state.frame_buffer_lock); + VCPU *vcpu = g_malloc0(sizeof(VCPU)); vcpu->registers = registers_init(vcpu_index); g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); + FrameBuffer *vcpu_frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE_DEFAULT); + g_ptr_array_insert(state.frame_buffer, vcpu_index, &vcpu_frame_buffer); + + g_rw_lock_writer_unlock(&state.frame_buffer_lock); g_rw_lock_writer_unlock(&state.vcpus_array_lock); } -static void plugin_exit(qemu_plugin_id_t id, void *udata) { - // Dump rest of frames to file. +OperandInfo *init_reg_operand_info(const char *name, const uint8_t *value, + size_t value_size, OperandAccess access) { + RegOperand *ro = g_new(RegOperand, 1); + reg_operand__init(ro); + ro->name = strdup(name); + + OperandInfoSpecific *ois = g_new(OperandInfoSpecific, 1); + operand_info_specific__init(ois); + ois->reg_operand = ro; + + OperandUsage *ou = g_new(OperandUsage, 1); + operand_usage__init(ou); + ou->read = access & OperandRead; + ou->written = access & OperandWritten; + OperandInfo *oi = g_new(OperandInfo, 1); + operand_info__init(oi); + oi->bit_length = 0; + oi->operand_info_specific = ois; + oi->operand_usage = ou; + oi->value.len = value_size; + oi->value.data = g_malloc(oi->value.len); + memcpy(oi->value.data, value, value_size); + + return oi; } Instruction *init_insn(struct qemu_plugin_insn *tb_insn) { @@ -97,11 +148,15 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { } } +static void plugin_exit(qemu_plugin_id_t id, void *udata) { + // Dump rest of frames to file. +} + QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { const char *target_path = "/tmp/test.trace"; - state.frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE_DEFAULT); + state.frame_buffer = g_ptr_array_new(); state.vcpus = g_array_new(false, true, sizeof(VCPU)); state.file = fopen(target_path, "r"); if (!(state.frame_buffer || state.vcpus || state.file)) { diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 872214ba50772..e20faf5888653 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -4,12 +4,19 @@ #include #include +#include "frame.piqi.pb-c-patched.h" +#include "glib.h" #include "tracewrap.h" QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; #define FRAME_BUFFER_SIZE_DEFAULT 1024 +typedef enum { + OperandRead = 1, + OperandWritten = 2, +} OperandAccess; + /** * \brief VLIW architecture have instructions longer than 4 or 8bytes. */ @@ -41,7 +48,7 @@ typedef struct { GArray /**/ *vcpus; GRWLock frame_buffer_lock; - FrameBuffer *frame_buffer; + GPtrArray /**/ *frame_buffer; ///< Indexed by vcpu id GRWLock file_lock; FILE *file; @@ -62,19 +69,25 @@ FrameBuffer *frame_buffer_init(size_t size); bool frame_buffer_push(FrameBuffer *buf, Frame *frame); /** - * \brief Flusehs the buffer and returns it's content. + * \brief Flushs the buffer and returns it's content. * The size of the returned buffer is written to \p fbuf_size. */ Frame **frame_buffer_flush(FrameBuffer *buf, size_t *fbuf_size); +void frame_buffer_new_frame(FrameBuffer *buf); +void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); + /** * \brief Create new std frame */ Frame *frame_new_std(uint64_t addr, int vcpu_id); -void frame_add_operand(Frame *frame, OperandInfo *oi, bool is_out); +void frame_add_operand(Frame *frame, OperandInfo *oi); Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc); Instruction *init_insn(struct qemu_plugin_insn *insn); +OperandInfo *init_reg_operand_info(const char *name, const uint8_t *value, + size_t value_size, OperandAccess access); + #endif From dbbeb5c3a0a0f2eafcabab94d72859ea25383317 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 07:14:28 -0500 Subject: [PATCH 16/59] Handle frame buffer flushing --- contrib/plugins/bap-tracing/tracing.c | 10 +++++++++- contrib/plugins/bap-tracing/tracing.h | 7 ++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index e740e27a3fcfb..ec0ff5f720d92 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -44,7 +44,15 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_assert(current_regs->len == vcpu->registers->len); add_post_state_regs(vcpu, vcpu_index, current_regs); - // Check if buffer should be dumped to file. + + g_rw_lock_writer_lock(&state.frame_buffer_lock); + g_rw_lock_writer_lock(&state.file_lock); + FrameBuffer *vcpu_buf = g_ptr_array_index(state.frame_buffer, vcpu_index); + if (frame_buffer_is_full(vcpu_buf)) { + frame_buffer_flush_to_file(vcpu_buf, state.file); + } + g_rw_lock_writer_unlock(&state.file_lock); + g_rw_lock_writer_unlock(&state.frame_buffer_lock); // Open new one. Instruction *insn = udata; diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index e20faf5888653..5b2488681562d 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -68,11 +68,8 @@ FrameBuffer *frame_buffer_init(size_t size); */ bool frame_buffer_push(FrameBuffer *buf, Frame *frame); -/** - * \brief Flushs the buffer and returns it's content. - * The size of the returned buffer is written to \p fbuf_size. - */ -Frame **frame_buffer_flush(FrameBuffer *buf, size_t *fbuf_size); +void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); +bool frame_buffer_is_full(const FrameBuffer *buf); void frame_buffer_new_frame(FrameBuffer *buf); void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); From 87407fe2ea9b0c77c9efcffff25ba0e967bf1f64 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 07:43:11 -0500 Subject: [PATCH 17/59] Add pre register states and some clean up --- contrib/plugins/bap-tracing/tracing.c | 68 +++++++++++++++++++++------ contrib/plugins/bap-tracing/tracing.h | 2 +- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index ec0ff5f720d92..24dbe595c5b88 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -11,7 +11,8 @@ static void log_insn_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *userdata) {} -static void add_post_state_regs(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs) { +static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, + GArray *current_regs, FrameBuffer *fbuf) { GByteArray *rtmp = g_byte_array_new(); for (size_t i = 0; i < current_regs->len; ++i) { Register *prev_reg = vcpu->registers->pdata[i]; @@ -26,36 +27,75 @@ static void add_post_state_regs(VCPU *vcpu, unsigned int vcpu_index, GArray *cur } OperandInfo *rinfo = init_reg_operand_info(prev_reg->name, rtmp->data, - rtmp->len, OperandRead); + rtmp->len, OperandWritten); g_assert(rinfo); + frame_buffer_append_op_info(fbuf, rinfo); + } +} - g_rw_lock_writer_lock(&state.frame_buffer_lock); - FrameBuffer *fb = g_ptr_array_index(state.frame_buffer, vcpu_index); - frame_buffer_append_op_info(fb, rinfo); - g_rw_lock_writer_unlock(&state.frame_buffer_lock); +static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, + GArray *current_regs, FrameBuffer *fbuf) { + GByteArray *rtmp = g_byte_array_new(); + for (size_t i = 0; i < current_regs->len; ++i) { + qemu_plugin_reg_descriptor *reg = + &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); + qemu_plugin_read_register(reg->handle, rtmp); + OperandInfo *rinfo = init_reg_operand_info(reg->name, rtmp->data, + rtmp->len, OperandRead); + g_assert(rinfo); + frame_buffer_append_op_info(fbuf, rinfo); } } +static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, + FrameBuffer *fbuf, Instruction *insn) { + Frame *frame = frame_buffer_new_frame(fbuf); + frame__init(frame); + + StdFrame *sframe = g_new(StdFrame, 1); + std_frame__init(sframe); + frame->std_frame = sframe; + + sframe->thread_id = vcpu_index; + sframe->address = insn->vaddr; + sframe->rawbytes.len = insn->size; + sframe->rawbytes.data = g_malloc(insn->size); + memcpy(sframe->rawbytes.data, insn->bytes, insn->size); + + OperandValueList *ol_in = g_new(OperandValueList, 1); + operand_value_list__init(ol_in); + ol_in->n_elem = 0; + sframe->operand_pre_list = ol_in; + + OperandValueList *ol_out = g_new(OperandValueList, 1); + operand_value_list__init(ol_out); + ol_out->n_elem = 0; + sframe->operand_post_list = ol_out; +} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); + g_rw_lock_writer_lock(&state.frame_buffer_lock); + g_rw_lock_writer_lock(&state.file_lock); + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); VCPU *vcpu = &g_array_index(state.vcpus, VCPU, vcpu_index); GArray *current_regs = qemu_plugin_get_registers(); g_assert(current_regs->len == vcpu->registers->len); - add_post_state_regs(vcpu, vcpu_index, current_regs); + add_post_reg_state(vcpu, vcpu_index, current_regs, fbuf); - g_rw_lock_writer_lock(&state.frame_buffer_lock); - g_rw_lock_writer_lock(&state.file_lock); - FrameBuffer *vcpu_buf = g_ptr_array_index(state.frame_buffer, vcpu_index); - if (frame_buffer_is_full(vcpu_buf)) { - frame_buffer_flush_to_file(vcpu_buf, state.file); + if (frame_buffer_is_full(fbuf)) { + frame_buffer_flush_to_file(fbuf, state.file); } - g_rw_lock_writer_unlock(&state.file_lock); - g_rw_lock_writer_unlock(&state.frame_buffer_lock); // Open new one. Instruction *insn = udata; + add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); + add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); + + g_rw_lock_writer_unlock(&state.file_lock); + g_rw_lock_writer_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); return; diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 5b2488681562d..0f3628761c300 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -71,7 +71,7 @@ bool frame_buffer_push(FrameBuffer *buf, Frame *frame); void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); -void frame_buffer_new_frame(FrameBuffer *buf); +Frame *frame_buffer_new_frame(FrameBuffer *buf); void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); /** From 81a4ebcd0965f3ebdb93a62a764f5b172a7fc16a Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 08:06:13 -0500 Subject: [PATCH 18/59] Add bit length --- contrib/plugins/bap-tracing/tracing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 24dbe595c5b88..18d6c329a1564 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -163,7 +163,7 @@ OperandInfo *init_reg_operand_info(const char *name, const uint8_t *value, ou->written = access & OperandWritten; OperandInfo *oi = g_new(OperandInfo, 1); operand_info__init(oi); - oi->bit_length = 0; + oi->bit_length = value_size * 8; oi->operand_info_specific = ois; oi->operand_usage = ou; oi->value.len = value_size; From de922e9b2e2c81cf3552eb10058ace71413c7686 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 08:06:45 -0500 Subject: [PATCH 19/59] Formatting --- contrib/plugins/bap-tracing/frame.c | 30 +++++++++++++-------------- contrib/plugins/bap-tracing/tracing.c | 4 ++-- contrib/plugins/bap-tracing/tracing.h | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame.c b/contrib/plugins/bap-tracing/frame.c index 37dea8d146af4..29fa3a1b1efed 100644 --- a/contrib/plugins/bap-tracing/frame.c +++ b/contrib/plugins/bap-tracing/frame.c @@ -24,19 +24,19 @@ Frame *frame_new_std(uint64_t addr, int vcpu_id) { } void frame_add_operand(Frame *frame, OperandInfo *oi) { - OperandValueList *ol; - if (oi->operand_usage->written) { - ol = frame->std_frame->operand_post_list; - } else { - ol = frame->std_frame->operand_pre_list; - } - - oi->taint_info = g_new(TaintInfo, 1); - taint_info__init(oi->taint_info); - oi->taint_info->no_taint = 1; - oi->taint_info->has_no_taint = 1; - - ol->n_elem += 1; - ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); - ol->elem[ol->n_elem - 1] = oi; + OperandValueList *ol; + if (oi->operand_usage->written) { + ol = frame->std_frame->operand_post_list; + } else { + ol = frame->std_frame->operand_pre_list; + } + + oi->taint_info = g_new(TaintInfo, 1); + taint_info__init(oi->taint_info); + oi->taint_info->no_taint = 1; + oi->taint_info->has_no_taint = 1; + + ol->n_elem += 1; + ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); + ol->elem[ol->n_elem - 1] = oi; } diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 18d6c329a1564..b69d403ee42f1 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -40,8 +40,8 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, qemu_plugin_reg_descriptor *reg = &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); qemu_plugin_read_register(reg->handle, rtmp); - OperandInfo *rinfo = init_reg_operand_info(reg->name, rtmp->data, - rtmp->len, OperandRead); + OperandInfo *rinfo = + init_reg_operand_info(reg->name, rtmp->data, rtmp->len, OperandRead); g_assert(rinfo); frame_buffer_append_op_info(fbuf, rinfo); } diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 0f3628761c300..8d4ea207a4e06 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -29,7 +29,7 @@ typedef struct { typedef struct { uint8_t bytes[MAX_INSTRUCTION_SIZE]; ///< Instruction bytes. - size_t size; ///< Len of instruction in bytes. + size_t size; ///< Len of instruction in bytes. uint64_t vaddr; } Instruction; From 8b0ae27993e2ccc1412a317587c84ba3ebb356fa Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 08:15:55 -0500 Subject: [PATCH 20/59] Add SPDX header --- contrib/plugins/bap-tracing/meson.build | 3 +++ contrib/plugins/bap-tracing/tracing.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index 0835eb7670563..4e5f45db00a8c 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 Rot127 +# SPDX-License-Identifier: GPL-2.0-only + piqi = find_program('piqi') protoc_c = find_program('protoc-c') diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 8d4ea207a4e06..f84366d7f93ac 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + #ifndef BAP_TRACING_H #define BAP_TRACING_H From 8323fc7066854e926c0964a9be06126c6b66b094 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 08:35:16 -0500 Subject: [PATCH 21/59] Move and implement frame buffer functionality --- contrib/plugins/bap-tracing/frame.c | 42 ------------ contrib/plugins/bap-tracing/frame_buffer.c | 74 ++++++++++++++++++++++ contrib/plugins/bap-tracing/frame_buffer.h | 31 +++++++++ contrib/plugins/bap-tracing/meson.build | 1 + contrib/plugins/bap-tracing/tracing.c | 23 +++---- contrib/plugins/bap-tracing/tracing.h | 34 +--------- 6 files changed, 117 insertions(+), 88 deletions(-) delete mode 100644 contrib/plugins/bap-tracing/frame.c create mode 100644 contrib/plugins/bap-tracing/frame_buffer.c create mode 100644 contrib/plugins/bap-tracing/frame_buffer.h diff --git a/contrib/plugins/bap-tracing/frame.c b/contrib/plugins/bap-tracing/frame.c deleted file mode 100644 index 29fa3a1b1efed..0000000000000 --- a/contrib/plugins/bap-tracing/frame.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "tracing.h" - -Frame *frame_new_std(uint64_t addr, int vcpu_id) { - Frame *frame = g_new(Frame, 1); - frame__init(frame); - - StdFrame *sframe = g_new(StdFrame, 1); - std_frame__init(sframe); - frame->std_frame = sframe; - - sframe->address = addr; - sframe->thread_id = vcpu_id; - - OperandValueList *ol_in = g_new(OperandValueList, 1); - operand_value_list__init(ol_in); - ol_in->n_elem = 0; - sframe->operand_pre_list = ol_in; - - OperandValueList *ol_out = g_new(OperandValueList, 1); - operand_value_list__init(ol_out); - ol_out->n_elem = 0; - sframe->operand_post_list = ol_out; - return frame; -} - -void frame_add_operand(Frame *frame, OperandInfo *oi) { - OperandValueList *ol; - if (oi->operand_usage->written) { - ol = frame->std_frame->operand_post_list; - } else { - ol = frame->std_frame->operand_pre_list; - } - - oi->taint_info = g_new(TaintInfo, 1); - taint_info__init(oi->taint_info); - oi->taint_info->no_taint = 1; - oi->taint_info->has_no_taint = 1; - - ol->n_elem += 1; - ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); - ol->elem[ol->n_elem - 1] = oi; -} diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c new file mode 100644 index 0000000000000..37e2509f2400a --- /dev/null +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#include "frame_buffer.h" + +static Frame *frame_new_std(uint64_t addr, int vcpu_id) { + Frame *frame = g_new(Frame, 1); + frame__init(frame); + + StdFrame *sframe = g_new(StdFrame, 1); + std_frame__init(sframe); + frame->std_frame = sframe; + + sframe->address = addr; + sframe->thread_id = vcpu_id; + + OperandValueList *ol_in = g_new(OperandValueList, 1); + operand_value_list__init(ol_in); + ol_in->n_elem = 0; + sframe->operand_pre_list = ol_in; + + OperandValueList *ol_out = g_new(OperandValueList, 1); + operand_value_list__init(ol_out); + ol_out->n_elem = 0; + sframe->operand_post_list = ol_out; + return frame; +} + +static void frame_add_operand(Frame *frame, OperandInfo *oi) { + OperandValueList *ol; + if (oi->operand_usage->written) { + ol = frame->std_frame->operand_post_list; + } else { + ol = frame->std_frame->operand_pre_list; + } + + oi->taint_info = g_new(TaintInfo, 1); + taint_info__init(oi->taint_info); + oi->taint_info->no_taint = 1; + oi->taint_info->has_no_taint = 1; + + ol->n_elem += 1; + ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); + ol->elem[ol->n_elem - 1] = oi; +} + +FrameBuffer *frame_buffer_new(size_t size) { + FrameBuffer *fb = g_malloc0(sizeof(FrameBuffer)); + fb->fbuf = g_malloc0(sizeof(Frame *) * size); + fb->max_size = size; + return fb; +} + +bool frame_buffer_is_full(const FrameBuffer *buf) { + return buf->idx >= buf->max_size; +} + +void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); + +StdFrame *frame_buffer_new_frame_std(FrameBuffer *buf) { + if (frame_buffer_is_full(buf)) { + return NULL; + } + Frame *frame = frame_new_std(0, -1); + frame__init(frame); + + StdFrame *sframe = g_new(StdFrame, 1); + std_frame__init(sframe); + frame->std_frame = sframe; + buf->fbuf[buf->idx++] = frame; + return sframe; +} + +void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h new file mode 100644 index 0000000000000..09df826acbeb5 --- /dev/null +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef BAP_FRAME_BUFFER_H +#define BAP_FRAME_BUFFER_H + +#include +#include +#include + +#include "frame.piqi.pb-c-patched.h" + +typedef struct { + Frame **fbuf; ///< The frames buffered. + size_t idx; ///< Points to currently open frame. + size_t max_size; ///< Maximum number of elements fbuf can hold. +} FrameBuffer; + +/** + * \brief Initializes a frame buffer with space for \p size frames. + * Returns the buffer or NULL in case of failure. + */ +FrameBuffer *frame_buffer_new(size_t size); + +void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); +bool frame_buffer_is_full(const FrameBuffer *buf); + +StdFrame *frame_buffer_new_frame_std(FrameBuffer *buf); +void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); + +#endif diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index 4e5f45db00a8c..6745afd9779c1 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -34,6 +34,7 @@ dep_libprotobuf = declare_dependency( ) bap_tracing_src = files( + 'frame_buffer.c', 'tracing.c', 'tracewrap.c', ) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index b69d403ee42f1..66eae89758f02 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -49,28 +49,23 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, Instruction *insn) { - Frame *frame = frame_buffer_new_frame(fbuf); - frame__init(frame); + StdFrame *stdframe = frame_buffer_new_frame_std(fbuf); - StdFrame *sframe = g_new(StdFrame, 1); - std_frame__init(sframe); - frame->std_frame = sframe; - - sframe->thread_id = vcpu_index; - sframe->address = insn->vaddr; - sframe->rawbytes.len = insn->size; - sframe->rawbytes.data = g_malloc(insn->size); - memcpy(sframe->rawbytes.data, insn->bytes, insn->size); + stdframe->thread_id = vcpu_index; + stdframe->address = insn->vaddr; + stdframe->rawbytes.len = insn->size; + stdframe->rawbytes.data = g_malloc(insn->size); + memcpy(stdframe->rawbytes.data, insn->bytes, insn->size); OperandValueList *ol_in = g_new(OperandValueList, 1); operand_value_list__init(ol_in); ol_in->n_elem = 0; - sframe->operand_pre_list = ol_in; + stdframe->operand_pre_list = ol_in; OperandValueList *ol_out = g_new(OperandValueList, 1); operand_value_list__init(ol_out); ol_out->n_elem = 0; - sframe->operand_post_list = ol_out; + stdframe->operand_post_list = ol_out; } static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { @@ -140,7 +135,7 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { VCPU *vcpu = g_malloc0(sizeof(VCPU)); vcpu->registers = registers_init(vcpu_index); g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); - FrameBuffer *vcpu_frame_buffer = frame_buffer_init(FRAME_BUFFER_SIZE_DEFAULT); + FrameBuffer *vcpu_frame_buffer = frame_buffer_new(FRAME_BUFFER_SIZE_DEFAULT); g_ptr_array_insert(state.frame_buffer, vcpu_index, &vcpu_frame_buffer); g_rw_lock_writer_unlock(&state.frame_buffer_lock); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index f84366d7f93ac..e28d6572f1252 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -6,10 +6,11 @@ #include #include +#include #include "frame.piqi.pb-c-patched.h" -#include "glib.h" #include "tracewrap.h" +#include "frame_buffer.h" QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; @@ -25,11 +26,6 @@ typedef enum { */ #define MAX_INSTRUCTION_SIZE 64 -typedef struct { - Frame **fbuf; - size_t len; -} FrameBuffer; - typedef struct { uint8_t bytes[MAX_INSTRUCTION_SIZE]; ///< Instruction bytes. size_t size; ///< Len of instruction in bytes. @@ -58,32 +54,6 @@ typedef struct { } TraceState; VCPU *vcpu_new(void); - -/** - * \brief Initializes a frame buffer with space for \p size frames. - * Returns the buffer or NULL in case of failure. - */ -FrameBuffer *frame_buffer_init(size_t size); - -/** - * \brief Push a frame into the buffer. - * Returns true on success. False otherwise. - */ -bool frame_buffer_push(FrameBuffer *buf, Frame *frame); - -void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); -bool frame_buffer_is_full(const FrameBuffer *buf); - -Frame *frame_buffer_new_frame(FrameBuffer *buf); -void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); - -/** - * \brief Create new std frame - */ -Frame *frame_new_std(uint64_t addr, int vcpu_id); - -void frame_add_operand(Frame *frame, OperandInfo *oi); - Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc); Instruction *init_insn(struct qemu_plugin_insn *insn); From 615f9bc9378b487fce04c947c3d23ac3ccb12d89 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 09:09:38 -0500 Subject: [PATCH 22/59] MOre implementation of frame functions. --- contrib/plugins/bap-tracing/frame_buffer.c | 77 +++++++++++++++++++--- contrib/plugins/bap-tracing/frame_buffer.h | 33 ++++++++-- contrib/plugins/bap-tracing/tracing.c | 55 +++------------- contrib/plugins/bap-tracing/tracing.h | 8 --- 4 files changed, 104 insertions(+), 69 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 37e2509f2400a..daac9d53ab460 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -26,7 +26,12 @@ static Frame *frame_new_std(uint64_t addr, int vcpu_id) { return frame; } -static void frame_add_operand(Frame *frame, OperandInfo *oi) { +static bool frame_add_operand(Frame *frame, OperandInfo *oi) { + if (!frame->std_frame) { + qemu_plugin_outs( + "Append operand info to non-std frames is not implemented."); + return false; + } OperandValueList *ol; if (oi->operand_usage->written) { ol = frame->std_frame->operand_post_list; @@ -42,6 +47,7 @@ static void frame_add_operand(Frame *frame, OperandInfo *oi) { ol->n_elem += 1; ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); ol->elem[ol->n_elem - 1] = oi; + return true; } FrameBuffer *frame_buffer_new(size_t size) { @@ -55,20 +61,73 @@ bool frame_buffer_is_full(const FrameBuffer *buf) { return buf->idx >= buf->max_size; } -void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); +void frame_buffer_flush_to_file(WLOCKED FrameBuffer *buf, WLOCKED FILE *file); -StdFrame *frame_buffer_new_frame_std(FrameBuffer *buf) { +bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, + unsigned int thread_id, uint64_t vaddr, + uint8_t *bytes, size_t bytes_len) { if (frame_buffer_is_full(buf)) { - return NULL; + return false; } Frame *frame = frame_new_std(0, -1); frame__init(frame); - StdFrame *sframe = g_new(StdFrame, 1); - std_frame__init(sframe); - frame->std_frame = sframe; + StdFrame *stdframe = g_new(StdFrame, 1); + std_frame__init(stdframe); + frame->std_frame = stdframe; + + stdframe->thread_id = thread_id; + stdframe->address = vaddr; + stdframe->rawbytes.len = bytes_len; + stdframe->rawbytes.data = g_malloc(bytes_len); + memcpy(stdframe->rawbytes.data, bytes, bytes_len); + + OperandValueList *ol_in = g_new(OperandValueList, 1); + operand_value_list__init(ol_in); + ol_in->n_elem = 0; + stdframe->operand_pre_list = ol_in; + + OperandValueList *ol_out = g_new(OperandValueList, 1); + operand_value_list__init(ol_out); + ol_out->n_elem = 0; + stdframe->operand_post_list = ol_out; + buf->fbuf[buf->idx++] = frame; - return sframe; + return true; +} + +bool frame_buffer_append_op_info(WLOCKED FrameBuffer *buf, OperandInfo *oi) { + Frame *frame = buf->fbuf[buf->idx]; + if (!frame) { + qemu_plugin_outs( + "Attempt to append operand info to a uninitialzied frame."); + return false; + } + return frame_add_operand(frame, oi); } -void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); +OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, + size_t value_size, OperandAccess access) { + RegOperand *ro = g_new(RegOperand, 1); + reg_operand__init(ro); + ro->name = strdup(name); + + OperandInfoSpecific *ois = g_new(OperandInfoSpecific, 1); + operand_info_specific__init(ois); + ois->reg_operand = ro; + + OperandUsage *ou = g_new(OperandUsage, 1); + operand_usage__init(ou); + ou->read = access & OperandRead; + ou->written = access & OperandWritten; + OperandInfo *oi = g_new(OperandInfo, 1); + operand_info__init(oi); + oi->bit_length = value_size * 8; + oi->operand_info_specific = ois; + oi->operand_usage = ou; + oi->value.len = value_size; + oi->value.data = g_malloc(oi->value.len); + memcpy(oi->value.data, value, value_size); + + return oi; +} diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 09df826acbeb5..efad3ca406f50 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -4,15 +4,26 @@ #ifndef BAP_FRAME_BUFFER_H #define BAP_FRAME_BUFFER_H +#include #include #include -#include #include "frame.piqi.pb-c-patched.h" +/** + * \brief Empty macros indicate the argument, variable etc. + * must be locked for writing. + */ +#define WLOCKED + +typedef enum { + OperandRead = 1, + OperandWritten = 2, +} OperandAccess; + typedef struct { - Frame **fbuf; ///< The frames buffered. - size_t idx; ///< Points to currently open frame. + Frame **fbuf; ///< The frames buffered. + size_t idx; ///< Points to currently open frame. size_t max_size; ///< Maximum number of elements fbuf can hold. } FrameBuffer; @@ -22,10 +33,20 @@ typedef struct { */ FrameBuffer *frame_buffer_new(size_t size); -void frame_buffer_flush_to_file(FrameBuffer *buf, FILE *file); +void frame_buffer_flush_to_file(WLOCKED FrameBuffer *buf, WLOCKED FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); -StdFrame *frame_buffer_new_frame_std(FrameBuffer *buf); -void frame_buffer_append_op_info(FrameBuffer *buf, OperandInfo *oi); +bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, + unsigned int thread_id, uint64_t vaddr, + uint8_t *bytes, size_t bytes_len); + +/** + * \brief Appends the given operand info to the open frame. + */ +bool frame_buffer_append_op_info(WLOCKED FrameBuffer *buf, OperandInfo *oi); + +OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, + size_t value_size, + OperandAccess access); #endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 66eae89758f02..a6f268231c207 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -3,6 +3,7 @@ #include +#include "frame_buffer.h" #include "tracing.h" static TraceState state; @@ -26,10 +27,13 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, continue; } - OperandInfo *rinfo = init_reg_operand_info(prev_reg->name, rtmp->data, + OperandInfo *rinfo = frame_init_reg_operand_info(prev_reg->name, rtmp->data, rtmp->len, OperandWritten); g_assert(rinfo); - frame_buffer_append_op_info(fbuf, rinfo); + if (!frame_buffer_append_op_info(fbuf, rinfo)) { + qemu_plugin_outs("Failed to append opinfo.\n"); + g_assert(false); + } } } @@ -41,7 +45,7 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); qemu_plugin_read_register(reg->handle, rtmp); OperandInfo *rinfo = - init_reg_operand_info(reg->name, rtmp->data, rtmp->len, OperandRead); + frame_init_reg_operand_info(reg->name, rtmp->data, rtmp->len, OperandRead); g_assert(rinfo); frame_buffer_append_op_info(fbuf, rinfo); } @@ -49,23 +53,8 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, Instruction *insn) { - StdFrame *stdframe = frame_buffer_new_frame_std(fbuf); - - stdframe->thread_id = vcpu_index; - stdframe->address = insn->vaddr; - stdframe->rawbytes.len = insn->size; - stdframe->rawbytes.data = g_malloc(insn->size); - memcpy(stdframe->rawbytes.data, insn->bytes, insn->size); - - OperandValueList *ol_in = g_new(OperandValueList, 1); - operand_value_list__init(ol_in); - ol_in->n_elem = 0; - stdframe->operand_pre_list = ol_in; - - OperandValueList *ol_out = g_new(OperandValueList, 1); - operand_value_list__init(ol_out); - ol_out->n_elem = 0; - stdframe->operand_post_list = ol_out; + frame_buffer_new_frame_std(fbuf, vcpu_index, insn->vaddr, insn->bytes, + insn->size); } static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { @@ -142,32 +131,6 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_unlock(&state.vcpus_array_lock); } -OperandInfo *init_reg_operand_info(const char *name, const uint8_t *value, - size_t value_size, OperandAccess access) { - RegOperand *ro = g_new(RegOperand, 1); - reg_operand__init(ro); - ro->name = strdup(name); - - OperandInfoSpecific *ois = g_new(OperandInfoSpecific, 1); - operand_info_specific__init(ois); - ois->reg_operand = ro; - - OperandUsage *ou = g_new(OperandUsage, 1); - operand_usage__init(ou); - ou->read = access & OperandRead; - ou->written = access & OperandWritten; - OperandInfo *oi = g_new(OperandInfo, 1); - operand_info__init(oi); - oi->bit_length = value_size * 8; - oi->operand_info_specific = ois; - oi->operand_usage = ou; - oi->value.len = value_size; - oi->value.data = g_malloc(oi->value.len); - memcpy(oi->value.data, value, value_size); - - return oi; -} - Instruction *init_insn(struct qemu_plugin_insn *tb_insn) { Instruction *insn = g_malloc0(sizeof(Instruction)); qemu_plugin_insn_data(tb_insn, &insn->bytes, sizeof(insn->bytes)); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index e28d6572f1252..84c4816b69673 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -16,11 +16,6 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; #define FRAME_BUFFER_SIZE_DEFAULT 1024 -typedef enum { - OperandRead = 1, - OperandWritten = 2, -} OperandAccess; - /** * \brief VLIW architecture have instructions longer than 4 or 8bytes. */ @@ -57,7 +52,4 @@ VCPU *vcpu_new(void); Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc); Instruction *init_insn(struct qemu_plugin_insn *insn); -OperandInfo *init_reg_operand_info(const char *name, const uint8_t *value, - size_t value_size, OperandAccess access); - #endif From 185c5bca1cece952edbe2d831deb82bb08068757 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 09:28:07 -0500 Subject: [PATCH 23/59] Move operand info generation to frame buffer --- contrib/plugins/bap-tracing/frame_buffer.c | 10 ++++++++-- contrib/plugins/bap-tracing/frame_buffer.h | 4 +++- contrib/plugins/bap-tracing/tracing.c | 20 +++++++------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index daac9d53ab460..5ba5cbc39b135 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -96,7 +96,12 @@ bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, return true; } -bool frame_buffer_append_op_info(WLOCKED FrameBuffer *buf, OperandInfo *oi) { +bool frame_buffer_append_reg_info(WLOCKED FrameBuffer *buf, const char *name, + const GByteArray *content, + OperandAccess acc) { + OperandInfo *rinfo = + frame_init_reg_operand_info(name, content->data, content->len, acc); + g_assert(rinfo); Frame *frame = buf->fbuf[buf->idx]; if (!frame) { qemu_plugin_outs( @@ -107,7 +112,8 @@ bool frame_buffer_append_op_info(WLOCKED FrameBuffer *buf, OperandInfo *oi) { } OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, - size_t value_size, OperandAccess access) { + size_t value_size, + OperandAccess access) { RegOperand *ro = g_new(RegOperand, 1); reg_operand__init(ro); ro->name = strdup(name); diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index efad3ca406f50..764508104dfda 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -43,7 +43,9 @@ bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, /** * \brief Appends the given operand info to the open frame. */ -bool frame_buffer_append_op_info(WLOCKED FrameBuffer *buf, OperandInfo *oi); +bool frame_buffer_append_reg_info(WLOCKED FrameBuffer *buf, const char *name, + const GByteArray *content, + OperandAccess acc); OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, size_t value_size, diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index a6f268231c207..4f130dd397a25 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -14,23 +14,20 @@ static void log_insn_mem_access(unsigned int vcpu_index, static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs, FrameBuffer *fbuf) { - GByteArray *rtmp = g_byte_array_new(); + GByteArray *rdata = g_byte_array_new(); for (size_t i = 0; i < current_regs->len; ++i) { Register *prev_reg = vcpu->registers->pdata[i]; qemu_plugin_reg_descriptor *reg = &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); - int s = qemu_plugin_read_register(reg->handle, rtmp); + int s = qemu_plugin_read_register(reg->handle, rdata); assert(s == prev_reg->content->len); - if (!memcmp(rtmp->data, prev_reg->content->data, s)) { + if (!memcmp(rdata->data, prev_reg->content->data, s)) { // No change continue; } - OperandInfo *rinfo = frame_init_reg_operand_info(prev_reg->name, rtmp->data, - rtmp->len, OperandWritten); - g_assert(rinfo); - if (!frame_buffer_append_op_info(fbuf, rinfo)) { + if (!frame_buffer_append_reg_info(fbuf, reg->name, rdata, OperandWritten)) { qemu_plugin_outs("Failed to append opinfo.\n"); g_assert(false); } @@ -39,15 +36,12 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs, FrameBuffer *fbuf) { - GByteArray *rtmp = g_byte_array_new(); + GByteArray *rdata = g_byte_array_new(); for (size_t i = 0; i < current_regs->len; ++i) { qemu_plugin_reg_descriptor *reg = &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); - qemu_plugin_read_register(reg->handle, rtmp); - OperandInfo *rinfo = - frame_init_reg_operand_info(reg->name, rtmp->data, rtmp->len, OperandRead); - g_assert(rinfo); - frame_buffer_append_op_info(fbuf, rinfo); + qemu_plugin_read_register(reg->handle, rdata); + frame_buffer_append_reg_info(fbuf, reg->name, rdata, OperandRead); } } From 451c231c004855012f68680663c85b541a7d3fcd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 09:34:22 -0500 Subject: [PATCH 24/59] Log the frame buffer array only as reader since each frame buffer is unique for each vcpu. --- contrib/plugins/bap-tracing/tracing.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 4f130dd397a25..5936a663801b5 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -53,8 +53,7 @@ static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); - g_rw_lock_writer_lock(&state.frame_buffer_lock); - g_rw_lock_writer_lock(&state.file_lock); + g_rw_lock_reader_lock(&state.frame_buffer_lock); FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); VCPU *vcpu = &g_array_index(state.vcpus, VCPU, vcpu_index); @@ -64,7 +63,9 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { add_post_reg_state(vcpu, vcpu_index, current_regs, fbuf); if (frame_buffer_is_full(fbuf)) { + g_rw_lock_writer_lock(&state.file_lock); frame_buffer_flush_to_file(fbuf, state.file); + g_rw_lock_writer_unlock(&state.file_lock); } // Open new one. @@ -72,8 +73,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); - g_rw_lock_writer_unlock(&state.file_lock); - g_rw_lock_writer_unlock(&state.frame_buffer_lock); + g_rw_lock_reader_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); return; From ff88570a4823421d601230753ddf052cd64b04f2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 09:54:49 -0500 Subject: [PATCH 25/59] Implement frame buffer flush --- contrib/plugins/bap-tracing/frame_buffer.c | 77 ++++++++++++++++++++-- contrib/plugins/bap-tracing/frame_buffer.h | 7 +- contrib/plugins/bap-tracing/tracing.c | 2 - 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 5ba5cbc39b135..17bd49ba19428 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -3,6 +3,18 @@ #include "frame_buffer.h" +#define WRITE(x) \ + do { \ + if (fwrite(&(x), sizeof(x), 1, file) != 1) \ + qemu_plugin_outs("fwrite failed"); \ + } while (0) + +#define WRITE_BUF(x, n) \ + do { \ + if (fwrite((x), 1, (n), file) != n) \ + qemu_plugin_outs("fwrite failed"); \ + } while (0) + static Frame *frame_new_std(uint64_t addr, int vcpu_id) { Frame *frame = g_new(Frame, 1); frame__init(frame); @@ -26,6 +38,47 @@ static Frame *frame_new_std(uint64_t addr, int vcpu_id) { return frame; } +static inline void free_operand(OperandInfo *oi) { + OperandInfoSpecific *ois = oi->operand_info_specific; + + //Free reg-operand + RegOperand *ro = ois->reg_operand; + if (ro && ro->name) + g_free(ro->name); + g_free(ro); + + //Free mem-operand + MemOperand *mo = ois->mem_operand; + g_free(mo); + g_free(oi->value.data); + g_free(oi->taint_info); + g_free(ois); + g_free(oi->operand_usage); + g_free(oi); +} + +static void frame_free(Frame *frame) { + if (!frame) { + return; + } + StdFrame *sframe = frame->std_frame; + for (size_t i = 0; i < sframe->operand_pre_list->n_elem; i++) { + free_operand(sframe->operand_pre_list->elem[i]); + } + g_free(sframe->operand_pre_list->elem); + g_free(sframe->operand_pre_list); + + for (size_t i = 0; i < sframe->operand_post_list->n_elem; i++) { + free_operand(sframe->operand_post_list->elem[i]); + } + g_free(sframe->operand_post_list->elem); + g_free(sframe->operand_post_list); + + g_free(sframe->rawbytes.data); + g_free(sframe); + g_free(frame); +} + static bool frame_add_operand(Frame *frame, OperandInfo *oi) { if (!frame->std_frame) { qemu_plugin_outs( @@ -61,11 +114,25 @@ bool frame_buffer_is_full(const FrameBuffer *buf) { return buf->idx >= buf->max_size; } -void frame_buffer_flush_to_file(WLOCKED FrameBuffer *buf, WLOCKED FILE *file); +void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { + for (size_t i = 0; i <= buf->idx && i < buf->max_size; ++i) { + Frame *frame = buf->fbuf[i]; + size_t msg_size = frame__get_packed_size(frame); + uint8_t *packed_buffer = g_alloca(msg_size); + uint64_t packed_size = frame__pack(frame, packed_buffer); + WRITE(packed_size); + WRITE_BUF(packed_buffer, packed_size); + buf->frames_written++; + frame_free(frame); + } + memset(buf->fbuf, 0, sizeof(Frame *) * buf->max_size); + buf->idx = 0; + // toc_update(); ?? +} -bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, - unsigned int thread_id, uint64_t vaddr, - uint8_t *bytes, size_t bytes_len) { +bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, + uint64_t vaddr, uint8_t *bytes, + size_t bytes_len) { if (frame_buffer_is_full(buf)) { return false; } @@ -96,7 +163,7 @@ bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, return true; } -bool frame_buffer_append_reg_info(WLOCKED FrameBuffer *buf, const char *name, +bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, const GByteArray *content, OperandAccess acc) { OperandInfo *rinfo = diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 764508104dfda..a48d6f02050b1 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -25,6 +25,7 @@ typedef struct { Frame **fbuf; ///< The frames buffered. size_t idx; ///< Points to currently open frame. size_t max_size; ///< Maximum number of elements fbuf can hold. + size_t frames_written; ///< Number of frames written from buffer to file. } FrameBuffer; /** @@ -33,17 +34,17 @@ typedef struct { */ FrameBuffer *frame_buffer_new(size_t size); -void frame_buffer_flush_to_file(WLOCKED FrameBuffer *buf, WLOCKED FILE *file); +void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); -bool frame_buffer_new_frame_std(WLOCKED FrameBuffer *buf, +bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, uint64_t vaddr, uint8_t *bytes, size_t bytes_len); /** * \brief Appends the given operand info to the open frame. */ -bool frame_buffer_append_reg_info(WLOCKED FrameBuffer *buf, const char *name, +bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, const GByteArray *content, OperandAccess acc); diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 5936a663801b5..2c14f8db2be8f 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -75,8 +75,6 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); - - return; } Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) { From 9d79ae17ad418de2e722bbc9d76b9f54d4d4dd4b Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 10:04:03 -0500 Subject: [PATCH 26/59] Fix faulty name --- contrib/plugins/bap-tracing/frame_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 17bd49ba19428..5cfb0a12e6aa4 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -166,9 +166,9 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, const GByteArray *content, OperandAccess acc) { - OperandInfo *rinfo = + OperandInfo *oi = frame_init_reg_operand_info(name, content->data, content->len, acc); - g_assert(rinfo); + g_assert(oi); Frame *frame = buf->fbuf[buf->idx]; if (!frame) { qemu_plugin_outs( From 6c245ae8797a13fe1f51b87e8df2d84d676546bc Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 11:19:14 -0500 Subject: [PATCH 27/59] Add back some of the meta frame code --- contrib/plugins/bap-tracing/frame_buffer.c | 60 ++++------ contrib/plugins/bap-tracing/frame_buffer.h | 7 +- contrib/plugins/bap-tracing/trace_meta.c | 129 +++++++++++++++++++++ contrib/plugins/bap-tracing/trace_meta.h | 25 ++++ contrib/plugins/bap-tracing/tracing.c | 4 +- 5 files changed, 180 insertions(+), 45 deletions(-) create mode 100644 contrib/plugins/bap-tracing/trace_meta.c create mode 100644 contrib/plugins/bap-tracing/trace_meta.h diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 5cfb0a12e6aa4..3686c04a7f939 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -2,18 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "frame_buffer.h" - -#define WRITE(x) \ - do { \ - if (fwrite(&(x), sizeof(x), 1, file) != 1) \ - qemu_plugin_outs("fwrite failed"); \ - } while (0) - -#define WRITE_BUF(x, n) \ - do { \ - if (fwrite((x), 1, (n), file) != n) \ - qemu_plugin_outs("fwrite failed"); \ - } while (0) +#include "trace_meta.h" static Frame *frame_new_std(uint64_t addr, int vcpu_id) { Frame *frame = g_new(Frame, 1); @@ -39,22 +28,22 @@ static Frame *frame_new_std(uint64_t addr, int vcpu_id) { } static inline void free_operand(OperandInfo *oi) { - OperandInfoSpecific *ois = oi->operand_info_specific; - - //Free reg-operand - RegOperand *ro = ois->reg_operand; - if (ro && ro->name) - g_free(ro->name); - g_free(ro); - - //Free mem-operand - MemOperand *mo = ois->mem_operand; - g_free(mo); - g_free(oi->value.data); - g_free(oi->taint_info); - g_free(ois); - g_free(oi->operand_usage); - g_free(oi); + OperandInfoSpecific *ois = oi->operand_info_specific; + + // Free reg-operand + RegOperand *ro = ois->reg_operand; + if (ro && ro->name) + g_free(ro->name); + g_free(ro); + + // Free mem-operand + MemOperand *mo = ois->mem_operand; + g_free(mo); + g_free(oi->value.data); + g_free(oi->taint_info); + g_free(ois); + g_free(oi->operand_usage); + g_free(oi); } static void frame_free(Frame *frame) { @@ -79,17 +68,12 @@ static void frame_free(Frame *frame) { g_free(frame); } -static bool frame_add_operand(Frame *frame, OperandInfo *oi) { - if (!frame->std_frame) { - qemu_plugin_outs( - "Append operand info to non-std frames is not implemented."); - return false; - } +static bool std_frame_add_operand(StdFrame *std_frame, OperandInfo *oi) { OperandValueList *ol; if (oi->operand_usage->written) { - ol = frame->std_frame->operand_post_list; + ol = std_frame->operand_post_list; } else { - ol = frame->std_frame->operand_pre_list; + ol = std_frame->operand_pre_list; } oi->taint_info = g_new(TaintInfo, 1); @@ -170,12 +154,12 @@ bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, frame_init_reg_operand_info(name, content->data, content->len, acc); g_assert(oi); Frame *frame = buf->fbuf[buf->idx]; - if (!frame) { + if (!frame || !frame->std_frame) { qemu_plugin_outs( "Attempt to append operand info to a uninitialzied frame."); return false; } - return frame_add_operand(frame, oi); + return std_frame_add_operand(frame->std_frame, oi); } OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index a48d6f02050b1..a9922cd60fa19 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -8,14 +8,9 @@ #include #include +#include "trace_meta.h" #include "frame.piqi.pb-c-patched.h" -/** - * \brief Empty macros indicate the argument, variable etc. - * must be locked for writing. - */ -#define WLOCKED - typedef enum { OperandRead = 1, OperandWritten = 2, diff --git a/contrib/plugins/bap-tracing/trace_meta.c b/contrib/plugins/bap-tracing/trace_meta.c new file mode 100644 index 0000000000000..edb4ff9e1961a --- /dev/null +++ b/contrib/plugins/bap-tracing/trace_meta.c @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +#include "frame.piqi.pb-c-patched.h" +#include "trace_meta.h" + +#define MD5LEN 16 + +static void compute_target_md5(const char *binary_path) { + const GChecksumType md5 = G_CHECKSUM_MD5; + guchar target_md5[MD5LEN]; + + GChecksum *cs = g_checksum_new(md5); + FILE *target = fopen(binary_path, "r"); + guchar buf[BUFSIZ]; + gsize expected_length = MD5LEN; + + if (!cs) + qemu_plugin_outs("failed to create a checksum"); + if (!target) + qemu_plugin_outs("failed to open target binary"); + if (g_checksum_type_get_length(md5) != expected_length) + abort(); + + while (!feof(target)) { + size_t len = fread(buf, 1, BUFSIZ, target); + if (ferror(target)) + qemu_plugin_outs("failed to read target binary"); + g_checksum_update(cs, buf, len); + } + + g_checksum_get_digest(cs, target_md5, &expected_length); + fclose(target); +} + +static void meta_write_header(FILE *file) { + // uint64_t toc_off = 0L; + // WRITE(magic_number); + // WRITE(out_trace_version); + // WRITE(frame_arch); + // WRITE(frame_mach); + // WRITE(toc_num_frames); + // WRITE(toc_off); +} + +static void init_tracer(Tracer *tracer, char **argv, char **envp) { + // tracer__init(tracer); + // tracer->name = tracer_name; + // tracer->n_args = list_length(argv); + // tracer->args = argv; + // tracer->n_envp = list_length(envp); + // tracer->envp = envp; + // tracer->version = tracer_version; +} + +static void init_target(Target *target, char **argv, char **envp) { + // compute_target_md5(); + + // target__init(target); + // target->path = target_path; + // target->n_args = list_length(argv); + // target->args = argv; + // target->n_envp = list_length(envp); + // target->envp = envp; + // target->md5sum.len = MD5LEN; + // target->md5sum.data = target_md5; +} + +#ifdef G_OS_UNIX +static bool unix_fill_fstats(Fstats *fstats, const char *path) { + struct stat stats; + if (stat(path, &stats) < 0) { + qemu_plugin_outs("failed to obtain file stats"); + return false; + } + + fstats->size = stats.st_size; + fstats->atime = stats.st_atime; + fstats->mtime = stats.st_mtime; + fstats->ctime = stats.st_ctime; + return true; +} +#endif + +static bool init_fstats(Fstats *fstats, const char *binary_path) { + fstats__init(fstats); +#ifdef G_OS_UNIX + return unix_fill_fstats(fstats, binary_path); +#endif + return true; +} + +static void write_meta(WLOCKED FILE *file, char **tracer_argv, + char **tracer_envp, char **target_argv, + char **target_envp) { + MetaFrame meta; + Tracer tracer; + Target target; + Fstats fstats; + + meta_frame__init(&meta); + init_tracer(&tracer, tracer_argv, tracer_envp); + init_target(&target, target_argv, target_envp); + init_fstats(&fstats, "target-path"); + + meta.tracer = &tracer; + meta.target = ⌖ + meta.fstats = &fstats; + meta.time = time(NULL); + char *user = g_strdup(g_get_real_name()); + meta.user = user; + + char *host = g_strdup(g_get_host_name()); + meta.host = host; + + size_t msg_size = meta_frame__get_packed_size(&meta); + uint8_t *packed_buffer = g_alloca(msg_size); + uint64_t packed_size = meta_frame__pack(&meta, packed_buffer); + WRITE(packed_size); + WRITE_BUF(&meta, packed_size); + + free(user); + free(host); +} diff --git a/contrib/plugins/bap-tracing/trace_meta.h b/contrib/plugins/bap-tracing/trace_meta.h new file mode 100644 index 0000000000000..a4eddce6eea56 --- /dev/null +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef BAP_TRACE_META_H +#define BAP_TRACE_META_H + +/** + * \brief Empty macros indicate the argument, variable etc. + * must be locked for writing. + */ +#define WLOCKED + +#define WRITE(x) \ + do { \ + if (fwrite(&(x), sizeof(x), 1, file) != 1) \ + qemu_plugin_outs("fwrite failed"); \ + } while (0) + +#define WRITE_BUF(x, n) \ + do { \ + if (fwrite((x), 1, (n), file) != n) \ + qemu_plugin_outs("fwrite failed"); \ + } while (0) + +#endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 2c14f8db2be8f..4ab45c9f601d3 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -156,10 +156,12 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const char *target_path = "/tmp/test.trace"; state.frame_buffer = g_ptr_array_new(); state.vcpus = g_array_new(false, true, sizeof(VCPU)); - state.file = fopen(target_path, "r"); + state.file = fopen(target_path, "wb"); if (!(state.frame_buffer || state.vcpus || state.file)) { return 1; } + // write_header(); + // write_meta(argv, envp, target_argv, target_envp); qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_vcpu_tb_trans_cb(id, cb_trans); From 8a209888aec317ac7f21957ab2ca51ad1464cb36 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 24 Jun 2025 11:44:00 -0500 Subject: [PATCH 28/59] Add missing libprotobuf-c dependency --- contrib/plugins/bap-tracing/meson.build | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index 6745afd9779c1..9a5d530817d6f 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -11,7 +11,7 @@ piqi_src = custom_target('piqi', command: [piqi, 'to-proto', '@INPUT@', '-o', '@OUTPUT@']) # protobuf file -> C code -proto_src_raw = custom_target('proto', +frame_proto_src_raw = custom_target('proto', input: piqi_src, output: ['frame.piqi.pb-c.c', 'frame.piqi.pb-c.h'], command: [protoc_c, '--c_out=.', '@INPUT@'], @@ -20,17 +20,19 @@ proto_src_raw = custom_target('proto', # Patch protobuf header: base -> __base. # Necessary for the C build. # See fix_proto_src.py -proto_src = custom_target( - input: proto_src_raw, +frame_proto_src = custom_target( + input: frame_proto_src_raw, output: ['frame.piqi.pb-c-patched.c', 'frame.piqi.pb-c-patched.h'], command: [files('fix_proto_src.py'), '@INPUT@', '-o', '@OUTPUT@'], - depends: proto_src_raw + depends: frame_proto_src_raw ) -libprotobuf = static_library('protobuf', [proto_src], pic: true) +libprotobuf = dependency('libprotobuf-c') +frame_protobuf = static_library('protobuf', [frame_proto_src], pic: true) dep_libprotobuf = declare_dependency( - sources : proto_src, - link_with : [libprotobuf], + sources : frame_proto_src, + link_with : [frame_protobuf], + dependencies: [libprotobuf] ) bap_tracing_src = files( From c62b751f87ebe5f1be375a02eb9c3023ea636fc0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 26 Jun 2025 12:44:09 -0500 Subject: [PATCH 29/59] Several segfault fixes --- contrib/plugins/bap-tracing/tracing.c | 62 ++++++++++++++++----------- contrib/plugins/bap-tracing/tracing.h | 2 +- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 4ab45c9f601d3..c9d5f2ad766c5 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -4,6 +4,7 @@ #include #include "frame_buffer.h" +#include "qemu-plugin.h" #include "tracing.h" static TraceState state; @@ -51,12 +52,39 @@ static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, insn->size); } +static GPtrArray *registers_init(void) { + GArray *reg_list = qemu_plugin_get_registers(); + + if (reg_list->len == 0) { + g_array_free(reg_list, false); + return NULL; + } + GPtrArray *registers = g_ptr_array_new(); + for (size_t r = 0; r < reg_list->len; r++) { + qemu_plugin_reg_descriptor *rd = + &g_array_index(reg_list, qemu_plugin_reg_descriptor, r); + Register *reg = init_vcpu_register(rd); + g_ptr_array_add(registers, reg); + } + + return registers->len ? g_steal_pointer(®isters) : NULL; +} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); g_rw_lock_reader_lock(&state.frame_buffer_lock); FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); - VCPU *vcpu = &g_array_index(state.vcpus, VCPU, vcpu_index); + VCPU *vcpu = g_ptr_array_index(state.vcpus, vcpu_index); + g_assert(vcpu); + if (!vcpu->registers) { + vcpu->registers = registers_init(); + if (!vcpu->registers) { + // Registers are still not available. So return until the VCPU is + // sufficiently initialized. + goto unlock_return; + } + } GArray *current_regs = qemu_plugin_get_registers(); g_assert(current_regs->len == vcpu->registers->len); @@ -73,6 +101,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); +unlock_return: g_rw_lock_reader_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); } @@ -80,44 +109,26 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) { Register *reg = g_new0(Register, 1); g_autofree gchar *lower = g_utf8_strdown(desc->name, -1); - int r; reg->handle = desc->handle; reg->name = g_intern_string(lower); reg->content = g_byte_array_new(); /* read the initial value */ - r = qemu_plugin_read_register(reg->handle, reg->content); + int r = qemu_plugin_read_register(reg->handle, reg->content); g_assert(r > 0); return reg; } -static GPtrArray *registers_init(int vcpu_index) { - g_autoptr(GPtrArray) registers = g_ptr_array_new(); - g_autoptr(GArray) reg_list = qemu_plugin_get_registers(); - - if (!reg_list->len) { - return NULL; - } - for (int r = 0; r < reg_list->len; r++) { - qemu_plugin_reg_descriptor *rd = - &g_array_index(reg_list, qemu_plugin_reg_descriptor, r); - Register *reg = init_vcpu_register(rd); - g_ptr_array_add(registers, reg); - } - - return registers->len ? g_steal_pointer(®isters) : NULL; -} - static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.vcpus_array_lock); g_rw_lock_writer_lock(&state.frame_buffer_lock); VCPU *vcpu = g_malloc0(sizeof(VCPU)); - vcpu->registers = registers_init(vcpu_index); - g_array_insert_vals(state.vcpus, vcpu_index, &vcpu, 1); + g_ptr_array_insert(state.vcpus, vcpu_index, vcpu); + FrameBuffer *vcpu_frame_buffer = frame_buffer_new(FRAME_BUFFER_SIZE_DEFAULT); - g_ptr_array_insert(state.frame_buffer, vcpu_index, &vcpu_frame_buffer); + g_ptr_array_insert(state.frame_buffer, vcpu_index, vcpu_frame_buffer); g_rw_lock_writer_unlock(&state.frame_buffer_lock); g_rw_lock_writer_unlock(&state.vcpus_array_lock); @@ -155,11 +166,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, char **argv) { const char *target_path = "/tmp/test.trace"; state.frame_buffer = g_ptr_array_new(); - state.vcpus = g_array_new(false, true, sizeof(VCPU)); + state.vcpus = g_ptr_array_new(); state.file = fopen(target_path, "wb"); if (!(state.frame_buffer || state.vcpus || state.file)) { return 1; } + for (size_t i = 0; i < argc; ++i) { + qemu_plugin_outs(argv[i]); + } // write_header(); // write_meta(argv, envp, target_argv, target_envp); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 84c4816b69673..5ee12c10564db 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -39,7 +39,7 @@ typedef struct { typedef struct { GRWLock vcpus_array_lock; - GArray /**/ *vcpus; + GPtrArray /**/ *vcpus; GRWLock frame_buffer_lock; GPtrArray /**/ *frame_buffer; ///< Indexed by vcpu id From 9d7f9aa21b7854bd55c25f33914a722a665d58ff Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 27 Jun 2025 04:51:25 -0500 Subject: [PATCH 30/59] Add gdb register xml files. --- configs/targets/sparc-linux-user.mak | 1 + configs/targets/sparc-softmmu.mak | 1 + configs/targets/sparc32plus-linux-user.mak | 1 + configs/targets/sparc64-linux-user.mak | 1 + configs/targets/sparc64-softmmu.mak | 1 + gdb-xml/sparc32-core.xml | 84 ++++++++++++++++++ gdb-xml/sparc32-cp0.xml | 19 +++++ gdb-xml/sparc32-cpu.xml | 42 +++++++++ gdb-xml/sparc32-fpu.xml | 43 ++++++++++ gdb-xml/sparc64-core.xml | 99 ++++++++++++++++++++++ gdb-xml/sparc64-cp0.xml | 17 ++++ gdb-xml/sparc64-cpu.xml | 42 +++++++++ gdb-xml/sparc64-fpu.xml | 60 +++++++++++++ target/sparc/cpu.c | 2 + 14 files changed, 413 insertions(+) create mode 100644 gdb-xml/sparc32-core.xml create mode 100644 gdb-xml/sparc32-cp0.xml create mode 100644 gdb-xml/sparc32-cpu.xml create mode 100644 gdb-xml/sparc32-fpu.xml create mode 100644 gdb-xml/sparc64-core.xml create mode 100644 gdb-xml/sparc64-cp0.xml create mode 100644 gdb-xml/sparc64-cpu.xml create mode 100644 gdb-xml/sparc64-fpu.xml diff --git a/configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak index 4ff4b7287d28f..18cc1ce29d71f 100644 --- a/configs/targets/sparc-linux-user.mak +++ b/configs/targets/sparc-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_XML_FILES= gdb-xml/sparc32-core.xml gdb-xml/sparc32-cp0.xml gdb-xml/sparc32-cpu.xml gdb-xml/sparc32-fpu.xml TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index 78c2e25bd13ec..0a14c1839c22a 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=sparc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/sparc32-core.xml gdb-xml/sparc32-cp0.xml gdb-xml/sparc32-cpu.xml gdb-xml/sparc32-fpu.xml TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak index 7a16934fd17bd..029fa69cb0288 100644 --- a/configs/targets/sparc32plus-linux-user.mak +++ b/configs/targets/sparc32plus-linux-user.mak @@ -5,4 +5,5 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_XML_FILES= gdb-xml/sparc64-core.xml gdb-xml/sparc64-cp0.xml gdb-xml/sparc64-cpu.xml gdb-xml/sparc64-fpu.xml TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 64ea04e3e2ee4..67a11f3c8adab 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,4 +4,5 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_XML_FILES= gdb-xml/sparc64-core.xml gdb-xml/sparc64-cp0.xml gdb-xml/sparc64-cpu.xml gdb-xml/sparc64-fpu.xml TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index f7bab97a00289..6929774e553e8 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/sparc64-core.xml gdb-xml/sparc64-cp0.xml gdb-xml/sparc64-cpu.xml gdb-xml/sparc64-fpu.xml TARGET_LONG_BITS=64 diff --git a/gdb-xml/sparc32-core.xml b/gdb-xml/sparc32-core.xml new file mode 100644 index 0000000000000..61964a79d121d --- /dev/null +++ b/gdb-xml/sparc32-core.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc32-cp0.xml b/gdb-xml/sparc32-cp0.xml new file mode 100644 index 0000000000000..a7f6e64de1b27 --- /dev/null +++ b/gdb-xml/sparc32-cp0.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc32-cpu.xml b/gdb-xml/sparc32-cpu.xml new file mode 100644 index 0000000000000..b28c533a4fbcc --- /dev/null +++ b/gdb-xml/sparc32-cpu.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc32-fpu.xml b/gdb-xml/sparc32-fpu.xml new file mode 100644 index 0000000000000..289c1d2b99aab --- /dev/null +++ b/gdb-xml/sparc32-fpu.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc64-core.xml b/gdb-xml/sparc64-core.xml new file mode 100644 index 0000000000000..375b9bb0cc6a7 --- /dev/null +++ b/gdb-xml/sparc64-core.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc64-cp0.xml b/gdb-xml/sparc64-cp0.xml new file mode 100644 index 0000000000000..cef58f312b6f8 --- /dev/null +++ b/gdb-xml/sparc64-cp0.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/gdb-xml/sparc64-cpu.xml b/gdb-xml/sparc64-cpu.xml new file mode 100644 index 0000000000000..b8a66d911aa36 --- /dev/null +++ b/gdb-xml/sparc64-cpu.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/sparc64-fpu.xml b/gdb-xml/sparc64-fpu.xml new file mode 100644 index 0000000000000..cef935ebd6b17 --- /dev/null +++ b/gdb-xml/sparc64-fpu.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 571612011730b..be2ecb989f56b 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1047,8 +1047,10 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) cc->disas_set_info = cpu_sparc_disas_set_info; #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + cc->gdb_core_xml_file = "sparc64-core.xml"; cc->gdb_num_core_regs = 86; #else + cc->gdb_core_xml_file = "sparc32-core.xml"; cc->gdb_num_core_regs = 72; #endif cc->tcg_ops = &sparc_tcg_ops; From 363aefe46501bc1d456d0fec167a550a4c2f39b4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 27 Jun 2025 06:57:57 -0500 Subject: [PATCH 31/59] Fix and print register tracing. --- contrib/plugins/bap-tracing/frame_buffer.c | 65 ++++++++++++++++++++-- contrib/plugins/bap-tracing/frame_buffer.h | 5 +- contrib/plugins/bap-tracing/tracing.c | 32 +++++------ 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 3686c04a7f939..30c2f13fc87af 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -95,7 +95,62 @@ FrameBuffer *frame_buffer_new(size_t size) { } bool frame_buffer_is_full(const FrameBuffer *buf) { - return buf->idx >= buf->max_size; + return buf->idx + 1 >= buf->max_size; +} + +void frame_buffer_close_frame(FrameBuffer *buf) { + char *str = frame_buffer_as_str(buf); + qemu_plugin_outs("Close frame: "); + qemu_plugin_outs(str); + qemu_plugin_outs("\n\n"); + g_free(str); + buf->idx++; +} + +#define FRAME_STR_SIZE 8192 + +#define APPEND(...) \ + snprintf(str + off, max - off, __VA_ARGS__); \ + off = strlen(str); + +char *frame_buffer_as_str(const FrameBuffer *buf) { + char *str = g_malloc0(FRAME_STR_SIZE); + const Frame *frame = buf->fbuf[buf->idx]; + if (!frame) { + snprintf(str, FRAME_STR_SIZE, ""); + return str; + } + size_t max = FRAME_STR_SIZE - 1; + snprintf(str, max, "{ pre: [ "); + size_t off = strlen(str); + + StdFrame *sframe = frame->std_frame; + for (size_t i = 0; i < sframe->operand_pre_list->n_elem; i++) { + OperandInfo *oi = sframe->operand_pre_list->elem[i]; + APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + + for (size_t k = 0; k < oi->value.len; ++k) { + APPEND("%02x", oi->value.data[k]); + } + APPEND(", "); + } + APPEND(" ], post: [ "); + for (size_t i = 0; i < sframe->operand_post_list->n_elem; i++) { + OperandInfo *oi = sframe->operand_post_list->elem[i]; + APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + + for (size_t k = 0; k < oi->value.len; ++k) { + APPEND("%02x", oi->value.data[k]); + } + APPEND(", "); + } + + APPEND("]}"); + return str; +} + +bool frame_buffer_is_empty(const FrameBuffer *buf) { + return buf->fbuf[buf->idx] == NULL; } void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { @@ -143,15 +198,15 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, ol_out->n_elem = 0; stdframe->operand_post_list = ol_out; - buf->fbuf[buf->idx++] = frame; + buf->fbuf[buf->idx] = frame; return true; } bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, - const GByteArray *content, + const GByteArray *content, size_t reg_size, OperandAccess acc) { - OperandInfo *oi = - frame_init_reg_operand_info(name, content->data, content->len, acc); + OperandInfo *oi = frame_init_reg_operand_info( + name, content->data + content->len - reg_size, reg_size, acc); g_assert(oi); Frame *frame = buf->fbuf[buf->idx]; if (!frame || !frame->std_frame) { diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index a9922cd60fa19..e3ab12d382b09 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -31,6 +31,9 @@ FrameBuffer *frame_buffer_new(size_t size); void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); +bool frame_buffer_is_empty(const FrameBuffer *buf); +void frame_buffer_close_frame(FrameBuffer *buf); +char *frame_buffer_as_str(const FrameBuffer *buf); bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, uint64_t vaddr, @@ -40,7 +43,7 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, * \brief Appends the given operand info to the open frame. */ bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, - const GByteArray *content, + const GByteArray *content, size_t reg_size, OperandAccess acc); OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c9d5f2ad766c5..8f934653a8580 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -15,6 +15,7 @@ static void log_insn_mem_access(unsigned int vcpu_index, static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs, FrameBuffer *fbuf) { + GByteArray *rdata = g_byte_array_new(); for (size_t i = 0; i < current_regs->len; ++i) { Register *prev_reg = vcpu->registers->pdata[i]; @@ -28,7 +29,8 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, continue; } - if (!frame_buffer_append_reg_info(fbuf, reg->name, rdata, OperandWritten)) { + if (!frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, + OperandWritten)) { qemu_plugin_outs("Failed to append opinfo.\n"); g_assert(false); } @@ -41,15 +43,15 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, for (size_t i = 0; i < current_regs->len; ++i) { qemu_plugin_reg_descriptor *reg = &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); - qemu_plugin_read_register(reg->handle, rdata); - frame_buffer_append_reg_info(fbuf, reg->name, rdata, OperandRead); + size_t s = qemu_plugin_read_register(reg->handle, rdata); + frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); } } -static void add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, +static bool add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, Instruction *insn) { - frame_buffer_new_frame_std(fbuf, vcpu_index, insn->vaddr, insn->bytes, - insn->size); + return frame_buffer_new_frame_std(fbuf, vcpu_index, insn->vaddr, insn->bytes, + insn->size); } static GPtrArray *registers_init(void) { @@ -77,18 +79,13 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); VCPU *vcpu = g_ptr_array_index(state.vcpus, vcpu_index); g_assert(vcpu); - if (!vcpu->registers) { - vcpu->registers = registers_init(); - if (!vcpu->registers) { - // Registers are still not available. So return until the VCPU is - // sufficiently initialized. - goto unlock_return; - } - } GArray *current_regs = qemu_plugin_get_registers(); g_assert(current_regs->len == vcpu->registers->len); - add_post_reg_state(vcpu, vcpu_index, current_regs, fbuf); + if (!frame_buffer_is_empty(fbuf)) { + add_post_reg_state(vcpu, vcpu_index, current_regs, fbuf); + frame_buffer_close_frame(fbuf); + } if (frame_buffer_is_full(fbuf)) { g_rw_lock_writer_lock(&state.file_lock); @@ -101,7 +98,6 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); -unlock_return: g_rw_lock_reader_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); } @@ -125,6 +121,10 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.frame_buffer_lock); VCPU *vcpu = g_malloc0(sizeof(VCPU)); + vcpu->registers = registers_init(); + if (!vcpu->registers) { + g_assert(false); + } g_ptr_array_insert(state.vcpus, vcpu_index, vcpu); FrameBuffer *vcpu_frame_buffer = frame_buffer_new(FRAME_BUFFER_SIZE_DEFAULT); From f6904888170adcae23e422b21d1504c344d0f1d0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 27 Jun 2025 08:27:45 -0500 Subject: [PATCH 32/59] Trace memory accesses --- contrib/plugins/bap-tracing/frame_buffer.c | 113 +++++++++++++++++++-- contrib/plugins/bap-tracing/frame_buffer.h | 17 ++-- contrib/plugins/bap-tracing/tracing.c | 30 +++++- 3 files changed, 142 insertions(+), 18 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 30c2f13fc87af..88f4853b7aaa9 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -127,7 +127,11 @@ char *frame_buffer_as_str(const FrameBuffer *buf) { StdFrame *sframe = frame->std_frame; for (size_t i = 0; i < sframe->operand_pre_list->n_elem; i++) { OperandInfo *oi = sframe->operand_pre_list->elem[i]; - APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + if (oi->operand_info_specific->reg_operand) { + APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + } else { + APPEND("m:0x%016lx=", oi->operand_info_specific->mem_operand->address); + } for (size_t k = 0; k < oi->value.len; ++k) { APPEND("%02x", oi->value.data[k]); @@ -137,7 +141,11 @@ char *frame_buffer_as_str(const FrameBuffer *buf) { APPEND(" ], post: [ "); for (size_t i = 0; i < sframe->operand_post_list->n_elem; i++) { OperandInfo *oi = sframe->operand_post_list->elem[i]; - APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + if (oi->operand_info_specific->reg_operand) { + APPEND("r:%s=", oi->operand_info_specific->reg_operand->name); + } else { + APPEND("m:0x%016lx=", oi->operand_info_specific->mem_operand->address); + } for (size_t k = 0; k < oi->value.len; ++k) { APPEND("%02x", oi->value.data[k]); @@ -202,12 +210,7 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, return true; } -bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, - const GByteArray *content, size_t reg_size, - OperandAccess acc) { - OperandInfo *oi = frame_init_reg_operand_info( - name, content->data + content->len - reg_size, reg_size, acc); - g_assert(oi); +static bool append_op_info(FrameBuffer *buf, OperandInfo *oi) { Frame *frame = buf->fbuf[buf->idx]; if (!frame || !frame->std_frame) { qemu_plugin_outs( @@ -217,6 +220,15 @@ bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, return std_frame_add_operand(frame->std_frame, oi); } +bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, + const GByteArray *content, size_t reg_size, + OperandAccess acc) { + OperandInfo *oi = frame_init_reg_operand_info( + name, content->data + content->len - reg_size, reg_size, acc); + g_assert(oi); + return append_op_info(buf, oi); +} + OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, size_t value_size, OperandAccess access) { @@ -243,3 +255,88 @@ OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, return oi; } + +static size_t mval_type_to_int(enum qemu_plugin_mem_value_type type) { + switch (type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + return 8; + case QEMU_PLUGIN_MEM_VALUE_U16: + return 16; + case QEMU_PLUGIN_MEM_VALUE_U32: + return 32; + case QEMU_PLUGIN_MEM_VALUE_U64: + return 64; + case QEMU_PLUGIN_MEM_VALUE_U128: + return 128; + default: + g_assert(false); + } +} + +static void mval_to_buf(qemu_plugin_mem_value *val, uint8_t *buf) { + switch (val->type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + buf[0] = val->data.u8; + return; + case QEMU_PLUGIN_MEM_VALUE_U16: + buf[0] = (uint8_t)val->data.u16; + buf[1] = (uint8_t)(val->data.u16 >> 8); + return; + case QEMU_PLUGIN_MEM_VALUE_U32: + buf[0] = (uint8_t)val->data.u32; + buf[1] = (uint8_t)(val->data.u32 >> 8); + buf[2] = (uint8_t)(val->data.u32 >> 16); + buf[3] = (uint8_t)(val->data.u32 >> 24); + return; + case QEMU_PLUGIN_MEM_VALUE_U64: + for (size_t i = 0; i < 8; ++i) { + buf[i] = (uint8_t)(val->data.u64 >> (i * 8)); + } + return; + case QEMU_PLUGIN_MEM_VALUE_U128: + for (size_t i = 0; i < 8; ++i) { + buf[i] = (uint8_t)(val->data.u128.low >> (i * 8)); + } + for (size_t i = 0; i < 8; ++i) { + buf[i + 8] = (uint8_t)(val->data.u128.high >> (i * 8)); + } + return; + default: + g_assert(false); + } +} + +static OperandInfo *frame_init_mem_operand_info(uint64_t vaddr, + qemu_plugin_mem_value *mval, + bool is_store) { + MemOperand *ro = g_new(MemOperand, 1); + mem_operand__init(ro); + ro->address = vaddr; + + OperandInfoSpecific *ois = g_new(OperandInfoSpecific, 1); + operand_info_specific__init(ois); + ois->mem_operand = ro; + + size_t byte_width = mval_type_to_int(mval->type) / 8; + OperandUsage *ou = g_new(OperandUsage, 1); + operand_usage__init(ou); + ou->read = !is_store; + ou->written = is_store; + OperandInfo *oi = g_new(OperandInfo, 1); + operand_info__init(oi); + oi->bit_length = mval_type_to_int(mval->type); + oi->operand_info_specific = ois; + oi->operand_usage = ou; + oi->value.len = byte_width; + oi->value.data = g_malloc(oi->value.len); + mval_to_buf(mval, oi->value.data); + + return oi; +} + +bool frame_buffer_append_mem_info(FrameBuffer *fbuf, uint64_t vaddr, + qemu_plugin_mem_value *mval, bool is_store) { + OperandInfo *oi = frame_init_mem_operand_info(vaddr, mval, is_store); + g_assert(oi); + return append_op_info(fbuf, oi); +} diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index e3ab12d382b09..5471f2503b3f8 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -8,8 +8,8 @@ #include #include -#include "trace_meta.h" #include "frame.piqi.pb-c-patched.h" +#include "trace_meta.h" typedef enum { OperandRead = 1, @@ -17,9 +17,9 @@ typedef enum { } OperandAccess; typedef struct { - Frame **fbuf; ///< The frames buffered. - size_t idx; ///< Points to currently open frame. - size_t max_size; ///< Maximum number of elements fbuf can hold. + Frame **fbuf; ///< The frames buffered. + size_t idx; ///< Points to currently open frame. + size_t max_size; ///< Maximum number of elements fbuf can hold. size_t frames_written; ///< Number of frames written from buffer to file. } FrameBuffer; @@ -35,9 +35,12 @@ bool frame_buffer_is_empty(const FrameBuffer *buf); void frame_buffer_close_frame(FrameBuffer *buf); char *frame_buffer_as_str(const FrameBuffer *buf); -bool frame_buffer_new_frame_std(FrameBuffer *buf, - unsigned int thread_id, uint64_t vaddr, - uint8_t *bytes, size_t bytes_len); +bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, + uint64_t vaddr, uint8_t *bytes, + size_t bytes_len); + +bool frame_buffer_append_mem_info(FrameBuffer *fbuf, uint64_t vaddr, + qemu_plugin_mem_value *mval, bool is_store); /** * \brief Appends the given operand info to the open frame. diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 8f934653a8580..a399b725bd578 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -9,9 +9,33 @@ static TraceState state; +static void add_mem_op(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, + uint64_t vaddr, qemu_plugin_mem_value *mval, + bool is_store) { + if (!frame_buffer_append_mem_info(fbuf, vaddr, mval, is_store)) { + qemu_plugin_outs("Failed to append memory info\n"); + } + return; +} + static void log_insn_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, - void *userdata) {} + void *userdata) { + g_rw_lock_reader_lock(&state.vcpus_array_lock); + g_rw_lock_reader_lock(&state.frame_buffer_lock); + + VCPU *vcpu = g_ptr_array_index(state.vcpus, vcpu_index); + g_assert(vcpu); + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); + + bool is_store = qemu_plugin_mem_is_store(info); + qemu_plugin_mem_value mval = qemu_plugin_mem_get_value(info); + + add_mem_op(vcpu, vcpu_index, fbuf, vaddr, &mval, is_store); + + g_rw_lock_writer_unlock(&state.frame_buffer_lock); + g_rw_lock_writer_unlock(&state.vcpus_array_lock); +} static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, GArray *current_regs, FrameBuffer *fbuf) { @@ -32,7 +56,7 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, if (!frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandWritten)) { qemu_plugin_outs("Failed to append opinfo.\n"); - g_assert(false); + return; } } } @@ -152,7 +176,7 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { qemu_plugin_register_vcpu_insn_exec_cb(tb_insn, log_insn_reg_access, QEMU_PLUGIN_CB_R_REGS, insn_data); qemu_plugin_register_vcpu_mem_cb(tb_insn, log_insn_mem_access, - QEMU_PLUGIN_CB_R_REGS, QEMU_PLUGIN_MEM_R, + QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_MEM_R, NULL); } } From cd459695c0a8ed138b3f9ac71acd23bf7df10e7e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 27 Jun 2025 11:15:57 -0500 Subject: [PATCH 33/59] Write trace header (without machine details yet). --- contrib/plugins/bap-tracing/meson.build | 5 +- contrib/plugins/bap-tracing/trace_consts.h | 33 +++++--- contrib/plugins/bap-tracing/tracing.c | 43 +++++++++- contrib/plugins/bap-tracing/tracing.h | 96 +++++++++++++++++++++- 4 files changed, 160 insertions(+), 17 deletions(-) diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index 9a5d530817d6f..0a64d499aea70 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -10,6 +10,9 @@ piqi_src = custom_target('piqi', output: 'frame.piqi.proto', command: [piqi, 'to-proto', '@INPUT@', '-o', '@OUTPUT@']) +frame_arch_h = fs.copyfile('bap-frames/libtrace/src/frame_arch.h', + 'frame_arch.h') + # protobuf file -> C code frame_proto_src_raw = custom_target('proto', input: piqi_src, @@ -30,7 +33,7 @@ frame_proto_src = custom_target( libprotobuf = dependency('libprotobuf-c') frame_protobuf = static_library('protobuf', [frame_proto_src], pic: true) dep_libprotobuf = declare_dependency( - sources : frame_proto_src, + sources : [frame_proto_src, frame_arch_h], link_with : [frame_protobuf], dependencies: [libprotobuf] ) diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h index 22dde6dfdb947..e6af59e01e273 100644 --- a/contrib/plugins/bap-tracing/trace_consts.h +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -1,13 +1,20 @@ -#pragma once - -// #include "trace_info.h" - -const uint64_t magic_number = 7456879624156307493LL; -const uint64_t magic_number_offset = 0LL; -const uint64_t trace_version_offset = 8LL; -const uint64_t bfd_arch_offset = 16LL; -const uint64_t bfd_machine_offset = 24LL; -const uint64_t num_trace_frames_offset = 32LL; -const uint64_t toc_offset_offset = 40LL; -const uint64_t first_frame_offset = 48LL; -const uint64_t out_trace_version = 2LL; +#ifndef BAP_TRACE_CONSTS_H +#define BAP_TRACE_CONSTS_H + +#include + +// Trace header constants + +static const uint64_t magic_number = 7456879624156307493LL; +static const uint64_t magic_number_offset = 0LL; +static const uint64_t trace_version_offset = 8LL; +static const uint64_t bfd_arch_offset = 16LL; +static const uint64_t bfd_machine_offset = 24LL; +static const uint64_t num_trace_frames_offset = 32LL; +static const uint64_t toc_offset_offset = 40LL; +static const uint64_t first_frame_offset = 48LL; +static const uint64_t out_trace_version = 2LL; + +// Arch specific + +#endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index a399b725bd578..664fb48135e4e 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -3,6 +3,7 @@ #include +#include "frame_arch.h" #include "frame_buffer.h" #include "qemu-plugin.h" #include "tracing.h" @@ -185,9 +186,46 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { // Dump rest of frames to file. } +static bool get_frame_arch_mach(const char *target_name, uint64_t *arch, + uint64_t *mach) { + *arch = 0; + *arch = frame_arch_last; + const char *aname = arch_map[0].name; + for (size_t i = 0; arch_map[i].name; ++i) { + aname = arch_map[i].name; + if (!strncmp(aname, target_name, strlen(aname))) { + *arch = arch_map[i].val; + break; + } + } + return *arch != frame_arch_last; +} + +static bool write_header(FILE *file, const char *target_name) { + uint64_t frame_arch = 0; + uint64_t frame_mach = 0; + if (!get_frame_arch_mach(target_name, &frame_arch, &frame_mach)) { + qemu_plugin_outs("Failed to get arch/mach.\n"); + return false; + } + uint64_t num_frames = 0ULL; + uint64_t toc_off = 0ULL; + WRITE(magic_number); + WRITE(out_trace_version); + WRITE(frame_arch); + WRITE(frame_mach); + WRITE(num_frames); + WRITE(toc_off); + return true; +} + QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + qemu_plugin_outs("Target name: "); + qemu_plugin_outs(info->target_name); + qemu_plugin_outs("\n"); + const char *target_path = "/tmp/test.trace"; state.frame_buffer = g_ptr_array_new(); state.vcpus = g_ptr_array_new(); @@ -198,7 +236,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (size_t i = 0; i < argc; ++i) { qemu_plugin_outs(argv[i]); } - // write_header(); + if (!write_header(state.file, info->target_name)) { + qemu_plugin_outs("Failed to header.\n"); + return 1; + } // write_meta(argv, envp, target_argv, target_envp); qemu_plugin_register_vcpu_init_cb(id, vcpu_init); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 5ee12c10564db..5e91c64215e9b 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -4,13 +4,105 @@ #ifndef BAP_TRACING_H #define BAP_TRACING_H +#include #include #include -#include #include "frame.piqi.pb-c-patched.h" -#include "tracewrap.h" +#include "frame_arch.h" #include "frame_buffer.h" +#include "trace_consts.h" + +struct arch_enum_entry { + const char *name; + enum frame_architecture val; +}; + +static struct arch_enum_entry arch_map[] = { + {.name = "unknown", .val = frame_arch_unknown}, + {.name = "obscure", .val = frame_arch_obscure}, + {.name = "m68k", .val = frame_arch_m68k}, + {.name = "vax", .val = frame_arch_vax}, + {.name = "i960", .val = frame_arch_i960}, + {.name = "or32", .val = frame_arch_or32}, + {.name = "sparc", .val = frame_arch_sparc}, + {.name = "spu", .val = frame_arch_spu}, + {.name = "mips", .val = frame_arch_mips}, + {.name = "i386", .val = frame_arch_i386}, + {.name = "l1om", .val = frame_arch_l1om}, + {.name = "we32k", .val = frame_arch_we32k}, + {.name = "tahoe", .val = frame_arch_tahoe}, + {.name = "i860", .val = frame_arch_i860}, + {.name = "i370", .val = frame_arch_i370}, + {.name = "romp", .val = frame_arch_romp}, + {.name = "convex", .val = frame_arch_convex}, + {.name = "m88k", .val = frame_arch_m88k}, + {.name = "m98k", .val = frame_arch_m98k}, + {.name = "pyramid", .val = frame_arch_pyramid}, + {.name = "h8300", .val = frame_arch_h8300}, + {.name = "pdp11", .val = frame_arch_pdp11}, + {.name = "plugin", .val = frame_arch_plugin}, + {.name = "powerpc", .val = frame_arch_powerpc}, + {.name = "rs6000", .val = frame_arch_rs6000}, + {.name = "hppa", .val = frame_arch_hppa}, + {.name = "d10v", .val = frame_arch_d10v}, + {.name = "d30v", .val = frame_arch_d30v}, + {.name = "dlx", .val = frame_arch_dlx}, + {.name = "m68hc11", .val = frame_arch_m68hc11}, + {.name = "m68hc12", .val = frame_arch_m68hc12}, + {.name = "z8k", .val = frame_arch_z8k}, + {.name = "h8500", .val = frame_arch_h8500}, + {.name = "sh", .val = frame_arch_sh}, + {.name = "alpha", .val = frame_arch_alpha}, + {.name = "arm", .val = frame_arch_arm}, + {.name = "ns32k", .val = frame_arch_ns32k}, + {.name = "w65", .val = frame_arch_w65}, + {.name = "tic30", .val = frame_arch_tic30}, + {.name = "tic4x", .val = frame_arch_tic4x}, + {.name = "tic54x", .val = frame_arch_tic54x}, + {.name = "tic6x", .val = frame_arch_tic6x}, + {.name = "tic80", .val = frame_arch_tic80}, + {.name = "v850", .val = frame_arch_v850}, + {.name = "arc", .val = frame_arch_arc}, + {.name = "m32c", .val = frame_arch_m32c}, + {.name = "m32r", .val = frame_arch_m32r}, + {.name = "mn10200", .val = frame_arch_mn10200}, + {.name = "mn10300", .val = frame_arch_mn10300}, + {.name = "fr30", .val = frame_arch_fr30}, + {.name = "frv", .val = frame_arch_frv}, + {.name = "moxie", .val = frame_arch_moxie}, + {.name = "mcore", .val = frame_arch_mcore}, + {.name = "mep", .val = frame_arch_mep}, + {.name = "ia64", .val = frame_arch_ia64}, + {.name = "ip2k", .val = frame_arch_ip2k}, + {.name = "iq2000", .val = frame_arch_iq2000}, + {.name = "mt", .val = frame_arch_mt}, + {.name = "pj", .val = frame_arch_pj}, + {.name = "avr", .val = frame_arch_avr}, + {.name = "bfin", .val = frame_arch_bfin}, + {.name = "cr16", .val = frame_arch_cr16}, + {.name = "cr16c", .val = frame_arch_cr16c}, + {.name = "crx", .val = frame_arch_crx}, + {.name = "cris", .val = frame_arch_cris}, + {.name = "rx", .val = frame_arch_rx}, + {.name = "s390", .val = frame_arch_s390}, + {.name = "score", .val = frame_arch_score}, + {.name = "openrisc", .val = frame_arch_openrisc}, + {.name = "mmix", .val = frame_arch_mmix}, + {.name = "xstormy16", .val = frame_arch_xstormy16}, + {.name = "msp430", .val = frame_arch_msp430}, + {.name = "xc16x", .val = frame_arch_xc16x}, + {.name = "xtensa", .val = frame_arch_xtensa}, + {.name = "z80", .val = frame_arch_z80}, + {.name = "lm32", .val = frame_arch_lm32}, + {.name = "microblaze", .val = frame_arch_microblaze}, + {.name = "6502", .val = frame_arch_6502}, + {.name = "aarch64", .val = frame_arch_aarch64}, + {.name = "8051", .val = frame_arch_8051}, + {.name = "sm83", .val = frame_arch_sm83}, + {.name = "hexagon", .val = frame_arch_hexagon}, + {.name = NULL, .val = frame_arch_last}, +}; QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; From 09c5728468e326f8bb44922ef007b9ab89e6f427 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Jun 2025 09:46:05 -0500 Subject: [PATCH 34/59] Docs add trace overview to README. --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 70e36ef9b6091..71354f5314d54 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,40 @@ cd build ../configure --enable-plugins --target-list= make ``` + +## Trace format + +The generated trace consists of three parts: the header, +a table of contents (TOC) holding the frame entries, and an index into the TOC. + +Each frame entry starts with the size of the frame, followed by the actual frame data. +A fixed number of frame entries are considered one _entry_ in the TOC. + +The TOC index is stored at the end. + +For specifics about the frame contents, please refer +to the [definitions](https://github.com/BinaryAnalysisPlatform/bap-frames/tree/master/piqi) in +the BAP-frames repository. + +**Format** + +| Offset | Type | Field | Trace section | +|--------|------|-------|------| +| 0x0 | uint64_t | magic number (7456879624156307493LL) | Header begin | +| 0x8 | uint64_t | trace version number | | +| 0x10 | uint64_t | frame_architecture | | +| 0x18 | uint64_t | frame_machine, 0 for unspecified. | | +| 0x20 | uint64_t | n = number of frames per TOC entry. | | +| 0x28 | uint64_t | T = offset to TOC index. | | +| 0x30 | uint64_t | sizeof(frame_0) | TOC begin | +| 0x38 | meta_frame | frame_0 | | +| 0x40 | uint64_t | sizeof(frame_1) | | +| 0x48 | type(frame_1) | frame_1 | | +| ... | ... | ... | | +| T-0x10 | uint64_t | sizeof(frame_n-1) | | +| T-0x8 | type(frame_n-1) | frame_n-1 | | +| T+0 | uint64_t | m = number of TOC entries | TOC index begin | +| T+0x8 | uint64_t | offset toc_entry(0) | | +| T+0x10 | uint64_t | offset toc_entry(1) | | +| ... | ... | ... | | +| T+0x8+(0x8*m) | uint64_t | offset toc_entry(m-1) | | From 4db51287897f790a8bc074e5eafcdf21e5d9ce6d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Jun 2025 10:28:05 -0500 Subject: [PATCH 35/59] Use defined constant values of trace --- contrib/plugins/bap-tracing/frame_buffer.c | 10 ++++------ contrib/plugins/bap-tracing/frame_buffer.h | 8 ++++---- contrib/plugins/bap-tracing/trace_consts.h | 22 ++++++++++++++-------- contrib/plugins/bap-tracing/tracing.c | 12 ++++++------ contrib/plugins/bap-tracing/tracing.h | 2 -- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 88f4853b7aaa9..06ca4d00169f9 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -87,15 +87,13 @@ static bool std_frame_add_operand(StdFrame *std_frame, OperandInfo *oi) { return true; } -FrameBuffer *frame_buffer_new(size_t size) { +FrameBuffer *frame_buffer_new(void) { FrameBuffer *fb = g_malloc0(sizeof(FrameBuffer)); - fb->fbuf = g_malloc0(sizeof(Frame *) * size); - fb->max_size = size; return fb; } bool frame_buffer_is_full(const FrameBuffer *buf) { - return buf->idx + 1 >= buf->max_size; + return buf->idx + 1 >= frames_per_toc_entry; } void frame_buffer_close_frame(FrameBuffer *buf) { @@ -162,7 +160,7 @@ bool frame_buffer_is_empty(const FrameBuffer *buf) { } void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { - for (size_t i = 0; i <= buf->idx && i < buf->max_size; ++i) { + for (size_t i = 0; i <= buf->idx && i < frames_per_toc_entry; ++i) { Frame *frame = buf->fbuf[i]; size_t msg_size = frame__get_packed_size(frame); uint8_t *packed_buffer = g_alloca(msg_size); @@ -172,7 +170,7 @@ void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { buf->frames_written++; frame_free(frame); } - memset(buf->fbuf, 0, sizeof(Frame *) * buf->max_size); + memset(buf->fbuf, 0, sizeof(buf->fbuf)); buf->idx = 0; // toc_update(); ?? } diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 5471f2503b3f8..a4cd92c2dafc3 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -9,6 +9,7 @@ #include #include "frame.piqi.pb-c-patched.h" +#include "trace_consts.h" #include "trace_meta.h" typedef enum { @@ -17,9 +18,8 @@ typedef enum { } OperandAccess; typedef struct { - Frame **fbuf; ///< The frames buffered. - size_t idx; ///< Points to currently open frame. - size_t max_size; ///< Maximum number of elements fbuf can hold. + Frame *fbuf[FRAMES_PER_TOC_ENTRY_]; ///< The frames buffered. + size_t idx; ///< Points to currently open frame. size_t frames_written; ///< Number of frames written from buffer to file. } FrameBuffer; @@ -27,7 +27,7 @@ typedef struct { * \brief Initializes a frame buffer with space for \p size frames. * Returns the buffer or NULL in case of failure. */ -FrameBuffer *frame_buffer_new(size_t size); +FrameBuffer *frame_buffer_new(void); void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h index e6af59e01e273..5aef8af7488b6 100644 --- a/contrib/plugins/bap-tracing/trace_consts.h +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -6,14 +6,20 @@ // Trace header constants static const uint64_t magic_number = 7456879624156307493LL; -static const uint64_t magic_number_offset = 0LL; -static const uint64_t trace_version_offset = 8LL; -static const uint64_t bfd_arch_offset = 16LL; -static const uint64_t bfd_machine_offset = 24LL; -static const uint64_t num_trace_frames_offset = 32LL; -static const uint64_t toc_offset_offset = 40LL; -static const uint64_t first_frame_offset = 48LL; -static const uint64_t out_trace_version = 2LL; + +static const uint64_t offset_magic_number = 0LL; +static const uint64_t offset_trace_version = 8LL; +static const uint64_t offset_target_arch = 16LL; +static const uint64_t offset_target_machine = 24LL; +static const uint64_t offset_frames_per_toc_entry = 32LL; +static const uint64_t offset_toc_index_offset = 40LL; +static const uint64_t offset_toc_start = 48LL; +static const uint64_t offset_first_frame = 48LL; + +static const uint64_t trace_version = 3LL; + +#define FRAMES_PER_TOC_ENTRY_ 64LL +static const uint64_t frames_per_toc_entry = FRAMES_PER_TOC_ENTRY_; // Arch specific diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 664fb48135e4e..7bc93aae4083f 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -152,7 +152,7 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { } g_ptr_array_insert(state.vcpus, vcpu_index, vcpu); - FrameBuffer *vcpu_frame_buffer = frame_buffer_new(FRAME_BUFFER_SIZE_DEFAULT); + FrameBuffer *vcpu_frame_buffer = frame_buffer_new(); g_ptr_array_insert(state.frame_buffer, vcpu_index, vcpu_frame_buffer); g_rw_lock_writer_unlock(&state.frame_buffer_lock); @@ -208,14 +208,14 @@ static bool write_header(FILE *file, const char *target_name) { qemu_plugin_outs("Failed to get arch/mach.\n"); return false; } - uint64_t num_frames = 0ULL; - uint64_t toc_off = 0ULL; + uint64_t num_toc_entries = 0ULL; + uint64_t toc_index_offset = 0ULL; WRITE(magic_number); - WRITE(out_trace_version); + WRITE(trace_version); WRITE(frame_arch); WRITE(frame_mach); - WRITE(num_frames); - WRITE(toc_off); + WRITE(num_toc_entries); // Gets updated later + WRITE(toc_index_offset); // Gets updated later return true; } diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 5e91c64215e9b..c0b06b742dab8 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -106,8 +106,6 @@ static struct arch_enum_entry arch_map[] = { QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -#define FRAME_BUFFER_SIZE_DEFAULT 1024 - /** * \brief VLIW architecture have instructions longer than 4 or 8bytes. */ From 512a13aa4cf5e3096621bf089bfcfa84bc466ce2 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Jun 2025 10:57:37 -0500 Subject: [PATCH 36/59] Save TOC entries when writing frames --- contrib/plugins/bap-tracing/frame_buffer.c | 3 +-- contrib/plugins/bap-tracing/frame_buffer.h | 1 - contrib/plugins/bap-tracing/tracing.c | 24 ++++++++++++++++------ contrib/plugins/bap-tracing/tracing.h | 3 +++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 06ca4d00169f9..53d122e6da2a8 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -159,6 +159,7 @@ bool frame_buffer_is_empty(const FrameBuffer *buf) { return buf->fbuf[buf->idx] == NULL; } +/// @brief Dumps the file buffer as TOC entry into the file. void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { for (size_t i = 0; i <= buf->idx && i < frames_per_toc_entry; ++i) { Frame *frame = buf->fbuf[i]; @@ -167,12 +168,10 @@ void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { uint64_t packed_size = frame__pack(frame, packed_buffer); WRITE(packed_size); WRITE_BUF(packed_buffer, packed_size); - buf->frames_written++; frame_free(frame); } memset(buf->fbuf, 0, sizeof(buf->fbuf)); buf->idx = 0; - // toc_update(); ?? } bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index a4cd92c2dafc3..272521e850716 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -20,7 +20,6 @@ typedef enum { typedef struct { Frame *fbuf[FRAMES_PER_TOC_ENTRY_]; ///< The frames buffered. size_t idx; ///< Points to currently open frame. - size_t frames_written; ///< Number of frames written from buffer to file. } FrameBuffer; /** diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 7bc93aae4083f..61ec112a73725 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -97,9 +97,19 @@ static GPtrArray *registers_init(void) { return registers->len ? g_steal_pointer(®isters) : NULL; } +static void write_toc_entry(FrameBuffer *fbuf) { + g_rw_lock_writer_lock(&state.file_lock); + g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); + frame_buffer_flush_to_file(fbuf, state.file); + uint64_t next_toc_entry = ftell(state.file); + g_array_append_val(state.toc_entries_offsets, next_toc_entry); + g_rw_lock_writer_unlock(&state.toc_entries_offsets_lock); + g_rw_lock_writer_unlock(&state.file_lock); +} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); - g_rw_lock_reader_lock(&state.frame_buffer_lock); + g_rw_lock_writer_lock(&state.frame_buffer_lock); FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); VCPU *vcpu = g_ptr_array_index(state.vcpus, vcpu_index); @@ -113,9 +123,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { } if (frame_buffer_is_full(fbuf)) { - g_rw_lock_writer_lock(&state.file_lock); - frame_buffer_flush_to_file(fbuf, state.file); - g_rw_lock_writer_unlock(&state.file_lock); + write_toc_entry(fbuf); } // Open new one. @@ -123,7 +131,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); - g_rw_lock_reader_unlock(&state.frame_buffer_lock); + g_rw_lock_writer_unlock(&state.frame_buffer_lock); g_rw_lock_reader_unlock(&state.vcpus_array_lock); } @@ -228,9 +236,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const char *target_path = "/tmp/test.trace"; state.frame_buffer = g_ptr_array_new(); + state.toc_entries_offsets = g_array_new(false, true, sizeof(uint64_t)); state.vcpus = g_ptr_array_new(); state.file = fopen(target_path, "wb"); - if (!(state.frame_buffer || state.vcpus || state.file)) { + if (!(state.frame_buffer || state.vcpus || state.file || + !state.toc_entries_offsets)) { return 1; } for (size_t i = 0; i < argc; ++i) { @@ -242,6 +252,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } // write_meta(argv, envp, target_argv, target_envp); + g_array_append_val(state.toc_entries_offsets, offset_toc_start); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_vcpu_tb_trans_cb(id, cb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index c0b06b742dab8..86abe8ee10cd8 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -134,6 +134,9 @@ typedef struct { GRWLock frame_buffer_lock; GPtrArray /**/ *frame_buffer; ///< Indexed by vcpu id + GRWLock toc_entries_offsets_lock; + GArray /**/ *toc_entries_offsets; + GRWLock file_lock; FILE *file; } TraceState; From c556a3ff0d331c931f9bf59afa3867a728ca23d0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Jun 2025 11:31:18 -0500 Subject: [PATCH 37/59] Finish write of trace. --- README.md | 2 +- contrib/plugins/bap-tracing/frame_buffer.c | 5 +- contrib/plugins/bap-tracing/frame_buffer.h | 2 +- contrib/plugins/bap-tracing/trace_consts.h | 2 +- contrib/plugins/bap-tracing/trace_meta.h | 12 ++++- contrib/plugins/bap-tracing/tracing.c | 56 ++++++++++++++++++---- contrib/plugins/bap-tracing/tracing.h | 3 ++ 7 files changed, 67 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 71354f5314d54..de30c17145b64 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ the BAP-frames repository. | 0x8 | uint64_t | trace version number | | | 0x10 | uint64_t | frame_architecture | | | 0x18 | uint64_t | frame_machine, 0 for unspecified. | | -| 0x20 | uint64_t | n = number of frames per TOC entry. | | +| 0x20 | uint64_t | n = total number of frames in trace. | | | 0x28 | uint64_t | T = offset to TOC index. | | | 0x30 | uint64_t | sizeof(frame_0) | TOC begin | | 0x38 | meta_frame | frame_0 | | diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 53d122e6da2a8..608d176df4076 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -160,7 +160,8 @@ bool frame_buffer_is_empty(const FrameBuffer *buf) { } /// @brief Dumps the file buffer as TOC entry into the file. -void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { + uint64_t n = 0; for (size_t i = 0; i <= buf->idx && i < frames_per_toc_entry; ++i) { Frame *frame = buf->fbuf[i]; size_t msg_size = frame__get_packed_size(frame); @@ -169,9 +170,11 @@ void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { WRITE(packed_size); WRITE_BUF(packed_buffer, packed_size); frame_free(frame); + n++; } memset(buf->fbuf, 0, sizeof(buf->fbuf)); buf->idx = 0; + return n; } bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 272521e850716..1a6bb0c15bd24 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -28,7 +28,7 @@ typedef struct { */ FrameBuffer *frame_buffer_new(void); -void frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); bool frame_buffer_is_full(const FrameBuffer *buf); bool frame_buffer_is_empty(const FrameBuffer *buf); void frame_buffer_close_frame(FrameBuffer *buf); diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h index 5aef8af7488b6..21c777d7cc39a 100644 --- a/contrib/plugins/bap-tracing/trace_consts.h +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -11,7 +11,7 @@ static const uint64_t offset_magic_number = 0LL; static const uint64_t offset_trace_version = 8LL; static const uint64_t offset_target_arch = 16LL; static const uint64_t offset_target_machine = 24LL; -static const uint64_t offset_frames_per_toc_entry = 32LL; +static const uint64_t offset_total_num_frames = 32LL; static const uint64_t offset_toc_index_offset = 40LL; static const uint64_t offset_toc_start = 48LL; static const uint64_t offset_first_frame = 48LL; diff --git a/contrib/plugins/bap-tracing/trace_meta.h b/contrib/plugins/bap-tracing/trace_meta.h index a4eddce6eea56..c918fad2f5632 100644 --- a/contrib/plugins/bap-tracing/trace_meta.h +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -4,6 +4,8 @@ #ifndef BAP_TRACE_META_H #define BAP_TRACE_META_H +#include + /** * \brief Empty macros indicate the argument, variable etc. * must be locked for writing. @@ -13,13 +15,19 @@ #define WRITE(x) \ do { \ if (fwrite(&(x), sizeof(x), 1, file) != 1) \ - qemu_plugin_outs("fwrite failed"); \ + err(1, "fwrite failed"); \ } while (0) #define WRITE_BUF(x, n) \ do { \ if (fwrite((x), 1, (n), file) != n) \ - qemu_plugin_outs("fwrite failed"); \ + err(1, "fwrite failed"); \ + } while (0) + +#define SEEK(off) \ + do { \ + if (fseek(file, (off), SEEK_SET) < 0) \ + err(1, "stream not seekable"); \ } while (0) #endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 61ec112a73725..342160c915b1c 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -2,13 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "frame_arch.h" #include "frame_buffer.h" #include "qemu-plugin.h" +#include "trace_consts.h" #include "tracing.h" -static TraceState state; +static TraceState state = {0}; static void add_mem_op(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, uint64_t vaddr, qemu_plugin_mem_value *mval, @@ -100,9 +102,13 @@ static GPtrArray *registers_init(void) { static void write_toc_entry(FrameBuffer *fbuf) { g_rw_lock_writer_lock(&state.file_lock); g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); - frame_buffer_flush_to_file(fbuf, state.file); + g_rw_lock_writer_lock(&state.total_num_frames_lock); + + state.total_num_frames += frame_buffer_flush_to_file(fbuf, state.file); uint64_t next_toc_entry = ftell(state.file); g_array_append_val(state.toc_entries_offsets, next_toc_entry); + + g_rw_lock_writer_unlock(&state.total_num_frames_lock); g_rw_lock_writer_unlock(&state.toc_entries_offsets_lock); g_rw_lock_writer_unlock(&state.file_lock); } @@ -155,9 +161,7 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { VCPU *vcpu = g_malloc0(sizeof(VCPU)); vcpu->registers = registers_init(); - if (!vcpu->registers) { - g_assert(false); - } + g_assert(vcpu->registers); g_ptr_array_insert(state.vcpus, vcpu_index, vcpu); FrameBuffer *vcpu_frame_buffer = frame_buffer_new(); @@ -191,12 +195,46 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { } static void plugin_exit(qemu_plugin_id_t id, void *udata) { - // Dump rest of frames to file. + g_rw_lock_writer_lock(&state.frame_buffer_lock); + for (size_t i = 0; i < state.vcpus->len; ++i) { + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); + write_toc_entry(fbuf); + } + g_rw_lock_writer_unlock(&state.frame_buffer_lock); + + g_rw_lock_writer_lock(&state.file_lock); + g_rw_lock_reader_lock(&state.toc_entries_offsets_lock); + g_rw_lock_reader_lock(&state.total_num_frames_lock); + + FILE *file = state.file; + + // Update fields in the header + uint64_t toc_index_offset = ftell(file); + SEEK(offset_toc_index_offset); + WRITE(toc_index_offset); + SEEK(offset_total_num_frames); + WRITE(state.total_num_frames); + + // Write the TOC index + SEEK(toc_index_offset); + uint64_t m = state.toc_entries_offsets->len; + WRITE(m); + + for (size_t i = 0; i < m; ++i) { + uint64_t toc_entry_off = + g_array_index(state.toc_entries_offsets, uint64_t, i); + WRITE(toc_entry_off); + } + fclose(file); + + g_rw_lock_reader_unlock(&state.total_num_frames_lock); + g_rw_lock_reader_unlock(&state.toc_entries_offsets_lock); + g_rw_lock_writer_unlock(&state.file_lock); } static bool get_frame_arch_mach(const char *target_name, uint64_t *arch, uint64_t *mach) { - *arch = 0; + *mach = 0; *arch = frame_arch_last; const char *aname = arch_map[0].name; for (size_t i = 0; arch_map[i].name; ++i) { @@ -216,13 +254,13 @@ static bool write_header(FILE *file, const char *target_name) { qemu_plugin_outs("Failed to get arch/mach.\n"); return false; } - uint64_t num_toc_entries = 0ULL; + uint64_t total_num_frames = 0ULL; uint64_t toc_index_offset = 0ULL; WRITE(magic_number); WRITE(trace_version); WRITE(frame_arch); WRITE(frame_mach); - WRITE(num_toc_entries); // Gets updated later + WRITE(total_num_frames); // Gets updated later WRITE(toc_index_offset); // Gets updated later return true; } diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 86abe8ee10cd8..ad9dfe908355e 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -137,6 +137,9 @@ typedef struct { GRWLock toc_entries_offsets_lock; GArray /**/ *toc_entries_offsets; + GRWLock total_num_frames_lock; + uint64_t total_num_frames; + GRWLock file_lock; FILE *file; } TraceState; From 4608ba4790f7f962900c8dca10f88a29150d460d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sat, 28 Jun 2025 12:25:33 -0500 Subject: [PATCH 38/59] Remove tracewrap files --- contrib/plugins/bap-tracing/meson.build | 1 - contrib/plugins/bap-tracing/tracewrap.c | 361 ------------------------ contrib/plugins/bap-tracing/tracewrap.h | 60 ---- 3 files changed, 422 deletions(-) delete mode 100644 contrib/plugins/bap-tracing/tracewrap.c delete mode 100644 contrib/plugins/bap-tracing/tracewrap.h diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index 0a64d499aea70..e90d0866bd45b 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -41,7 +41,6 @@ dep_libprotobuf = declare_dependency( bap_tracing_src = files( 'frame_buffer.c', 'tracing.c', - 'tracewrap.c', ) if host_os == 'windows' diff --git a/contrib/plugins/bap-tracing/tracewrap.c b/contrib/plugins/bap-tracing/tracewrap.c deleted file mode 100644 index e1bf8afbb0a2e..0000000000000 --- a/contrib/plugins/bap-tracing/tracewrap.c +++ /dev/null @@ -1,361 +0,0 @@ -#include "tracewrap.h" -#include "trace_consts.h" - -#include -#include - -#include -#include -#include - -#include -#include - - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -char tracer_name[] = "qemu"; -char tracer_version[] = "2.0.0/tracewrap"; - -static Frame * g_frame; -static uint64_t frames_per_toc_entry = 64LL; -static uint32_t open_frame = 0; -// static FILE *file = NULL; - -/* don't use the following data directly! - use toc_init, toc_update and toc_write functions instead */ -static uint64_t *toc = NULL; -static int toc_entries = 0; -static int toc_capacity = 0; -static uint64_t toc_num_frames = 0; - -#define MD5LEN 16 -static guchar target_md5[MD5LEN]; -static char target_path[PATH_MAX] = "unknown"; -#pragma GCC diagnostic pop - - -#define WRITE(x) do { \ - if (!file) \ - err(1, "qemu_trace is not initialized"); \ - if (fwrite(&(x), sizeof(x),1,file) != 1) \ - err(1, "fwrite failed"); \ - } while(0) - -#define WRITE_BUF(x,n) do { \ - if (!file) \ - err(1, "qemu_trace is not initialized"); \ - if (fwrite((x),1,(n),file) != n) \ - err(1, "fwrite failed"); \ - } while(0) - -#define SEEK(off) do { \ - if (fseek(file,(off), SEEK_SET) < 0) \ - err(1, "stream not seekable"); \ - } while(0) - - -// static void toc_init(void) { -// if (toc_entries != 0) -// err(1, "qemu_trace was initialized twice"); -// toc = g_new(uint64_t, 1024); -// toc_capacity = 1024; -// toc_entries = 0; -// } - -// static void toc_append(uint64_t entry) { -// if (toc_capacity <= toc_entries) { -// toc = g_renew(uint64_t, toc, toc_capacity * 2); -// toc_capacity *= 2; -// } -// toc[toc_entries++] = entry; -// } - -// static void toc_write(void) { -// int64_t toc_offset = ftell(file); -// if (toc_offset > 0) { -// int i = 0; -// WRITE(frames_per_toc_entry); -// for (i = 0; i < toc_entries; i++) -// WRITE(toc[i]); -// SEEK(num_trace_frames_offset); -// WRITE(toc_num_frames); -// SEEK(toc_offset_offset); -// WRITE(toc_offset); -// } -// } - -// static void toc_update(void) { -// toc_num_frames++; -// if (toc_num_frames % frames_per_toc_entry == 0) { -// int64_t off = ftell(file); -// if (off >= 0) toc_append(off); -// } -// } - -// static void write_header(void) { -// uint64_t toc_off = 0L; -// WRITE(magic_number); -// WRITE(out_trace_version); -// WRITE(frame_arch); -// WRITE(frame_mach); -// WRITE(toc_num_frames); -// WRITE(toc_off); -// } - -// static int list_length(char **list) { -// int n=0; -// if (list) { -// char **p = list; -// for (;*p;p++,n++); -// } -// return n; -// } - -// static void compute_target_md5(void) { -// const GChecksumType md5 = G_CHECKSUM_MD5; -// GChecksum *cs = g_checksum_new(md5); -// FILE *target = fopen(target_path, "r"); -// guchar buf[BUFSIZ]; -// gsize expected_length = MD5LEN; - -// if (!cs) err(1, "failed to create a checksum"); -// if (!target) err(1, "failed to open target binary"); -// if (g_checksum_type_get_length(md5) != expected_length) abort(); - -// while (!feof(target)) { -// size_t len = fread(buf,1,BUFSIZ,target); -// if (ferror(target)) -// err(1, "failed to read target binary"); -// g_checksum_update(cs, buf, len); -// } - -// g_checksum_get_digest(cs, target_md5, &expected_length); -// fclose(target); -// } - -// static void store_to_trace(ProtobufCBuffer *self, size_t len, const uint8_t *data) { -// WRITE_BUF(data,len); -// } - -// static void init_tracer(Tracer *tracer, char **argv, char **envp) { -// tracer__init(tracer); -// tracer->name = tracer_name; -// tracer->n_args = list_length(argv); -// tracer->args = argv; -// tracer->n_envp = list_length(envp); -// tracer->envp = envp; -// tracer->version = tracer_version; -// } - -// static void init_target(Target *target, char **argv, char **envp) { -// compute_target_md5(); - -// target__init(target); -// target->path = target_path; -// target->n_args = list_length(argv); -// target->args = argv; -// target->n_envp = list_length(envp); -// target->envp = envp; -// target->md5sum.len = MD5LEN; -// target->md5sum.data = target_md5; -// } - -// #ifdef G_OS_UNIX -// static void unix_fill_fstats(Fstats *fstats, char *path) { -// struct stat stats; -// if (stat(path, &stats) < 0) -// err(1, "failed to obtain file stats"); - -// fstats->size = stats.st_size; -// fstats->atime = stats.st_atime; -// fstats->mtime = stats.st_mtime; -// fstats->ctime = stats.st_ctime; -// } -// #endif - - -// static void init_fstats(Fstats *fstats) { -// fstats__init(fstats); -// #ifdef G_OS_UNIX -// unix_fill_fstats(fstats, target_path); -// #endif -// } - - -// static void write_meta( -// char **tracer_argv, -// char **tracer_envp, -// char **target_argv, -// char **target_envp) -// { -// MetaFrame meta; -// Tracer tracer; -// Target target; -// Fstats fstats; -// ProtobufCBuffer buffer; - -// buffer.append = store_to_trace; - - -// meta_frame__init(&meta); -// init_tracer(&tracer, tracer_argv, tracer_envp); -// init_target(&target, target_argv, target_envp); -// init_fstats(&fstats); - -// meta.tracer = &tracer; -// meta.target = ⌖ -// meta.fstats = &fstats; -// meta.time = time(NULL); -// char *user = g_strdup(g_get_real_name()); -// meta.user = user; - -// char *host = g_strdup(g_get_host_name()); -// meta.host = host; - -// uint64_t size = meta_frame__get_packed_size(&meta); -// WRITE(size); - -// meta_frame__pack_to_buffer(&meta, &buffer); - -// free(user); -// free(host); -// } - - -void qemu_trace_init(const char *filename, - const char *targetname, - char **argv, char **envp, - char **target_argv, - char **target_envp) { - // qemu_log("Initializing tracer\n"); - // if (realpath(targetname,target_path) == NULL) - // err(1, "can't get target path"); - - - // char *name = filename - // ? g_strdup(filename) - // : g_strdup_printf("%s.frames", basename(target_path)); - // file = fopen(name, "wb"); - // if (file == NULL) - // err(1, "tracewrap: can't open trace file %s", name); - // write_header(); - // write_meta(argv, envp, target_argv, target_envp); - // toc_init(); - // g_free(name); -} - - -void qemu_trace_newframe(uint64_t addr, int __unused/*thread_id*/ ) { -// int thread_id = 1; -// if (open_frame) { -// qemu_log("frame is still open"); -// qemu_trace_endframe(NULL, 0, 0); -// } - -// open_frame = 1; -// g_frame = g_new(Frame,1); -// frame__init(g_frame); - -// StdFrame *sframe = g_new(StdFrame, 1); -// std_frame__init(sframe); -// g_frame->std_frame = sframe; - -// sframe->address = addr; -// sframe->thread_id = thread_id; - -// OperandValueList *ol_in = g_new(OperandValueList,1); -// operand_value_list__init(ol_in); -// ol_in->n_elem = 0; -// sframe->operand_pre_list = ol_in; - -// OperandValueList *ol_out = g_new(OperandValueList,1); -// operand_value_list__init(ol_out); -// ol_out->n_elem = 0; -// sframe->operand_post_list = ol_out; -} - -static inline void free_operand(OperandInfo *oi) { -// OperandInfoSpecific *ois = oi->operand_info_specific; - -// //Free reg-operand -// RegOperand *ro = ois->reg_operand; -// if (ro && ro->name) -// g_free(ro->name); -// g_free(ro); - -// //Free mem-operand -// MemOperand *mo = ois->mem_operand; -// g_free(mo); -// g_free(oi->value.data); -// g_free(oi->taint_info); -// g_free(ois); -// g_free(oi->operand_usage); -// g_free(oi); -// } - -// void qemu_trace_add_operand(OperandInfo *oi, int inout) { -// if (!open_frame) { -// if (oi) -// free_operand(oi); -// return; -// } -// OperandValueList *ol; -// if (inout & 0x1) { -// ol = g_frame->std_frame->operand_pre_list; -// } else { -// ol = g_frame->std_frame->operand_post_list; -// } - -// oi->taint_info = g_new(TaintInfo, 1); -// taint_info__init(oi->taint_info); -// oi->taint_info->no_taint = 1; -// oi->taint_info->has_no_taint = 1; - -// ol->n_elem += 1; -// ol->elem = g_renew(OperandInfo *, ol->elem, ol->n_elem); -// ol->elem[ol->n_elem - 1] = oi; -// } - -// void qemu_trace_endframe(CPUArchState *env, target_ulong pc, target_ulong size) { -// int i = 0; -// StdFrame *sframe = g_frame->std_frame; - -// if (!open_frame) return; - -// sframe->rawbytes.len = size; -// sframe->rawbytes.data = g_malloc(size); -// for (i = 0; i < size; i++) { -// sframe->rawbytes.data[i] = cpu_ldub_code(env, pc+i); -// } - -// size_t msg_size = frame__get_packed_size(g_frame); -// uint8_t *packed_buffer = g_alloca(msg_size); -// uint64_t packed_size = frame__pack(g_frame, packed_buffer); -// WRITE(packed_size); -// WRITE_BUF(packed_buffer, packed_size); -// toc_update(); - -// //counting num_frames in newframe does not work by far ... -// //how comes? disas_arm_insn might not always return at the end? -// for (i = 0; i < sframe->operand_pre_list->n_elem; i++) -// free_operand(sframe->operand_pre_list->elem[i]); -// g_free(sframe->operand_pre_list->elem); -// g_free(sframe->operand_pre_list); - -// for (i = 0; i < sframe->operand_post_list->n_elem; i++) -// free_operand(sframe->operand_post_list->elem[i]); -// g_free(sframe->operand_post_list->elem); -// g_free(sframe->operand_post_list); - -// g_free(sframe->rawbytes.data); -// g_free(sframe); -// g_free(g_frame); -// open_frame = 0; -} - -void qemu_trace_finish(uint32_t exit_code) { - // toc_write(); - // if (fclose(file) != 0) - // err(1,"failed to write trace file, the file maybe corrupted"); -} diff --git a/contrib/plugins/bap-tracing/tracewrap.h b/contrib/plugins/bap-tracing/tracewrap.h deleted file mode 100644 index c52d17bbf04df..0000000000000 --- a/contrib/plugins/bap-tracing/tracewrap.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include - -#include "frame.piqi.pb-c-patched.h" - - -/** initializes trace subsystem. - - All pointers are owned by the caller. - - @param filename a name of filesystem entry where trace will be dumpled, - if NULL then the name is basename(argv[0]).frames - - @param targetname a path to the executable, must be non NULL - - - @param argv a full list of arguments passed to the tracer, NULL terminated. - Can be NULL or empty (i.e., contain only a NULL element). - The list may include target arguments. - - @param envp a null terminated list of environment parameters, - can be NULL or empty. - - @param target_argv a null terminated list of target arguments, - can be NULL or empty. - - @param target_envp a null terminated list of target environment, - can be NULL or empty. - */ -void qemu_trace_init(const char *filename, const char *targetname, - char **argv, char **envp, - char **target_argv, - char **target_envp); -void qemu_trace_newframe(uint64_t addr, int tread_id); -void qemu_trace_add_operand(OperandInfo *oi, int inout); -void qemu_trace_endframe(void *env, uint64_t pc, uint64_t size); -void qemu_trace_finish(uint32_t exit_code); - -OperandInfo * load_store_reg(uint64_t reg, uint64_t val, int ls); -OperandInfo * load_store_mem(uint64_t addr, uint64_t val, int ls, int len); - -#define REG_EFLAGS 66 -#define REG_LO 33 -#define REG_HI 34 - -#define REG_CPSR 64 -#define REG_APSR 65 -#define REG_SP 13 -#define REG_LR 14 -#define REG_PC 15 - -#define REG_NF 94 -#define REG_ZF 95 -#define REG_CF 96 -#define REG_VF 97 -#define REG_QF 98 -#define REG_GE 99 - -#define SEG_BIT 8 From f88cb26a83938f4c810d5edd4b232435bf26a2c1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 30 Jun 2025 12:56:04 -0500 Subject: [PATCH 39/59] Write meta frames and add outputfile argument. --- contrib/plugins/bap-tracing/frame_buffer.c | 6 +- contrib/plugins/bap-tracing/meson.build | 1 + contrib/plugins/bap-tracing/trace_consts.h | 4 + contrib/plugins/bap-tracing/trace_meta.c | 152 +++++++++++++++------ contrib/plugins/bap-tracing/trace_meta.h | 5 + contrib/plugins/bap-tracing/tracing.c | 18 ++- 6 files changed, 133 insertions(+), 53 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 608d176df4076..d94cb294370d7 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -98,9 +98,9 @@ bool frame_buffer_is_full(const FrameBuffer *buf) { void frame_buffer_close_frame(FrameBuffer *buf) { char *str = frame_buffer_as_str(buf); - qemu_plugin_outs("Close frame: "); - qemu_plugin_outs(str); - qemu_plugin_outs("\n\n"); + // qemu_plugin_outs("Close frame: "); + // qemu_plugin_outs(str); + // qemu_plugin_outs("\n\n"); g_free(str); buf->idx++; } diff --git a/contrib/plugins/bap-tracing/meson.build b/contrib/plugins/bap-tracing/meson.build index e90d0866bd45b..08ddca168141b 100644 --- a/contrib/plugins/bap-tracing/meson.build +++ b/contrib/plugins/bap-tracing/meson.build @@ -41,6 +41,7 @@ dep_libprotobuf = declare_dependency( bap_tracing_src = files( 'frame_buffer.c', 'tracing.c', + 'trace_meta.c', ) if host_os == 'windows' diff --git a/contrib/plugins/bap-tracing/trace_consts.h b/contrib/plugins/bap-tracing/trace_consts.h index 21c777d7cc39a..892d7cf885794 100644 --- a/contrib/plugins/bap-tracing/trace_consts.h +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -1,6 +1,7 @@ #ifndef BAP_TRACE_CONSTS_H #define BAP_TRACE_CONSTS_H +#include "qemu-version.h" #include // Trace header constants @@ -18,6 +19,9 @@ static const uint64_t offset_first_frame = 48LL; static const uint64_t trace_version = 3LL; +#define TRACER_NAME "qemu" +#define TRACER_VERSION "plugin " QEMU_FULL_VERSION + #define FRAMES_PER_TOC_ENTRY_ 64LL static const uint64_t frames_per_toc_entry = FRAMES_PER_TOC_ENTRY_; diff --git a/contrib/plugins/bap-tracing/trace_meta.c b/contrib/plugins/bap-tracing/trace_meta.c index edb4ff9e1961a..b3ec4596c0d6f 100644 --- a/contrib/plugins/bap-tracing/trace_meta.c +++ b/contrib/plugins/bap-tracing/trace_meta.c @@ -7,13 +7,14 @@ #include #include "frame.piqi.pb-c-patched.h" +#include "trace_consts.h" #include "trace_meta.h" #define MD5LEN 16 -static void compute_target_md5(const char *binary_path) { +static void compute_target_md5(const char *binary_path, + guchar target_md5[MD5LEN]) { const GChecksumType md5 = G_CHECKSUM_MD5; - guchar target_md5[MD5LEN]; GChecksum *cs = g_checksum_new(md5); FILE *target = fopen(binary_path, "r"); @@ -38,37 +39,31 @@ static void compute_target_md5(const char *binary_path) { fclose(target); } -static void meta_write_header(FILE *file) { - // uint64_t toc_off = 0L; - // WRITE(magic_number); - // WRITE(out_trace_version); - // WRITE(frame_arch); - // WRITE(frame_mach); - // WRITE(toc_num_frames); - // WRITE(toc_off); +static void init_tracer(Tracer *tracer, char **argv, int argc) { + tracer__init(tracer); + tracer->name = g_strdup(TRACER_NAME); + tracer->n_args = argc; + tracer->args = argv; + tracer->n_envp = 0; + tracer->envp = NULL; + tracer->version = g_strdup(TRACER_VERSION); } -static void init_tracer(Tracer *tracer, char **argv, char **envp) { - // tracer__init(tracer); - // tracer->name = tracer_name; - // tracer->n_args = list_length(argv); - // tracer->args = argv; - // tracer->n_envp = list_length(envp); - // tracer->envp = envp; - // tracer->version = tracer_version; -} +static void init_target(Target *target, const char *bin_path, char **argv, + int argc) { + target__init(target); -static void init_target(Target *target, char **argv, char **envp) { - // compute_target_md5(); - - // target__init(target); - // target->path = target_path; - // target->n_args = list_length(argv); - // target->args = argv; - // target->n_envp = list_length(envp); - // target->envp = envp; - // target->md5sum.len = MD5LEN; - // target->md5sum.data = target_md5; + if (bin_path) { + guchar *target_md5 = g_malloc0(MD5LEN); + compute_target_md5(bin_path, target_md5); + target->path = g_strdup(bin_path); + target->md5sum.len = MD5LEN; + target->md5sum.data = target_md5; + } + target->n_args = argc; + target->args = argv; + target->n_envp = 0; + target->envp = NULL; } #ifdef G_OS_UNIX @@ -95,18 +90,74 @@ static bool init_fstats(Fstats *fstats, const char *binary_path) { return true; } -static void write_meta(WLOCKED FILE *file, char **tracer_argv, - char **tracer_envp, char **target_argv, - char **target_envp) { - MetaFrame meta; - Tracer tracer; - Target target; - Fstats fstats; +char *get_argv_val(char **argv, int argc, const char *key) { + for (size_t i = 0; i < argc; ++i) { + if (!strncmp(argv[i], key, strlen(key))) { + const char *val = argv[i] + strlen(key); + if (val[0] != '=') { + qemu_plugin_outs("Invalid argument value for "); + qemu_plugin_outs(key); + qemu_plugin_outs("\n"); + qemu_plugin_outs("Should be 'key=val'\n"); + return NULL; + } + val++; + const char *end = strchr(val, ','); + while (end && *(end - 1) == '\\') { + // Allow escaped commas. + end = strchr(val, ','); + } + size_t len = !end ? strlen(val) : end - val; + char *argument = g_malloc0(len + 1); + memcpy(argument, val, len); + return argument; + } + } + return NULL; +} + +void file_exists_exit(const char *file) { + FILE *test = fopen(file, "r"); + if (!test) { + qemu_plugin_outs("Failed to open binary file: "); + qemu_plugin_outs(file); + qemu_plugin_outs("\n"); + exit(1); + } + fclose(test); +} + +void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc) { + char *arg_bin_path = get_argv_val(plugin_argv, plugin_argc, "bin_path"); + // Note: Usually we should get the binary path from + // qemu_plugin_path_to_binary(). But it doesn't seem to work due to dependency + // issues. See: https://gitlab.com/qemu-project/qemu/-/issues/3014 + const char *bin_path = NULL; // qemu_plugin_path_to_binary(); + if (!bin_path && !arg_bin_path) { + qemu_plugin_outs("\nFailed to retrieve the binary path\n"); + qemu_plugin_outs("This is required.\n"); + qemu_plugin_outs("You can pass it as plugin argument " + "'bin_path='.\n\n"); + exit(1); + } else { + file_exists_exit(arg_bin_path); + } + if (bin_path && arg_bin_path) { + qemu_plugin_outs( + "'bin_path' argument found, but the binary path is known to the module.\n\ + Argument 'bin_path' is ignored.\n"); + } + + MetaFrame meta = {0}; + Tracer tracer = {0}; + Target target = {0}; + Fstats fstats = {0}; meta_frame__init(&meta); - init_tracer(&tracer, tracer_argv, tracer_envp); - init_target(&target, target_argv, target_envp); - init_fstats(&fstats, "target-path"); + init_tracer(&tracer, plugin_argv, plugin_argc); + init_target(&target, bin_path ? bin_path : arg_bin_path, plugin_argv, + plugin_argc); + init_fstats(&fstats, bin_path ? bin_path : arg_bin_path); meta.tracer = &tracer; meta.target = ⌖ @@ -119,11 +170,24 @@ static void write_meta(WLOCKED FILE *file, char **tracer_argv, meta.host = host; size_t msg_size = meta_frame__get_packed_size(&meta); - uint8_t *packed_buffer = g_alloca(msg_size); + uint8_t *packed_buffer = g_malloc0(msg_size); uint64_t packed_size = meta_frame__pack(&meta, packed_buffer); + g_assert(msg_size == packed_size); WRITE(packed_size); - WRITE_BUF(&meta, packed_size); - free(user); - free(host); + // I don't know why, but ASAN crashes at this line if the WRITE_BUF macro + // is used. Although it should be the exact same code. + if (fwrite((packed_buffer), 1, (packed_size), file) != packed_size) { + err(1, "fwrite failed"); + } + + g_free(packed_buffer); + g_free(tracer.name); + g_free(tracer.version); + g_free(target.path); + g_free(target.md5sum.data); + + g_free(user); + g_free(host); + g_free(arg_bin_path); } diff --git a/contrib/plugins/bap-tracing/trace_meta.h b/contrib/plugins/bap-tracing/trace_meta.h index c918fad2f5632..2e9e1c19eef87 100644 --- a/contrib/plugins/bap-tracing/trace_meta.h +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -5,6 +5,7 @@ #define BAP_TRACE_META_H #include +#include /** * \brief Empty macros indicate the argument, variable etc. @@ -30,4 +31,8 @@ err(1, "stream not seekable"); \ } while (0) +void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc); +char *get_argv_val(char **argv, int argc, const char *key); +void file_exists_exit(const char *file); + #endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 342160c915b1c..98a5047464eba 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -3,6 +3,7 @@ #include #include +#include #include "frame_arch.h" #include "frame_buffer.h" @@ -230,6 +231,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { g_rw_lock_reader_unlock(&state.total_num_frames_lock); g_rw_lock_reader_unlock(&state.toc_entries_offsets_lock); g_rw_lock_writer_unlock(&state.file_lock); + qemu_plugin_outs("Finished trace\n"); } static bool get_frame_arch_mach(const char *target_name, uint64_t *arch, @@ -271,24 +273,28 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, qemu_plugin_outs("Target name: "); qemu_plugin_outs(info->target_name); qemu_plugin_outs("\n"); + char *output = get_argv_val(argv, argc, "out"); + if (!output) { + qemu_plugin_outs("'out' argument is missing.\n"); + qemu_plugin_outs("This is required.\n"); + qemu_plugin_outs("Pass it with 'out='.\n\n"); + exit(1); + } - const char *target_path = "/tmp/test.trace"; state.frame_buffer = g_ptr_array_new(); state.toc_entries_offsets = g_array_new(false, true, sizeof(uint64_t)); state.vcpus = g_ptr_array_new(); - state.file = fopen(target_path, "wb"); + state.file = fopen(output, "wb"); if (!(state.frame_buffer || state.vcpus || state.file || !state.toc_entries_offsets)) { return 1; } - for (size_t i = 0; i < argc; ++i) { - qemu_plugin_outs(argv[i]); - } + g_free(output); if (!write_header(state.file, info->target_name)) { qemu_plugin_outs("Failed to header.\n"); return 1; } - // write_meta(argv, envp, target_argv, target_envp); + write_meta(state.file, argv, argc); g_array_append_val(state.toc_entries_offsets, offset_toc_start); From 00fd9c57df761158295aae8e9d148cc9087c9557 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 30 Jun 2025 13:55:04 -0500 Subject: [PATCH 40/59] Document tracing --- README.md | 12 ++++++++++++ contrib/plugins/bap-tracing/frame_buffer.c | 1 + 2 files changed, 13 insertions(+) diff --git a/README.md b/README.md index de30c17145b64..22aacb8455730 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,18 @@ cd build make ``` +## Tracing a binary + +The plugin takes two required arguments: + +`bin_path`: The path to the binary emulated. Due to a [QEMU bug](https://gitlab.com/qemu-project/qemu/-/issues/3014) this cannot be inferred. +`out`: The output file to save the trace into. + +```bash +./qemu-sparc64 -plugin file=./contrib/plugins/bap-tracing/libbap_tracing.so,bin_path=,out= -d plugin +ls +``` + ## Trace format The generated trace consists of three parts: the header, diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index d94cb294370d7..39fd178e4bdb8 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -271,6 +271,7 @@ static size_t mval_type_to_int(enum qemu_plugin_mem_value_type type) { default: g_assert(false); } + return 0; } static void mval_to_buf(qemu_plugin_mem_value *val, uint8_t *buf) { From 799153c77eeca9c7bdea74dd13bbc55fb4d8f87d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 1 Jul 2025 07:30:16 -0500 Subject: [PATCH 41/59] Remove duplicate code. --- contrib/plugins/bap-tracing/frame_buffer.c | 33 ++++++---------------- contrib/plugins/bap-tracing/tracing.c | 4 ++- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 39fd178e4bdb8..7e6c90ee524fb 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -4,7 +4,8 @@ #include "frame_buffer.h" #include "trace_meta.h" -static Frame *frame_new_std(uint64_t addr, int vcpu_id) { +static Frame *frame_new_std(uint64_t addr, int vcpu_id, uint8_t *bytes, + size_t bytes_len) { Frame *frame = g_new(Frame, 1); frame__init(frame); @@ -14,6 +15,9 @@ static Frame *frame_new_std(uint64_t addr, int vcpu_id) { sframe->address = addr; sframe->thread_id = vcpu_id; + sframe->rawbytes.len = bytes_len; + sframe->rawbytes.data = g_malloc(bytes_len); + memcpy(sframe->rawbytes.data, bytes, bytes_len); OperandValueList *ol_in = g_new(OperandValueList, 1); operand_value_list__init(ol_in); @@ -183,29 +187,10 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, if (frame_buffer_is_full(buf)) { return false; } - Frame *frame = frame_new_std(0, -1); - frame__init(frame); - - StdFrame *stdframe = g_new(StdFrame, 1); - std_frame__init(stdframe); - frame->std_frame = stdframe; - - stdframe->thread_id = thread_id; - stdframe->address = vaddr; - stdframe->rawbytes.len = bytes_len; - stdframe->rawbytes.data = g_malloc(bytes_len); - memcpy(stdframe->rawbytes.data, bytes, bytes_len); - - OperandValueList *ol_in = g_new(OperandValueList, 1); - operand_value_list__init(ol_in); - ol_in->n_elem = 0; - stdframe->operand_pre_list = ol_in; - - OperandValueList *ol_out = g_new(OperandValueList, 1); - operand_value_list__init(ol_out); - ol_out->n_elem = 0; - stdframe->operand_post_list = ol_out; - + Frame *frame = frame_new_std(vaddr, thread_id, bytes, bytes_len); + if (!frame) { + return false; + } buf->fbuf[buf->idx] = frame; return true; } diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 98a5047464eba..f5f3b9f72f8d4 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -135,7 +135,9 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { // Open new one. Instruction *insn = udata; - add_new_insn_frame(vcpu, vcpu_index, fbuf, insn); + if (!add_new_insn_frame(vcpu, vcpu_index, fbuf, insn)) { + err(1, "Failed to add new frame.\n"); + } add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); g_rw_lock_writer_unlock(&state.frame_buffer_lock); From bc07dbfb544d70a79baebee4522c37127b9f39a6 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 1 Jul 2025 07:35:49 -0500 Subject: [PATCH 42/59] Fix off by one --- contrib/plugins/bap-tracing/frame_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 7e6c90ee524fb..e384a631755c2 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -97,7 +97,7 @@ FrameBuffer *frame_buffer_new(void) { } bool frame_buffer_is_full(const FrameBuffer *buf) { - return buf->idx + 1 >= frames_per_toc_entry; + return buf->idx >= frames_per_toc_entry; } void frame_buffer_close_frame(FrameBuffer *buf) { From d26720626708e31e63a17171078d51debbacae8d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Tue, 1 Jul 2025 11:03:33 -0500 Subject: [PATCH 43/59] Fix: Don't push invalid toc entry offset --- contrib/plugins/bap-tracing/tracing.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index f5f3b9f72f8d4..b4fc7628f4579 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -199,6 +199,7 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { static void plugin_exit(qemu_plugin_id_t id, void *udata) { g_rw_lock_writer_lock(&state.frame_buffer_lock); + // Dump the rest of the frames. for (size_t i = 0; i < state.vcpus->len; ++i) { FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); write_toc_entry(fbuf); @@ -223,7 +224,14 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { uint64_t m = state.toc_entries_offsets->len; WRITE(m); - for (size_t i = 0; i < m; ++i) { + for (size_t i = 0; i < m - 1; ++i) { + // All except the last address in state.toc_entries_offsets + // point to an entry. The last one points to nothing, because + // we first push the offset and then push the frames later + // when the buffer is full. + // When we dumped the last frames above it lastly + // pushed an additional offset. + // This one we skip here with m - 1. uint64_t toc_entry_off = g_array_index(state.toc_entries_offsets, uint64_t, i); WRITE(toc_entry_off); From e91696ef9e412e991e2031ad1e5398f49972de9d Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 2 Jul 2025 09:22:04 -0500 Subject: [PATCH 44/59] Fix incorrect description of m in TOC index. --- README.md | 4 ++-- contrib/plugins/bap-tracing/tracing.c | 16 +++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 22aacb8455730..49e29eb8ca0fc 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,8 @@ the BAP-frames repository. | ... | ... | ... | | | T-0x10 | uint64_t | sizeof(frame_n-1) | | | T-0x8 | type(frame_n-1) | frame_n-1 | | -| T+0 | uint64_t | m = number of TOC entries | TOC index begin | +| T+0 | uint64_t | m = number of frames per TOC entry | TOC index begin | | T+0x8 | uint64_t | offset toc_entry(0) | | | T+0x10 | uint64_t | offset toc_entry(1) | | | ... | ... | ... | | -| T+0x8+(0x8*m) | uint64_t | offset toc_entry(m-1) | | +| T+0x8+(0x8*ceil(n/m)) | uint64_t | offset toc_entry(ceil(n/m)) | | diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index b4fc7628f4579..c83524f14b7cd 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -221,17 +221,11 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { // Write the TOC index SEEK(toc_index_offset); - uint64_t m = state.toc_entries_offsets->len; - WRITE(m); - - for (size_t i = 0; i < m - 1; ++i) { - // All except the last address in state.toc_entries_offsets - // point to an entry. The last one points to nothing, because - // we first push the offset and then push the frames later - // when the buffer is full. - // When we dumped the last frames above it lastly - // pushed an additional offset. - // This one we skip here with m - 1. + WRITE(frames_per_toc_entry); + size_t add = state.total_num_frames % frames_per_toc_entry != 0 ? 1 : 0; + size_t entries = ((state.total_num_frames) / frames_per_toc_entry) + add; + + for (size_t i = 0; i < entries; ++i) { uint64_t toc_entry_off = g_array_index(state.toc_entries_offsets, uint64_t, i); WRITE(toc_entry_off); From 44a49b61ca068e9dca8785c1c30c825efa2269c0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 2 Jul 2025 11:21:06 -0500 Subject: [PATCH 45/59] Pad TOC entries when closing the trace. --- contrib/plugins/bap-tracing/frame_buffer.c | 28 ++++++++++++++-------- contrib/plugins/bap-tracing/frame_buffer.h | 2 +- contrib/plugins/bap-tracing/tracing.c | 8 +++---- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index e384a631755c2..b71bea9f66ba6 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -164,17 +164,25 @@ bool frame_buffer_is_empty(const FrameBuffer *buf) { } /// @brief Dumps the file buffer as TOC entry into the file. -uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file, bool add_padding) { uint64_t n = 0; - for (size_t i = 0; i <= buf->idx && i < frames_per_toc_entry; ++i) { - Frame *frame = buf->fbuf[i]; - size_t msg_size = frame__get_packed_size(frame); - uint8_t *packed_buffer = g_alloca(msg_size); - uint64_t packed_size = frame__pack(frame, packed_buffer); - WRITE(packed_size); - WRITE_BUF(packed_buffer, packed_size); - frame_free(frame); - n++; + for (size_t i = 0; i < frames_per_toc_entry; ++i) { + if (i <= buf->idx) { + Frame *frame = buf->fbuf[i]; + size_t msg_size = frame__get_packed_size(frame); + uint8_t *packed_buffer = g_alloca(msg_size); + uint64_t packed_size = frame__pack(frame, packed_buffer); + WRITE(packed_size); + WRITE_BUF(packed_buffer, packed_size); + frame_free(frame); + n++; + } else if (add_padding) { + uint64_t pad = 0; + WRITE(pad); + n++; + } else { + break; + } } memset(buf->fbuf, 0, sizeof(buf->fbuf)); buf->idx = 0; diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 1a6bb0c15bd24..598b909867b50 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -28,7 +28,7 @@ typedef struct { */ FrameBuffer *frame_buffer_new(void); -uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file, bool add_padding); bool frame_buffer_is_full(const FrameBuffer *buf); bool frame_buffer_is_empty(const FrameBuffer *buf); void frame_buffer_close_frame(FrameBuffer *buf); diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c83524f14b7cd..7d3d3299fc199 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -100,12 +100,12 @@ static GPtrArray *registers_init(void) { return registers->len ? g_steal_pointer(®isters) : NULL; } -static void write_toc_entry(FrameBuffer *fbuf) { +static void write_toc_entry(FrameBuffer *fbuf, bool add_padding) { g_rw_lock_writer_lock(&state.file_lock); g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); g_rw_lock_writer_lock(&state.total_num_frames_lock); - state.total_num_frames += frame_buffer_flush_to_file(fbuf, state.file); + state.total_num_frames += frame_buffer_flush_to_file(fbuf, state.file, add_padding); uint64_t next_toc_entry = ftell(state.file); g_array_append_val(state.toc_entries_offsets, next_toc_entry); @@ -130,7 +130,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { } if (frame_buffer_is_full(fbuf)) { - write_toc_entry(fbuf); + write_toc_entry(fbuf, false); } // Open new one. @@ -202,7 +202,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { // Dump the rest of the frames. for (size_t i = 0; i < state.vcpus->len; ++i) { FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); - write_toc_entry(fbuf); + write_toc_entry(fbuf, true); } g_rw_lock_writer_unlock(&state.frame_buffer_lock); From 52e4c5a5cb094e10bcf4d04dd52d4ee7ea194b31 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 2 Jul 2025 12:08:45 -0500 Subject: [PATCH 46/59] Add mode of frame. --- README.md | 15 +- contrib/plugins/bap-tracing/frame_buffer.c | 13 +- contrib/plugins/bap-tracing/frame_buffer.h | 4 +- contrib/plugins/bap-tracing/tracing.c | 51 +++--- contrib/plugins/bap-tracing/tracing.h | 191 ++++++++++++--------- 5 files changed, 159 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 49e29eb8ca0fc..c47733d604d69 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,21 @@ This QEMU fork implements the TCG plugin to generate execution traces in the [bap-frame](https://github.com/BinaryAnalysisPlatform/bap-frames) format. +This plugin does not yet support all targets. +If not listed below it is untested. + +Known to work: + +- Sparc +- Hexagon +- PPC + +Needs fixes: + +- ARM (cannot get current mode of VCPU if target can switch between ARM/Thumb). + Previous traces were generated with a patched QEMU. -You can find these in the branches tracewrap-6.2.0 for ARM and x86 and tracewrap-8.1 for Hexagon. +You can find these in tracewrap-* branches. ## Dependencies diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index b71bea9f66ba6..b151903608da9 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -4,8 +4,8 @@ #include "frame_buffer.h" #include "trace_meta.h" -static Frame *frame_new_std(uint64_t addr, int vcpu_id, uint8_t *bytes, - size_t bytes_len) { +static Frame *frame_new_std(uint64_t addr, int vcpu_id, const char *mode_id, + uint8_t *bytes, size_t bytes_len) { Frame *frame = g_new(Frame, 1); frame__init(frame); @@ -14,6 +14,9 @@ static Frame *frame_new_std(uint64_t addr, int vcpu_id, uint8_t *bytes, frame->std_frame = sframe; sframe->address = addr; + if (mode_id) { + sframe->mode = g_strdup(mode_id); + } sframe->thread_id = vcpu_id; sframe->rawbytes.len = bytes_len; sframe->rawbytes.data = g_malloc(bytes_len); @@ -190,12 +193,12 @@ uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file, bool a } bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, - uint64_t vaddr, uint8_t *bytes, - size_t bytes_len) { + uint64_t vaddr, const char *mode, + uint8_t *bytes, size_t bytes_len) { if (frame_buffer_is_full(buf)) { return false; } - Frame *frame = frame_new_std(vaddr, thread_id, bytes, bytes_len); + Frame *frame = frame_new_std(vaddr, thread_id, mode, bytes, bytes_len); if (!frame) { return false; } diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 598b909867b50..dee5adce12b69 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -35,8 +35,8 @@ void frame_buffer_close_frame(FrameBuffer *buf); char *frame_buffer_as_str(const FrameBuffer *buf); bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, - uint64_t vaddr, uint8_t *bytes, - size_t bytes_len); + uint64_t vaddr, const char *mode_id, + uint8_t *bytes, size_t bytes_len); bool frame_buffer_append_mem_info(FrameBuffer *fbuf, uint64_t vaddr, qemu_plugin_mem_value *mval, bool is_store); diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 7d3d3299fc199..40761a8e76e73 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -76,12 +76,6 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, } } -static bool add_new_insn_frame(VCPU *vcpu, unsigned int vcpu_index, - FrameBuffer *fbuf, Instruction *insn) { - return frame_buffer_new_frame_std(fbuf, vcpu_index, insn->vaddr, insn->bytes, - insn->size); -} - static GPtrArray *registers_init(void) { GArray *reg_list = qemu_plugin_get_registers(); @@ -105,7 +99,8 @@ static void write_toc_entry(FrameBuffer *fbuf, bool add_padding) { g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); g_rw_lock_writer_lock(&state.total_num_frames_lock); - state.total_num_frames += frame_buffer_flush_to_file(fbuf, state.file, add_padding); + state.total_num_frames += + frame_buffer_flush_to_file(fbuf, state.file, add_padding); uint64_t next_toc_entry = ftell(state.file); g_array_append_val(state.toc_entries_offsets, next_toc_entry); @@ -135,9 +130,15 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { // Open new one. Instruction *insn = udata; - if (!add_new_insn_frame(vcpu, vcpu_index, fbuf, insn)) { + g_rw_lock_reader_lock(&state.vcpu_mode_lock); + if (!frame_buffer_new_frame_std( + fbuf, vcpu_index, insn->vaddr, + g_ptr_array_index(state.vcpu_modes, vcpu_index), insn->bytes, + insn->size)) { err(1, "Failed to add new frame.\n"); } + g_rw_lock_reader_unlock(&state.vcpu_mode_lock); + add_pre_reg_state(vcpu, vcpu_index, current_regs, fbuf); g_rw_lock_writer_unlock(&state.frame_buffer_lock); @@ -161,6 +162,7 @@ Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) { static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { g_rw_lock_writer_lock(&state.vcpus_array_lock); g_rw_lock_writer_lock(&state.frame_buffer_lock); + g_rw_lock_writer_lock(&state.vcpu_mode_lock); VCPU *vcpu = g_malloc0(sizeof(VCPU)); vcpu->registers = registers_init(); @@ -170,6 +172,22 @@ static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) { FrameBuffer *vcpu_frame_buffer = frame_buffer_new(); g_ptr_array_insert(state.frame_buffer, vcpu_index, vcpu_frame_buffer); + uint64_t frame_arch = 0; + uint64_t frame_mach = 0; + if (!get_frame_arch_mach(state.target_name, &frame_arch, &frame_mach)) { + qemu_plugin_outs("Failed to get arch/mach.\n"); + } + const char *mode = FRAME_MODE_NONE; + if (frame_arch == frame_arch_powerpc && frame_mach == frame_mach_ppc64) { + mode = FRAME_MODE_PPC64; + } else if (frame_arch == frame_arch_powerpc && frame_mach == frame_mach_ppc) { + mode = FRAME_MODE_PPC32; + } + // TODO: handle ARM + g_ptr_array_insert(state.vcpu_modes, vcpu_index, + mode ? g_strdup(mode) : NULL); + + g_rw_lock_writer_unlock(&state.vcpu_mode_lock); g_rw_lock_writer_unlock(&state.frame_buffer_lock); g_rw_lock_writer_unlock(&state.vcpus_array_lock); } @@ -238,21 +256,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) { qemu_plugin_outs("Finished trace\n"); } -static bool get_frame_arch_mach(const char *target_name, uint64_t *arch, - uint64_t *mach) { - *mach = 0; - *arch = frame_arch_last; - const char *aname = arch_map[0].name; - for (size_t i = 0; arch_map[i].name; ++i) { - aname = arch_map[i].name; - if (!strncmp(aname, target_name, strlen(aname))) { - *arch = arch_map[i].val; - break; - } - } - return *arch != frame_arch_last; -} - static bool write_header(FILE *file, const char *target_name) { uint64_t frame_arch = 0; uint64_t frame_mach = 0; @@ -285,9 +288,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, exit(1); } + state.target_name = g_strdup(info->target_name); state.frame_buffer = g_ptr_array_new(); state.toc_entries_offsets = g_array_new(false, true, sizeof(uint64_t)); state.vcpus = g_ptr_array_new(); + state.vcpu_modes = g_ptr_array_new(); state.file = fopen(output, "wb"); if (!(state.frame_buffer || state.vcpus || state.file || !state.toc_entries_offsets)) { diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index ad9dfe908355e..9ae4434b43e74 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -15,95 +15,113 @@ struct arch_enum_entry { const char *name; - enum frame_architecture val; + enum frame_architecture arch; + size_t machine; }; static struct arch_enum_entry arch_map[] = { - {.name = "unknown", .val = frame_arch_unknown}, - {.name = "obscure", .val = frame_arch_obscure}, - {.name = "m68k", .val = frame_arch_m68k}, - {.name = "vax", .val = frame_arch_vax}, - {.name = "i960", .val = frame_arch_i960}, - {.name = "or32", .val = frame_arch_or32}, - {.name = "sparc", .val = frame_arch_sparc}, - {.name = "spu", .val = frame_arch_spu}, - {.name = "mips", .val = frame_arch_mips}, - {.name = "i386", .val = frame_arch_i386}, - {.name = "l1om", .val = frame_arch_l1om}, - {.name = "we32k", .val = frame_arch_we32k}, - {.name = "tahoe", .val = frame_arch_tahoe}, - {.name = "i860", .val = frame_arch_i860}, - {.name = "i370", .val = frame_arch_i370}, - {.name = "romp", .val = frame_arch_romp}, - {.name = "convex", .val = frame_arch_convex}, - {.name = "m88k", .val = frame_arch_m88k}, - {.name = "m98k", .val = frame_arch_m98k}, - {.name = "pyramid", .val = frame_arch_pyramid}, - {.name = "h8300", .val = frame_arch_h8300}, - {.name = "pdp11", .val = frame_arch_pdp11}, - {.name = "plugin", .val = frame_arch_plugin}, - {.name = "powerpc", .val = frame_arch_powerpc}, - {.name = "rs6000", .val = frame_arch_rs6000}, - {.name = "hppa", .val = frame_arch_hppa}, - {.name = "d10v", .val = frame_arch_d10v}, - {.name = "d30v", .val = frame_arch_d30v}, - {.name = "dlx", .val = frame_arch_dlx}, - {.name = "m68hc11", .val = frame_arch_m68hc11}, - {.name = "m68hc12", .val = frame_arch_m68hc12}, - {.name = "z8k", .val = frame_arch_z8k}, - {.name = "h8500", .val = frame_arch_h8500}, - {.name = "sh", .val = frame_arch_sh}, - {.name = "alpha", .val = frame_arch_alpha}, - {.name = "arm", .val = frame_arch_arm}, - {.name = "ns32k", .val = frame_arch_ns32k}, - {.name = "w65", .val = frame_arch_w65}, - {.name = "tic30", .val = frame_arch_tic30}, - {.name = "tic4x", .val = frame_arch_tic4x}, - {.name = "tic54x", .val = frame_arch_tic54x}, - {.name = "tic6x", .val = frame_arch_tic6x}, - {.name = "tic80", .val = frame_arch_tic80}, - {.name = "v850", .val = frame_arch_v850}, - {.name = "arc", .val = frame_arch_arc}, - {.name = "m32c", .val = frame_arch_m32c}, - {.name = "m32r", .val = frame_arch_m32r}, - {.name = "mn10200", .val = frame_arch_mn10200}, - {.name = "mn10300", .val = frame_arch_mn10300}, - {.name = "fr30", .val = frame_arch_fr30}, - {.name = "frv", .val = frame_arch_frv}, - {.name = "moxie", .val = frame_arch_moxie}, - {.name = "mcore", .val = frame_arch_mcore}, - {.name = "mep", .val = frame_arch_mep}, - {.name = "ia64", .val = frame_arch_ia64}, - {.name = "ip2k", .val = frame_arch_ip2k}, - {.name = "iq2000", .val = frame_arch_iq2000}, - {.name = "mt", .val = frame_arch_mt}, - {.name = "pj", .val = frame_arch_pj}, - {.name = "avr", .val = frame_arch_avr}, - {.name = "bfin", .val = frame_arch_bfin}, - {.name = "cr16", .val = frame_arch_cr16}, - {.name = "cr16c", .val = frame_arch_cr16c}, - {.name = "crx", .val = frame_arch_crx}, - {.name = "cris", .val = frame_arch_cris}, - {.name = "rx", .val = frame_arch_rx}, - {.name = "s390", .val = frame_arch_s390}, - {.name = "score", .val = frame_arch_score}, - {.name = "openrisc", .val = frame_arch_openrisc}, - {.name = "mmix", .val = frame_arch_mmix}, - {.name = "xstormy16", .val = frame_arch_xstormy16}, - {.name = "msp430", .val = frame_arch_msp430}, - {.name = "xc16x", .val = frame_arch_xc16x}, - {.name = "xtensa", .val = frame_arch_xtensa}, - {.name = "z80", .val = frame_arch_z80}, - {.name = "lm32", .val = frame_arch_lm32}, - {.name = "microblaze", .val = frame_arch_microblaze}, - {.name = "6502", .val = frame_arch_6502}, - {.name = "aarch64", .val = frame_arch_aarch64}, - {.name = "8051", .val = frame_arch_8051}, - {.name = "sm83", .val = frame_arch_sm83}, - {.name = "hexagon", .val = frame_arch_hexagon}, - {.name = NULL, .val = frame_arch_last}, + {.name = "unknown", .arch = frame_arch_unknown, .machine = 0}, + {.name = "obscure", .arch = frame_arch_obscure, .machine = 0}, + {.name = "m68k", .arch = frame_arch_m68k, .machine = 0}, + {.name = "vax", .arch = frame_arch_vax, .machine = 0}, + {.name = "i960", .arch = frame_arch_i960, .machine = 0}, + {.name = "or32", .arch = frame_arch_or32, .machine = 0}, + {.name = "sparc", .arch = frame_arch_sparc, .machine = 0}, + {.name = "spu", .arch = frame_arch_spu, .machine = 0}, + {.name = "mips", .arch = frame_arch_mips, .machine = 0}, + {.name = "i386", .arch = frame_arch_i386, .machine = 0}, + {.name = "l1om", .arch = frame_arch_l1om, .machine = 0}, + {.name = "we32k", .arch = frame_arch_we32k, .machine = 0}, + {.name = "tahoe", .arch = frame_arch_tahoe, .machine = 0}, + {.name = "i860", .arch = frame_arch_i860, .machine = 0}, + {.name = "i370", .arch = frame_arch_i370, .machine = 0}, + {.name = "romp", .arch = frame_arch_romp, .machine = 0}, + {.name = "convex", .arch = frame_arch_convex, .machine = 0}, + {.name = "m88k", .arch = frame_arch_m88k, .machine = 0}, + {.name = "m98k", .arch = frame_arch_m98k, .machine = 0}, + {.name = "pyramid", .arch = frame_arch_pyramid, .machine = 0}, + {.name = "h8300", .arch = frame_arch_h8300, .machine = 0}, + {.name = "pdp11", .arch = frame_arch_pdp11, .machine = 0}, + {.name = "plugin", .arch = frame_arch_plugin, .machine = 0}, + {.name = "ppc", .arch = frame_arch_powerpc, .machine = frame_mach_ppc}, + {.name = "ppc64", .arch = frame_arch_powerpc, .machine = frame_mach_ppc64}, + {.name = "rs6000", .arch = frame_arch_rs6000, .machine = 0}, + {.name = "hppa", .arch = frame_arch_hppa, .machine = 0}, + {.name = "d10v", .arch = frame_arch_d10v, .machine = 0}, + {.name = "d30v", .arch = frame_arch_d30v, .machine = 0}, + {.name = "dlx", .arch = frame_arch_dlx, .machine = 0}, + {.name = "m68hc11", .arch = frame_arch_m68hc11, .machine = 0}, + {.name = "m68hc12", .arch = frame_arch_m68hc12, .machine = 0}, + {.name = "z8k", .arch = frame_arch_z8k, .machine = 0}, + {.name = "h8500", .arch = frame_arch_h8500, .machine = 0}, + {.name = "sh", .arch = frame_arch_sh, .machine = 0}, + {.name = "alpha", .arch = frame_arch_alpha, .machine = 0}, + {.name = "arm", .arch = frame_arch_arm, .machine = 0}, + {.name = "ns32k", .arch = frame_arch_ns32k, .machine = 0}, + {.name = "w65", .arch = frame_arch_w65, .machine = 0}, + {.name = "tic30", .arch = frame_arch_tic30, .machine = 0}, + {.name = "tic4x", .arch = frame_arch_tic4x, .machine = 0}, + {.name = "tic54x", .arch = frame_arch_tic54x, .machine = 0}, + {.name = "tic6x", .arch = frame_arch_tic6x, .machine = 0}, + {.name = "tic80", .arch = frame_arch_tic80, .machine = 0}, + {.name = "v850", .arch = frame_arch_v850, .machine = 0}, + {.name = "arc", .arch = frame_arch_arc, .machine = 0}, + {.name = "m32c", .arch = frame_arch_m32c, .machine = 0}, + {.name = "m32r", .arch = frame_arch_m32r, .machine = 0}, + {.name = "mn10200", .arch = frame_arch_mn10200, .machine = 0}, + {.name = "mn10300", .arch = frame_arch_mn10300, .machine = 0}, + {.name = "fr30", .arch = frame_arch_fr30, .machine = 0}, + {.name = "frv", .arch = frame_arch_frv, .machine = 0}, + {.name = "moxie", .arch = frame_arch_moxie, .machine = 0}, + {.name = "mcore", .arch = frame_arch_mcore, .machine = 0}, + {.name = "mep", .arch = frame_arch_mep, .machine = 0}, + {.name = "ia64", .arch = frame_arch_ia64, .machine = 0}, + {.name = "ip2k", .arch = frame_arch_ip2k, .machine = 0}, + {.name = "iq2000", .arch = frame_arch_iq2000, .machine = 0}, + {.name = "mt", .arch = frame_arch_mt, .machine = 0}, + {.name = "pj", .arch = frame_arch_pj, .machine = 0}, + {.name = "avr", .arch = frame_arch_avr, .machine = 0}, + {.name = "bfin", .arch = frame_arch_bfin, .machine = 0}, + {.name = "cr16", .arch = frame_arch_cr16, .machine = 0}, + {.name = "cr16c", .arch = frame_arch_cr16c, .machine = 0}, + {.name = "crx", .arch = frame_arch_crx, .machine = 0}, + {.name = "cris", .arch = frame_arch_cris, .machine = 0}, + {.name = "rx", .arch = frame_arch_rx, .machine = 0}, + {.name = "s390", .arch = frame_arch_s390, .machine = 0}, + {.name = "score", .arch = frame_arch_score, .machine = 0}, + {.name = "openrisc", .arch = frame_arch_openrisc, .machine = 0}, + {.name = "mmix", .arch = frame_arch_mmix, .machine = 0}, + {.name = "xstormy16", .arch = frame_arch_xstormy16, .machine = 0}, + {.name = "msp430", .arch = frame_arch_msp430, .machine = 0}, + {.name = "xc16x", .arch = frame_arch_xc16x, .machine = 0}, + {.name = "xtensa", .arch = frame_arch_xtensa, .machine = 0}, + {.name = "z80", .arch = frame_arch_z80, .machine = 0}, + {.name = "lm32", .arch = frame_arch_lm32, .machine = 0}, + {.name = "microblaze", .arch = frame_arch_microblaze, .machine = 0}, + {.name = "6502", .arch = frame_arch_6502, .machine = 0}, + {.name = "aarch64", .arch = frame_arch_aarch64, .machine = 0}, + {.name = "8051", .arch = frame_arch_8051, .machine = 0}, + {.name = "sm83", .arch = frame_arch_sm83, .machine = 0}, + {.name = "hexagon", .arch = frame_arch_hexagon, .machine = 0}, + {.name = NULL, .arch = frame_arch_last, .machine = 0}, }; +static inline bool get_frame_arch_mach(const char *target_name, uint64_t *arch, + uint64_t *mach) { + *mach = 0; + *arch = frame_arch_last; + const char *aname = arch_map[0].name; + for (size_t i = 0; arch_map[i].name; ++i) { + aname = arch_map[i].name; + if (!strncmp(aname, target_name, strlen(aname))) { + *arch = arch_map[i].arch; + *mach = arch_map[i].machine; + break; + } + } + return *arch != frame_arch_last; +} + QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; /** @@ -142,6 +160,11 @@ typedef struct { GRWLock file_lock; FILE *file; + + GRWLock vcpu_mode_lock; + GPtrArray /**/ *vcpu_modes; ///< Indexed by vcpu id. + + const char *target_name; } TraceState; VCPU *vcpu_new(void); From f0525cb3bfe82a6bbaa3998199b68f6552982186 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 2 Jul 2025 12:18:37 -0500 Subject: [PATCH 47/59] Fix typo --- contrib/plugins/bap-tracing/tracing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 40761a8e76e73..464c0471a12a4 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -300,7 +300,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } g_free(output); if (!write_header(state.file, info->target_name)) { - qemu_plugin_outs("Failed to header.\n"); + qemu_plugin_outs("Failed to write header.\n"); return 1; } write_meta(state.file, argv, argc); From 958dfd8dd111838f03947914b42fa22cdd63ab56 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 2 Jul 2025 12:59:35 -0500 Subject: [PATCH 48/59] Fix too early return of partial mdoe match --- contrib/plugins/bap-tracing/tracing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 9ae4434b43e74..799371988d1c0 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -113,7 +113,7 @@ static inline bool get_frame_arch_mach(const char *target_name, uint64_t *arch, const char *aname = arch_map[0].name; for (size_t i = 0; arch_map[i].name; ++i) { aname = arch_map[i].name; - if (!strncmp(aname, target_name, strlen(aname))) { + if (!strcmp(aname, target_name)) { *arch = arch_map[i].arch; *mach = arch_map[i].machine; break; From 486c6b7a2cc2bb997f93f1a308c21f13b32459c4 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Sun, 6 Jul 2025 13:22:11 -0500 Subject: [PATCH 49/59] Add machine info for Sparc --- contrib/plugins/bap-tracing/tracing.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 799371988d1c0..bac9cb6a1ae54 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -26,7 +26,8 @@ static struct arch_enum_entry arch_map[] = { {.name = "vax", .arch = frame_arch_vax, .machine = 0}, {.name = "i960", .arch = frame_arch_i960, .machine = 0}, {.name = "or32", .arch = frame_arch_or32, .machine = 0}, - {.name = "sparc", .arch = frame_arch_sparc, .machine = 0}, + {.name = "sparc", .arch = frame_arch_sparc, .machine = frame_mach_sparc_v8plusa}, + {.name = "sparc64", .arch = frame_arch_sparc, .machine = frame_mach_sparc_v9b}, {.name = "spu", .arch = frame_arch_spu, .machine = 0}, {.name = "mips", .arch = frame_arch_mips, .machine = 0}, {.name = "i386", .arch = frame_arch_i386, .machine = 0}, @@ -119,6 +120,11 @@ static inline bool get_frame_arch_mach(const char *target_name, uint64_t *arch, break; } } + if (*arch == frame_arch_last) { + qemu_plugin_outs("Could not find frame_arch/mach value for target name: "); + qemu_plugin_outs(target_name); + qemu_plugin_outs("\nConsider adding it.\n"); + } return *arch != frame_arch_last; } From 3ee79194d232c32a3f52faf95a8d5e947dc4bae0 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 7 Jul 2025 12:41:22 -0500 Subject: [PATCH 50/59] Fix register comparison for trace. --- contrib/plugins/bap-tracing/tracing.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 464c0471a12a4..337e86a34a1cb 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -54,6 +54,8 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, assert(s == prev_reg->content->len); if (!memcmp(rdata->data, prev_reg->content->data, s)) { // No change + // Flush byte array + g_byte_array_set_size(rdata, 0); continue; } @@ -62,6 +64,8 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, qemu_plugin_outs("Failed to append opinfo.\n"); return; } + // Flush byte array + g_byte_array_set_size(rdata, 0); } } @@ -72,7 +76,12 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, qemu_plugin_reg_descriptor *reg = &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); size_t s = qemu_plugin_read_register(reg->handle, rdata); + Register *prev_reg = g_ptr_array_index(vcpu->registers, i); + g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); + memcpy(prev_reg->content->data, rdata->data, prev_reg->content->len); frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); + // Flush byte array + g_byte_array_set_size(rdata, 0); } } From cc179405e27479b63241062f5d4501578c55da80 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 7 Jul 2025 14:00:06 -0500 Subject: [PATCH 51/59] Remove padding frames again. --- contrib/plugins/bap-tracing/frame_buffer.c | 44 ++++++++------- contrib/plugins/bap-tracing/frame_buffer.h | 4 +- contrib/plugins/bap-tracing/tracing.c | 66 ++++++++++++++++++---- 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index b151903608da9..5992c4980f29b 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -166,29 +166,33 @@ bool frame_buffer_is_empty(const FrameBuffer *buf) { return buf->fbuf[buf->idx] == NULL; } +void frame_buffer_clean(FrameBuffer *buf) { + memset(buf->fbuf, 0, sizeof(buf->fbuf)); + buf->idx = 0; +} + +bool frame_buffer_write_frame_to_file(FrameBuffer *buf, WLOCKED FILE *file, size_t i) { + if (i > buf->idx) { + return false; + } + Frame *frame = buf->fbuf[i]; + size_t msg_size = frame__get_packed_size(frame); + uint8_t *packed_buffer = g_alloca(msg_size); + uint64_t packed_size = frame__pack(frame, packed_buffer); + WRITE(packed_size); + WRITE_BUF(packed_buffer, packed_size); + frame_free(frame); + return true; +} + /// @brief Dumps the file buffer as TOC entry into the file. -uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file, bool add_padding) { +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file) { uint64_t n = 0; - for (size_t i = 0; i < frames_per_toc_entry; ++i) { - if (i <= buf->idx) { - Frame *frame = buf->fbuf[i]; - size_t msg_size = frame__get_packed_size(frame); - uint8_t *packed_buffer = g_alloca(msg_size); - uint64_t packed_size = frame__pack(frame, packed_buffer); - WRITE(packed_size); - WRITE_BUF(packed_buffer, packed_size); - frame_free(frame); - n++; - } else if (add_padding) { - uint64_t pad = 0; - WRITE(pad); - n++; - } else { - break; - } + for (size_t i = 0; i < buf->idx; ++i) { + frame_buffer_write_frame_to_file(buf, file, i); + n++; } - memset(buf->fbuf, 0, sizeof(buf->fbuf)); - buf->idx = 0; + frame_buffer_clean(buf); return n; } diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index dee5adce12b69..70cc94b4af9a7 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -28,7 +28,9 @@ typedef struct { */ FrameBuffer *frame_buffer_new(void); -uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file, bool add_padding); +uint64_t frame_buffer_flush_to_file(FrameBuffer *buf, WLOCKED FILE *file); +void frame_buffer_clean(FrameBuffer *buf); +bool frame_buffer_write_frame_to_file(FrameBuffer *buf, WLOCKED FILE *file, size_t i); bool frame_buffer_is_full(const FrameBuffer *buf); bool frame_buffer_is_empty(const FrameBuffer *buf); void frame_buffer_close_frame(FrameBuffer *buf); diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 337e86a34a1cb..c56f0f90474c6 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -77,7 +77,8 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); size_t s = qemu_plugin_read_register(reg->handle, rdata); Register *prev_reg = g_ptr_array_index(vcpu->registers, i); - g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); + g_assert(!strcmp(prev_reg->name, reg->name) && + prev_reg->handle == reg->handle); memcpy(prev_reg->content->data, rdata->data, prev_reg->content->len); frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); // Flush byte array @@ -103,13 +104,12 @@ static GPtrArray *registers_init(void) { return registers->len ? g_steal_pointer(®isters) : NULL; } -static void write_toc_entry(FrameBuffer *fbuf, bool add_padding) { +static void flush_and_write_toc_entry(FrameBuffer *fbuf) { g_rw_lock_writer_lock(&state.file_lock); g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); g_rw_lock_writer_lock(&state.total_num_frames_lock); - state.total_num_frames += - frame_buffer_flush_to_file(fbuf, state.file, add_padding); + state.total_num_frames += frame_buffer_flush_to_file(fbuf, state.file); uint64_t next_toc_entry = ftell(state.file); g_array_append_val(state.toc_entries_offsets, next_toc_entry); @@ -118,6 +118,54 @@ static void write_toc_entry(FrameBuffer *fbuf, bool add_padding) { g_rw_lock_writer_unlock(&state.file_lock); } +static void flush_all_frame_bufs(void) { + g_rw_lock_writer_lock(&state.file_lock); + g_rw_lock_writer_lock(&state.toc_entries_offsets_lock); + g_rw_lock_writer_lock(&state.total_num_frames_lock); + g_rw_lock_writer_lock(&state.frame_buffer_lock); + + FILE *file = state.file; + + // Dump the rest of the frames but be mindeful about the + // maximum number of frames per TOC entry. + + size_t total_to_write = 0; + for (size_t i = 0; i < state.vcpus->len; ++i) { + // Add post operands to last instructions. + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); + VCPU *vcpu = g_ptr_array_index(state.vcpus, i); + g_assert(vcpu); + GArray *current_regs = qemu_plugin_get_registers(); + g_assert(current_regs->len == vcpu->registers->len); + add_post_reg_state(vcpu, i, current_regs, fbuf); + frame_buffer_close_frame(fbuf); + + total_to_write += fbuf->idx; + } + + size_t entry_count = 0; + for (size_t i = 0; i < state.vcpus->len && total_to_write > 0; ++i) { + if (entry_count == frames_per_toc_entry) { + entry_count = 0; + uint64_t next_toc_entry = ftell(state.file); + g_array_append_val(state.toc_entries_offsets, next_toc_entry); + } + + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); + for (size_t k = 0; k < fbuf->idx; ++k) { + frame_buffer_write_frame_to_file(fbuf, file, k); + entry_count++; + state.total_num_frames++; + total_to_write--; + } + } + + g_rw_lock_writer_unlock(&state.frame_buffer_lock); + g_rw_lock_writer_unlock(&state.total_num_frames_lock); + g_rw_lock_writer_unlock(&state.toc_entries_offsets_lock); + g_rw_lock_writer_unlock(&state.file_lock); +} + static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { g_rw_lock_reader_lock(&state.vcpus_array_lock); g_rw_lock_writer_lock(&state.frame_buffer_lock); @@ -134,7 +182,7 @@ static void log_insn_reg_access(unsigned int vcpu_index, void *udata) { } if (frame_buffer_is_full(fbuf)) { - write_toc_entry(fbuf, false); + flush_and_write_toc_entry(fbuf); } // Open new one. @@ -225,13 +273,7 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { } static void plugin_exit(qemu_plugin_id_t id, void *udata) { - g_rw_lock_writer_lock(&state.frame_buffer_lock); - // Dump the rest of the frames. - for (size_t i = 0; i < state.vcpus->len; ++i) { - FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, i); - write_toc_entry(fbuf, true); - } - g_rw_lock_writer_unlock(&state.frame_buffer_lock); + flush_all_frame_bufs(); g_rw_lock_writer_lock(&state.file_lock); g_rw_lock_reader_lock(&state.toc_entries_offsets_lock); From 6d61f9c7ed0351f9e3ce9ed30c4210efc71f869e Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 9 Jul 2025 08:51:46 -0500 Subject: [PATCH 52/59] Store values always in little endian as the trace defines it. --- contrib/plugins/bap-tracing/trace_meta.c | 22 ++++++++++++++++++++++ contrib/plugins/bap-tracing/trace_meta.h | 4 ++++ contrib/plugins/bap-tracing/tracing.c | 12 +++++++++++- contrib/plugins/bap-tracing/tracing.h | 1 + 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/trace_meta.c b/contrib/plugins/bap-tracing/trace_meta.c index b3ec4596c0d6f..dc1d08ef375e3 100644 --- a/contrib/plugins/bap-tracing/trace_meta.c +++ b/contrib/plugins/bap-tracing/trace_meta.c @@ -191,3 +191,25 @@ void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc) { g_free(host); g_free(arg_bin_path); } + +/// Copies src to dst. dst will always be in little endian byte order. +void memcpy_le(uint8_t *dst, uint8_t *src, size_t len, bool big_endian) { + if (!big_endian) { + memcpy(dst, src, len); + return; + } + for (size_t k = 0; k < len; ++k) { + dst[k] = src[len - 1 - k]; + } +} + +void swap_to_le(uint8_t *buf, size_t len, bool big_endian) { + if (!big_endian || len == 1) { + return; + } + for (size_t k = 0; k < len / 2; ++k) { + uint8_t tmp = buf[k]; + buf[k] = buf[len - 1 - k]; + buf[len - 1 - k] = tmp; + } +} diff --git a/contrib/plugins/bap-tracing/trace_meta.h b/contrib/plugins/bap-tracing/trace_meta.h index 2e9e1c19eef87..22c964542eb66 100644 --- a/contrib/plugins/bap-tracing/trace_meta.h +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -5,6 +5,8 @@ #define BAP_TRACE_META_H #include +#include +#include #include /** @@ -34,5 +36,7 @@ void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc); char *get_argv_val(char **argv, int argc, const char *key); void file_exists_exit(const char *file); +void memcpy_le(uint8_t *dst, uint8_t *src, size_t len, bool big_endian); +void swap_to_le(uint8_t *buf, size_t len, bool big_endian); #endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index c56f0f90474c6..23faf84ed2dff 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -9,6 +9,7 @@ #include "frame_buffer.h" #include "qemu-plugin.h" #include "trace_consts.h" +#include "trace_meta.h" #include "tracing.h" static TraceState state = {0}; @@ -52,6 +53,7 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); int s = qemu_plugin_read_register(reg->handle, rdata); assert(s == prev_reg->content->len); + swap_to_le(rdata->data, s, state.is_big_endian); if (!memcmp(rdata->data, prev_reg->content->data, s)) { // No change // Flush byte array @@ -79,7 +81,7 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, Register *prev_reg = g_ptr_array_index(vcpu->registers, i); g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); - memcpy(prev_reg->content->data, rdata->data, prev_reg->content->len); + memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, state.is_big_endian); frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); // Flush byte array g_byte_array_set_size(rdata, 0); @@ -338,6 +340,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, qemu_plugin_outs("Pass it with 'out='.\n\n"); exit(1); } + char *endianess = get_argv_val(argv, argc, "endianess"); + if (!endianess || (strcmp(endianess, "b") && strcmp(endianess, "l"))) { + qemu_plugin_outs("'endianess' argument is missing or is not 'b' or 'l'.\n"); + qemu_plugin_outs("This is required until QEMU plugins get a richer API.\n"); + qemu_plugin_outs("Pass it with 'endianess=[b/l]'.\n\n"); + exit(1); + } + state.is_big_endian = endianess[0] == 'b'; state.target_name = g_strdup(info->target_name); state.frame_buffer = g_ptr_array_new(); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index bac9cb6a1ae54..131ac73a27b2b 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -171,6 +171,7 @@ typedef struct { GPtrArray /**/ *vcpu_modes; ///< Indexed by vcpu id. const char *target_name; + bool is_big_endian; } TraceState; VCPU *vcpu_new(void); From 9ea6295f61252b39f202f9662afde0b765c03fef Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 9 Jul 2025 08:52:34 -0500 Subject: [PATCH 53/59] Formatting --- contrib/plugins/bap-tracing/frame_buffer.c | 3 ++- contrib/plugins/bap-tracing/trace_meta.c | 2 +- contrib/plugins/bap-tracing/tracing.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 5992c4980f29b..93fc56caa61b5 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -171,7 +171,8 @@ void frame_buffer_clean(FrameBuffer *buf) { buf->idx = 0; } -bool frame_buffer_write_frame_to_file(FrameBuffer *buf, WLOCKED FILE *file, size_t i) { +bool frame_buffer_write_frame_to_file(FrameBuffer *buf, WLOCKED FILE *file, + size_t i) { if (i > buf->idx) { return false; } diff --git a/contrib/plugins/bap-tracing/trace_meta.c b/contrib/plugins/bap-tracing/trace_meta.c index dc1d08ef375e3..3c31881fdf0ca 100644 --- a/contrib/plugins/bap-tracing/trace_meta.c +++ b/contrib/plugins/bap-tracing/trace_meta.c @@ -178,7 +178,7 @@ void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc) { // I don't know why, but ASAN crashes at this line if the WRITE_BUF macro // is used. Although it should be the exact same code. if (fwrite((packed_buffer), 1, (packed_size), file) != packed_size) { - err(1, "fwrite failed"); + err(1, "fwrite failed"); } g_free(packed_buffer); diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 23faf84ed2dff..b793deefcf783 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -81,7 +81,8 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, Register *prev_reg = g_ptr_array_index(vcpu->registers, i); g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); - memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, state.is_big_endian); + memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, + state.is_big_endian); frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); // Flush byte array g_byte_array_set_size(rdata, 0); From fe9340b942bcd518ae964ac7220419388fcac5d8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 14 Jul 2025 11:10:26 -0500 Subject: [PATCH 54/59] GDB stub should return data with endianess of the host machine. --- contrib/plugins/bap-tracing/tracing.c | 13 +++---------- contrib/plugins/bap-tracing/tracing.h | 1 - 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index b793deefcf783..239619edf6b86 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -5,6 +5,7 @@ #include #include +#include "compiler.h" #include "frame_arch.h" #include "frame_buffer.h" #include "qemu-plugin.h" @@ -53,7 +54,7 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); int s = qemu_plugin_read_register(reg->handle, rdata); assert(s == prev_reg->content->len); - swap_to_le(rdata->data, s, state.is_big_endian); + swap_to_le(rdata->data, s, HOST_BIG_ENDIAN); if (!memcmp(rdata->data, prev_reg->content->data, s)) { // No change // Flush byte array @@ -82,7 +83,7 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, - state.is_big_endian); + HOST_BIG_ENDIAN); frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); // Flush byte array g_byte_array_set_size(rdata, 0); @@ -341,14 +342,6 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, qemu_plugin_outs("Pass it with 'out='.\n\n"); exit(1); } - char *endianess = get_argv_val(argv, argc, "endianess"); - if (!endianess || (strcmp(endianess, "b") && strcmp(endianess, "l"))) { - qemu_plugin_outs("'endianess' argument is missing or is not 'b' or 'l'.\n"); - qemu_plugin_outs("This is required until QEMU plugins get a richer API.\n"); - qemu_plugin_outs("Pass it with 'endianess=[b/l]'.\n\n"); - exit(1); - } - state.is_big_endian = endianess[0] == 'b'; state.target_name = g_strdup(info->target_name); state.frame_buffer = g_ptr_array_new(); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index 131ac73a27b2b..bac9cb6a1ae54 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -171,7 +171,6 @@ typedef struct { GPtrArray /**/ *vcpu_modes; ///< Indexed by vcpu id. const char *target_name; - bool is_big_endian; } TraceState; VCPU *vcpu_new(void); From 3dc6e1806baef75a0e79c0bc7a1bbdcfc88d6935 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Wed, 16 Jul 2025 11:22:02 -0500 Subject: [PATCH 55/59] Fix the endian issue once and for all. The GDB stub returned and the rmemory values are all in the endianess of the arch. Now they are normalize accordingly. --- contrib/plugins/bap-tracing/frame_buffer.c | 66 +++--------------- contrib/plugins/bap-tracing/frame_buffer.h | 3 +- contrib/plugins/bap-tracing/trace_meta.c | 2 +- contrib/plugins/bap-tracing/trace_meta.h | 2 +- contrib/plugins/bap-tracing/tracing.c | 78 ++++++++++++++++++++-- contrib/plugins/bap-tracing/tracing.h | 1 + 6 files changed, 88 insertions(+), 64 deletions(-) diff --git a/contrib/plugins/bap-tracing/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c index 93fc56caa61b5..bbfa0c313534d 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.c +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -257,59 +257,9 @@ OperandInfo *frame_init_reg_operand_info(const char *name, const uint8_t *value, return oi; } -static size_t mval_type_to_int(enum qemu_plugin_mem_value_type type) { - switch (type) { - case QEMU_PLUGIN_MEM_VALUE_U8: - return 8; - case QEMU_PLUGIN_MEM_VALUE_U16: - return 16; - case QEMU_PLUGIN_MEM_VALUE_U32: - return 32; - case QEMU_PLUGIN_MEM_VALUE_U64: - return 64; - case QEMU_PLUGIN_MEM_VALUE_U128: - return 128; - default: - g_assert(false); - } - return 0; -} - -static void mval_to_buf(qemu_plugin_mem_value *val, uint8_t *buf) { - switch (val->type) { - case QEMU_PLUGIN_MEM_VALUE_U8: - buf[0] = val->data.u8; - return; - case QEMU_PLUGIN_MEM_VALUE_U16: - buf[0] = (uint8_t)val->data.u16; - buf[1] = (uint8_t)(val->data.u16 >> 8); - return; - case QEMU_PLUGIN_MEM_VALUE_U32: - buf[0] = (uint8_t)val->data.u32; - buf[1] = (uint8_t)(val->data.u32 >> 8); - buf[2] = (uint8_t)(val->data.u32 >> 16); - buf[3] = (uint8_t)(val->data.u32 >> 24); - return; - case QEMU_PLUGIN_MEM_VALUE_U64: - for (size_t i = 0; i < 8; ++i) { - buf[i] = (uint8_t)(val->data.u64 >> (i * 8)); - } - return; - case QEMU_PLUGIN_MEM_VALUE_U128: - for (size_t i = 0; i < 8; ++i) { - buf[i] = (uint8_t)(val->data.u128.low >> (i * 8)); - } - for (size_t i = 0; i < 8; ++i) { - buf[i + 8] = (uint8_t)(val->data.u128.high >> (i * 8)); - } - return; - default: - g_assert(false); - } -} - static OperandInfo *frame_init_mem_operand_info(uint64_t vaddr, - qemu_plugin_mem_value *mval, + const uint8_t *mval, + size_t mval_bits, bool is_store) { MemOperand *ro = g_new(MemOperand, 1); mem_operand__init(ro); @@ -319,26 +269,28 @@ static OperandInfo *frame_init_mem_operand_info(uint64_t vaddr, operand_info_specific__init(ois); ois->mem_operand = ro; - size_t byte_width = mval_type_to_int(mval->type) / 8; + size_t byte_width = mval_bits / 8; OperandUsage *ou = g_new(OperandUsage, 1); operand_usage__init(ou); ou->read = !is_store; ou->written = is_store; OperandInfo *oi = g_new(OperandInfo, 1); operand_info__init(oi); - oi->bit_length = mval_type_to_int(mval->type); + oi->bit_length = mval_bits; oi->operand_info_specific = ois; oi->operand_usage = ou; oi->value.len = byte_width; oi->value.data = g_malloc(oi->value.len); - mval_to_buf(mval, oi->value.data); + memcpy(oi->value.data, mval, oi->value.len); return oi; } bool frame_buffer_append_mem_info(FrameBuffer *fbuf, uint64_t vaddr, - qemu_plugin_mem_value *mval, bool is_store) { - OperandInfo *oi = frame_init_mem_operand_info(vaddr, mval, is_store); + const uint8_t *mval, size_t mval_bits, + bool is_store) { + OperandInfo *oi = + frame_init_mem_operand_info(vaddr, mval, mval_bits, is_store); g_assert(oi); return append_op_info(fbuf, oi); } diff --git a/contrib/plugins/bap-tracing/frame_buffer.h b/contrib/plugins/bap-tracing/frame_buffer.h index 70cc94b4af9a7..f6126ce00dc26 100644 --- a/contrib/plugins/bap-tracing/frame_buffer.h +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -41,7 +41,8 @@ bool frame_buffer_new_frame_std(FrameBuffer *buf, unsigned int thread_id, uint8_t *bytes, size_t bytes_len); bool frame_buffer_append_mem_info(FrameBuffer *fbuf, uint64_t vaddr, - qemu_plugin_mem_value *mval, bool is_store); + const uint8_t *mval, size_t mval_bits, + bool is_store); /** * \brief Appends the given operand info to the open frame. diff --git a/contrib/plugins/bap-tracing/trace_meta.c b/contrib/plugins/bap-tracing/trace_meta.c index 3c31881fdf0ca..4f15265435bee 100644 --- a/contrib/plugins/bap-tracing/trace_meta.c +++ b/contrib/plugins/bap-tracing/trace_meta.c @@ -193,7 +193,7 @@ void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc) { } /// Copies src to dst. dst will always be in little endian byte order. -void memcpy_le(uint8_t *dst, uint8_t *src, size_t len, bool big_endian) { +void memcpy_le(uint8_t *dst, const uint8_t *src, size_t len, bool big_endian) { if (!big_endian) { memcpy(dst, src, len); return; diff --git a/contrib/plugins/bap-tracing/trace_meta.h b/contrib/plugins/bap-tracing/trace_meta.h index 22c964542eb66..03eb57ce3d85c 100644 --- a/contrib/plugins/bap-tracing/trace_meta.h +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -36,7 +36,7 @@ void write_meta(WLOCKED FILE *file, char **plugin_argv, size_t plugin_argc); char *get_argv_val(char **argv, int argc, const char *key); void file_exists_exit(const char *file); -void memcpy_le(uint8_t *dst, uint8_t *src, size_t len, bool big_endian); +void memcpy_le(uint8_t *dst, const uint8_t *src, size_t len, bool big_endian); void swap_to_le(uint8_t *buf, size_t len, bool big_endian); #endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 239619edf6b86..6c36499dfccda 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -15,10 +15,71 @@ static TraceState state = {0}; +static void mval_to_buf(qemu_plugin_mem_value *val, uint8_t *buf) { + size_t mem_val_size = 0; + switch (val->type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + buf[0] = val->data.u8; + mem_val_size = 1; + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + buf[0] = (uint8_t)val->data.u16; + buf[1] = (uint8_t)(val->data.u16 >> 8); + mem_val_size = 2; + break; + case QEMU_PLUGIN_MEM_VALUE_U32: + buf[0] = (uint8_t)val->data.u32; + buf[1] = (uint8_t)(val->data.u32 >> 8); + buf[2] = (uint8_t)(val->data.u32 >> 16); + buf[3] = (uint8_t)(val->data.u32 >> 24); + mem_val_size = 4; + break; + case QEMU_PLUGIN_MEM_VALUE_U64: + for (size_t i = 0; i < 8; ++i) { + buf[i] = (uint8_t)(val->data.u64 >> (i * 8)); + } + mem_val_size = 8; + break; + case QEMU_PLUGIN_MEM_VALUE_U128: + for (size_t i = 0; i < 8; ++i) { + buf[i] = (uint8_t)(val->data.u128.low >> (i * 8)); + } + for (size_t i = 0; i < 8; ++i) { + buf[i + 8] = (uint8_t)(val->data.u128.high >> (i * 8)); + } + mem_val_size = 16; + break; + default: + g_assert(false); + } + swap_to_le(buf, mem_val_size, state.is_big_endian); +} + +static size_t mval_type_to_int(enum qemu_plugin_mem_value_type type) { + switch (type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + return 8; + case QEMU_PLUGIN_MEM_VALUE_U16: + return 16; + case QEMU_PLUGIN_MEM_VALUE_U32: + return 32; + case QEMU_PLUGIN_MEM_VALUE_U64: + return 64; + case QEMU_PLUGIN_MEM_VALUE_U128: + return 128; + default: + g_assert(false); + } + return 0; +} + static void add_mem_op(VCPU *vcpu, unsigned int vcpu_index, FrameBuffer *fbuf, uint64_t vaddr, qemu_plugin_mem_value *mval, bool is_store) { - if (!frame_buffer_append_mem_info(fbuf, vaddr, mval, is_store)) { + size_t mval_bits = mval_type_to_int(mval->type); + uint8_t *buf = g_malloc(mval_bits / 8); + mval_to_buf(mval, buf); + if (!frame_buffer_append_mem_info(fbuf, vaddr, buf, mval_bits, is_store)) { qemu_plugin_outs("Failed to append memory info\n"); } return; @@ -54,7 +115,7 @@ static void add_post_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); int s = qemu_plugin_read_register(reg->handle, rdata); assert(s == prev_reg->content->len); - swap_to_le(rdata->data, s, HOST_BIG_ENDIAN); + swap_to_le(rdata->data, s, state.is_big_endian); if (!memcmp(rdata->data, prev_reg->content->data, s)) { // No change // Flush byte array @@ -83,8 +144,9 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, g_assert(!strcmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, - HOST_BIG_ENDIAN); - frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); + state.is_big_endian); + frame_buffer_append_reg_info(fbuf, reg->name, prev_reg->content, s, + OperandRead); // Flush byte array g_byte_array_set_size(rdata, 0); } @@ -342,6 +404,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, qemu_plugin_outs("Pass it with 'out='.\n\n"); exit(1); } + char *endianess = get_argv_val(argv, argc, "endianess"); + if (!endianess || (strcmp(endianess, "b") && strcmp(endianess, "l"))) { + qemu_plugin_outs("'endianess' argument is missing or is not 'b' or 'l'.\n"); + qemu_plugin_outs("This is required until QEMU plugins get a richer API.\n"); + qemu_plugin_outs("Pass it with 'endianess=[b/l]'.\n\n"); + exit(1); + } + state.is_big_endian = endianess[0] == 'b'; state.target_name = g_strdup(info->target_name); state.frame_buffer = g_ptr_array_new(); diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h index bac9cb6a1ae54..131ac73a27b2b 100644 --- a/contrib/plugins/bap-tracing/tracing.h +++ b/contrib/plugins/bap-tracing/tracing.h @@ -171,6 +171,7 @@ typedef struct { GPtrArray /**/ *vcpu_modes; ///< Indexed by vcpu id. const char *target_name; + bool is_big_endian; } TraceState; VCPU *vcpu_new(void); From adb35d9f1bb45204c1f3a30f8bca49a7cf6bf2cd Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 21 Jul 2025 10:57:22 -0500 Subject: [PATCH 56/59] Use QEMU_PLUGIN_MEM_RW so also memory writes are detected. --- contrib/plugins/bap-tracing/tracing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 6c36499dfccda..514ef8e69e979 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -333,7 +333,7 @@ static void cb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { qemu_plugin_register_vcpu_insn_exec_cb(tb_insn, log_insn_reg_access, QEMU_PLUGIN_CB_R_REGS, insn_data); qemu_plugin_register_vcpu_mem_cb(tb_insn, log_insn_mem_access, - QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_MEM_R, + QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_MEM_RW, NULL); } } From eca5b1ec96320de28ece2e44c200c0565b24a3f1 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 25 Jul 2025 10:08:46 -0500 Subject: [PATCH 57/59] Fix typos, extend docs add tracing helper script --- README.md | 21 ++++++++++++++++++++- contrib/plugins/bap-tracing/tracing.c | 10 +++++----- gen-trace.sh | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100755 gen-trace.sh diff --git a/README.md b/README.md index c47733d604d69..fb1e593029e4b 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,31 @@ The plugin takes two required arguments: `bin_path`: The path to the binary emulated. Due to a [QEMU bug](https://gitlab.com/qemu-project/qemu/-/issues/3014) this cannot be inferred. `out`: The output file to save the trace into. +`endianness`: The architecture endanness. ```bash -./qemu-sparc64 -plugin file=./contrib/plugins/bap-tracing/libbap_tracing.so,bin_path=,out= -d plugin +./qemu-sparc64 -plugin file=buil/contrib/plugins/bap-tracing/libbap_tracing.so,bin_path=,out=,endianness=[b/l] -d plugin ls ``` +You can also use the helper shell script: + +```bash +./gen-trace.sh ./build/ sparc64 b +``` + +> [!NOTE] +> The trace plugin currently only generates standard frames. +> This is due to the limitations of the QEMU plugin API. +> +> If the traced binary exits due to an exception it can only indirectly be observed. +> It will produce a standard frame without any logged post register state. +> Any completed memory read/write might still be logged. +> +> If you suspect this, execute the binary with the `execlog` plugin (see `gen-trace.sh`) +> to check of the execution stops earlier than expected. + + ## Trace format The generated trace consists of three parts: the header, diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 514ef8e69e979..47441451e02a7 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -404,14 +404,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, qemu_plugin_outs("Pass it with 'out='.\n\n"); exit(1); } - char *endianess = get_argv_val(argv, argc, "endianess"); - if (!endianess || (strcmp(endianess, "b") && strcmp(endianess, "l"))) { - qemu_plugin_outs("'endianess' argument is missing or is not 'b' or 'l'.\n"); + char *endianness = get_argv_val(argv, argc, "endianness"); + if (!endianness || (strcmp(endianness, "b") && strcmp(endianness, "l"))) { + qemu_plugin_outs("'endianness' argument is missing or is not 'b' or 'l'.\n"); qemu_plugin_outs("This is required until QEMU plugins get a richer API.\n"); - qemu_plugin_outs("Pass it with 'endianess=[b/l]'.\n\n"); + qemu_plugin_outs("Pass it with 'endianness=[b/l]'.\n\n"); exit(1); } - state.is_big_endian = endianess[0] == 'b'; + state.is_big_endian = endianness[0] == 'b'; state.target_name = g_strdup(info->target_name); state.frame_buffer = g_ptr_array_new(); diff --git a/gen-trace.sh b/gen-trace.sh new file mode 100755 index 0000000000000..3596b1499667c --- /dev/null +++ b/gen-trace.sh @@ -0,0 +1,17 @@ +#!/bin/sh -x + +if [ "$#" -lt 4 ]; then + echo "$0 [args...]" + echo " is attached to qemu-" + exit 1 +fi + +BUILDIR=$1 +ARCH=$2 +EN=$3 +BIN=$4 +BNAME=$(basename $BIN) + +$BUILDIR/qemu-$ARCH -plugin file="$BUILDIR/contrib/plugins/bap-tracing/libbap_tracing.so,bin_path=$BIN",out="$BNAME.trace",endianness="$EN" -d plugin "$BIN" ""${@:5}"" +ls -lh "$BNAME.trace" +# $BUILDIR/qemu-$ARCH -plugin file="$BUILDIR/contrib/plugins/libexeclog.so" -d plugin "$BIN" From d1b330e76ff4089acb7154d308a37b9716923039 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Mon, 28 Jul 2025 08:59:46 -0500 Subject: [PATCH 58/59] Fix typos, add execlog helper script --- README.md | 2 +- gen-execlog.sh | 14 ++++++++++++++ gen-trace.sh | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100755 gen-execlog.sh diff --git a/README.md b/README.md index fb1e593029e4b..c48b0cff261dd 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ You can also use the helper shell script: > It will produce a standard frame without any logged post register state. > Any completed memory read/write might still be logged. > -> If you suspect this, execute the binary with the `execlog` plugin (see `gen-trace.sh`) +> If you suspect this, execute the binary with the `execlog` plugin (see `gen-trace.sh` or `gen-execlog.sh`) > to check of the execution stops earlier than expected. diff --git a/gen-execlog.sh b/gen-execlog.sh new file mode 100755 index 0000000000000..e65cc50f8a936 --- /dev/null +++ b/gen-execlog.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +if [ "$#" -lt 2 ]; then + echo "$0 [args...]" + echo " is appended to qemu-" + exit 1 +fi + +BUILDIR=$1 +ARCH=$2 +BIN=$3 + +$BUILDIR/qemu-$ARCH -plugin file="$BUILDIR/contrib/plugins/libexeclog.so",reg=* -d plugin "$BIN" + diff --git a/gen-trace.sh b/gen-trace.sh index 3596b1499667c..ccb007fb7e12a 100755 --- a/gen-trace.sh +++ b/gen-trace.sh @@ -1,8 +1,8 @@ -#!/bin/sh -x +#!/bin/sh if [ "$#" -lt 4 ]; then echo "$0 [args...]" - echo " is attached to qemu-" + echo " is appended to qemu-" exit 1 fi From 004ebab5c261b0eacca2e539af4847a8c33ef388 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Thu, 31 Jul 2025 12:26:52 -0500 Subject: [PATCH 59/59] Compare register names case insenitive (fixes ARM). --- contrib/plugins/bap-tracing/tracing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c index 47441451e02a7..fbb7161f0e5ce 100644 --- a/contrib/plugins/bap-tracing/tracing.c +++ b/contrib/plugins/bap-tracing/tracing.c @@ -141,7 +141,7 @@ static void add_pre_reg_state(VCPU *vcpu, unsigned int vcpu_index, &g_array_index(current_regs, qemu_plugin_reg_descriptor, i); size_t s = qemu_plugin_read_register(reg->handle, rdata); Register *prev_reg = g_ptr_array_index(vcpu->registers, i); - g_assert(!strcmp(prev_reg->name, reg->name) && + g_assert(!g_ascii_strcasecmp(prev_reg->name, reg->name) && prev_reg->handle == reg->handle); memcpy_le(prev_reg->content->data, rdata->data, prev_reg->content->len, state.is_big_endian);