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"
22 /* Skip "." and ".." directories */
23 static int filter(const struct dirent
*dir
)
25 if (dir
->d_name
[0] == '.')
31 #define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr)
33 struct perf_thread_map
*thread_map__new_by_pid(pid_t pid
)
35 struct perf_thread_map
*threads
;
38 struct dirent
**namelist
= NULL
;
41 sprintf(name
, "/proc/%d/task", pid
);
42 items
= scandir(name
, &namelist
, filter
, NULL
);
46 threads
= thread_map__alloc(items
);
47 if (threads
!= NULL
) {
48 for (i
= 0; i
< items
; i
++)
49 perf_thread_map__set_pid(threads
, i
, atoi(namelist
[i
]->d_name
));
51 refcount_set(&threads
->refcnt
, 1);
54 for (i
=0; i
<items
; i
++)
61 struct perf_thread_map
*thread_map__new_by_tid(pid_t tid
)
63 struct perf_thread_map
*threads
= thread_map__alloc(1);
65 if (threads
!= NULL
) {
66 perf_thread_map__set_pid(threads
, 0, tid
);
68 refcount_set(&threads
->refcnt
, 1);
74 static struct perf_thread_map
*__thread_map__new_all_cpus(uid_t uid
)
77 int max_threads
= 32, items
, i
;
78 char path
[NAME_MAX
+ 1 + 6];
79 struct dirent
*dirent
, **namelist
= NULL
;
80 struct perf_thread_map
*threads
= thread_map__alloc(max_threads
);
85 proc
= opendir("/proc");
87 goto out_free_threads
;
90 refcount_set(&threads
->refcnt
, 1);
92 while ((dirent
= readdir(proc
)) != NULL
) {
95 pid_t pid
= strtol(dirent
->d_name
, &end
, 10);
97 if (*end
) /* only interested in proper numerical dirents */
100 snprintf(path
, sizeof(path
), "/proc/%s", dirent
->d_name
);
102 if (uid
!= UINT_MAX
) {
105 if (stat(path
, &st
) != 0 || st
.st_uid
!= uid
)
109 snprintf(path
, sizeof(path
), "/proc/%d/task", pid
);
110 items
= scandir(path
, &namelist
, filter
, NULL
);
112 goto out_free_closedir
;
114 while (threads
->nr
+ items
>= max_threads
) {
120 struct perf_thread_map
*tmp
;
122 tmp
= perf_thread_map__realloc(threads
, max_threads
);
124 goto out_free_namelist
;
129 for (i
= 0; i
< items
; i
++) {
130 perf_thread_map__set_pid(threads
, threads
->nr
+ i
,
131 atoi(namelist
[i
]->d_name
));
134 for (i
= 0; i
< items
; i
++)
138 threads
->nr
+= items
;
151 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
;
283 refcount_set(&threads
->refcnt
, 1);
288 strlist__delete(slist
);
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];