From: Athira Rajeev <atrajeev@linux.ibm.com>
To: acme@kernel.org, jolsa@kernel.org, adrian.hunter@intel.com,
maddy@linux.ibm.com, irogers@google.com, namhyung@kernel.org
Cc: linux-perf-users@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
atrajeev@linux.ibm.com, hbathini@linux.vnet.ibm.com,
tejas05@linux.ibm.com, tshah@linux.ibm.com,
venkat88@linux.ibm.com, usha.r2@ibm.com
Subject: [PATCH V2 6/6] tools/perf: Add perf tool support for processing powerpc HTM AUXTRACE records
Date: Mon, 20 Jul 2026 16:22:18 +0530 [thread overview]
Message-ID: <20260720105218.14277-7-atrajeev@linux.ibm.com> (raw)
In-Reply-To: <20260720105218.14277-1-atrajeev@linux.ibm.com>
powerpc_htm_process_auxtrace_info() reads the (cpu, attr.config) pairs
stored by htm_info_fill() at record time and builds a cpu_configs[]
table. This lets process_auxtrace_event() map each AUX buffer, which
carries event->auxtrace.cpu, to the correct (node, chip, core) target
and write the raw trace data to htm.bin.nX.pX.cX immediately.
PERF_RECORD_SAMPLE events carrying PERF_SAMPLE_RAW data are handled by
process_event(), which extracts memory configuration records and writes
them to translation.nX.pX.cX.
Per-run first-write tracking (htm_target_seen()) ensures each output
file is opened with O_TRUNC on the first write and O_APPEND on
subsequent writes, correctly handling multiple AUX chunks from the same
target and stale files from prior runs.
For perf report -D the AUX buffer size is printed.
Example:
# perf record -C 9 -m,256 \
-e htm/nodalchipindex=2,nodeindex=0,htm_type=1/ sleep 3
# perf report
# ls htm.bin.* translation.*
htm.bin.n0.p2.c0 translation.n0.p2.c0
Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com>
---
Changes in V2:
- Consolidated: the file-writing, memory-configuration processing, and
address-mapping logic that was spread across old patches 5 to 9 is
rationalised into a single, focused implementation in
tools/perf/util/powerpc-htm.c.
- Synthetic sample generation (old patch 8: PERF_TYPE_SYNTH "htm" event
with logical addresses) and the separate logical-address mapping output
file (old patch 9: .out.l file) are not included in V2. These features
require further discussion on the appropriate abstraction and will be
posted as a follow-on series.
- Physical-to-logical address mapping (old patch 7) is not included in
V2 for the same reason.
- Memory configuration records are now written via process_event()
handling PERF_RECORD_SAMPLE with PERF_SAMPLE_RAW, keyed on the "htm"
PMU name from evsel__pmu_name(). V1 used PERF_SAMPLE_RAW boundary
markers inside the AUX buffer to locate the configuration data.
- AUX buffer processing uses htm_config_for_cpu() to map
event->auxtrace.cpu to the correct (node, chip, core) target using
the (cpu, attr.config) table populated from AUXTRACE_INFO priv[] in
powerpc_htm_process_auxtrace_info(). V1 iterated the evlist at
report time.
- Per-run first-write tracking (htm_target_seen()) uses O_TRUNC on the
first write and O_APPEND on subsequent writes for both htm.bin.* and
translation.* files, correctly handling multiple AUX chunks per target
and stale files from prior runs. V1 always opened with O_TRUNC.
- HTM_MAX_TARGETS compile-time constant is replaced by dynamic allocation
sized to num_events read from AUXTRACE_INFO priv[POWERPC_HTM_NUM_EVENTS].
- Patch is now 6/6 instead of spanning patches 5 to 9.
tools/perf/util/powerpc-htm.c | 312 +++++++++++++++++++++++++++++++++-
1 file changed, 306 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/powerpc-htm.c b/tools/perf/util/powerpc-htm.c
index 8d28d02031d6..409ab563a0a7 100644
--- a/tools/perf/util/powerpc-htm.c
+++ b/tools/perf/util/powerpc-htm.c
@@ -11,9 +11,12 @@
#include "util/session.h"
#include "color.h"
#include "powerpc-htm.h"
+#include <errno.h>
#include "debug.h"
#include "sample.h"
+struct perf_session;
+
struct powerpc_htm {
struct auxtrace auxtrace;
struct auxtrace_queues queues;
@@ -21,8 +24,52 @@ struct powerpc_htm {
u32 auxtrace_type;
struct perf_session *session;
struct machine *machine;
+
+ /*
+ * Capacity: number of distinct HTM targets (node/chip/core tuples)
+ * recorded, read from auxtrace_info->priv[POWERPC_HTM_NUM_EVENTS] at
+ * init time. All three arrays below are allocated to this size.
+ */
+ int nr_targets;
+
+ /*
+ * Per-run first-write tracking for htm.bin.* and translation.* files.
+ * Each entry is a packed u32: node<<16 | chip<<8 | core.
+ * First write for a given key -> O_TRUNC; subsequent writes -> O_APPEND.
+ */
+ u32 *htm_bin_targets;
+ int nr_htm_bin_targets;
+ u32 *translation_targets;
+ int nr_translation_targets;
+
+ /*
+ * CPU -> attr.config table, populated from auxtrace_info->priv[] at
+ * init time. htm_info_fill() (record side) stores the CPU and config
+ * for each htm evsel; we read them back here to map each
+ * event->auxtrace.cpu to the correct (node, chip, core) config.
+ */
+ struct {
+ int cpu;
+ u64 config;
+ } *cpu_configs;
+ int nr_cpu_configs;
};
+/*
+ * Look up attr.config by the CPU number carried in event->auxtrace.cpu.
+ * Returns 0 if not found.
+ */
+static u64 htm_config_for_cpu(struct powerpc_htm *htm, int cpu)
+{
+ int i;
+
+ for (i = 0; i < htm->nr_cpu_configs; i++) {
+ if (htm->cpu_configs[i].cpu == cpu)
+ return htm->cpu_configs[i].config;
+ }
+ return 0;
+}
+
static void powerpc_htm_dump_event(size_t len)
{
const char *color = PERF_COLOR_BLUE;
@@ -33,22 +80,234 @@ static void powerpc_htm_dump_event(size_t len)
}
}
-static int powerpc_htm_process_event(struct perf_session *session __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_sample *sample __maybe_unused,
+#define HTM_MEM_ENTRY_SIZE 32
+
+static inline u32 htm_pack_target(u32 node, u32 chip, u32 core)
+{
+ return (node << 16) | (chip << 8) | core;
+}
+
+static bool htm_target_seen(u32 *targets, int *nr, int capacity, u32 key)
+{
+ int i;
+
+ for (i = 0; i < *nr; i++) {
+ if (targets[i] == key)
+ return true;
+ }
+
+ if (*nr < capacity)
+ targets[(*nr)++] = key;
+ else
+ pr_warning("htm: too many targets (max %d), appending to existing file\n",
+ capacity);
+
+ return false;
+}
+
+/*
+ * Write HTM data to a file.
+ *
+ * mem_maps == 0: AUX bus-trace path -> htm.bin.nX.pX.cX
+ * mem_maps != 0: memory config path -> translation.nX.pX.cX
+ *
+ * htm_target_seen() decides O_TRUNC (first write this run) vs O_APPEND
+ * (subsequent writes), keyed on what this process has already written,
+ * not on whether the file exists on disk.
+ */
+static int write_htm(struct powerpc_htm *htm, void *data, size_t size,
+ u32 node, u32 chip, u32 core, int mem_maps)
+{
+ u32 target_key = htm_pack_target(node, chip, core);
+ char target_file[128];
+ size_t written;
+ int flags;
+ int fd;
+
+ if (!data || !size)
+ return -EINVAL;
+
+ flags = O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
+
+ if (mem_maps) {
+ uint8_t *byte_ptr = (uint8_t *)data;
+ __be64 *num_entries_ptr;
+ size_t entries;
+ size_t payload;
+
+ if (size < HTM_MEM_ENTRY_SIZE) {
+ pr_err("Malformed memory mapping entry trace segment\n");
+ return -EINVAL;
+ }
+
+ /* Entry count is at offset 0x10; add 1 for the 32-byte header */
+ num_entries_ptr = (__be64 *)(byte_ptr + 0x10);
+ entries = be64_to_cpu(*num_entries_ptr) + 1;
+ payload = entries * HTM_MEM_ENTRY_SIZE;
+
+ if (payload != size) {
+ pr_err("Bad memory mapping data, invalid number of entries\n");
+ return -EINVAL;
+ }
+
+ snprintf(target_file, sizeof(target_file),
+ "translation.n%d.p%d.c%d", node, chip, core);
+ flags |= htm_target_seen(htm->translation_targets,
+ &htm->nr_translation_targets,
+ htm->nr_targets,
+ target_key) ? O_APPEND : O_TRUNC;
+ fd = open(target_file, flags, 0644);
+ if (fd == -1) {
+ pr_err("Failed to open %s: %s\n", target_file, strerror(errno));
+ return -errno;
+ }
+
+ written = write(fd, data, payload);
+ close(fd);
+
+ if (written != payload) {
+ pr_err("Failed to write memory config: expected %zu bytes, wrote %zu\n",
+ payload, written);
+ return -EIO;
+ }
+
+ return 0;
+ }
+
+ /* AUX bus-trace path */
+ snprintf(target_file, sizeof(target_file),
+ "htm.bin.n%d.p%d.c%d", node, chip, core);
+ flags |= htm_target_seen(htm->htm_bin_targets,
+ &htm->nr_htm_bin_targets,
+ htm->nr_targets,
+ target_key) ? O_APPEND : O_TRUNC;
+ fd = open(target_file, flags, 0644);
+ if (fd == -1) {
+ pr_err("Failed to open %s: %s\n", target_file, strerror(errno));
+ return -errno;
+ }
+
+ written = write(fd, data, size);
+ close(fd);
+
+ if (written != size) {
+ pr_err("Failed to write htm trace data: expected %zu bytes, wrote %zu\n",
+ size, written);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int powerpc_htm_process_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
const struct perf_tool *tool __maybe_unused)
{
+ struct powerpc_htm *htm;
+ struct evsel *evsel;
+ u32 node, chip, core;
+ u64 ev_config;
+
+ if (!session || !session->auxtrace || !event || !sample)
+ return 0;
+
+ if (event->header.type != PERF_RECORD_SAMPLE || !sample->raw_data)
+ return 0;
+
+ htm = container_of(session->auxtrace, struct powerpc_htm, auxtrace);
+ evsel = evlist__event2evsel(session->evlist, event);
+
+ if (!evsel || strcmp(evsel__pmu_name(evsel), "htm") != 0)
+ return 0;
+
+ ev_config = evsel->core.attr.config;
+ node = (ev_config >> 4) & 0xff;
+ chip = (ev_config >> 12) & 0xff;
+ core = (ev_config >> 20) & 0xff;
+
+ /*
+ * raw_size includes 4 bytes of u64 alignment padding added by the
+ * kernel. Subtract sizeof(u32) to recover the true payload byte count.
+ */
+ if (write_htm(htm, sample->raw_data,
+ sample->raw_size - sizeof(uint32_t),
+ node, chip, core, 1) < 0) {
+ pr_err("Failed to write memory translation block\n");
+ return -EIO;
+ }
+
return 0;
}
-static int powerpc_htm_process_auxtrace_event(struct perf_session *session __maybe_unused,
+static int powerpc_htm_process_auxtrace_event(struct perf_session *session,
union perf_event *event,
const struct perf_tool *tool __maybe_unused)
{
+ struct powerpc_htm *htm;
+ struct auxtrace_buffer *buffer;
+ off_t data_offset;
+ u32 node, chip, core;
+ u64 ev_config;
+ int fd;
+ int err;
+
+ if (!session || !session->auxtrace)
+ return 0;
+
+ htm = container_of(session->auxtrace, struct powerpc_htm, auxtrace);
+ fd = perf_data__fd(session->data);
+
+ if (perf_data__is_pipe(session->data)) {
+ data_offset = 0;
+ } else {
+ data_offset = lseek(fd, 0, SEEK_CUR);
+ if (data_offset == -1)
+ return -errno;
+ }
+
+ /*
+ * Queue the buffer and get back a pointer to it. We immediately load
+ * and write the data so htm.bin.* exists on disk before subsequent
+ * patches invoke htmdecode during the same session pass.
+ */
+ err = auxtrace_queues__add_event(&htm->queues, session, event,
+ data_offset, &buffer);
+ if (err)
+ return err;
+
+ if (!buffer)
+ return 0;
+
+ /*
+ * Map event->auxtrace.cpu -> attr.config using the table built from
+ * auxtrace_info->priv[] at init time. This is reliable because
+ * htm_info_fill() stored the exact (cpu, config) pair for each evsel
+ * at record time, no CPU map or evlist iteration needed here.
+ */
+ ev_config = htm_config_for_cpu(htm, (int)event->auxtrace.cpu);
+ if (!ev_config) {
+ pr_err("htm: no config found for auxtrace cpu %u\n",
+ event->auxtrace.cpu);
+ return 0;
+ }
+
+ node = (ev_config >> 4) & 0xff;
+ chip = (ev_config >> 12) & 0xff;
+ core = (ev_config >> 20) & 0xff;
+
+ if (!auxtrace_buffer__get_data(buffer, fd)) {
+ pr_err("Failed to read AUX buffer data\n");
+ return -ENOMEM;
+ }
+
if (dump_trace)
- powerpc_htm_dump_event(event->auxtrace.size);
+ powerpc_htm_dump_event(buffer->size);
- return 0;
+ err = write_htm(htm, buffer->data, buffer->size, node, chip, core, 0);
+ auxtrace_buffer__put_data(buffer);
+
+ return err < 0 ? err : 0;
}
static int powerpc_htm_flush(struct perf_session *session __maybe_unused,
@@ -78,6 +337,9 @@ static void powerpc_htm_free(struct perf_session *session)
htm = container_of(session->auxtrace, struct powerpc_htm, auxtrace);
powerpc_htm_free_events(session);
session->auxtrace = NULL;
+ free(htm->cpu_configs);
+ free(htm->htm_bin_targets);
+ free(htm->translation_targets);
free(htm);
}
@@ -86,6 +348,8 @@ int powerpc_htm_process_auxtrace_info(union perf_event *event,
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct powerpc_htm *htm;
+ u64 num_events;
+ u64 i;
int err;
if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
@@ -102,6 +366,42 @@ int powerpc_htm_process_auxtrace_info(union perf_event *event,
return err;
}
+ htm->auxtrace_type = auxtrace_info->priv[POWERPC_HTM_PMU_TYPE];
+ num_events = auxtrace_info->priv[POWERPC_HTM_NUM_EVENTS];
+
+ /*
+ * All three arrays are sized to num_events, the exact count of HTM
+ * targets written by htm_info_fill() at record time. This replaces the
+ * old compile-time HTM_MAX_TARGETS constant so the limit is always
+ * consistent with what was actually recorded.
+ */
+ htm->nr_targets = (int)num_events;
+ htm->cpu_configs = calloc(num_events, sizeof(*htm->cpu_configs));
+ htm->htm_bin_targets = calloc(num_events, sizeof(*htm->htm_bin_targets));
+ htm->translation_targets = calloc(num_events, sizeof(*htm->translation_targets));
+ if (!htm->cpu_configs || !htm->htm_bin_targets || !htm->translation_targets) {
+ free(htm->cpu_configs);
+ free(htm->htm_bin_targets);
+ free(htm->translation_targets);
+ auxtrace_queues__free(&htm->queues);
+ free(htm);
+ return -ENOMEM;
+ }
+
+ /*
+ * Read (cpu, config) pairs from priv[]. These were written by
+ * htm_info_fill() at record time, one pair per htm evsel in evlist
+ * order. Keying by CPU lets process_auxtrace_event() look up the
+ * correct attr.config for each AUX buffer using event->auxtrace.cpu.
+ */
+ for (i = 0; i < num_events; i++) {
+ htm->cpu_configs[i].cpu =
+ (int)auxtrace_info->priv[POWERPC_HTM_EVENT_DATA + i * 2];
+ htm->cpu_configs[i].config =
+ auxtrace_info->priv[POWERPC_HTM_EVENT_DATA + i * 2 + 1];
+ htm->nr_cpu_configs++;
+ }
+
htm->session = session;
htm->machine = &session->machines.host;
htm->auxtrace.process_event = powerpc_htm_process_event;
--
2.43.0
next prev parent reply other threads:[~2026-07-20 11:04 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-20 10:52 [PATCH V2 0/6] tools/perf: Add powerpc HTM auxtrace support Athira Rajeev
2026-07-20 10:52 ` [PATCH V2 1/6] tools/perf: Move powerpc VPA-DTL auxtrace init into a separate file Athira Rajeev
2026-07-20 11:15 ` sashiko-bot
2026-07-20 10:52 ` [PATCH V2 2/6] tools/perf: Add AUXTRACE recording support for powerpc HTM Athira Rajeev
2026-07-20 11:20 ` sashiko-bot
2026-07-20 10:52 ` [PATCH V2 3/6] tools/perf: Add arch hook to drain remaining data before event close Athira Rajeev
2026-07-20 11:16 ` sashiko-bot
2026-07-20 10:52 ` [PATCH V2 4/6] tools/perf: Add powerpc callback support for arch_perf_record__need_read Athira Rajeev
2026-07-20 11:18 ` sashiko-bot
2026-07-20 10:52 ` [PATCH V2 5/6] tools/perf: Add powerpc HTM auxtrace event processing support Athira Rajeev
2026-07-20 11:18 ` sashiko-bot
2026-07-20 10:52 ` Athira Rajeev [this message]
2026-07-20 11:25 ` [PATCH V2 6/6] tools/perf: Add perf tool support for processing powerpc HTM AUXTRACE records sashiko-bot
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=20260720105218.14277-7-atrajeev@linux.ibm.com \
--to=atrajeev@linux.ibm.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=hbathini@linux.vnet.ibm.com \
--cc=irogers@google.com \
--cc=jolsa@kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=namhyung@kernel.org \
--cc=tejas05@linux.ibm.com \
--cc=tshah@linux.ibm.com \
--cc=usha.r2@ibm.com \
--cc=venkat88@linux.ibm.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