public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
From: Zane Leung <liangzhen@linux.spacemit.com>
To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org,
	alexander.shishkin@linux.intel.com, irogers@google.com
Cc: coresight@lists.linaro.org, peterz@infradead.org,
	mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com,
	jolsa@kernel.org, adrian.hunter@intel.com,
	kan.liang@linux.intel.com, mchitale@gmail.com,
	anup@brainfault.org, atish.patra@linux.dev,
	andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
	linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org,
	anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com,
	zhuangqiubin@linux.spacemit.com
Subject: [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace
Date: Tue, 14 Apr 2026 11:41:53 +0800	[thread overview]
Message-ID: <20260414034153.3272485-13-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com>

From: liangzhen <zhen.liang@spacemit.com>

Add RISC-V trace decoder implementation and integrate it into perf's
auxtrace infrastructure. This enables processing of RISC-V Nexus trace
data through the standard perf auxtrace pipeline.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/perf/util/Build             |    1 +
 tools/perf/util/auxtrace.c        |    3 +
 tools/perf/util/rvtrace-decoder.c | 1039 +++++++++++++++++++++++++++++
 tools/perf/util/rvtrace.h         |    2 +
 4 files changed, 1045 insertions(+)
 create mode 100644 tools/perf/util/rvtrace-decoder.c

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 648a8552df9e..5913eec9b847 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -148,6 +148,7 @@ perf-util-y += cs-etm-decoder/
 endif
 perf-util-y += cs-etm-base.o
 
+perf-util-y += rvtrace-decoder.o
 perf-util-y += nexus-rv-decoder/
 
 perf-util-y += parse-branch-options.o
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 944a43d48739..7539252a4543 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -56,6 +56,7 @@
 #include "s390-cpumsf.h"
 #include "util/mmap.h"
 #include "powerpc-vpadtl.h"
+#include "rvtrace.h"
 
 #include <linux/ctype.h>
 #include "symbol/kallsyms.h"
@@ -1412,6 +1413,8 @@ int perf_event__process_auxtrace_info(const struct perf_tool *tool __maybe_unuse
 		err = powerpc_vpadtl_process_auxtrace_info(event, session);
 		break;
 	case PERF_AUXTRACE_RISCV_TRACE:
+		err = rvtrace_process_auxtrace_info(event, session);
+		break;
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/rvtrace-decoder.c b/tools/perf/util/rvtrace-decoder.c
new file mode 100644
index 000000000000..ce103783d0c3
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder.c
@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ * Author: liangzhen <zhen.liang@spacemit.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+#include <sys/mman.h>
+
+#include <stdlib.h>
+
+#include "auxtrace.h"
+#include "color.h"
+#include "debug.h"
+#include "evlist.h"
+#include "intlist.h"
+#include "machine.h"
+#include "map.h"
+#include "perf.h"
+#include "session.h"
+#include "tool.h"
+#include "thread.h"
+#include "thread_map.h"
+#include "thread-stack.h"
+#include "util.h"
+#include "dso.h"
+#include "addr_location.h"
+#include <inttypes.h>
+#include "util/synthetic-events.h"
+#include "rvtrace.h"
+#include "nexus-rv-decoder/nexus-rv-decoder.h"
+#include "../../arch/riscv/include/asm/insn.h"
+
+struct rvtrace_auxtrace {
+	struct auxtrace auxtrace;
+	struct auxtrace_queues queues;
+	struct auxtrace_heap heap;
+	struct itrace_synth_opts synth_opts;
+	struct perf_session *session;
+	struct machine *machine;
+
+	u8 timeless_decoding;
+	u8 snapshot_mode;
+	u8 data_queued;
+
+	int num_cpu;
+	u64 latest_kernel_timestamp;
+	u32 auxtrace_type;
+	u64 branches_sample_type;
+	u64 branches_id;
+	u64 **metadata;
+	unsigned int pmu_type;
+};
+
+struct rvtrace_queue {
+	struct rvtrace_auxtrace *rvtrace;
+	struct thread *thread;
+	struct nexus_rv_insn_decoder *decoder;
+	struct auxtrace_buffer *buffer;
+	union perf_event *event_buf;
+	unsigned int queue_nr;
+	u64 offset;
+};
+
+static void rvtrace_set_thread(struct rvtrace_queue *rvtraceq,
+			       pid_t tid)
+{
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+
+	if (tid != -1) {
+		thread__zput(rvtraceq->thread);
+		rvtraceq->thread = machine__find_thread(rvtrace->machine, -1, tid);
+	}
+
+	/* Couldn't find a known thread */
+	if (!rvtraceq->thread)
+		rvtraceq->thread = machine__idle_thread(rvtrace->machine);
+}
+
+static u32 rvtrace_devmem_access(u64 address, size_t size, u8 *buffer)
+{
+	int fd;
+	void *map_base, *virt_addr;
+	u64 page_size = 4096, mapped_size = 4096;
+	u64 page_base = address & ~(page_size - 1);
+	u64 offset_in_page = address - page_base;
+	unsigned int width = 8 * size;
+
+	if (offset_in_page + width > page_size)
+		mapped_size *= 2;
+
+	fd = open("/dev/mem", O_RDONLY | O_SYNC);
+	if (fd < 0)
+		return 0;
+
+	map_base = mmap(NULL, mapped_size, PROT_READ, MAP_SHARED, fd, (off_t)(page_base));
+	if (map_base == MAP_FAILED) {
+		pr_debug("failed to mmap device address 0x%lx\n", address);
+		close(fd);
+		return 0;
+	}
+
+	virt_addr = (char *)map_base + offset_in_page;
+
+	/*
+	 * Note: higher versions of glibc use automatic vectorization by
+	 * default for memcpy, which can lead to incorrect memory results.
+	 */
+	/* memcpy(buffer, virt_addr, size); */
+	for (size_t i = 0; i < size; i++)
+		buffer[i] = ((volatile u8 *)virt_addr)[i];
+
+	munmap(map_base, mapped_size);
+	close(fd);
+	return size;
+}
+
+static u32 rvtrace_mem_access(void *data, u64 address, enum riscv_privilege_mode prv,
+			      int context, size_t size, u8 *buffer)
+{
+	u8 cpumode;
+	u64 offset;
+	int len;
+	struct machine *machine;
+	struct addr_location al;
+	struct dso *dso;
+	int ret = 0;
+	struct rvtrace_queue *rvtraceq = data;
+
+	if (!rvtraceq)
+		goto out;
+
+	addr_location__init(&al);
+
+	/* If the riscv_privilege_mode is machine mode, access physical address by /dev/mem */
+	if (prv == RISCV_PRIV_MACHINE_MODE)
+		return rvtrace_devmem_access(address, size, buffer);
+
+	machine = rvtraceq->rvtrace->machine;
+	if (address >= machine__kernel_start(machine))
+		cpumode = PERF_RECORD_MISC_KERNEL;
+	else
+		cpumode = PERF_RECORD_MISC_USER;
+
+	if (thread__tid(rvtraceq->thread) != context)
+		rvtrace_set_thread(rvtraceq, context);
+
+	if (!thread__find_map(rvtraceq->thread, cpumode, address, &al))
+		goto out;
+
+	dso = map__dso(al.map);
+	if (!dso)
+		goto out;
+
+	if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR &&
+	    dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE))
+		goto out;
+
+	offset = map__map_ip(al.map, address);
+
+	map__load(al.map);
+
+	len = dso__data_read_offset(dso, machine, offset, buffer, size);
+	if (len <= 0) {
+		ui__warning_once("RISC-V Nexus Trace: Missing DSO. Use 'perf archive' or debuginfod to export data from the traced system.\n"
+				 "                  Enable CONFIG_PROC_KCORE or use option '-k /path/to/vmlinux' for kernel symbols.\n");
+		if (!dso__auxtrace_warned(dso)) {
+			pr_err("RISC-V Nexus Trace: Debug data not found for address %#"PRIx64" in %s\n",
+				address,
+				dso__long_name(dso) ? dso__long_name(dso) : "Unknown");
+			dso__set_auxtrace_warned(dso);
+		}
+		goto out;
+	}
+	ret = len;
+out:
+	addr_location__exit(&al);
+	return ret;
+}
+
+
+static u8 rvtrace_cpu_mode(enum riscv_privilege_mode prv)
+{
+	u8 cpumode;
+
+	switch (prv) {
+	case RISCV_PRIV_USER_MODE:
+		cpumode = PERF_RECORD_MISC_USER;
+		break;
+	case RISCV_PRIV_SUPERVISOR_MODE:
+	case RISCV_PRIV_MACHINE_MODE:
+	default:
+		cpumode = PERF_RECORD_MISC_KERNEL;
+		break;
+	}
+	return cpumode;
+}
+
+static void rvtrace_synth_copy_insn(struct rvtrace_queue *rvtraceq,
+				    struct nexus_rv_packet *packet,
+				    struct perf_sample *sample)
+{
+	int ret;
+	u32 insn;
+
+	ret = rvtrace_mem_access(rvtraceq, sample->ip, packet->prv, packet->context,
+				 sizeof(insn), (u8 *)&insn);
+	if (!ret)
+		return;
+
+	sample->insn_len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_GE_32) ? 4 : 2;
+	memcpy(sample->insn, &insn, sample->insn_len);
+}
+
+static inline u64 rvtrace_resolve_sample_time(struct rvtrace_queue *rvtraceq,
+					      struct nexus_rv_packet *packet)
+{
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+
+	if (!rvtrace->timeless_decoding)
+		return packet->timestamp;
+	else
+		return rvtrace->latest_kernel_timestamp;
+}
+
+static int rvtrace_synth_branch_sample(struct rvtrace_queue *rvtraceq,
+				       struct nexus_rv_packet *packet)
+{
+	int ret = 0;
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+	struct perf_sample sample;
+	union perf_event *event = rvtraceq->event_buf;
+
+	perf_sample__init(&sample, /*all=*/true);
+	event->sample.header.type = PERF_RECORD_SAMPLE;
+	event->sample.header.misc = rvtrace_cpu_mode(packet->prv);
+	event->sample.header.size = sizeof(struct perf_event_header);
+
+	if (thread__tid(rvtraceq->thread) != packet->context)
+		rvtrace_set_thread(rvtraceq, packet->context);
+
+	/* Set time field based on rvtrace auxtrace config. */
+	sample.time = rvtrace_resolve_sample_time(rvtraceq, packet);
+
+	sample.ip = packet->start_addr;
+	sample.pid = thread__pid(rvtraceq->thread);
+	sample.tid = thread__tid(rvtraceq->thread);
+	sample.addr = packet->end_addr;
+	sample.insn_cnt = packet->insn_cnt;
+	sample.id = rvtraceq->rvtrace->branches_id;
+	sample.stream_id = rvtraceq->rvtrace->branches_id;
+	sample.period = 1;
+	sample.cpu = packet->cpu;
+	sample.flags = 0;
+	sample.cpumode = event->sample.header.misc;
+
+	rvtrace_synth_copy_insn(rvtraceq, packet, &sample);
+
+	ret = perf_session__deliver_synth_event(rvtrace->session, event, &sample);
+	if (ret)
+		pr_err(
+		"RISC-V Trace: failed to deliver instruction event, error %d\n",
+		ret);
+	perf_sample__exit(&sample);
+
+	return ret;
+}
+
+static int rvtrace_synth_events(struct rvtrace_auxtrace *rvtrace,
+				struct perf_session *session)
+{
+	struct evlist *evlist = session->evlist;
+	struct evsel *evsel;
+	struct perf_event_attr attr;
+	bool found = false;
+	u64 id;
+	int err;
+
+	evlist__for_each_entry(evlist, evsel) {
+		if (evsel->core.attr.type == rvtrace->pmu_type) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_debug("No selected events with RISC-V Trace data\n");
+		return 0;
+	}
+
+	memset(&attr, 0, sizeof(struct perf_event_attr));
+	attr.size = sizeof(struct perf_event_attr);
+	attr.type = PERF_TYPE_HARDWARE;
+	attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
+	attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
+			    PERF_SAMPLE_PERIOD;
+	if (rvtrace->timeless_decoding)
+		attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
+	else
+		attr.sample_type |= PERF_SAMPLE_TIME;
+
+	attr.exclude_user = evsel->core.attr.exclude_user;
+	attr.exclude_kernel = evsel->core.attr.exclude_kernel;
+	attr.exclude_hv = evsel->core.attr.exclude_hv;
+	attr.exclude_host = evsel->core.attr.exclude_host;
+	attr.exclude_guest = evsel->core.attr.exclude_guest;
+	attr.sample_id_all = evsel->core.attr.sample_id_all;
+	attr.read_format = evsel->core.attr.read_format;
+
+	/* create new id val to be a fixed offset from evsel id */
+	id = evsel->core.id[0] + 1000000000;
+	if (!id)
+		id = 1;
+
+	if (rvtrace->synth_opts.branches) {
+		attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
+		attr.sample_period = 1;
+		attr.sample_type |= PERF_SAMPLE_ADDR;
+
+		err = perf_session__deliver_synth_attr_event(session, &attr, id);
+		if (err)
+			return err;
+		rvtrace->branches_sample_type = attr.sample_type;
+		rvtrace->branches_id = id;
+		id += 1;
+		attr.sample_type &= ~(u64)PERF_SAMPLE_ADDR;
+	}
+
+	return 0;
+}
+
+static int rvtrace_process_queue(struct rvtrace_queue *rvtraceq)
+{
+	struct nexus_rv_packet_buffer *packet_buffer = &rvtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct nexus_rv_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			rvtrace_synth_branch_sample(rvtraceq, &packet);
+		else if (packet.sample_type == RVTRACE_LOSS)
+			fprintf(stdout, "RISC-V Trace: A FIFO overrun has resulted in the loss of one or more messages\n");
+	}
+
+	return 0;
+}
+
+static int rvtrace_get_trace(struct rvtrace_queue *rvtraceq)
+{
+	struct auxtrace_buffer *aux_buffer = rvtraceq->buffer;
+	struct auxtrace_buffer *old_buffer = aux_buffer;
+	struct auxtrace_queue *queue;
+
+	queue = &rvtraceq->rvtrace->queues.queue_array[rvtraceq->queue_nr];
+
+	aux_buffer = auxtrace_buffer__next(queue, aux_buffer);
+
+	/* If no more data, drop the previous auxtrace_buffer and return */
+	if (!aux_buffer) {
+		if (old_buffer)
+			auxtrace_buffer__drop_data(old_buffer);
+		return 0;
+	}
+
+	rvtraceq->buffer = aux_buffer;
+
+	/* If the aux_buffer doesn't have data associated, try to load it */
+	if (!aux_buffer->data) {
+		/* get the file desc associated with the perf data file */
+		int fd = perf_data__fd(rvtraceq->rvtrace->session->data);
+
+		aux_buffer->data = auxtrace_buffer__get_data(aux_buffer, fd);
+		if (!aux_buffer->data)
+			return -ENOMEM;
+	}
+
+	/* If valid, drop the previous buffer */
+	if (old_buffer)
+		auxtrace_buffer__drop_data(old_buffer);
+
+	return aux_buffer->size;
+}
+
+/*
+ * rvtrace_get_data_block: Fetch a block from the auxtrace_buffer queue
+ *                         if need be.
+ * Returns:     < 0     if error
+ *              = 0     if no more auxtrace_buffer to read
+ *              > 0     if the current buffer isn't empty yet
+ */
+static int rvtrace_get_data_block(struct rvtrace_queue *rvtraceq)
+{
+	int ret;
+
+	ret = rvtrace_get_trace(rvtraceq);
+	if (ret <= 0)
+		return ret;
+
+	/*
+	 * We cannot assume consecutive blocks in the data file
+	 * are contiguous, reset the decoder to force re-sync.
+	 */
+	ret = nexus_rv_insn_decoder_reset(rvtraceq->decoder);
+	if (ret)
+		return ret;
+
+	return rvtraceq->buffer->size;
+}
+
+static int rvtrace_run_timeless_decoder(struct rvtrace_queue *rvtraceq)
+{
+	int ret;
+
+	while (1) {
+		ret = rvtrace_get_data_block(rvtraceq);
+		if (ret < 0)
+			return ret;
+
+		if (ret == 0)
+			break;
+
+		ret = nexus_rv_insn_decode_data(rvtraceq->decoder,
+						rvtraceq->buffer->data,
+						rvtraceq->buffer->size);
+		if (ret)
+			return ret;
+
+		ret = rvtrace_process_queue(rvtraceq);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int rvtrace_process_timeless_queues(struct rvtrace_auxtrace *rvtrace,
+					   pid_t tid)
+{
+	unsigned int i;
+	struct auxtrace_queues *queues = &rvtrace->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		struct auxtrace_queue *queue = &rvtrace->queues.queue_array[i];
+		struct rvtrace_queue *rvtraceq = queue->priv;
+
+		if (rvtraceq && ((tid == -1) || (queue->tid == tid))) {
+			rvtrace_set_thread(rvtraceq, queue->tid);
+			rvtrace_run_timeless_decoder(rvtraceq);
+		}
+	}
+
+	return 0;
+}
+
+static u64 rvtrace_queue_get_timestamp(struct rvtrace_queue *rvtraceq)
+{
+	struct nexus_rv_packet_buffer *packet_buffer = &rvtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct nexus_rv_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			return packet.timestamp;
+	}
+
+	return 0;
+}
+
+static int rvtrace_queue_first_timestamp(struct rvtrace_auxtrace *rvtrace,
+					 struct rvtrace_queue *rvtraceq,
+					 unsigned int queue_nr)
+{
+	int ret;
+	u64 timestamp = 0;
+
+	/* Decode the first segment of data until a timestamp is found,
+	 * then add it to the heap for sorting by time across multiple
+	 * queues.
+	 */
+	while (1) {
+		/*
+		 * Fetch an aux_buffer from this rvtraceq. Bail if no more
+		 * blocks or an error has been encountered.
+		 */
+		ret = rvtrace_get_data_block(rvtraceq);
+		if (ret <= 0)
+			goto out;
+
+		ret = nexus_rv_insn_decode_data(rvtraceq->decoder,
+						rvtraceq->buffer->data,
+						rvtraceq->buffer->size);
+		if (ret)
+			goto out;
+
+		timestamp = rvtrace_queue_get_timestamp(rvtraceq);
+
+		/* We found a timestamp, no need to continue. */
+		if (timestamp)
+			break;
+	}
+
+	/* We have a timestamp and add it to the min heap */
+	ret = auxtrace_heap__add(&rvtrace->heap, queue_nr, timestamp);
+out:
+	return ret;
+}
+
+static int rvtrace_process_timestamped_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	int ret = 0;
+	unsigned int queue_nr, i;
+	struct auxtrace_queue *queue;
+	struct rvtrace_queue *rvtraceq;
+
+	/* First, find the first timestamp for each queue and add it to the heap. */
+	for (i = 0; i < rvtrace->queues.nr_queues; i++) {
+		queue = &rvtrace->queues.queue_array[i];
+		rvtraceq = queue->priv;
+		if (!rvtraceq)
+			continue;
+
+		rvtrace_set_thread(rvtraceq, queue->tid);
+
+		ret = rvtrace_queue_first_timestamp(rvtrace, rvtraceq, i);
+		if (ret)
+			return ret;
+	}
+
+	/* Process queues in the heap in timestamp order */
+	while (1) {
+		if (!rvtrace->heap.heap_cnt)
+			break;
+
+		/* Take the entry at the top of the min heap */
+		queue_nr = rvtrace->heap.heap_array[0].queue_nr;
+		queue = &rvtrace->queues.queue_array[queue_nr];
+		rvtraceq = queue->priv;
+
+		/*
+		 * Remove the top entry from the heap since we are about
+		 * to process it.
+		 */
+		auxtrace_heap__pop(&rvtrace->heap);
+
+		/*
+		 * Packets associated with this timestamp are already in
+		 * the rvtraceq->packet_buffer, so process them.
+		 */
+		ret = rvtrace_process_queue(rvtraceq);
+		if (ret)
+			return ret;
+
+		/*
+		 * Packets for this timestamp have been processed, time to
+		 * move on to the next timestamp, find the next timestamp
+		 * for this rvtraceq.
+		 */
+		ret = rvtrace_queue_first_timestamp(rvtrace, rvtraceq, queue_nr);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct rvtrace_queue *rvtrace_alloc_queue(struct rvtrace_auxtrace *rvtrace,
+						 unsigned int queue_nr)
+{
+	struct nexus_rv_insn_decoder_params params;
+	struct rvtrace_queue *rvtraceq;
+
+	rvtraceq = zalloc(sizeof(*rvtraceq));
+	if (!rvtraceq)
+		return NULL;
+
+	rvtraceq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
+	if (!rvtraceq->event_buf)
+		goto out_free;
+
+	rvtraceq->rvtrace = rvtrace;
+	rvtraceq->queue_nr = queue_nr;
+
+	params.mem_access = rvtrace_mem_access;
+	params.data = rvtraceq;
+	params.formatted = true;
+	if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC])
+		params.src_bits = rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS];
+
+	rvtraceq->decoder = nexus_rv_insn_decoder_new(&params);
+	if (!rvtraceq->decoder)
+		goto out_free;
+
+	rvtraceq->offset = 0;
+
+	return rvtraceq;
+
+out_free:
+	zfree(&rvtraceq->event_buf);
+	free(rvtraceq);
+
+	return NULL;
+}
+
+static int rvtrace_setup_queue(struct rvtrace_auxtrace *rvtrace,
+			       struct auxtrace_queue *queue,
+			       unsigned int queue_nr)
+{
+	struct rvtrace_queue *rvtraceq = queue->priv;
+
+	if (list_empty(&queue->head) || rvtraceq)
+		return 0;
+
+	rvtraceq = rvtrace_alloc_queue(rvtrace, queue_nr);
+
+	if (!rvtraceq)
+		return -ENOMEM;
+
+	queue->priv = rvtraceq;
+
+	return 0;
+}
+
+static int rvtrace_setup_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < rvtrace->queues.nr_queues; i++) {
+		ret = rvtrace_setup_queue(rvtrace, &rvtrace->queues.queue_array[i], i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rvtrace_update_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	if (rvtrace->queues.new_data) {
+		rvtrace->queues.new_data = false;
+		return rvtrace_setup_queues(rvtrace);
+	}
+
+	return 0;
+}
+
+static int rvtrace_flush_events(struct perf_session *session,
+				const struct perf_tool *tool)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+					struct rvtrace_auxtrace,
+					auxtrace);
+	int ret;
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events)
+		return -EINVAL;
+
+	ret = rvtrace_update_queues(rvtrace);
+	if (ret < 0)
+		return ret;
+
+	if (rvtrace->timeless_decoding) {
+		/*
+		 * Pass tid = -1 to process all queues. But likely they will have
+		 * already been processed on PERF_RECORD_EXIT anyway.
+		 */
+		return rvtrace_process_timeless_queues(rvtrace, -1);
+	}
+
+	return rvtrace_process_timestamped_queues(rvtrace);
+}
+
+static void rvtrace_free_queue(void *priv)
+{
+	struct rvtrace_queue *rvtraceq = priv;
+
+	if (!rvtraceq)
+		return;
+
+	thread__zput(rvtraceq->thread);
+	nexus_rv_insn_decoder_free(rvtraceq->decoder);
+	zfree(&rvtraceq->event_buf);
+	free(rvtraceq);
+}
+
+static void rvtrace_free_events(struct perf_session *session)
+{
+	unsigned int i;
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+						    struct rvtrace_auxtrace,
+						    auxtrace);
+	struct auxtrace_queues *queues = &rvtrace->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		rvtrace_free_queue(queues->queue_array[i].priv);
+		queues->queue_array[i].priv = NULL;
+	}
+
+	auxtrace_queues__free(queues);
+}
+
+static void rvtrace_free(struct perf_session *session)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+							struct rvtrace_auxtrace,
+							auxtrace);
+	rvtrace_free_events(session);
+	session->auxtrace = NULL;
+	for (int i = 0; i < rvtrace->num_cpu; i++)
+		zfree(&rvtrace->metadata[i]);
+
+	zfree(&rvtrace->metadata);
+	zfree(&rvtrace);
+}
+
+static bool rvtrace_evsel_is_auxtrace(struct perf_session *session,
+					     struct evsel *evsel)
+{
+	struct rvtrace_auxtrace *aux = container_of(session->auxtrace,
+						    struct rvtrace_auxtrace,
+						    auxtrace);
+
+	return evsel->core.attr.type == aux->pmu_type;
+}
+
+static int rvtrace_process_itrace_start(struct rvtrace_auxtrace *rvtrace,
+					union perf_event *event)
+{
+	struct thread *th;
+
+	if (rvtrace->timeless_decoding)
+		return 0;
+
+	/*
+	 * Add the tid/pid to the log so that we can get a match when
+	 * we get a contextID from the decoder.
+	 */
+	th = machine__findnew_thread(rvtrace->machine,
+				     event->itrace_start.pid,
+				     event->itrace_start.tid);
+	if (!th)
+		return -ENOMEM;
+
+	thread__put(th);
+
+	return 0;
+}
+
+static int rvtrace_process_event(struct perf_session *session,
+				 union perf_event *event,
+				 struct perf_sample *sample,
+				 const struct perf_tool *tool)
+{
+	int err = 0;
+	u64 timestamp;
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+							struct rvtrace_auxtrace,
+							auxtrace);
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events) {
+		pr_err("RISCV Trace requires ordered events\n");
+		return -EINVAL;
+	}
+
+	if (sample->time && (sample->time != (u64) -1))
+		timestamp = sample->time;
+	else
+		timestamp = 0;
+
+	if (timestamp || rvtrace->timeless_decoding) {
+		err = rvtrace_update_queues(rvtrace);
+		if (err)
+			return err;
+	}
+
+	switch (event->header.type) {
+	case PERF_RECORD_EXIT:
+		/*
+		 * Don't need to wait for rvtrace_flush_events() in per-thread/timeless
+		 * mode to start the decode because we know there will be no more trace
+		 * from this thread. All this does is emit samples earlier than waiting
+		 * for the flush in other modes, but with timestamps it makes sense to
+		 * wait for flush so that events from different threads are interleaved
+		 * properly.
+		 */
+		if (rvtrace->timeless_decoding)
+			return rvtrace_process_timeless_queues(rvtrace, event->fork.tid);
+		break;
+
+	case PERF_RECORD_ITRACE_START:
+		return rvtrace_process_itrace_start(rvtrace, event);
+
+	case PERF_RECORD_AUX:
+		/*
+		 * Record the latest kernel timestamp available for rollback when
+		 * no trace timestamp is available.
+		 */
+		if (timestamp)
+			rvtrace->latest_kernel_timestamp = timestamp;
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void rvtrace_dump(struct rvtrace_auxtrace *rvtrace,
+			 unsigned char *buf, size_t len)
+{
+	int ret;
+	struct nexus_rv_pkt_decoder_params params;
+	struct nexus_rv_pkt_decoder *decoder;
+	const char *color = PERF_COLOR_BLUE;
+
+	// TODO: Determine whether the trace data contains a CoreSight formatter frame
+	params.formatted = true;
+	if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC])
+		params.src_bits = rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS];
+
+	decoder = nexus_rv_pkt_decoder_new(&params);
+	if (!decoder) {
+		color_fprintf(stdout, color, " Failed to create decoder\n");
+		return;
+	}
+
+	color_fprintf(stdout, color,
+			". ... Trace Encoder Trace data: size %#zx bytes\n",
+			len);
+
+	ret = nexus_rv_pkt_desc(decoder, buf, len);
+	if (ret)
+		color_fprintf(stdout, color, " Bad packet!\n");
+
+	nexus_rv_pkt_decoder_free(decoder);
+}
+
+static void rvtrace_dump_event(struct rvtrace_auxtrace *rvtrace,
+			       unsigned char *buf, size_t len)
+{
+	printf(".\n");
+	rvtrace_dump(rvtrace, buf, len);
+}
+
+static int rvtrace_process_auxtrace_event(struct perf_session *session,
+					  union perf_event *event,
+					  const struct perf_tool *tool __maybe_unused)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+								 struct rvtrace_auxtrace,
+								 auxtrace);
+	if (!rvtrace->data_queued) {
+		struct auxtrace_buffer *buffer;
+		off_t  data_offset;
+		int fd = perf_data__fd(session->data);
+		bool is_pipe = perf_data__is_pipe(session->data);
+		int err;
+
+		if (is_pipe)
+			data_offset = 0;
+		else {
+			data_offset = lseek(fd, 0, SEEK_CUR);
+			if (data_offset == -1)
+				return -errno;
+		}
+
+		err = auxtrace_queues__add_event(&rvtrace->queues, session,
+						 event, data_offset, &buffer);
+
+		if (err)
+			return err;
+
+		if (dump_trace)
+			if (auxtrace_buffer__get_data(buffer, fd)) {
+				rvtrace_dump_event(rvtrace, buffer->data, buffer->size);
+				auxtrace_buffer__put_data(buffer);
+			}
+	}
+
+
+	return 0;
+}
+
+static bool rvtrace_is_timeless_decoding(struct rvtrace_auxtrace *rvtrace)
+{
+	struct evsel *evsel;
+	struct evlist *evlist = rvtrace->session->evlist;
+	bool timeless_decoding = true;
+
+	/*
+	 * Circle through the list of event and complain if we find one
+	 * with the time bit set.
+	 */
+	evlist__for_each_entry(evlist, evsel) {
+		if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
+			timeless_decoding = false;
+	}
+
+	return timeless_decoding;
+}
+
+static const char * const rvtrace_global_header_fmts[] = {
+	[RVTRACE_PMU_TYPE_CPUS]		    = "	    PMU type/num cpus	    %llx\n",
+};
+
+static const char * const rvtrace_encoder_priv_fmts[] = {
+	[RVTRACE_ENCODER_CPU]		    = "	    CPU			    %lld\n",
+	[RVTRACE_ENCODER_NR_TRC_PARAMS]	    = "	    NR_TRC_PARAMS	    %lld\n",
+	[RVTRACE_ENCODER_FORMAT]	    = "	    FORMAT		    %lld\n",
+	[RVTRACE_ENCODER_CONTEXT]	    = "	    CONTEXT		    %lld\n",
+	[RVTRACE_ENCODER_INHB_SRC]	    = "	    INHB_SRC		    %lld\n",
+	[RVTRACE_ENCODER_SRCBITS]	    = "	    SRCBITS		    %lld\n",
+	[RVTRACE_ENCODER_SRCID]		    = "	    SRCID		    %lld\n",
+};
+
+static void rvtrace_print_auxtrace_info(u64 *val, int num_cpu)
+{
+	int i, j, cpu = 0, nr_params = 0, fmt_offset = 0;
+
+	for (i = 0; i < RVTRACE_HEADER_MAX; i++)
+		fprintf(stdout, rvtrace_global_header_fmts[i], val[i]);
+
+	for (i = RVTRACE_HEADER_MAX; cpu < num_cpu; cpu++) {
+		fprintf(stdout, rvtrace_encoder_priv_fmts[RVTRACE_ENCODER_CPU], val[i++]);
+		nr_params = val[i++];
+		fmt_offset = RVTRACE_ENCODER_FORMAT;
+		for (j = fmt_offset; j < nr_params + fmt_offset; j++, i++)
+			fprintf(stdout, rvtrace_encoder_priv_fmts[j], val[i]);
+	}
+}
+
+int rvtrace_process_auxtrace_info(union perf_event *event,
+				  struct perf_session *session)
+{
+	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
+	struct rvtrace_auxtrace *rvtrace = NULL;
+	int err = 0;
+	int i;
+	int num_cpu = 0;
+	u64 *ptr = NULL;
+	u64 **metadata = NULL;
+
+	/* First the global part */
+	ptr = (u64 *) auxtrace_info->priv;
+	num_cpu = ptr[RVTRACE_PMU_TYPE_CPUS] & 0xffffffff;
+	metadata = zalloc(sizeof(*metadata) * num_cpu);
+	if (!metadata)
+		err = -ENOMEM;
+
+	/* Start parsing after the common part of the header */
+	i = RVTRACE_HEADER_MAX;
+
+	for (int j = 0; j < num_cpu; j++) {
+		metadata[j] = zalloc(sizeof(*metadata[j]) * RVTRACE_ENCODER_PRIV_MAX);
+		if (!metadata[j]) {
+			err = -ENOMEM;
+			goto err_free_metadata;
+		}
+
+		for (int k = 0; k < RVTRACE_ENCODER_PRIV_MAX; k++)
+			metadata[j][k] = ptr[i + k];
+		i += RVTRACE_ENCODER_PRIV_MAX;
+	}
+
+	rvtrace = zalloc(sizeof(*rvtrace));
+	if (!rvtrace) {
+		err = -ENOMEM;
+		goto err_free_metadata;
+	}
+
+	err = auxtrace_queues__init(&rvtrace->queues);
+	if (err)
+		goto err_free_rvtrace;
+
+	if (session->itrace_synth_opts->set) {
+		rvtrace->synth_opts = *session->itrace_synth_opts;
+	} else {
+		itrace_synth_opts__set_default(&rvtrace->synth_opts,
+			session->itrace_synth_opts->default_no_sample);
+		rvtrace->synth_opts.callchain = false;
+	}
+
+	rvtrace->session = session;
+	rvtrace->machine = &session->machines.host;
+	rvtrace->num_cpu = num_cpu;
+	rvtrace->pmu_type = (unsigned int) ((ptr[RVTRACE_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+	rvtrace->metadata = metadata;
+
+	rvtrace->auxtrace_type = auxtrace_info->type;
+	rvtrace->timeless_decoding = rvtrace_is_timeless_decoding(rvtrace);
+
+	rvtrace->auxtrace.process_event = rvtrace_process_event;
+	rvtrace->auxtrace.process_auxtrace_event = rvtrace_process_auxtrace_event;
+	rvtrace->auxtrace.flush_events = rvtrace_flush_events;
+	rvtrace->auxtrace.free_events = rvtrace_free_events;
+	rvtrace->auxtrace.free = rvtrace_free;
+	rvtrace->auxtrace.evsel_is_auxtrace = rvtrace_evsel_is_auxtrace;
+	session->auxtrace = &rvtrace->auxtrace;
+
+	if (dump_trace) {
+		rvtrace_print_auxtrace_info(ptr, num_cpu);
+		return 0;
+	}
+
+	err = rvtrace_synth_events(rvtrace, session);
+	if (err)
+		goto err_free_queues;
+
+	err = auxtrace_queues__process_index(&rvtrace->queues, session);
+	if (err)
+		goto err_free_queues;
+
+	rvtrace->data_queued = rvtrace->queues.populated;
+
+	return 0;
+
+err_free_queues:
+	auxtrace_queues__free(&rvtrace->queues);
+	session->auxtrace = NULL;
+err_free_rvtrace:
+	zfree(&rvtrace);
+err_free_metadata:
+	for (int j = 0; j < num_cpu; j++)
+		zfree(&metadata[j]);
+	zfree(&metadata);
+
+	return -EINVAL;
+}
diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h
index 1e48ed989dd7..84a59d3dc296 100644
--- a/tools/perf/util/rvtrace.h
+++ b/tools/perf/util/rvtrace.h
@@ -35,4 +35,6 @@ enum {
 #define RVTRACE_HEADER_SIZE		(RVTRACE_HEADER_MAX * sizeof(u64))
 #define RVTRACE_ENCODER_PRIV_SIZE	(RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
 
+int rvtrace_process_auxtrace_info(union perf_event *event, struct perf_session *session);
+
 #endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  parent reply	other threads:[~2026-04-14  3:45 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
2026-04-14  3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
2026-04-14  3:41 ` [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Zane Leung
2026-04-14  3:41 ` [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Zane Leung
2026-04-14  3:41 ` [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Zane Leung
2026-04-14  3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
2026-04-14 23:31   ` Bo Gan
2026-04-14  3:41 ` [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
2026-04-14  3:41 ` [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Zane Leung
2026-04-14  3:41 ` Zane Leung [this message]
2026-04-14  4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
2026-04-14  8:08   ` Zane Leung
2026-04-14  7:23 ` Anup Patel
2026-04-14  9:04   ` Zane Leung
2026-04-15  0:10     ` Bo Gan
2026-04-15  1:23       ` Zane Leung

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260414034153.3272485-13-liangzhen@linux.spacemit.com \
    --to=liangzhen@linux.spacemit.com \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=andrew.jones@oss.qualcomm.com \
    --cc=anup.patel@oss.qualcomm.com \
    --cc=anup@brainfault.org \
    --cc=atish.patra@linux.dev \
    --cc=conor+dt@kernel.org \
    --cc=coresight@lists.linaro.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=irogers@google.com \
    --cc=jolsa@kernel.org \
    --cc=kan.liang@linux.intel.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=mark.rutland@arm.com \
    --cc=mayuresh.chitale@oss.qualcomm.com \
    --cc=mchitale@gmail.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=palmer@dabbelt.com \
    --cc=peterz@infradead.org \
    --cc=pjw@kernel.org \
    --cc=robh@kernel.org \
    --cc=sunilvl@oss.qualcomm.com \
    --cc=zhuangqiubin@linux.spacemit.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox