mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Zane Leung <liangzhen@linux.spacemit.com>
To: gregkh@linuxfoundation.org, palmer@dabbelt.com, pjw@kernel.org,
	alexander.shishkin@linux.intel.com, suzuki.poulose@arm.com,
	mchitale@gmail.com, anup@brainfault.org, irogers@google.com
Cc: mike.leach@arm.com, james.clark@linaro.org, leo.yan@arm.com,
	peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org,
	mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com,
	alex@ghiti.fr, atish.patra@linux.dev,
	andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
	liangzhen@linux.spacemit.com, zhuangqiubin@linux.spacemit.com,
	coresight@lists.linaro.org, linux-riscv@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 24/24] perf tools: gtrace: Add AUX trace decoder and branch sample synthesis
Date: Fri, 23 Oct 2026 11:15:05 +0800	[thread overview]
Message-ID: <20261023031505.2020925-25-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20261023031505.2020925-1-liangzhen@linux.spacemit.com>

Implement the gtrace auxtrace decoder: queue AUX buffers, run timeless or
timestamped decoding, and synthesize branch samples from decoded trace
packets.  Support machine-mode instruction access via new '-m/--firmware'
and '--firmware-base' options.
---
 tools/perf/builtin-script.c      |    4 +
 tools/perf/util/gtrace-decoder.c | 1206 +++++++++++++++++++++++++++++-
 tools/perf/util/symbol_conf.h    |    2 +
 3 files changed, 1197 insertions(+), 15 deletions(-)

diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index ad8ca08ceb5f..d1173c80b6fa 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -4088,6 +4088,10 @@ int cmd_script(int argc, const char **argv)
 		   "file", "vmlinux pathname"),
 	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
 		   "file", "kallsyms pathname"),
+	OPT_STRING('m', "firmware", &symbol_conf.firmware_name,
+		   "file", "machine mode firmware binary pathname"),
+	OPT_STRING(0, "firmware-base", &symbol_conf.firmware_base,
+		   "address", "base address for machine mode firmware (e.g., 0x100000000)"),
 	OPT_BOOLEAN('G', "hide-call-graph", &no_callchain,
 		    "When printing symbols do not display call chain"),
 	OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP,
diff --git a/tools/perf/util/gtrace-decoder.c b/tools/perf/util/gtrace-decoder.c
index d70cb7cd399f..b2973f7ad3fe 100644
--- a/tools/perf/util/gtrace-decoder.c
+++ b/tools/perf/util/gtrace-decoder.c
@@ -13,6 +13,24 @@
 #include "session.h"
 #include "gtrace.h"
 #include "debug.h"
+#include "color.h"
+#include <linux/bitfield.h>
+#include "intlist.h"
+#include "machine.h"
+#include "map.h"
+#include "thread.h"
+#include "thread_map.h"
+#include "thread-stack.h"
+#include "util.h"
+#include "dso.h"
+#include "maps.h"
+#include "symbol.h"
+#include "addr_location.h"
+#include "util/synthetic-events.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include "rvtrace-decoder/rvtrace-decoder.h"
+#include "../../arch/riscv/include/asm/insn.h"
 
 struct gtrace_decoder {
 	struct auxtrace auxtrace;
@@ -22,31 +40,242 @@ struct gtrace_decoder {
 	u32 pmu_type;
 	int num_cpu;
 	u64 **metadata;
+	struct auxtrace_queues queues;
+	struct auxtrace_heap heap;
+	struct itrace_synth_opts synth_opts;
+	struct dso *firmware_dso;
+	struct maps *firmware_maps;
+	bool timeless_decoding;
+	bool per_thread_decoding;
+	bool data_queued;
+	u64 latest_kernel_timestamp;
+	u64 branches_sample_type;
+	u64 branches_id;
 };
 
-static int gtrace_process_event(struct perf_session *session __maybe_unused,
-				 union perf_event *event __maybe_unused,
-				 struct perf_sample *sample __maybe_unused,
-				 const struct perf_tool *tool __maybe_unused)
+enum coresight_format {
+	UNSET,
+	FORMATTED,
+	UNFORMATTED
+};
+
+struct gtrace_queue {
+	struct gtrace_decoder *ptr;
+	struct thread *thread;
+	struct rvtrace_decoder *decoder;
+	struct auxtrace_buffer *buffer;
+	union perf_event *event_buf;
+	unsigned int queue_nr;
+	u64 offset;
+	enum coresight_format cs_format;
+};
+
+static int gtrace_process_timeless_queues(struct gtrace_decoder *ptr,
+					  pid_t tid);
+static int gtrace_process_timestamped_queues(struct gtrace_decoder *ptr);
+
+static int gtrace_process_itrace_start(struct gtrace_decoder *ptr,
+				       union perf_event *event)
 {
+	struct thread *th;
+
+	if (ptr->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(ptr->machine,
+				     event->itrace_start.pid,
+				     event->itrace_start.tid);
+	if (!th)
+		return -ENOMEM;
+
+	thread__put(th);
+
 	return 0;
 }
 
-static int gtrace_process_auxtrace_event(struct perf_session *session __maybe_unused,
-					  union perf_event *event __maybe_unused,
-					  const struct perf_tool *tool __maybe_unused)
+static int gtrace_process_switch_cpu_wide(struct gtrace_decoder *ptr,
+					  union perf_event *event)
 {
+	struct thread *th;
+	bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
+
+	if (ptr->timeless_decoding)
+		return 0;
+
+	if (!out)
+		return 0;
+
+	th = machine__findnew_thread(&ptr->session->machines.host,
+				     event->context_switch.next_prev_pid,
+				     event->context_switch.next_prev_tid);
+
+	if (!th)
+		return -ENOMEM;
+
+	thread__put(th);
+
 	return 0;
 }
 
-static int gtrace_flush(struct perf_session *session __maybe_unused,
-			 const struct perf_tool *tool __maybe_unused)
+static int gtrace_process_event(struct perf_session *session,
+				 union perf_event *event,
+				 struct perf_sample *sample,
+				 const struct perf_tool *tool)
 {
+	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
+					    auxtrace);
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events) {
+		pr_err("GTRACE requires ordered events\n");
+		return -EINVAL;
+	}
+
+	switch (event->header.type) {
+	case PERF_RECORD_EXIT:
+		if (ptr->per_thread_decoding && ptr->timeless_decoding)
+			return gtrace_process_timeless_queues(ptr, event->fork.tid);
+		break;
+
+	case PERF_RECORD_ITRACE_START:
+		return gtrace_process_itrace_start(ptr, event);
+
+	case PERF_RECORD_SWITCH_CPU_WIDE:
+		return gtrace_process_switch_cpu_wide(ptr, event);
+
+	case PERF_RECORD_AUX:
+		if (sample->time && (sample->time != (u64)-1))
+			ptr->latest_kernel_timestamp = sample->time;
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void gtrace_dump_event(struct gtrace_queue *gtraceq,
+			      struct auxtrace_buffer *buffer)
+{
+	int ret;
+	const char *color = PERF_COLOR_BLUE;
+	struct rvtrace_decoder *decoder = gtraceq->decoder;
+
+	color_fprintf(stdout, color,
+		      ". ... Trace Encoder Trace data: size %#zx bytes\n",
+		      buffer->size);
+
+	ret = decoder->decode(gtraceq->decoder, RVTRACE_DECODE_MSG,
+			      buffer->data, buffer->size);
+	if (ret)
+		color_fprintf(stdout, color, " Bad packet!\n");
+}
+
+static void dump_queued_data(struct gtrace_decoder *ptr,
+			     struct perf_record_auxtrace *event)
+{
+	struct auxtrace_buffer *buf;
+	unsigned int i;
+	/*
+	 * Find all buffers with same reference in the queues and dump them.
+	 * This is because the queues can contain multiple entries of the same
+	 * buffer that were split on aux records.
+	 */
+	for (i = 0; i < ptr->queues.nr_queues; ++i)
+		list_for_each_entry(buf, &ptr->queues.queue_array[i].head, list)
+			if (buf->reference == event->reference)
+				gtrace_dump_event(ptr->queues.queue_array[i].priv, buf);
+}
+
+static int gtrace_process_auxtrace_event(struct perf_session *session,
+					 union perf_event *event,
+					 const struct perf_tool *tool __maybe_unused)
+{
+	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
+					    auxtrace);
+
+	if (!ptr->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;
+		int idx = event->auxtrace.idx;
+
+		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(&ptr->queues, session,
+						 event, data_offset, &buffer);
+		if (err)
+			return err;
+
+		if (dump_trace)
+			if (auxtrace_buffer__get_data(buffer, fd)) {
+				gtrace_dump_event(ptr->queues.queue_array[idx].priv, buffer);
+				auxtrace_buffer__put_data(buffer);
+			}
+	} else if (dump_trace)
+		dump_queued_data(ptr, &event->auxtrace);
+
 	return 0;
 }
 
-static void gtrace_free_events(struct perf_session *session __maybe_unused)
+static int gtrace_flush(struct perf_session *session,
+			 const struct perf_tool *tool)
 {
+	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
+					    auxtrace);
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events)
+		return -EINVAL;
+
+	if (ptr->timeless_decoding)
+		return gtrace_process_timeless_queues(ptr, -1);
+
+	return gtrace_process_timestamped_queues(ptr);
+}
+
+static void gtrace_free_queue(void *priv)
+{
+	struct gtrace_queue *gtraceq = priv;
+
+	if (!gtraceq)
+		return;
+
+	if (gtraceq->decoder)
+		rvtrace_decoder_free(gtraceq->decoder);
+	thread__zput(gtraceq->thread);
+	zfree(&gtraceq->event_buf);
+	free(gtraceq);
+}
+
+static void gtrace_free_events(struct perf_session *session)
+{
+	unsigned int i;
+	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
+					    auxtrace);
+	struct auxtrace_queues *queues = &ptr->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		gtrace_free_queue(queues->queue_array[i].priv);
+		queues->queue_array[i].priv = NULL;
+	}
+
+	auxtrace_queues__free(queues);
 }
 
 static void gtrace_free(struct perf_session *session)
@@ -54,6 +283,7 @@ static void gtrace_free(struct perf_session *session)
 	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
 					    auxtrace);
 
+	gtrace_free_events(session);
 	session->auxtrace = NULL;
 	for (int i = 0; i < ptr->num_cpu; i++)
 		zfree(&ptr->metadata[i]);
@@ -70,6 +300,643 @@ static bool gtrace_evsel_is_auxtrace(struct perf_session *session,
 	return evsel->core.attr.type == ptr->pmu_type;
 }
 
+static void gtrace_set_thread(struct gtrace_queue *gtraceq,
+			      pid_t tid)
+{
+	struct gtrace_decoder *ptr = gtraceq->ptr;
+
+	if (tid != -1) {
+		thread__zput(gtraceq->thread);
+		gtraceq->thread = machine__find_thread(ptr->machine, -1, tid);
+	}
+
+	/* Couldn't find a known thread */
+	if (!gtraceq->thread)
+		gtraceq->thread = machine__idle_thread(ptr->machine);
+}
+
+/*
+ * Register firmware ELF PT_LOAD segments as maps in the machine's kernel maps,
+ * so that perf script can resolve symbols for M-mode (machine mode) samples.
+ *
+ * base is the absolute physical load address of the firmware (from --firmware-base).
+ * p_paddr in the ELF is the relative address within the firmware image.
+ *
+ *   map->start = base + p_paddr
+ *   map->end   = base + p_paddr + p_memsz
+ *   map->pgoff = p_offset
+ *
+ * map__map_ip(paddr) = paddr - start + pgoff = p_offset + (paddr - base - p_paddr)
+ */
+static int gtrace_register_firmware_maps(struct gtrace_decoder *ptr,
+					 struct dso *dso, u64 base)
+{
+	Elf64_Ehdr ehdr;
+	Elf64_Phdr phdr;
+	int fd, i, ret = 0;
+	const char *filename = dso__long_name(dso);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	if (pread(fd, &ehdr, sizeof(ehdr), 0) != sizeof(ehdr)) {
+		ret = -1;
+		goto out;
+	}
+
+	for (i = 0; i < ehdr.e_phnum; i++) {
+		struct map *map;
+
+		if (pread(fd, &phdr, sizeof(phdr),
+			  ehdr.e_phoff + (u64)i * ehdr.e_phentsize) != sizeof(phdr))
+			continue;
+
+		if (phdr.p_type != PT_LOAD || phdr.p_memsz == 0)
+			continue;
+
+		map = map__new2(base + phdr.p_paddr, dso);
+		if (!map) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		map__set_end(map, base + phdr.p_paddr + phdr.p_memsz);
+		map__set_pgoff(map, phdr.p_offset);
+
+		pr_debug("gtrace: machine code map [%#"PRIx64"-%#"PRIx64"] pgoff=%#"PRIx64" %s\n",
+			 base + phdr.p_paddr, base + phdr.p_paddr + phdr.p_memsz,
+			 (u64)phdr.p_offset, dso__long_name(dso));
+
+		ret = maps__insert(ptr->firmware_maps, map);
+		map__put(map);
+		if (ret)
+			goto out;
+	}
+out:
+	close(fd);
+	return ret;
+}
+
+static int gtrace_insert_map_cb(struct map *map, void *data)
+{
+	struct maps *kmaps = data;
+
+	maps__insert(kmaps, map);
+	return 0;
+}
+
+static u32 gtrace_machine_mode_access(struct gtrace_queue *gtraceq, u64 address,
+				       size_t size, u8 *buffer)
+{
+	struct machine *machine = gtraceq->ptr->machine;
+	struct map *map;
+	struct dso *dso;
+	u64 offset;
+	int len;
+
+	if (!gtraceq->ptr->firmware_dso) {
+		ui__warning_once("RISC-V Trace: Machine mode code binary not provided.\n"
+				 "              Failed to read address 0x%lx.\n"
+				 "              Use option '-m /path/to/firmware --firmware-base 0xBASE'\n"
+				 "              to specify the binary and its base address.\n",
+				 address);
+		return 0;
+	}
+
+	map = maps__find(gtraceq->ptr->firmware_maps, address);
+	if (!map)
+		return 0;
+
+	/*
+	 * Insert firmware maps into kernel maps for symbol resolution.
+	 * Re-insert if kcore loading has removed them (kcore calls
+	 * maps__remove_maps which clears all non-kernel maps).
+	 */
+	if (!maps__find(machine__kernel_maps(machine), address)) {
+		maps__for_each_map(gtraceq->ptr->firmware_maps,
+				   gtrace_insert_map_cb,
+				   machine__kernel_maps(machine));
+		map__load(map);
+	}
+
+	dso = map__dso(map);
+	offset = map__map_ip(map, address);
+	len = dso__data_read_offset(dso, machine, offset, buffer, size);
+	map__put(map);
+	return len > 0 ? len : 0;
+}
+
+static u32 gtrace_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 gtrace_queue *gtraceq = data;
+
+	if (!gtraceq)
+		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 gtrace_machine_mode_access(gtraceq, address, size, buffer);
+
+	machine = gtraceq->ptr->machine;
+	if (address >= machine__kernel_start(machine))
+		cpumode = PERF_RECORD_MISC_KERNEL;
+	else
+		cpumode = PERF_RECORD_MISC_USER;
+
+	if (thread__tid(gtraceq->thread) != context)
+		gtrace_set_thread(gtraceq, context);
+
+	if (!thread__find_map(gtraceq->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("GTRACE: 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 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 gtrace_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 gtrace_synth_copy_insn(struct gtrace_queue *gtraceq,
+				   struct rvtrace_packet *packet,
+				   struct perf_sample *sample)
+{
+	int ret;
+	u32 insn;
+
+	ret = gtrace_mem_access(gtraceq, 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 gtrace_resolve_sample_time(struct gtrace_queue *gtraceq,
+					     struct rvtrace_packet *packet)
+{
+	struct gtrace_decoder *ptr = gtraceq->ptr;
+
+	if (!ptr->timeless_decoding)
+		return packet->timestamp;
+	else
+		return ptr->latest_kernel_timestamp;
+}
+
+static int gtrace_synth_branch_sample(struct gtrace_queue *gtraceq,
+				      struct rvtrace_packet *packet)
+{
+	int ret = 0;
+	struct gtrace_decoder *ptr = gtraceq->ptr;
+	struct perf_sample sample;
+	union perf_event *event = gtraceq->event_buf;
+
+	perf_sample__init(&sample, /*all=*/true);
+	event->sample.header.type = PERF_RECORD_SAMPLE;
+	event->sample.header.misc = gtrace_cpu_mode(packet->prv);
+	event->sample.header.size = sizeof(struct perf_event_header);
+
+	if (thread__tid(gtraceq->thread) != packet->context)
+		gtrace_set_thread(gtraceq, packet->context);
+
+	/* Set time field based on rvtrace auxtrace config. */
+	sample.time = gtrace_resolve_sample_time(gtraceq, packet);
+
+	sample.ip = packet->start_addr;
+	sample.pid = thread__pid(gtraceq->thread);
+	sample.tid = thread__tid(gtraceq->thread);
+	sample.addr = packet->end_addr;
+	sample.insn_cnt = packet->insn_cnt;
+	sample.id = gtraceq->ptr->branches_id;
+	sample.stream_id = gtraceq->ptr->branches_id;
+	sample.period = 1;
+	sample.cpu = packet->cpu;
+	sample.flags = 0;
+	sample.cpumode = event->sample.header.misc;
+
+	gtrace_synth_copy_insn(gtraceq, packet, &sample);
+
+	ret = perf_session__deliver_synth_event(ptr->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 gtrace_synth_events(struct gtrace_decoder *ptr,
+			       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 == ptr->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 (ptr->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 (ptr->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;
+		ptr->branches_sample_type = attr.sample_type;
+		ptr->branches_id = id;
+		id += 1;
+		attr.sample_type &= ~(u64)PERF_SAMPLE_ADDR;
+	}
+
+	return 0;
+}
+
+static int gtrace_process_queue(struct gtrace_queue *gtraceq)
+{
+	struct rvtrace_packet_buffer *packet_buffer = &gtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct rvtrace_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			gtrace_synth_branch_sample(gtraceq, &packet);
+		else if (packet.sample_type == RVTRACE_LOSS)
+			fprintf(stdout, "RISC-V Trace: FIFO overrun, messages lost\n");
+		else if (packet.sample_type == RVTRACE_ERROR)
+			fprintf(stdout, "RISC-V Trace: Packet data parsing error\n");
+	}
+
+	return 0;
+}
+
+static int gtrace_get_trace(struct gtrace_queue *gtraceq)
+{
+	struct auxtrace_buffer *aux_buffer = gtraceq->buffer;
+	struct auxtrace_buffer *old_buffer = aux_buffer;
+	struct auxtrace_queue *queue;
+
+	queue = &gtraceq->ptr->queues.queue_array[gtraceq->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;
+	}
+
+	gtraceq->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(gtraceq->ptr->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;
+}
+
+/*
+ * gtrace_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 gtrace_get_data_block(struct gtrace_queue *gtraceq)
+{
+	int ret;
+
+	ret = gtrace_get_trace(gtraceq);
+	if (ret <= 0)
+		return ret;
+
+	/*
+	 * We cannot assume consecutive blocks in the data file
+	 * are contiguous, reset the decoder to force re-sync.
+	 */
+	ret = rvtrace_decoder_reset(gtraceq->decoder);
+	if (ret)
+		return ret;
+
+	return gtraceq->buffer->size;
+}
+
+static int gtrace_run_timeless_decoder(struct gtrace_queue *gtraceq)
+{
+	struct rvtrace_decoder *decoder = gtraceq->decoder;
+	int ret;
+
+	while (1) {
+		ret = gtrace_get_data_block(gtraceq);
+		if (ret < 0)
+			return ret;
+
+		if (ret == 0)
+			break;
+
+		ret = decoder->decode(decoder, RVTRACE_DECODE_INSN,
+				      gtraceq->buffer->data,
+				      gtraceq->buffer->size);
+		if (ret)
+			return ret;
+
+		ret = gtrace_process_queue(gtraceq);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int gtrace_process_timeless_queues(struct gtrace_decoder *ptr,
+					  pid_t tid)
+{
+	unsigned int i;
+	struct auxtrace_queues *queues = &ptr->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		struct auxtrace_queue *queue = &ptr->queues.queue_array[i];
+		struct gtrace_queue *gtraceq = queue->priv;
+
+		if (gtraceq && ((tid == -1) || (queue->tid == tid))) {
+			gtrace_set_thread(gtraceq, queue->tid);
+			gtrace_run_timeless_decoder(gtraceq);
+		}
+	}
+
+	return 0;
+}
+
+static u64 gtrace_queue_get_timestamp(struct gtrace_queue *gtraceq)
+{
+	struct rvtrace_packet_buffer *packet_buffer = &gtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct rvtrace_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			return packet.timestamp;
+	}
+
+	return 0;
+}
+
+static int gtrace_queue_first_timestamp(struct gtrace_decoder *ptr,
+					struct gtrace_queue *gtraceq,
+					unsigned int queue_nr)
+{
+	struct rvtrace_decoder *decoder = gtraceq->decoder;
+	u64 timestamp = 0;
+	int ret;
+
+	/* 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 gtraceq. Bail if no more
+		 * blocks or an error has been encountered.
+		 */
+		ret = gtrace_get_data_block(gtraceq);
+		if (ret <= 0)
+			goto out;
+
+		ret = decoder->decode(decoder, RVTRACE_DECODE_INSN,
+				      gtraceq->buffer->data,
+				      gtraceq->buffer->size);
+		if (ret)
+			goto out;
+
+		timestamp = gtrace_queue_get_timestamp(gtraceq);
+
+		/* 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(&ptr->heap, queue_nr, timestamp);
+out:
+	return ret;
+}
+
+static int gtrace_process_timestamped_queues(struct gtrace_decoder *ptr)
+{
+	int ret = 0;
+	unsigned int queue_nr, i;
+	struct auxtrace_queue *queue;
+	struct gtrace_queue *gtraceq;
+
+	/* First, find the first timestamp for each queue and add it to the heap. */
+	for (i = 0; i < ptr->queues.nr_queues; i++) {
+		queue = &ptr->queues.queue_array[i];
+		gtraceq = queue->priv;
+		if (!gtraceq)
+			continue;
+
+		gtrace_set_thread(gtraceq, queue->tid);
+
+		ret = gtrace_queue_first_timestamp(ptr, gtraceq, i);
+		if (ret)
+			return ret;
+	}
+
+	/* Process queues in the heap in timestamp order */
+	while (1) {
+		if (!ptr->heap.heap_cnt)
+			break;
+
+		/* Take the entry at the top of the min heap */
+		queue_nr = ptr->heap.heap_array[0].queue_nr;
+		queue = &ptr->queues.queue_array[queue_nr];
+		gtraceq = queue->priv;
+
+		/*
+		 * Remove the top entry from the heap since we are about
+		 * to process it.
+		 */
+		auxtrace_heap__pop(&ptr->heap);
+
+		/*
+		 * Packets associated with this timestamp are already in
+		 * the gtraceq->packet_buffer, so process them.
+		 */
+		ret = gtrace_process_queue(gtraceq);
+		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 gtraceq.
+		 */
+		ret = gtrace_queue_first_timestamp(ptr, gtraceq, queue_nr);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct gtrace_queue *gtrace_alloc_queue(struct gtrace_decoder *ptr,
+					       unsigned int queue_nr)
+{
+	struct gtrace_queue *gtraceq;
+
+	gtraceq = zalloc(sizeof(*gtraceq));
+	if (!gtraceq)
+		return NULL;
+
+	gtraceq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
+	if (!gtraceq->event_buf)
+		goto out_free;
+
+	gtraceq->ptr = ptr;
+	gtraceq->queue_nr = queue_nr;
+	gtraceq->offset = 0;
+
+	return gtraceq;
+
+out_free:
+	zfree(&gtraceq->event_buf);
+	free(gtraceq);
+
+	return NULL;
+
+}
+
+static int gtrace_setup_queue(struct gtrace_decoder *ptr,
+			      struct auxtrace_queue *queue,
+			      unsigned int queue_nr)
+{
+	struct gtrace_queue *gtraceq = queue->priv;
+
+	if (gtraceq)
+		return 0;
+
+	gtraceq = gtrace_alloc_queue(ptr, queue_nr);
+	if (!gtraceq)
+		return -ENOMEM;
+
+	queue->priv = gtraceq;
+	queue->cpu = queue_nr;
+
+	return 0;
+}
+
+static void gtrace_setup_timeless_decoding(struct gtrace_decoder *ptr)
+{
+	u64 *metadata = ptr->metadata[0];
+
+	/* Override timeless mode with user input from --itrace=Z */
+	if (ptr->synth_opts.timeless_decoding) {
+		ptr->timeless_decoding = true;
+		return;
+	}
+
+	ptr->timeless_decoding = metadata[RVTRACE_ENCODER_TIMESTAMP];
+}
+
 static const char * const gtrace_global_header_fmts[] = {
 	[GTRACE_PMU_TYPE_CPUS]		    = "	    PMU type/num cpus	    %llx\n",
 };
@@ -99,6 +966,251 @@ static void gtrace_print_auxtrace_info(u64 *val, int num_cpu)
 	}
 }
 
+static struct gtrace_queue *gtrace_get_queue(struct gtrace_decoder *ptr, int cpu)
+{
+	if (ptr->per_thread_decoding)
+		return ptr->queues.queue_array[0].priv;
+
+	if (cpu < 0 || cpu >= (int)ptr->queues.nr_queues)
+		return NULL;
+
+	return ptr->queues.queue_array[cpu].priv;
+}
+
+/**
+ * Puts a fragment of an auxtrace buffer into the auxtrace queues based
+ * on the bounds of aux_event, if it matches with the buffer that's at
+ * file_offset.
+ *
+ * Normally, whole auxtrace buffers would be added to the queue. But we
+ * want to reset the decoder for every PERF_RECORD_AUX event, and the decoder
+ * is reset across each buffer, so splitting the buffers up in advance has
+ * the same effect.
+ */
+static int gtrace_queue_aux_fragment(struct perf_session *session, off_t file_offset, size_t sz,
+				     struct perf_record_aux *aux_event, struct perf_sample *sample)
+{
+	int err;
+	char buf[PERF_SAMPLE_MAX_SIZE];
+	union perf_event *auxtrace_event_union;
+	struct perf_record_auxtrace *auxtrace_event;
+	union perf_event auxtrace_fragment;
+	__u64 aux_offset, aux_size;
+	enum coresight_format cs_format;
+
+	struct gtrace_decoder *ptr = container_of(session->auxtrace, struct gtrace_decoder,
+					    auxtrace);
+
+	/*
+	 * There should be a PERF_RECORD_AUXTRACE event at the file_offset that we got
+	 * from looping through the auxtrace index.
+	 */
+	err = perf_session__peek_event(session, file_offset, buf,
+				       PERF_SAMPLE_MAX_SIZE, &auxtrace_event_union, NULL);
+	if (err)
+		return err;
+	auxtrace_event = &auxtrace_event_union->auxtrace;
+	if (auxtrace_event->header.type != PERF_RECORD_AUXTRACE)
+		return -EINVAL;
+
+	if (auxtrace_event->header.size < sizeof(struct perf_record_auxtrace) ||
+		auxtrace_event->header.size != sz) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Compare only TIDs if auxtrace CPU is -1, and CPUs if auxtrace CPU is not -1.
+	 * Return 'not found' if mismatch.
+	 */
+	if (auxtrace_event->cpu == (__u32) -1) {
+		ptr->per_thread_decoding = true;
+		if (auxtrace_event->tid != sample->tid)
+			return 1;
+	} else if (auxtrace_event->cpu != sample->cpu) {
+		if (ptr->per_thread_decoding) {
+			/*
+			 * Found a per-cpu buffer after a per-thread one was
+			 * already found
+			 */
+			pr_err("GTRACE: Inconsistent per-thread/per-cpu mode.\n");
+			return -EINVAL;
+		}
+		return 1;
+	}
+
+	if (aux_event->flags & PERF_AUX_FLAG_OVERWRITE) {
+		/*
+		 * Clamp size in snapshot mode. The buffer size is clamped in
+		 * __auxtrace_mmap__read() for snapshots, so the aux record size doesn't reflect
+		 * the buffer size.
+		 */
+		aux_size = min(aux_event->aux_size, auxtrace_event->size);
+
+		/*
+		 * In this mode, the head also points to the end of the buffer so aux_offset
+		 * needs to have the size subtracted so it points to the beginning as in normal mode
+		 */
+		aux_offset = aux_event->aux_offset - aux_size;
+	} else {
+		aux_size = aux_event->aux_size;
+		aux_offset = aux_event->aux_offset;
+	}
+
+	if (aux_offset >= auxtrace_event->offset &&
+	    aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) {
+		struct gtrace_queue *gtraceq = gtrace_get_queue(ptr, auxtrace_event->cpu);
+
+		if (!gtraceq)
+			return -EINVAL;
+
+		/*
+		 * If this AUX event was inside this buffer somewhere, create a new auxtrace event
+		 * based on the sizes of the aux event, and queue that fragment.
+		 */
+		auxtrace_fragment.auxtrace = *auxtrace_event;
+		auxtrace_fragment.auxtrace.size = aux_size;
+		auxtrace_fragment.auxtrace.offset = aux_offset;
+		auxtrace_fragment.auxtrace.idx = gtraceq->queue_nr;
+		file_offset += aux_offset - auxtrace_event->offset + auxtrace_event->header.size;
+
+		pr_debug3("GTRACE: Queue buffer size: %#"PRI_lx64" offset: %#"PRI_lx64
+			  " tid: %d cpu: %d\n", aux_size, aux_offset, sample->tid, sample->cpu);
+		err = auxtrace_queues__add_event(&ptr->queues, session, &auxtrace_fragment,
+						 file_offset, NULL);
+		if (err)
+			return err;
+
+		cs_format = (aux_event->flags & PERF_AUX_FLAG_GTRACE_FORMAT_CORESIGHT) ?
+					FORMATTED : UNFORMATTED;
+		if (gtraceq->cs_format != UNSET && cs_format != gtraceq->cs_format) {
+			pr_err("GTRACE: mixed coresight formatted and unformatted trace not supported\n");
+			return -EINVAL;
+		}
+		gtraceq->cs_format = cs_format;
+		return 0;
+	}
+
+	/* Wasn't inside this buffer, but there were no parse errors. 1 == 'not found' */
+	return 1;
+}
+
+static int gtrace_queue_aux_records_cb(struct perf_session *session, union perf_event *event,
+				       u64 offset __maybe_unused, void *data __maybe_unused)
+{
+	struct perf_sample sample;
+	int ret;
+	struct auxtrace_index_entry *ent;
+	struct auxtrace_index *auxtrace_index;
+	struct evsel *evsel;
+	size_t i;
+
+	/* Don't care about any other events, we're only queuing buffers for AUX events */
+	if (event->header.type != PERF_RECORD_AUX)
+		return 0;
+
+	if (event->header.size < sizeof(struct perf_record_aux))
+		return -EINVAL;
+
+	/* Truncated Aux records can have 0 size and shouldn't result in anything being queued. */
+	if (!event->aux.aux_size)
+		return 0;
+
+	/*
+	 * Parse the sample, we need the sample_id_all data that comes after the event so that the
+	 * CPU or PID can be matched to an AUXTRACE buffer's CPU or PID.
+	 */
+	evsel = evlist__event2evsel(session->evlist, event);
+	if (!evsel)
+		return -EINVAL;
+	perf_sample__init(&sample, /*all=*/false);
+	ret = evsel__parse_sample(evsel, event, &sample);
+	if (ret)
+		goto out;
+
+	/*
+	 * Loop through the auxtrace index to find the buffer that matches up with this aux event.
+	 */
+	list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) {
+		for (i = 0; i < auxtrace_index->nr; i++) {
+			ent = &auxtrace_index->entries[i];
+			ret = gtrace_queue_aux_fragment(session, ent->file_offset,
+							ent->sz, &event->aux, &sample);
+			/*
+			 * Stop search on error or successful values. Continue search on
+			 * 1 ('not found')
+			 */
+			if (ret != 1)
+				goto out;
+		}
+	}
+
+	/*
+	 * Couldn't find the buffer corresponding to this aux record, something went wrong. Warn but
+	 * don't exit with an error because it will still be possible to decode other aux records.
+	 */
+	pr_err("CS ETM: Couldn't find auxtrace buffer for aux_offset: %#"PRI_lx64
+	       " tid: %d cpu: %d\n", event->aux.aux_offset, sample.tid, sample.cpu);
+	ret = 0;
+out:
+	perf_sample__exit(&sample);
+	return ret;
+
+}
+
+static int gtrace_queue_aux_records(struct perf_session *session)
+{
+	struct auxtrace_index *index = list_first_entry_or_null(&session->auxtrace_index,
+								struct auxtrace_index, list);
+
+	if (index && index->nr > 0)
+		return perf_session__peek_events(session, session->header.data_offset,
+						 session->header.data_size,
+						 gtrace_queue_aux_records_cb, NULL);
+
+	return 0;
+}
+
+static int gtrace_create_queue_decoder(struct gtrace_queue *gtraceq)
+{
+	struct gtrace_decoder *ptr = gtraceq->ptr;
+	struct rvtrace_decoder_params params;
+
+	params.data = gtraceq;
+	params.mem_access = gtrace_mem_access;
+	params.coresight_formatted = gtraceq->cs_format == FORMATTED;
+	params.format = ptr->metadata[gtraceq->queue_nr][RVTRACE_ENCODER_FORMAT];
+	params.srcbits = ptr->metadata[gtraceq->queue_nr][RVTRACE_ENCODER_SRCBITS];
+	gtraceq->decoder = rvtrace_decoder_new(&params);
+	if (!gtraceq->decoder)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int gtrace_create_decoders(struct gtrace_decoder *ptr)
+{
+	struct auxtrace_queues *queues = &ptr->queues;
+
+	for (unsigned int i = 0; i < queues->nr_queues; i++) {
+		bool empty = list_empty(&queues->queue_array[i].head);
+		struct gtrace_queue *gtraceq = queues->queue_array[i].priv;
+		int ret;
+
+		/*
+		 * Don't create decoders for empty queues, mainly because
+		 * q->format is unknown for empty queues.
+		 */
+		assert(empty || gtraceq->cs_format != UNSET);
+		if (empty)
+			continue;
+
+		ret = gtrace_create_queue_decoder(gtraceq);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
 int gtrace__process_auxtrace_info(union perf_event *event,
 				   struct perf_session *session)
 {
@@ -106,7 +1218,7 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 	struct gtrace_decoder *ptr;
 	u64 *priv;
 	u64 **metadata;
-	int num_cpu;
+	int num_cpu, max_cpu = 0;
 	int priv_size;
 	int err;
 	int i;
@@ -115,7 +1227,6 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 	num_cpu = priv[GTRACE_PMU_TYPE_CPUS] & 0xffffffff;
 	if (dump_trace)
 		gtrace_print_auxtrace_info(priv, num_cpu);
-
 	priv_size = auxtrace_info->header.size - sizeof(struct perf_event_header) -
 		    INFO_HEADER_SIZE - GTRACE_HEADER_SIZE;
 	if (num_cpu <= 0 || priv_size <= 0 ||
@@ -126,7 +1237,7 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 	if (!metadata)
 		return -ENOMEM;
 
-	i = GTRACE_HEADER_SIZE;
+	i = GTRACE_HEADER_MAX;
 
 	for (int j = 0; j < num_cpu; j++) {
 		metadata[j] = zalloc(sizeof(*metadata[j]) * RVTRACE_ENCODER_PRIV_MAX);
@@ -137,6 +1248,9 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 
 		for (int k = 0; k < RVTRACE_ENCODER_PRIV_MAX; k++)
 			metadata[j][k] = priv[i + k];
+
+		if ((int)metadata[j][RVTRACE_ENCODER_CPU] > max_cpu)
+			max_cpu = metadata[j][RVTRACE_ENCODER_CPU];
 		i += RVTRACE_ENCODER_PRIV_MAX;
 	}
 
@@ -146,6 +1260,26 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 		goto err_free_metadata;
 	}
 
+	err = auxtrace_queues__init_nr(&ptr->queues, max_cpu + 1);
+	if (err)
+		goto err_free_metadata;
+
+	for (unsigned int j = 0; j < ptr->queues.nr_queues; ++j) {
+		err = gtrace_setup_queue(ptr, &ptr->queues.queue_array[j], j);
+		if (err)
+			goto err_free_queues;
+	}
+
+	if (session->itrace_synth_opts->set) {
+		ptr->synth_opts = *session->itrace_synth_opts;
+	} else {
+		itrace_synth_opts__set_default(&ptr->synth_opts,
+				session->itrace_synth_opts->default_no_sample);
+		ptr->synth_opts.callchain = false;
+		ptr->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack;
+	}
+
+
 	ptr->session = session;
 	ptr->machine = &session->machines.host;
 	ptr->auxtrace_type = auxtrace_info->type;
@@ -161,10 +1295,52 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 	ptr->auxtrace.evsel_is_auxtrace = gtrace_evsel_is_auxtrace;
 	session->auxtrace = &ptr->auxtrace;
 
-	return 0;
+	gtrace_setup_timeless_decoding(ptr);
+
+	/* Load firmware binary if specified */
+	if (symbol_conf.firmware_name) {
+		u64 base_addr = 0;
+
+		if (symbol_conf.firmware_base)
+			base_addr = strtoull(symbol_conf.firmware_base, NULL, 0);
+
+		ptr->firmware_dso = dso__new(symbol_conf.firmware_name);
+		if (ptr->firmware_dso) {
+			ptr->firmware_maps = maps__new(ptr->machine);
+			if (!ptr->firmware_maps ||
+				gtrace_register_firmware_maps(ptr,
+							      ptr->firmware_dso,
+							      base_addr) < 0) {
+				pr_err("Failed to register machine code maps: %s\n",
+					symbol_conf.firmware_name);
+				dso__put(ptr->firmware_dso);
+				ptr->firmware_dso = NULL;
+				maps__put(ptr->firmware_maps);
+				ptr->firmware_maps = NULL;
+			}
+		}
+	}
 
+	err = gtrace_synth_events(ptr, session);
+	if (err)
+		goto err_free_queues;
+
+	err = gtrace_queue_aux_records(session);
+	if (err)
+		goto err_free_queues;
+
+	err = gtrace_create_decoders(ptr);
+	if (err)
+		goto err_free_queues;
+
+	ptr->data_queued = ptr->queues.populated;
+	return 0;
+err_free_queues:
+	auxtrace_queues__free(&ptr->queues);
+	session->auxtrace = NULL;
 err_free_metadata:
 	for (int j = 0; j < num_cpu; j++)
-		zfree(&metadata);
+		zfree(&metadata[j]);
+	zfree(&metadata);
 	return err;
 }
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 0dee5aa6a534..83fc134ea8ac 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -72,6 +72,8 @@ struct symbol_conf {
 			prefer_latency;
 	const char	*vmlinux_name,
 			*kallsyms_name,
+			*firmware_name,
+			*firmware_base,
 			*source_prefix,
 			*field_sep,
 			*graph_function;
-- 
2.34.1


      parent reply	other threads:[~2026-10-23  3:18 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23  3:14 [RFC PATCH 00/24] Add Linux RISC-V trace support via the gtrace framework Zane Leung
2026-10-23  3:14 ` [PATCH 01/24] gtrace: Move connection release into component unregister Zane Leung
2026-10-23  3:14 ` [PATCH 02/24] gtrace: Initial implementation of gtrace sysfs framework Zane Leung
2026-10-23  3:14 ` [PATCH 03/24] gtrace: Drop trace format field and helper function Zane Leung
2026-10-23  3:14 ` [PATCH 04/24] gtrace: Add RISC-V trace encoder sysfs support Zane Leung
2026-10-23  3:14 ` [PATCH 05/24] gtrace: Introduce some PMU format attributes Zane Leung
2026-10-23  3:14 ` [PATCH 06/24] gtrace: Replace owner PID with perf_event pointer in gtrace_path Zane Leung
2026-10-23  3:14 ` [PATCH 07/24] gtrace: Parse RISC-V Trace encoder config from perf event Zane Leung
2026-10-23  3:14 ` [PATCH 08/24] gtrace: add perf symlink support for gtrace components Zane Leung
2026-10-23  3:14 ` [PATCH 09/24] gtrace: Add port-level start/stop driver callbacks Zane Leung
2026-10-23  3:14 ` [PATCH 10/24] gtrace: Add RISC-V trace funnel driver Zane Leung
2026-10-23  3:14 ` [PATCH 11/24] gtrace: Add RISC-V trace timestamp support Zane Leung
2026-10-23  3:14 ` [PATCH 12/24] gtrace: Add RISC-V trace ATB bridge driver Zane Leung
2026-10-23  3:14 ` [PATCH 13/24] gtrace: Add CoreSight platform core APIs Zane Leung
2026-10-23  3:14 ` [PATCH 14/24] gtrace: Add CoreSight funnel driver Zane Leung
2026-10-23  3:14 ` [PATCH 15/24] gtrace: Only copy AUX data from the path sink Zane Leung
2026-10-23  3:14 ` [PATCH 16/24] perf: Flag whether gtrace AUX data carries CoreSight framing Zane Leung
2026-10-23  3:14 ` [PATCH 17/24] gtrace: Unify default sink selection across CPUs in perf setup Zane Leung
2026-10-23  3:14 ` [PATCH 18/24] gtrace: Add sink-specific buffer alloc/free callbacks Zane Leung
2026-10-23  3:15 ` [PATCH 19/24] gtrace: Add setup_sysfs_buf callback to setup sysfs sink buffer Zane Leung
2026-10-23  3:15 ` [PATCH 20/24] gtrace: Add CoreSight TMC driver support Zane Leung
2026-10-23  3:15 ` [PATCH 21/24] perf tools: riscv: making gtrace PMU listable Zane Leung
2026-10-23  3:15 ` [PATCH 22/24] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
2026-10-23  3:15 ` [PATCH 23/24] perf tools: gtrace: Record and parse trace encoder metadata Zane Leung
2026-10-23  3:15 ` Zane Leung [this message]

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=20261023031505.2020925-25-liangzhen@linux.spacemit.com \
    --to=liangzhen@linux.spacemit.com \
    --cc=adrian.hunter@intel.com \
    --cc=alex@ghiti.fr \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=andrew.jones@oss.qualcomm.com \
    --cc=anup@brainfault.org \
    --cc=atish.patra@linux.dev \
    --cc=coresight@lists.linaro.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=irogers@google.com \
    --cc=james.clark@linaro.org \
    --cc=jolsa@kernel.org \
    --cc=leo.yan@arm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=mark.rutland@arm.com \
    --cc=mchitale@gmail.com \
    --cc=mike.leach@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=palmer@dabbelt.com \
    --cc=peterz@infradead.org \
    --cc=pjw@kernel.org \
    --cc=sunilvl@oss.qualcomm.com \
    --cc=suzuki.poulose@arm.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

all inboxes | Powered by JetHome®