GNU Linux-libre 4.19.245-gnu1
[releases.git] / tools / perf / util / thread_map.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <limits.h>
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include "string2.h"
12 #include "strlist.h"
13 #include <string.h>
14 #include <api/fs/fs.h>
15 #include "asm/bug.h"
16 #include "thread_map.h"
17 #include "util.h"
18 #include "debug.h"
19 #include "event.h"
20
21 /* Skip "." and ".." directories */
22 static int filter(const struct dirent *dir)
23 {
24         if (dir->d_name[0] == '.')
25                 return 0;
26         else
27                 return 1;
28 }
29
30 static void thread_map__reset(struct thread_map *map, int start, int nr)
31 {
32         size_t size = (nr - start) * sizeof(map->map[0]);
33
34         memset(&map->map[start], 0, size);
35         map->err_thread = -1;
36 }
37
38 static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
39 {
40         size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
41         int start = map ? map->nr : 0;
42
43         map = realloc(map, size);
44         /*
45          * We only realloc to add more items, let's reset new items.
46          */
47         if (map)
48                 thread_map__reset(map, start, nr);
49
50         return map;
51 }
52
53 #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
54
55 struct thread_map *thread_map__new_by_pid(pid_t pid)
56 {
57         struct thread_map *threads;
58         char name[256];
59         int items;
60         struct dirent **namelist = NULL;
61         int i;
62
63         sprintf(name, "/proc/%d/task", pid);
64         items = scandir(name, &namelist, filter, NULL);
65         if (items <= 0)
66                 return NULL;
67
68         threads = thread_map__alloc(items);
69         if (threads != NULL) {
70                 for (i = 0; i < items; i++)
71                         thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
72                 threads->nr = items;
73                 refcount_set(&threads->refcnt, 1);
74         }
75
76         for (i=0; i<items; i++)
77                 zfree(&namelist[i]);
78         free(namelist);
79
80         return threads;
81 }
82
83 struct thread_map *thread_map__new_by_tid(pid_t tid)
84 {
85         struct thread_map *threads = thread_map__alloc(1);
86
87         if (threads != NULL) {
88                 thread_map__set_pid(threads, 0, tid);
89                 threads->nr = 1;
90                 refcount_set(&threads->refcnt, 1);
91         }
92
93         return threads;
94 }
95
96 static struct thread_map *__thread_map__new_all_cpus(uid_t uid)
97 {
98         DIR *proc;
99         int max_threads = 32, items, i;
100         char path[NAME_MAX + 1 + 6];
101         struct dirent *dirent, **namelist = NULL;
102         struct thread_map *threads = thread_map__alloc(max_threads);
103
104         if (threads == NULL)
105                 goto out;
106
107         proc = opendir("/proc");
108         if (proc == NULL)
109                 goto out_free_threads;
110
111         threads->nr = 0;
112         refcount_set(&threads->refcnt, 1);
113
114         while ((dirent = readdir(proc)) != NULL) {
115                 char *end;
116                 bool grow = false;
117                 pid_t pid = strtol(dirent->d_name, &end, 10);
118
119                 if (*end) /* only interested in proper numerical dirents */
120                         continue;
121
122                 snprintf(path, sizeof(path), "/proc/%s", dirent->d_name);
123
124                 if (uid != UINT_MAX) {
125                         struct stat st;
126
127                         if (stat(path, &st) != 0 || st.st_uid != uid)
128                                 continue;
129                 }
130
131                 snprintf(path, sizeof(path), "/proc/%d/task", pid);
132                 items = scandir(path, &namelist, filter, NULL);
133                 if (items <= 0)
134                         goto out_free_closedir;
135
136                 while (threads->nr + items >= max_threads) {
137                         max_threads *= 2;
138                         grow = true;
139                 }
140
141                 if (grow) {
142                         struct thread_map *tmp;
143
144                         tmp = thread_map__realloc(threads, max_threads);
145                         if (tmp == NULL)
146                                 goto out_free_namelist;
147
148                         threads = tmp;
149                 }
150
151                 for (i = 0; i < items; i++) {
152                         thread_map__set_pid(threads, threads->nr + i,
153                                             atoi(namelist[i]->d_name));
154                 }
155
156                 for (i = 0; i < items; i++)
157                         zfree(&namelist[i]);
158                 free(namelist);
159
160                 threads->nr += items;
161         }
162
163 out_closedir:
164         closedir(proc);
165 out:
166         return threads;
167
168 out_free_threads:
169         free(threads);
170         return NULL;
171
172 out_free_namelist:
173         for (i = 0; i < items; i++)
174                 zfree(&namelist[i]);
175         free(namelist);
176
177 out_free_closedir:
178         zfree(&threads);
179         goto out_closedir;
180 }
181
182 struct thread_map *thread_map__new_all_cpus(void)
183 {
184         return __thread_map__new_all_cpus(UINT_MAX);
185 }
186
187 struct thread_map *thread_map__new_by_uid(uid_t uid)
188 {
189         return __thread_map__new_all_cpus(uid);
190 }
191
192 struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
193 {
194         if (pid != -1)
195                 return thread_map__new_by_pid(pid);
196
197         if (tid == -1 && uid != UINT_MAX)
198                 return thread_map__new_by_uid(uid);
199
200         return thread_map__new_by_tid(tid);
201 }
202
203 static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
204 {
205         struct thread_map *threads = NULL, *nt;
206         char name[256];
207         int items, total_tasks = 0;
208         struct dirent **namelist = NULL;
209         int i, j = 0;
210         pid_t pid, prev_pid = INT_MAX;
211         char *end_ptr;
212         struct str_node *pos;
213         struct strlist_config slist_config = { .dont_dupstr = true, };
214         struct strlist *slist = strlist__new(pid_str, &slist_config);
215
216         if (!slist)
217                 return NULL;
218
219         strlist__for_each_entry(pos, slist) {
220                 pid = strtol(pos->s, &end_ptr, 10);
221
222                 if (pid == INT_MIN || pid == INT_MAX ||
223                     (*end_ptr != '\0' && *end_ptr != ','))
224                         goto out_free_threads;
225
226                 if (pid == prev_pid)
227                         continue;
228
229                 sprintf(name, "/proc/%d/task", pid);
230                 items = scandir(name, &namelist, filter, NULL);
231                 if (items <= 0)
232                         goto out_free_threads;
233
234                 total_tasks += items;
235                 nt = thread_map__realloc(threads, total_tasks);
236                 if (nt == NULL)
237                         goto out_free_namelist;
238
239                 threads = nt;
240
241                 for (i = 0; i < items; i++) {
242                         thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
243                         zfree(&namelist[i]);
244                 }
245                 threads->nr = total_tasks;
246                 free(namelist);
247         }
248
249 out:
250         strlist__delete(slist);
251         if (threads)
252                 refcount_set(&threads->refcnt, 1);
253         return threads;
254
255 out_free_namelist:
256         for (i = 0; i < items; i++)
257                 zfree(&namelist[i]);
258         free(namelist);
259
260 out_free_threads:
261         zfree(&threads);
262         goto out;
263 }
264
265 struct thread_map *thread_map__new_dummy(void)
266 {
267         struct thread_map *threads = thread_map__alloc(1);
268
269         if (threads != NULL) {
270                 thread_map__set_pid(threads, 0, -1);
271                 threads->nr = 1;
272                 refcount_set(&threads->refcnt, 1);
273         }
274         return threads;
275 }
276
277 struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
278 {
279         struct thread_map *threads = NULL, *nt;
280         int ntasks = 0;
281         pid_t tid, prev_tid = INT_MAX;
282         char *end_ptr;
283         struct str_node *pos;
284         struct strlist_config slist_config = { .dont_dupstr = true, };
285         struct strlist *slist;
286
287         /* perf-stat expects threads to be generated even if tid not given */
288         if (!tid_str)
289                 return thread_map__new_dummy();
290
291         slist = strlist__new(tid_str, &slist_config);
292         if (!slist)
293                 return NULL;
294
295         strlist__for_each_entry(pos, slist) {
296                 tid = strtol(pos->s, &end_ptr, 10);
297
298                 if (tid == INT_MIN || tid == INT_MAX ||
299                     (*end_ptr != '\0' && *end_ptr != ','))
300                         goto out_free_threads;
301
302                 if (tid == prev_tid)
303                         continue;
304
305                 ntasks++;
306                 nt = thread_map__realloc(threads, ntasks);
307
308                 if (nt == NULL)
309                         goto out_free_threads;
310
311                 threads = nt;
312                 thread_map__set_pid(threads, ntasks - 1, tid);
313                 threads->nr = ntasks;
314         }
315 out:
316         if (threads)
317                 refcount_set(&threads->refcnt, 1);
318         return threads;
319
320 out_free_threads:
321         zfree(&threads);
322         strlist__delete(slist);
323         goto out;
324 }
325
326 struct thread_map *thread_map__new_str(const char *pid, const char *tid,
327                                        uid_t uid, bool all_threads)
328 {
329         if (pid)
330                 return thread_map__new_by_pid_str(pid);
331
332         if (!tid && uid != UINT_MAX)
333                 return thread_map__new_by_uid(uid);
334
335         if (all_threads)
336                 return thread_map__new_all_cpus();
337
338         return thread_map__new_by_tid_str(tid);
339 }
340
341 static void thread_map__delete(struct thread_map *threads)
342 {
343         if (threads) {
344                 int i;
345
346                 WARN_ONCE(refcount_read(&threads->refcnt) != 0,
347                           "thread map refcnt unbalanced\n");
348                 for (i = 0; i < threads->nr; i++)
349                         free(thread_map__comm(threads, i));
350                 free(threads);
351         }
352 }
353
354 struct thread_map *thread_map__get(struct thread_map *map)
355 {
356         if (map)
357                 refcount_inc(&map->refcnt);
358         return map;
359 }
360
361 void thread_map__put(struct thread_map *map)
362 {
363         if (map && refcount_dec_and_test(&map->refcnt))
364                 thread_map__delete(map);
365 }
366
367 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
368 {
369         int i;
370         size_t printed = fprintf(fp, "%d thread%s: ",
371                                  threads->nr, threads->nr > 1 ? "s" : "");
372         for (i = 0; i < threads->nr; ++i)
373                 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
374
375         return printed + fprintf(fp, "\n");
376 }
377
378 static int get_comm(char **comm, pid_t pid)
379 {
380         char *path;
381         size_t size;
382         int err;
383
384         if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1)
385                 return -ENOMEM;
386
387         err = filename__read_str(path, comm, &size);
388         if (!err) {
389                 /*
390                  * We're reading 16 bytes, while filename__read_str
391                  * allocates data per BUFSIZ bytes, so we can safely
392                  * mark the end of the string.
393                  */
394                 (*comm)[size] = 0;
395                 rtrim(*comm);
396         }
397
398         free(path);
399         return err;
400 }
401
402 static void comm_init(struct thread_map *map, int i)
403 {
404         pid_t pid = thread_map__pid(map, i);
405         char *comm = NULL;
406
407         /* dummy pid comm initialization */
408         if (pid == -1) {
409                 map->map[i].comm = strdup("dummy");
410                 return;
411         }
412
413         /*
414          * The comm name is like extra bonus ;-),
415          * so just warn if we fail for any reason.
416          */
417         if (get_comm(&comm, pid))
418                 pr_warning("Couldn't resolve comm name for pid %d\n", pid);
419
420         map->map[i].comm = comm;
421 }
422
423 void thread_map__read_comms(struct thread_map *threads)
424 {
425         int i;
426
427         for (i = 0; i < threads->nr; ++i)
428                 comm_init(threads, i);
429 }
430
431 static void thread_map__copy_event(struct thread_map *threads,
432                                    struct thread_map_event *event)
433 {
434         unsigned i;
435
436         threads->nr = (int) event->nr;
437
438         for (i = 0; i < event->nr; i++) {
439                 thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid);
440                 threads->map[i].comm = strndup(event->entries[i].comm, 16);
441         }
442
443         refcount_set(&threads->refcnt, 1);
444 }
445
446 struct thread_map *thread_map__new_event(struct thread_map_event *event)
447 {
448         struct thread_map *threads;
449
450         threads = thread_map__alloc(event->nr);
451         if (threads)
452                 thread_map__copy_event(threads, event);
453
454         return threads;
455 }
456
457 bool thread_map__has(struct thread_map *threads, pid_t pid)
458 {
459         int i;
460
461         for (i = 0; i < threads->nr; ++i) {
462                 if (threads->map[i].pid == pid)
463                         return true;
464         }
465
466         return false;
467 }
468
469 int thread_map__remove(struct thread_map *threads, int idx)
470 {
471         int i;
472
473         if (threads->nr < 1)
474                 return -EINVAL;
475
476         if (idx >= threads->nr)
477                 return -EINVAL;
478
479         /*
480          * Free the 'idx' item and shift the rest up.
481          */
482         free(threads->map[idx].comm);
483
484         for (i = idx; i < threads->nr - 1; i++)
485                 threads->map[i] = threads->map[i + 1];
486
487         threads->nr--;
488         return 0;
489 }