[PATCH 23/24] perf tools: gtrace: Record and parse trace encoder metadata

Zane Leung liangzhen at linux.spacemit.com
Thu Oct 22 20:15:04 PDT 2026


Collect the read-only trace encoder control, features and timestamp
registers per CPU into the AUXTRACE_INFO metadata and validate the
requested context, syncmode, syncmax and timestamp options against the
hardware before recording. Parse the metadata back in the decoder.

Signed-off-by: Zane Leung <liangzhen at linux.spacemit.com>
---
 tools/perf/arch/riscv/util/auxtrace.c | 203 +++++++++++++++++++++++++-
 tools/perf/util/gtrace-decoder.c      |  89 ++++++++++-
 tools/perf/util/gtrace.h              |  30 +++-
 3 files changed, 311 insertions(+), 11 deletions(-)

diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
index 882632511d09..c11c5c34cbc8 100644
--- a/tools/perf/arch/riscv/util/auxtrace.c
+++ b/tools/perf/arch/riscv/util/auxtrace.c
@@ -9,6 +9,7 @@
 #include <linux/bitops.h>
 #include <linux/log2.h>
 #include <linux/zalloc.h>
+#include <linux/bitfield.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
@@ -39,28 +40,217 @@ struct rvtrace_recording {
 	struct evlist *evlist;
 };
 
+static const char * const metadata_encoder_ro[] = {
+	[RVTRACE_ENCODER_FORMAT]    = "control/format",
+	[RVTRACE_ENCODER_CONTEXT]   = "control/has_context",
+	[RVTRACE_ENCODER_SRCBITS]   = "features/srcbits",
+	[RVTRACE_ENCODER_TIMESTAMP] = "timestamp/source",
+};
+
 static size_t rvtrace_info_priv_size(struct auxtrace_record *itr __maybe_unused,
 				     struct evlist *evlist __maybe_unused)
 {
-	return GTRACE_AUXTRACE_PRIV_SIZE;
+	int encoder;
+	struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
+	struct perf_cpu_map *intersect_cpus;
+
+	if (!perf_cpu_map__has_any_cpu(event_cpus)) {
+		/* cpu map is not "any" CPU , we have specific CPUs to work with */
+		struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+
+		intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
+		perf_cpu_map__put(online_cpus);
+	} else {
+		/* Event can be "any" CPU so count all online CPUs. */
+		intersect_cpus = perf_cpu_map__new_online_cpus();
+	}
+
+	encoder = perf_cpu_map__nr(intersect_cpus);
+	perf_cpu_map__put(intersect_cpus);
+
+	return (GTRACE_HEADER_SIZE + encoder * RVTRACE_ENCODER_PRIV_SIZE);
+
+}
+
+static bool gtrace_pmu_path_exists(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path)
+{
+	char pmu_path[PATH_MAX];
+
+	/* Get RO metadata from sysfs */
+	snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
+
+	return perf_pmu__file_exists(pmu, pmu_path);
+}
+
+static int rvtrace_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val)
+{
+	char pmu_path[PATH_MAX];
+	int scan;
+
+	/* Get RO metadata from sysfs */
+	snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
+
+	scan = perf_pmu__scan_file(pmu, pmu_path, "%llx", val);
+	if (scan != 1) {
+		pr_err("%s: error reading: %s\n", __func__, pmu_path);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rvtrace_get_metadata(struct perf_cpu cpu, u32 *offset,
+				 struct auxtrace_record *itr,
+				 struct perf_record_auxtrace_info *info)
+{
+	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
+	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+	bool timestamp_exists;
+
+	info->priv[*offset + RVTRACE_ENCODER_CPU] = cpu.cpu;
+	info->priv[*offset + RVTRACE_ENCODER_NR_TRC_PARAMS] = RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH;
+
+	/* Get read-only information from sysFS */
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_FORMAT],
+		       &info->priv[*offset + RVTRACE_ENCODER_FORMAT]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT],
+		       &info->priv[*offset + RVTRACE_ENCODER_CONTEXT]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCBITS],
+		       &info->priv[*offset + RVTRACE_ENCODER_SRCBITS]);
+
+	timestamp_exists = gtrace_pmu_path_exists(rvtrace_pmu, cpu,
+			metadata_encoder_ro[RVTRACE_ENCODER_TIMESTAMP]);
+	info->priv[*offset + RVTRACE_ENCODER_TIMESTAMP] = timestamp_exists ? 1 : 0;
+
+	/* Where the next CPU entry should start from */
+	*offset += RVTRACE_ENCODER_PRIV_MAX;
 }
 
 static int rvtrace_info_fill(struct auxtrace_record *itr,
 			     struct perf_session *session __maybe_unused,
 			     struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size)
 {
+	unsigned int i;
+	u32 offset;
+	u64 nr_cpu, type;
+	struct perf_cpu_map *cpu_map;
+	struct perf_cpu_map *event_cpus = evlist__core(session->evlist)->user_requested_cpus;
+	struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
 	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
 	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+	struct perf_cpu cpu;
+
+	if (priv_size != rvtrace_info_priv_size(itr, session->evlist))
+		return -EINVAL;
 
-	if (priv_size != GTRACE_AUXTRACE_PRIV_SIZE)
+	if (!evlist__core(session->evlist)->nr_mmaps)
 		return -EINVAL;
 
+	if (perf_cpu_map__has_any_cpu(event_cpus)) {
+		cpu_map = online_cpus;
+	} else {
+		perf_cpu_map__for_each_cpu(cpu, i, event_cpus) {
+			if (!perf_cpu_map__has(online_cpus, cpu))
+				return -EINVAL;
+		}
+
+		cpu_map = event_cpus;
+	}
+
+	nr_cpu = perf_cpu_map__nr(cpu_map);
+	type = rvtrace_pmu->type;
+
 	auxtrace_info->type = PERF_AUXTRACE_GTRACE;
-	auxtrace_info->priv[0] = rvtrace_pmu->type;
+	auxtrace_info->priv[GTRACE_PMU_TYPE_CPUS] = type << 32;
+	auxtrace_info->priv[GTRACE_PMU_TYPE_CPUS] |= nr_cpu;
+
+	offset = GTRACE_HEADER_MAX;
+
+	perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
+		assert(offset < priv_size);
+		rvtrace_get_metadata(cpu, &offset, itr, auxtrace_info);
+	}
+
+	perf_cpu_map__put(online_cpus);
+
+	return 0;
+}
+
+static int rvtrace_encoder_validate_context(struct perf_pmu *rvtrace_pmu, struct evsel *evsel,
+					    struct perf_cpu cpu)
+{
+	int err;
+	u64 ctxt;
+	__u64 context;
+
+	evsel__get_config_val(evsel, "context", &ctxt);
+
+	if (!ctxt)
+		return 0;
+
+	err = rvtrace_get_ro(rvtrace_pmu, cpu,
+			     metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT], &context);
+	if (err)
+		return err;
+
+	if (!context) {
+		pr_err("context not supported");
+		return -EINVAL;
+	}
 
 	return 0;
 }
 
+static int rvtrace_encoder_validate_timestamp(struct perf_pmu *rvtrace_pmu, struct evsel *evsel,
+					      struct perf_cpu cpu)
+{
+	u64 ts;
+
+	evsel__get_config_val(evsel, "timestamp", &ts);
+
+	if (!ts)
+		return 0;
+
+	if (!gtrace_pmu_path_exists(rvtrace_pmu, cpu,
+		    metadata_encoder_ro[RVTRACE_ENCODER_TIMESTAMP])) {
+		pr_err("timestamp not supported");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rvtrace_validate_config(struct perf_pmu *rvtrace_pmu, struct evsel *evsel)
+{
+	unsigned int idx;
+	int err = 0;
+	struct perf_cpu_map *event_cpus = evlist__core(evsel->evlist)->user_requested_cpus;
+	struct perf_cpu_map *intersect_cpus;
+	struct perf_cpu cpu;
+
+	if (!perf_cpu_map__has_any_cpu(event_cpus)) {
+		struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+
+		intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
+		perf_cpu_map__put(online_cpus);
+	} else {
+		intersect_cpus = perf_cpu_map__new_online_cpus();
+	}
+
+	perf_cpu_map__for_each_cpu_skip_any(cpu, idx, intersect_cpus) {
+		err = rvtrace_encoder_validate_context(rvtrace_pmu, evsel, cpu);
+		if (err)
+			break;
+
+		err = rvtrace_encoder_validate_timestamp(rvtrace_pmu, evsel, cpu);
+		if (err)
+			break;
+	}
+
+	perf_cpu_map__put(intersect_cpus);
+	return err;
+}
+
 static int rvtrace_set_auxtrace_mmap_page(struct record_opts *opts)
 {
 	bool privileged = perf_event_paranoid_check(-1);
@@ -127,6 +317,9 @@ static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist
 	evlist__to_front(evlist, rvtrace_evsel);
 	evsel__set_sample_bit(rvtrace_evsel, TIME);
 
+	if (opts->sample_time_set)
+		evsel__set_config_if_unset(rvtrace_evsel, "timestamp", 1);
+
 	/* Add dummy event to keep tracking */
 	err = parse_event(evlist, "dummy:u");
 	if (err)
@@ -139,6 +332,10 @@ static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist
 	tracking_evsel->core.attr.sample_period = 1;
 	evsel__set_sample_bit(tracking_evsel, TIME);
 
+	err = rvtrace_validate_config(rvtrace_pmu, rvtrace_evsel);
+	if (err)
+		return err;
+
 	return 0;
 }
 
diff --git a/tools/perf/util/gtrace-decoder.c b/tools/perf/util/gtrace-decoder.c
index c25fbfc61928..d70cb7cd399f 100644
--- a/tools/perf/util/gtrace-decoder.c
+++ b/tools/perf/util/gtrace-decoder.c
@@ -12,6 +12,7 @@
 #include "evlist.h"
 #include "session.h"
 #include "gtrace.h"
+#include "debug.h"
 
 struct gtrace_decoder {
 	struct auxtrace auxtrace;
@@ -19,6 +20,8 @@ struct gtrace_decoder {
 	struct perf_session *session;
 	struct machine *machine;
 	u32 pmu_type;
+	int num_cpu;
+	u64 **metadata;
 };
 
 static int gtrace_process_event(struct perf_session *session __maybe_unused,
@@ -52,7 +55,10 @@ static void gtrace_free(struct perf_session *session)
 					    auxtrace);
 
 	session->auxtrace = NULL;
-	free(ptr);
+	for (int i = 0; i < ptr->num_cpu; i++)
+		zfree(&ptr->metadata[i]);
+	zfree(&ptr->metadata);
+	zfree(&ptr);
 }
 
 static bool gtrace_evsel_is_auxtrace(struct perf_session *session,
@@ -64,24 +70,88 @@ static bool gtrace_evsel_is_auxtrace(struct perf_session *session,
 	return evsel->core.attr.type == ptr->pmu_type;
 }
 
+static const char * const gtrace_global_header_fmts[] = {
+	[GTRACE_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_SRCBITS]	    = "	    SRCBITS		    %lld\n",
+	[RVTRACE_ENCODER_TIMESTAMP]	    = "	    TIMESTAMP		    %lld\n",
+};
+
+static void gtrace_print_auxtrace_info(u64 *val, int num_cpu)
+{
+	int i, j, cpu = 0, nr_params = 0, fmt_offset = 0;
+
+	for (i = 0; i < GTRACE_HEADER_MAX; i++)
+		fprintf(stdout, gtrace_global_header_fmts[i], val[i]);
+
+	for (i = GTRACE_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 gtrace__process_auxtrace_info(union perf_event *event,
 				   struct perf_session *session)
 {
 	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
 	struct gtrace_decoder *ptr;
-
-	if (auxtrace_info->header.size < GTRACE_AUXTRACE_PRIV_SIZE +
-	    sizeof(struct perf_record_auxtrace_info))
+	u64 *priv;
+	u64 **metadata;
+	int num_cpu;
+	int priv_size;
+	int err;
+	int i;
+
+	priv = (u64 *) auxtrace_info->priv;
+	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 ||
+	    num_cpu > priv_size / (int)sizeof(u64))
 		return -EINVAL;
 
-	ptr = zalloc(sizeof(*ptr));
-	if (!ptr)
+	metadata = zalloc(sizeof(*metadata) * num_cpu);
+	if (!metadata)
 		return -ENOMEM;
 
+	i = GTRACE_HEADER_SIZE;
+
+	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] = priv[i + k];
+		i += RVTRACE_ENCODER_PRIV_MAX;
+	}
+
+	ptr = zalloc(sizeof(*ptr));
+	if (!ptr) {
+		err = -ENOMEM;
+		goto err_free_metadata;
+	}
+
 	ptr->session = session;
 	ptr->machine = &session->machines.host;
 	ptr->auxtrace_type = auxtrace_info->type;
-	ptr->pmu_type = auxtrace_info->priv[0];
+	ptr->pmu_type = (unsigned int) ((priv[GTRACE_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+	ptr->num_cpu = num_cpu;
+	ptr->metadata = metadata;
 
 	ptr->auxtrace.process_event = gtrace_process_event;
 	ptr->auxtrace.process_auxtrace_event = gtrace_process_auxtrace_event;
@@ -92,4 +162,9 @@ int gtrace__process_auxtrace_info(union perf_event *event,
 	session->auxtrace = &ptr->auxtrace;
 
 	return 0;
+
+err_free_metadata:
+	for (int j = 0; j < num_cpu; j++)
+		zfree(&metadata);
+	return err;
 }
diff --git a/tools/perf/util/gtrace.h b/tools/perf/util/gtrace.h
index 02db4f2ff9f5..76006e30e7c3 100644
--- a/tools/perf/util/gtrace.h
+++ b/tools/perf/util/gtrace.h
@@ -11,7 +11,35 @@
 
 struct perf_session;
 
-#define GTRACE_AUXTRACE_PRIV_SIZE      sizeof(u64)
+/* Supported trace protocol formats */
+enum gtrace_format {
+	GTRACE_FORMAT_ETRACE,
+	GTRACE_FORMAT_NTRACE,
+};
+
+enum {
+	GTRACE_PMU_TYPE_CPUS,
+	GTRACE_HEADER_MAX,
+};
+
+/* RISC-V Trace Encoder metadata */
+enum {
+	RVTRACE_ENCODER_CPU,
+	RVTRACE_ENCODER_NR_TRC_PARAMS,
+	RVTRACE_ENCODER_FORMAT,
+	RVTRACE_ENCODER_CONTEXT,
+	RVTRACE_ENCODER_SRCBITS,
+	RVTRACE_ENCODER_TIMESTAMP,
+	RVTRACE_ENCODER_PRIV_MAX,
+};
+
+#define RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH (RVTRACE_ENCODER_PRIV_MAX - RVTRACE_ENCODER_FORMAT)
+
+#define GTRACE_HEADER_SIZE             (GTRACE_HEADER_MAX * sizeof(u64))
+#define RVTRACE_ENCODER_PRIV_SIZE      (RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
+
+#define INFO_HEADER_SIZE (sizeof(((struct perf_record_auxtrace_info *)0)->type) + \
+			  sizeof(((struct perf_record_auxtrace_info *)0)->reserved__))
 
 int gtrace__process_auxtrace_info(union perf_event *event, struct perf_session *session);
 #endif
-- 
2.34.1




More information about the linux-riscv mailing list