Linux Perf Users
 help / color / mirror / Atom feed
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
Subject: [PATCH 7/9] perf tools powerpc: Add physical to logical address mapping for HTM traces
Date: Wed,  1 Jul 2026 14:11:13 +0530	[thread overview]
Message-ID: <20260701084115.80383-8-atrajeev@linux.ibm.com> (raw)
In-Reply-To: <20260701084115.80383-1-atrajeev@linux.ibm.com>

From: Tanushree Shah <tshah@linux.ibm.com>

Add support for mapping physical addresses from HTM (Hardware Trace Macro)
traces to logical addresses within the current LPAR (Logical Partition).
This enables correlation of HTM trace data with the logical address space
visible to applications and the kernel.

HTM traces capture physical memory addresses from the transactions, but for
meaningful analysis, these need to be mapped to the logical addresses used
by the partition. This patch implements the mapping by:

1. Reading the current partition ID from /proc/powerpc/lparcfg to identify
   which LPAR the trace belongs to

2. Extracting memory map entries from the HTM system memory configuration
   data, which contains:

3. Parsing the decoded HTM trace file using regex patterns to extract
   physical addresses and their associated event labels from each trace
   entry

4. For each physical address in the trace, finding the matching memory
   map entry by:
   - Checking if the address falls within the entry's physical range
   - Verifying the entry belongs to the current partition (LP index match)
   - Computing the offset from the physical range start
   - Adding the offset to the logical range start to get the logical address

The implementation uses dynamic memory allocation to handle variable numbers
of trace entries and memory map entries.

Data structures:
- struct mem_entries: Stores memory map metadata (physical start, logical
  start, LP index, size) extracted from HTM system memory configuration

- struct addr_map: Stores the mapping results (physical address,
  logical address) for each trace entry

The mapping results are output via pr_debug() for verification during
development and debugging. This information is essential for subsequent
patches that will use the logical addresses to generate synthetic perf
samples.

Error handling includes:
- NULL checks for all memory allocations
- Validation of file operations (fopen, fstat, mmap)
- Proper resource cleanup on all error paths
- Consistent error return codes using negative errno values
- Descriptive error messages using perf's pr_err() infrastructure

This patch is part of the HTM trace processing pipeline and works in
conjunction with:
- HTM trace collection (kernel driver)
- HTM trace decoding (htmdecode tool)
- Synthetic sample generation (subsequent patch)

This patch incorporates the following changes:
Store HTM memory map entries (start physical, start logical,
LP index) into a dedicated struct.

Parse HTM decoded trace file and extract the "addr" and
"label" fields and store it in a struct.

For each address in the trace, the code checks for a matching
memory map entry with the same LP index. If the address falls
within the entry's range, the offset is computed and added to
the logical start address (got from the memory map entry) to
get the logical address of the given address.

Signed-off-by: Tanushree Shah <tshah@linux.ibm.com>
---
 tools/perf/util/powerpc-htm.c | 264 ++++++++++++++++++++++++++++++++++
 1 file changed, 264 insertions(+)

diff --git a/tools/perf/util/powerpc-htm.c b/tools/perf/util/powerpc-htm.c
index 487989ca4fc7..83253850870c 100644
--- a/tools/perf/util/powerpc-htm.c
+++ b/tools/perf/util/powerpc-htm.c
@@ -20,6 +20,13 @@
 #include "sample.h"
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <regex.h>
+#include <ctype.h>
+#include <errno.h>
+
+/* mask the 64th bit of a physical address */
+#define PHYS_ADDR_MASK        0x7FFFFFFFFFFFFFFFUL
+const char *lpar_cfg = "/proc/powerpc/lparcfg";
 
 struct perf_session;
 
@@ -47,6 +54,19 @@ struct htm_mem {
 	uint64_t size;
 };
 
+struct mem_entries {
+	unsigned long long phy_addr;
+	unsigned long logical_addr;
+	u32 lp_index;
+	u64 size;
+};
+
+struct addr_map {
+	char event[64];
+	unsigned long long phys_addr;
+	unsigned long logical_addr;
+};
+
 static int run_htmdecode(const char *input_file, const char *output_file)
 {
 	pid_t pid;
@@ -108,6 +128,187 @@ static int run_htmdecode(const char *input_file, const char *output_file)
 	return 0;
 }
 
+static void *safe_realloc(void *ptr, size_t new_size)
+{
+	void *tmp = realloc(ptr, new_size);
+
+	if (!tmp) {
+		pr_err("realloc failed: %s\n", strerror(errno));
+		return NULL;
+	}
+
+	return tmp;
+}
+
+static int add_map_entry(struct addr_map **arr, size_t *count, size_t *cap, struct addr_map entry)
+{
+	if (*count >= *cap) {
+		size_t new_cap = (*cap == 0) ? 1024 : (*cap * 2);
+		void *tmp = safe_realloc(*arr, new_cap * sizeof(struct addr_map));
+
+		if (!tmp)
+			return -1;  // allocation failed
+		*arr = tmp;
+		*cap = new_cap;
+
+	}
+
+	(*arr)[(*count)++] = entry;
+	return 0;
+}
+
+/*
+ * This effectively maps a physical address to its logical address
+ * within the selected LP partition.
+ */
+static unsigned long find_logical_addr(unsigned long long given_addr,
+				struct mem_entries *mem_entries_array,
+				size_t n_entries,
+				u32 lp_filter)
+{
+	for (size_t i = 0; i < n_entries; i++) {
+		unsigned long long start = mem_entries_array[i].phy_addr & PHYS_ADDR_MASK;
+		unsigned long long end   = start + mem_entries_array[i].size;
+
+		/* Skip entries with invalid logical_start sentinel */
+		if (mem_entries_array[i].logical_addr == UINT64_MAX) {
+			pr_debug("  SKIP i=%zu: logical_start sentinel=0x%016llx\n",
+			i, (unsigned long long)mem_entries_array[i].logical_addr);
+			continue;
+		}
+
+		/*
+		 * Check if 'given_addr' falls within the physical memory range of this entry
+		 * and belongs to the LP partition indicated by 'lp_filter'.
+		 * If so, calculate:
+		 * 'offset' and the 'logical address
+		 */
+		if (start <= given_addr && given_addr < end &&
+			mem_entries_array[i].lp_index == lp_filter) {
+			unsigned long long offset = given_addr - start;
+			unsigned long logical = mem_entries_array[i].logical_addr + offset;
+
+			pr_debug("DEBUG: Condition hit at i=%zu given_addr=0x%llx start=0x%llx end=0x%llx lp_index=%u\n",
+				i, given_addr,
+				start, end,
+				(unsigned int)mem_entries_array[i].lp_index);
+			pr_debug("logical = 0x%016lx\n", logical);
+			return logical;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Parse the HTM trace file line by line, extracting memory addresses and labels.
+ * Map each memory addresses to a corresponding logical address for a given
+ * lp_filter. Store the results in a dynamically growing map of entries.
+ */
+static struct addr_map *process_trace_file(const char *trace_file,
+				struct mem_entries *mem_entries_array,
+				size_t n_entries,
+				u32 lp_filter,
+				size_t *count_out)
+{
+	regex_t addr_regex, label_regex;
+	struct addr_map *maps;
+	size_t count, cap;
+	char *line = NULL;
+	size_t len;
+	regmatch_t pmatch[2];
+	const char *ptr;
+	unsigned long logical_addr;
+	size_t total_phys = 0;
+	size_t total_phys_to_logical = 0;
+
+	FILE *fp = fopen(trace_file, "r");
+
+	if (!fp) {
+		pr_err("Failed to open trace file %s: %s\n", trace_file, strerror(errno));
+		return NULL;
+	}
+
+	if (regcomp(&addr_regex, "addr:0x[0-9A-Fa-f]+", REG_EXTENDED) != 0) {
+		pr_err("Failed to compile addr_regex\n");
+		return NULL;
+	}
+
+	if (regcomp(&label_regex,
+		   "^[[:space:]]*[0-9A-Fa-f]+ : [^[:space:]]+[[:space:]]+([^[:space:]]+)",
+		   REG_EXTENDED) != 0) {
+		pr_err("Failed to compile label_regex\n");
+		return NULL;
+	}
+
+	maps = NULL;
+	count = 0;
+	cap = 0;
+
+	while (getline(&line, &len, fp) != -1) {
+		if (regexec(&label_regex, line, 2, pmatch, 0) == 0) {
+			char label[64] = {0};
+			int start = pmatch[1].rm_so;
+			int end   = pmatch[1].rm_eo;
+			int line_len   = end - start;
+
+			if (line_len < 0)
+				line_len = 0;
+			if ((size_t)line_len > sizeof(label) - 1)
+				line_len = (int)(sizeof(label) - 1);
+
+			/* Use snprintf to copy exactly len characters and
+			 * always null terminate
+			 */
+			snprintf(label, sizeof(label), "%.*s", line_len, line + start);
+			ptr = line;
+			while (regexec(&addr_regex, ptr, 1, pmatch, 0) == 0) {
+				unsigned long long phys_addr = 0;
+				struct addr_map entry = {0};
+
+				if (sscanf(ptr + pmatch[0].rm_so + strlen("addr:"),
+					  "%llx", &phys_addr) != 1) {
+					pr_debug("Failed to parse phys addr from trace line\n");
+					continue;
+				}
+
+				total_phys++;
+				pr_debug("Total Phys[%zu]: 0x%016llx\n", total_phys, phys_addr);
+				logical_addr = find_logical_addr(phys_addr,
+							mem_entries_array,
+							n_entries,
+							lp_filter);
+				if (logical_addr == 0) {
+					ptr += pmatch[0].rm_eo;
+					continue;
+				} else {
+					total_phys_to_logical++;
+					pr_debug("  Phys: 0x%016llx to Logical: 0x%016lx\n",
+					phys_addr,
+					logical_addr);
+				}
+				pr_debug("Total physical to logical found    : %zu\n",
+					total_phys_to_logical);
+				snprintf(entry.event, sizeof(entry.event), "%s", label);
+				entry.phys_addr   = phys_addr;
+				entry.logical_addr = logical_addr;
+
+				add_map_entry(&maps, &count, &cap, entry);
+
+				ptr += pmatch[0].rm_eo;
+			}
+		}
+	}
+
+	free(line);
+	fclose(fp);
+	regfree(&addr_regex);
+	regfree(&label_regex);
+
+	*count_out = count;
+	return maps;
+}
+
 static int create_mem_maps(struct powerpc_htm *htm)
 {
 	off_t file_size;
@@ -117,6 +318,13 @@ static int create_mem_maps(struct powerpc_htm *htm)
 	struct htm_mem *mem;
 	char tracefile[128];
 	int ret;
+	u32 lp_filter = 0;
+	size_t n_entries = htm->htm_mem_entries;
+	struct mem_entries *mem_entries_array;
+	size_t num_maps = 0;
+	struct addr_map *maps;
+	FILE *fp;
+	char lp_line[256];
 
 	snprintf(tracefile, sizeof(tracefile), "%s.out", htm->htmbin_file);
 
@@ -129,6 +337,27 @@ static int create_mem_maps(struct powerpc_htm *htm)
 		return ret;
 	}
 
+	/* get the lp index */
+	fp = fopen(lpar_cfg, "r");
+	if (!fp) {
+		pr_err("Failed to open %s: %s\n", lpar_cfg, strerror(errno));
+		return -errno;
+	}
+
+	while (fgets(lp_line, sizeof(lp_line), fp)) {
+		if (strncmp(lp_line, "partition_id=", 13) == 0) {
+			lp_filter = strtoul(lp_line + 13, NULL, 10);
+			break;
+		}
+	}
+
+	fclose(fp);
+
+	if (lp_filter == 0)
+		pr_info("partition_id not found in %s\n", lpar_cfg);
+	else
+		pr_info("Using partition_id = %" PRIu32 "\n", lp_filter);
+
 	fd = open(htm->trans_file, O_RDONLY);
 	if (fd == -1) {
 		pr_err("Failed to open %s: %s\n", htm->trans_file, strerror(errno));
@@ -160,9 +389,44 @@ static int create_mem_maps(struct powerpc_htm *htm)
 		return -1;
 	}
 
+	mem_entries_array = malloc(n_entries * sizeof(struct mem_entries));
+	if (!mem_entries_array) {
+		pr_err("Failed to allocate memory for mem entries: %s\n", strerror(errno));
+		munmap(mapped_data, file_size);
+		close(fd);
+		return -ENOMEM;
+	}
+
+	/* get the HTM memory map data and store it in mem_entries_array
+	 * to use it later on for physical->logical mapping
+	 */
+	for (u64 i = 0; i < n_entries; i++, mem++) {
+		mem_entries_array[i].phy_addr = bswap_64(mem->phy_real);
+		mem_entries_array[i].logical_addr = bswap_64(mem->logical_real);
+		mem_entries_array[i].lp_index = bswap_32(mem->lp_index);
+		mem_entries_array[i].size = bswap_64(mem->size);
+	}
+
 	munmap(mapped_data, file_size);
 	close(fd);
 
+	maps = process_trace_file(tracefile, mem_entries_array, n_entries, lp_filter, &num_maps);
+	if (!maps) {
+		pr_err("Error processing physical addresses from trace file\n");
+		free(mem_entries_array);
+		return -EINVAL;
+	}
+
+	for (size_t i = 0; i < num_maps; i++) {
+		pr_debug("Event: %-20s | Phys: 0x%016llx | Logical: 0x%lx\n",
+			maps[i].event,
+			maps[i].phys_addr,
+			(unsigned long)maps[i].logical_addr);
+	}
+
+	free(maps);
+	free(mem_entries_array);
+
 	return 0;
 }
 
-- 
2.52.0


  parent reply	other threads:[~2026-07-01  8:42 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-01  8:41 [PATCH 0/9] tools/perf: Add interface to expose HTM trace data via perf Athira Rajeev
2026-07-01  8:41 ` [PATCH 1/9] tool/perf: Move auxtrace_record__init for powerpc-vpadtl as separate utility Athira Rajeev
2026-07-01  8:56   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 2/9] tools/perf: Add CONFIG_AUXTRACE support for HTM pmu on powerpc Athira Rajeev
2026-07-01  8:55   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 3/9] tools/perf: Add arch_record__collect_final_data to collect additional data before closing the event Athira Rajeev
2026-07-01  8:54   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 4/9] tools/perf: Add powerpc callback support for arch_record__collect_final_data Athira Rajeev
2026-07-01  8:55   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 5/9] tools/perf: process htm auxtrace events and display in perf report -D Athira Rajeev
2026-07-01  9:05   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 6/9] perf tools powerpc: Add HTM trace data processing and decoding support Athira Rajeev
2026-07-01  9:06   ` sashiko-bot
2026-07-01  8:41 ` Athira Rajeev [this message]
2026-07-01  9:07   ` [PATCH 7/9] perf tools powerpc: Add physical to logical address mapping for HTM traces sashiko-bot
2026-07-01  8:41 ` [PATCH 8/9] tools/perf/powerpc: Add event name as htm of PERF_TYPE_SYNTH type to present htm samples Athira Rajeev
2026-07-01  9:12   ` sashiko-bot
2026-07-01  8:41 ` [PATCH 9/9] tools/perf/powerpc: Add logical address in decoded traces Athira Rajeev
2026-07-01  9:13   ` 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=20260701084115.80383-8-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=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