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 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 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/README.md b/README.md new file mode 100644 index 0000000000000..de30c17145b64 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# 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. + +## 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 +mkdir build +cd build +# See `../configure --help` for a list of targets. +../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 = 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 | | +| 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) | | 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/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/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/frame_buffer.c b/contrib/plugins/bap-tracing/frame_buffer.c new file mode 100644 index 0000000000000..608d176df4076 --- /dev/null +++ b/contrib/plugins/bap-tracing/frame_buffer.c @@ -0,0 +1,342 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#include "frame_buffer.h" +#include "trace_meta.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 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 std_frame_add_operand(StdFrame *std_frame, OperandInfo *oi) { + OperandValueList *ol; + if (oi->operand_usage->written) { + ol = std_frame->operand_post_list; + } else { + ol = 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; + return true; +} + +FrameBuffer *frame_buffer_new(void) { + FrameBuffer *fb = g_malloc0(sizeof(FrameBuffer)); + return fb; +} + +bool frame_buffer_is_full(const FrameBuffer *buf) { + return buf->idx + 1 >= frames_per_toc_entry; +} + +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]; + 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]); + } + APPEND(", "); + } + APPEND(" ], post: [ "); + for (size_t i = 0; i < sframe->operand_post_list->n_elem; i++) { + OperandInfo *oi = sframe->operand_post_list->elem[i]; + 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]); + } + APPEND(", "); + } + + APPEND("]}"); + return str; +} + +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. +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); + 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++; + } + memset(buf->fbuf, 0, sizeof(buf->fbuf)); + buf->idx = 0; + return n; +} + +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; + } + 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; + + buf->fbuf[buf->idx] = frame; + return true; +} + +static bool append_op_info(FrameBuffer *buf, OperandInfo *oi) { + Frame *frame = buf->fbuf[buf->idx]; + if (!frame || !frame->std_frame) { + qemu_plugin_outs( + "Attempt to append operand info to a uninitialzied frame."); + return false; + } + 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) { + 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; +} + +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 new file mode 100644 index 0000000000000..1a6bb0c15bd24 --- /dev/null +++ b/contrib/plugins/bap-tracing/frame_buffer.h @@ -0,0 +1,55 @@ +// 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" +#include "trace_consts.h" +#include "trace_meta.h" + +typedef enum { + OperandRead = 1, + OperandWritten = 2, +} OperandAccess; + +typedef struct { + Frame *fbuf[FRAMES_PER_TOC_ENTRY_]; ///< The frames buffered. + size_t idx; ///< Points to currently open frame. +} 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(void); + +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); +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_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. + */ +bool frame_buffer_append_reg_info(FrameBuffer *buf, const char *name, + const GByteArray *content, size_t reg_size, + OperandAccess acc); + +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/meson.build b/contrib/plugins/bap-tracing/meson.build new file mode 100644 index 0000000000000..e90d0866bd45b --- /dev/null +++ b/contrib/plugins/bap-tracing/meson.build @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: 2025 Rot127 +# SPDX-License-Identifier: GPL-2.0-only + +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@']) + +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, + 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 +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: frame_proto_src_raw +) + +libprotobuf = dependency('libprotobuf-c') +frame_protobuf = static_library('protobuf', [frame_proto_src], pic: true) +dep_libprotobuf = declare_dependency( + sources : [frame_proto_src, frame_arch_h], + link_with : [frame_protobuf], + dependencies: [libprotobuf] +) + +bap_tracing_src = files( + 'frame_buffer.c', + 'tracing.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 new file mode 100644 index 0000000000000..21c777d7cc39a --- /dev/null +++ b/contrib/plugins/bap-tracing/trace_consts.h @@ -0,0 +1,26 @@ +#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 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_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; + +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 + +#endif 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..c918fad2f5632 --- /dev/null +++ b/contrib/plugins/bap-tracing/trace_meta.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef BAP_TRACE_META_H +#define BAP_TRACE_META_H + +#include + +/** + * \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) \ + err(1, "fwrite failed"); \ + } while (0) + +#define WRITE_BUF(x, n) \ + do { \ + 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) + +#endif diff --git a/contrib/plugins/bap-tracing/tracing.c b/contrib/plugins/bap-tracing/tracing.c new file mode 100644 index 0000000000000..342160c915b1c --- /dev/null +++ b/contrib/plugins/bap-tracing/tracing.c @@ -0,0 +1,300 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// 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 = {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)) { + 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) { + 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) { + + 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, rdata); + assert(s == prev_reg->content->len); + if (!memcmp(rdata->data, prev_reg->content->data, s)) { + // No change + continue; + } + + if (!frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, + OperandWritten)) { + qemu_plugin_outs("Failed to append opinfo.\n"); + return; + } + } +} + +static void add_pre_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) { + 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); + frame_buffer_append_reg_info(fbuf, reg->name, rdata, s, OperandRead); + } +} + +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(); + + 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 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); + 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); +} + +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); + + FrameBuffer *fbuf = g_ptr_array_index(state.frame_buffer, vcpu_index); + VCPU *vcpu = g_ptr_array_index(state.vcpus, vcpu_index); + g_assert(vcpu); + GArray *current_regs = qemu_plugin_get_registers(); + g_assert(current_regs->len == vcpu->registers->len); + + 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)) { + write_toc_entry(fbuf); + } + + // 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.frame_buffer_lock); + g_rw_lock_reader_unlock(&state.vcpus_array_lock); +} + +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); + + reg->handle = desc->handle; + reg->name = g_intern_string(lower); + reg->content = g_byte_array_new(); + + /* read the initial value */ + int r = qemu_plugin_read_register(reg->handle, reg->content); + g_assert(r > 0); + return reg; +} + +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(); + g_assert(vcpu->registers); + g_ptr_array_insert(state.vcpus, vcpu_index, vcpu); + + 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); + g_rw_lock_writer_unlock(&state.vcpus_array_lock); +} + +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 *tb_insn; + size_t n_insns = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < n_insns; i++) { + 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_NO_REGS, QEMU_PLUGIN_MEM_R, + NULL); + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) { + 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) { + *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; + if (!get_frame_arch_mach(target_name, &frame_arch, &frame_mach)) { + qemu_plugin_outs("Failed to get arch/mach.\n"); + return false; + } + 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(total_num_frames); // Gets updated later + WRITE(toc_index_offset); // Gets updated later + 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.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 || + !state.toc_entries_offsets)) { + return 1; + } + for (size_t i = 0; i < argc; ++i) { + qemu_plugin_outs(argv[i]); + } + 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); + + 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); + + return 0; +} diff --git a/contrib/plugins/bap-tracing/tracing.h b/contrib/plugins/bap-tracing/tracing.h new file mode 100644 index 0000000000000..ad9dfe908355e --- /dev/null +++ b/contrib/plugins/bap-tracing/tracing.h @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2025 Rot127 +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef BAP_TRACING_H +#define BAP_TRACING_H + +#include +#include +#include + +#include "frame.piqi.pb-c-patched.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; + +/** + * \brief VLIW architecture have instructions longer than 4 or 8bytes. + */ +#define MAX_INSTRUCTION_SIZE 64 + +typedef struct { + 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 { + GPtrArray /**/ *registers; +} VCPU; + +typedef struct { + GRWLock vcpus_array_lock; + GPtrArray /**/ *vcpus; + + GRWLock frame_buffer_lock; + GPtrArray /**/ *frame_buffer; ///< Indexed by vcpu id + + 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; + +VCPU *vcpu_new(void); +Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc); +Instruction *init_insn(struct qemu_plugin_insn *insn); + +#endif 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', 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;