[PATCH v6 14/26] perf thread_map: Add thread_map__tgid()

From: Ian Rogers

Date: Mon Sep 28 2026 - 14:29:03 EST


bpf-filter reads the tgid of each thread in a thread map from /proc, to
key its BPF map by process, and perf trace is about to do the same. Move
convert_to_tgid() to thread_map.c as thread_map__tgid(), taking the
thread map and index both users have.

While moving it, fix two bugs. Match Tgid: at the start of a line, as a
task can set its comm, shown on the Name: line before it, to contain
"Tgid: <n>". And check the character after the number before freeing the
buffer it points into, rather than after.

Fixes: eb1693b1150d ("perf bpf-filter: Split per-task filter use case")
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/bpf-filter.c | 29 ++---------------------------
tools/perf/util/thread_map.c | 26 ++++++++++++++++++++++++++
tools/perf/util/thread_map.h | 1 +
3 files changed, 29 insertions(+), 27 deletions(-)

diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
index bcd81084e342..72e83ac1d6a4 100644
--- a/tools/perf/util/bpf-filter.c
+++ b/tools/perf/util/bpf-filter.c
@@ -56,6 +56,7 @@
#include "util/debug.h"
#include "util/evsel.h"
#include "util/target.h"
+#include "util/thread_map.h"
#include "util/bpf-utils.h"

#include "util/bpf-filter.h"
@@ -202,31 +203,6 @@ static int get_filter_entries(struct evsel *evsel, struct perf_bpf_filter_entry
return 0;
}

-static int convert_to_tgid(int tid)
-{
- char path[128];
- char *buf, *p, *q;
- int tgid;
- size_t len;
-
- scnprintf(path, sizeof(path), "%d/status", tid);
- if (procfs__read_str(path, &buf, &len) < 0)
- return -1;
-
- p = strstr(buf, "Tgid:");
- if (p == NULL) {
- free(buf);
- return -1;
- }
-
- tgid = strtol(p + 6, &q, 0);
- free(buf);
- if (*q != '\n')
- return -1;
-
- return tgid;
-}
-
/*
* The event might be closed already so we cannot get the list of ids using FD
* like in create_event_hash() below, let's iterate the event_hash map and
@@ -407,14 +383,13 @@ static int create_idx_hash(struct evsel *evsel, struct perf_bpf_filter_entry *en
last = -1;
nr = perf_thread_map__nr(threads);
for (int i = 0; i < nr; i++) {
- int pid = perf_thread_map__pid(threads, i);
int tgid;
struct idx_hash_key key = {
.evt_id = event_id,
};

/* it actually needs tgid, let's get tgid from /proc. */
- tgid = convert_to_tgid(pid);
+ tgid = thread_map__tgid(threads, i);
if (tgid < 0) {
/* the thread may be dead, ignore. */
continue;
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 48c70f149e92..918ab3180232 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -395,3 +395,29 @@ int thread_map__remove(struct perf_thread_map *threads, int idx)
threads->nr--;
return 0;
}
+
+/* The tgid of the thread at idx, read from /proc, or -1 if it has exited. */
+pid_t thread_map__tgid(struct perf_thread_map *threads, int idx)
+{
+ char path[128];
+ char *buf, *p, *q;
+ pid_t tgid;
+ size_t len;
+
+ snprintf(path, sizeof(path), "%d/status", perf_thread_map__pid(threads, idx));
+ if (procfs__read_str(path, &buf, &len) < 0)
+ return -1;
+
+ /* From a line start, as the Name: line before it can contain "Tgid:". */
+ p = strstr(buf, "\nTgid:");
+ if (p == NULL) {
+ free(buf);
+ return -1;
+ }
+
+ tgid = strtol(p + 6, &q, 0);
+ if (*q != '\n')
+ tgid = -1;
+ free(buf);
+ return tgid;
+}
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index fc16d87f32fb..d700a22b7ae4 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -24,4 +24,5 @@ size_t thread_map__fprintf(struct perf_thread_map *threads, FILE *fp);
void thread_map__read_comms(struct perf_thread_map *threads);
bool thread_map__has(struct perf_thread_map *threads, pid_t pid);
int thread_map__remove(struct perf_thread_map *threads, int idx);
+pid_t thread_map__tgid(struct perf_thread_map *threads, int idx);
#endif /* __PERF_THREAD_MAP_H */
--
2.56.0.rc1.315.gc6ed9934b7-goog