Linux Perf
thread_map.c
Go to the documentation of this file.
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 
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 
84 {
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 
183 {
184  return __thread_map__new_all_cpus(UINT_MAX);
185 }
186 
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 
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 
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 
355 {
356  if (map)
357  refcount_inc(&map->refcnt);
358  return map;
359 }
360 
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 
424 {
425  int i;
426 
427  for (i = 0; i < threads->nr; ++i)
428  comm_init(threads, i);
429 }
430 
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 
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 }
struct thread_map * thread_map__new_by_tid_str(const char *tid_str)
Definition: thread_map.c:277
const char * comm
Definition: hists_common.c:16
static struct thread_map * thread_map__realloc(struct thread_map *map, int nr)
Definition: thread_map.c:38
int thread_map__remove(struct thread_map *threads, int idx)
Definition: thread_map.c:469
struct thread_map * thread_map__new_dummy(void)
Definition: thread_map.c:265
static struct thread_map * __thread_map__new_all_cpus(uid_t uid)
Definition: thread_map.c:96
struct thread_map_event_entry entries[]
Definition: event.h:561
static int filter(const struct dirent *dir)
Definition: thread_map.c:22
size_t size
Definition: evsel.c:60
static char * dir
Definition: attr.c:39
struct thread_map * thread_map__new_str(const char *pid, const char *tid, uid_t uid, bool all_threads)
Definition: thread_map.c:326
struct thread_map * thread_map__get(struct thread_map *map)
Definition: thread_map.c:354
int int err
Definition: 5sec.c:44
static void thread_map__set_pid(struct thread_map *map, int thread, pid_t pid)
Definition: thread_map.h:52
char comm[16]
Definition: event.h:555
void thread_map__read_comms(struct thread_map *threads)
Definition: thread_map.c:423
struct thread_map * thread_map__new_by_tid(pid_t tid)
Definition: thread_map.c:83
struct thread_map * thread_map__new_by_pid(pid_t pid)
Definition: thread_map.c:55
x86 movsq based memset() in arch/x86/lib/memset_64.S") MEMSET_FN(memset_erms
Definition: comm.h:11
char * rtrim(char *s)
Definition: string.c:345
const char * name
static void thread_map__copy_event(struct thread_map *threads, struct thread_map_event *event)
Definition: thread_map.c:431
bool dont_dupstr
Definition: strlist.h:27
static void thread_map__reset(struct thread_map *map, int start, int nr)
Definition: thread_map.c:30
static int get_comm(char **comm, pid_t pid)
Definition: thread_map.c:378
struct thread_map * thread_map__new_event(struct thread_map_event *event)
Definition: thread_map.c:446
bool thread_map__has(struct thread_map *threads, pid_t pid)
Definition: thread_map.c:457
#define strlist__for_each_entry(pos, slist)
Definition: strlist.h:77
static char * thread_map__comm(struct thread_map *map, int thread)
Definition: thread_map.h:57
struct thread_map * thread_map__new_all_cpus(void)
Definition: thread_map.c:182
u64 pid
Definition: event.h:554
#define event
struct thread_map_data map[]
Definition: thread_map.h:18
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
Definition: thread_map.c:367
static void thread_map__delete(struct thread_map *threads)
Definition: thread_map.c:341
#define zfree(ptr)
Definition: util.h:25
refcount_t refcnt
Definition: thread_map.h:15
static struct thread_data threads[THREADS]
u32 pid
Definition: hists_common.c:15
u64 start
Definition: hists_common.c:25
Definition: jevents.c:228
void strlist__delete(struct strlist *slist)
Definition: strlist.c:193
static void comm_init(struct thread_map *map, int i)
Definition: thread_map.c:402
void free(void *)
static pid_t thread_map__pid(struct thread_map *map, int thread)
Definition: thread_map.h:46
static struct thread_map * thread_map__new_by_pid_str(const char *pid_str)
Definition: thread_map.c:203
#define thread_map__alloc(__nr)
Definition: thread_map.c:53
void thread_map__put(struct thread_map *map)
Definition: thread_map.c:361
int err_thread
Definition: thread_map.h:17
struct thread_map * thread_map__new_by_uid(uid_t uid)
Definition: thread_map.c:187
struct thread_map * thread_map__new(pid_t pid, pid_t tid, uid_t uid)
Definition: thread_map.c:192
#define pr_warning(fmt,...)
Definition: debug.h:25
const char * s
Definition: strlist.h:12
struct strlist * strlist__new(const char *list, const struct strlist_config *config)
Definition: strlist.c:160