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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.