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