1 // SPDX-License-Identifier: GPL-2.0
14 #include <api/fs/fs.h>
15 #include <linux/string.h>
16 #include <linux/zalloc.h>
18 #include "thread_map.h"
21 #include <internal/threadmap.h>
23 /* Skip "." and ".." directories */
24 static int filter(const struct dirent
*dir
)
26 if (dir
->d_name
[0] == '.')
32 #define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr)
34 struct perf_thread_map
*thread_map__new_by_pid(pid_t pid
)
36 struct perf_thread_map
*threads
;
39 struct dirent
**namelist
= NULL
;
42 sprintf(name
, "/proc/%d/task", pid
);
43 items
= scandir(name
, &namelist
, filter
, NULL
);
47 threads
= thread_map__alloc(items
);
48 if (threads
!= NULL
) {
49 for (i
= 0; i
< items
; i
++)
50 perf_thread_map__set_pid(threads
, i
, atoi(namelist
[i
]->d_name
));
52 refcount_set(&threads
->refcnt
, 1);
55 for (i
=0; i
<items
; i
++)
62 struct perf_thread_map
*thread_map__new_by_tid(pid_t tid
)
64 struct perf_thread_map
*threads
= thread_map__alloc(1);
66 if (threads
!= NULL
) {
67 perf_thread_map__set_pid(threads
, 0, tid
);
69 refcount_set(&threads
->refcnt
, 1);
75 static struct perf_thread_map
*__thread_map__new_all_cpus(uid_t uid
)
78 int max_threads
= 32, items
, i
;
79 char path
[NAME_MAX
+ 1 + 6];
80 struct dirent
*dirent
, **namelist
= NULL
;
81 struct perf_thread_map
*threads
= thread_map__alloc(max_threads
);
86 proc
= opendir("/proc");
88 goto out_free_threads
;
91 refcount_set(&threads
->refcnt
, 1);
93 while ((dirent
= readdir(proc
)) != NULL
) {
96 pid_t pid
= strtol(dirent
->d_name
, &end
, 10);
98 if (*end
) /* only interested in proper numerical dirents */
101 snprintf(path
, sizeof(path
), "/proc/%s", dirent
->d_name
);
103 if (uid
!= UINT_MAX
) {
106 if (stat(path
, &st
) != 0 || st
.st_uid
!= uid
)
110 snprintf(path
, sizeof(path
), "/proc/%d/task", pid
);
111 items
= scandir(path
, &namelist
, filter
, NULL
);
113 pr_debug("scandir for %d returned empty, skipping\n", pid
);
116 while (threads
->nr
+ items
>= max_threads
) {
122 struct perf_thread_map
*tmp
;
124 tmp
= perf_thread_map__realloc(threads
, max_threads
);
126 goto out_free_namelist
;
131 for (i
= 0; i
< items
; i
++) {
132 perf_thread_map__set_pid(threads
, threads
->nr
+ i
,
133 atoi(namelist
[i
]->d_name
));
136 for (i
= 0; i
< items
; i
++)
140 threads
->nr
+= items
;
153 for (i
= 0; i
< items
; i
++)
160 struct perf_thread_map
*thread_map__new_all_cpus(void)
162 return __thread_map__new_all_cpus(UINT_MAX
);
165 struct perf_thread_map
*thread_map__new_by_uid(uid_t uid
)
167 return __thread_map__new_all_cpus(uid
);
170 struct perf_thread_map
*thread_map__new(pid_t pid
, pid_t tid
, uid_t uid
)
173 return thread_map__new_by_pid(pid
);
175 if (tid
== -1 && uid
!= UINT_MAX
)
176 return thread_map__new_by_uid(uid
);
178 return thread_map__new_by_tid(tid
);
181 static struct perf_thread_map
*thread_map__new_by_pid_str(const char *pid_str
)
183 struct perf_thread_map
*threads
= NULL
, *nt
;
185 int items
, total_tasks
= 0;
186 struct dirent
**namelist
= NULL
;
188 pid_t pid
, prev_pid
= INT_MAX
;
190 struct str_node
*pos
;
191 struct strlist_config slist_config
= { .dont_dupstr
= true, };
192 struct strlist
*slist
= strlist__new(pid_str
, &slist_config
);
197 strlist__for_each_entry(pos
, slist
) {
198 pid
= strtol(pos
->s
, &end_ptr
, 10);
200 if (pid
== INT_MIN
|| pid
== INT_MAX
||
201 (*end_ptr
!= '\0' && *end_ptr
!= ','))
202 goto out_free_threads
;
207 sprintf(name
, "/proc/%d/task", pid
);
208 items
= scandir(name
, &namelist
, filter
, NULL
);
210 goto out_free_threads
;
212 total_tasks
+= items
;
213 nt
= perf_thread_map__realloc(threads
, total_tasks
);
215 goto out_free_namelist
;
219 for (i
= 0; i
< items
; i
++) {
220 perf_thread_map__set_pid(threads
, j
++, atoi(namelist
[i
]->d_name
));
223 threads
->nr
= total_tasks
;
228 strlist__delete(slist
);
230 refcount_set(&threads
->refcnt
, 1);
234 for (i
= 0; i
< items
; i
++)
243 struct perf_thread_map
*thread_map__new_by_tid_str(const char *tid_str
)
245 struct perf_thread_map
*threads
= NULL
, *nt
;
247 pid_t tid
, prev_tid
= INT_MAX
;
249 struct str_node
*pos
;
250 struct strlist_config slist_config
= { .dont_dupstr
= true, };
251 struct strlist
*slist
;
253 /* perf-stat expects threads to be generated even if tid not given */
255 return perf_thread_map__new_dummy();
257 slist
= strlist__new(tid_str
, &slist_config
);
261 strlist__for_each_entry(pos
, slist
) {
262 tid
= strtol(pos
->s
, &end_ptr
, 10);
264 if (tid
== INT_MIN
|| tid
== INT_MAX
||
265 (*end_ptr
!= '\0' && *end_ptr
!= ','))
266 goto out_free_threads
;
272 nt
= perf_thread_map__realloc(threads
, ntasks
);
275 goto out_free_threads
;
278 perf_thread_map__set_pid(threads
, ntasks
- 1, tid
);
279 threads
->nr
= ntasks
;
282 strlist__delete(slist
);
284 refcount_set(&threads
->refcnt
, 1);
292 struct perf_thread_map
*thread_map__new_str(const char *pid
, const char *tid
,
293 uid_t uid
, bool all_threads
)
296 return thread_map__new_by_pid_str(pid
);
298 if (!tid
&& uid
!= UINT_MAX
)
299 return thread_map__new_by_uid(uid
);
302 return thread_map__new_all_cpus();
304 return thread_map__new_by_tid_str(tid
);
307 size_t thread_map__fprintf(struct perf_thread_map
*threads
, FILE *fp
)
310 size_t printed
= fprintf(fp
, "%d thread%s: ",
311 threads
->nr
, threads
->nr
> 1 ? "s" : "");
312 for (i
= 0; i
< threads
->nr
; ++i
)
313 printed
+= fprintf(fp
, "%s%d", i
? ", " : "", perf_thread_map__pid(threads
, i
));
315 return printed
+ fprintf(fp
, "\n");
318 static int get_comm(char **comm
, pid_t pid
)
324 if (asprintf(&path
, "%s/%d/comm", procfs__mountpoint(), pid
) == -1)
327 err
= filename__read_str(path
, comm
, &size
);
330 * We're reading 16 bytes, while filename__read_str
331 * allocates data per BUFSIZ bytes, so we can safely
332 * mark the end of the string.
342 static void comm_init(struct perf_thread_map
*map
, int i
)
344 pid_t pid
= perf_thread_map__pid(map
, i
);
347 /* dummy pid comm initialization */
349 map
->map
[i
].comm
= strdup("dummy");
354 * The comm name is like extra bonus ;-),
355 * so just warn if we fail for any reason.
357 if (get_comm(&comm
, pid
))
358 pr_warning("Couldn't resolve comm name for pid %d\n", pid
);
360 map
->map
[i
].comm
= comm
;
363 void thread_map__read_comms(struct perf_thread_map
*threads
)
367 for (i
= 0; i
< threads
->nr
; ++i
)
368 comm_init(threads
, i
);
371 static void thread_map__copy_event(struct perf_thread_map
*threads
,
372 struct perf_record_thread_map
*event
)
376 threads
->nr
= (int) event
->nr
;
378 for (i
= 0; i
< event
->nr
; i
++) {
379 perf_thread_map__set_pid(threads
, i
, (pid_t
) event
->entries
[i
].pid
);
380 threads
->map
[i
].comm
= strndup(event
->entries
[i
].comm
, 16);
383 refcount_set(&threads
->refcnt
, 1);
386 struct perf_thread_map
*thread_map__new_event(struct perf_record_thread_map
*event
)
388 struct perf_thread_map
*threads
;
390 threads
= thread_map__alloc(event
->nr
);
392 thread_map__copy_event(threads
, event
);
397 bool thread_map__has(struct perf_thread_map
*threads
, pid_t pid
)
401 for (i
= 0; i
< threads
->nr
; ++i
) {
402 if (threads
->map
[i
].pid
== pid
)
409 int thread_map__remove(struct perf_thread_map
*threads
, int idx
)
416 if (idx
>= threads
->nr
)
420 * Free the 'idx' item and shift the rest up.
422 zfree(&threads
->map
[idx
].comm
);
424 for (i
= idx
; i
< threads
->nr
- 1; i
++)
425 threads
->map
[i
] = threads
->map
[i
+ 1];