blob: 920136dd8c2ec19d6c77e1f037d9582460a222d4 [file] [log] [blame]
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -02001#include <dirent.h>
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02002#include <limits.h>
3#include <stdbool.h>
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -02004#include <stdlib.h>
5#include <stdio.h>
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02006#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
David Ahernb52956c2012-02-08 09:32:52 -07009#include "strlist.h"
10#include <string.h>
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020011#include "thread_map.h"
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -030012#include "util.h"
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020013
14/* Skip "." and ".." directories */
15static int filter(const struct dirent *dir)
16{
17 if (dir->d_name[0] == '.')
18 return 0;
19 else
20 return 1;
21}
22
Jiri Olsa9d7e8c32015-06-14 10:19:17 +020023static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
24{
Arnaldo Carvalho de Melo060664f2015-06-25 14:48:49 -030025 size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
Jiri Olsa9d7e8c32015-06-14 10:19:17 +020026
27 return realloc(map, size);
28}
29
30#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
31
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020032struct thread_map *thread_map__new_by_pid(pid_t pid)
33{
34 struct thread_map *threads;
35 char name[256];
36 int items;
37 struct dirent **namelist = NULL;
38 int i;
39
40 sprintf(name, "/proc/%d/task", pid);
41 items = scandir(name, &namelist, filter, NULL);
42 if (items <= 0)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -020043 return NULL;
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020044
Jiri Olsa9d7e8c32015-06-14 10:19:17 +020045 threads = thread_map__alloc(items);
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020046 if (threads != NULL) {
47 for (i = 0; i < items; i++)
Jiri Olsae13798c2015-06-23 00:36:02 +020048 thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020049 threads->nr = items;
50 }
51
52 for (i=0; i<items; i++)
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -030053 zfree(&namelist[i]);
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020054 free(namelist);
55
56 return threads;
57}
58
59struct thread_map *thread_map__new_by_tid(pid_t tid)
60{
Jiri Olsa9d7e8c32015-06-14 10:19:17 +020061 struct thread_map *threads = thread_map__alloc(1);
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020062
63 if (threads != NULL) {
Jiri Olsae13798c2015-06-23 00:36:02 +020064 thread_map__set_pid(threads, 0, tid);
65 threads->nr = 1;
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -020066 }
67
68 return threads;
69}
70
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -020071struct thread_map *thread_map__new_by_uid(uid_t uid)
72{
73 DIR *proc;
74 int max_threads = 32, items, i;
75 char path[256];
76 struct dirent dirent, *next, **namelist = NULL;
Jiri Olsa9d7e8c32015-06-14 10:19:17 +020077 struct thread_map *threads = thread_map__alloc(max_threads);
78
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -020079 if (threads == NULL)
80 goto out;
81
82 proc = opendir("/proc");
83 if (proc == NULL)
84 goto out_free_threads;
85
86 threads->nr = 0;
87
88 while (!readdir_r(proc, &dirent, &next) && next) {
89 char *end;
90 bool grow = false;
91 struct stat st;
92 pid_t pid = strtol(dirent.d_name, &end, 10);
93
94 if (*end) /* only interested in proper numerical dirents */
95 continue;
96
97 snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
98
99 if (stat(path, &st) != 0)
100 continue;
101
102 if (st.st_uid != uid)
103 continue;
104
105 snprintf(path, sizeof(path), "/proc/%d/task", pid);
106 items = scandir(path, &namelist, filter, NULL);
107 if (items <= 0)
108 goto out_free_closedir;
109
110 while (threads->nr + items >= max_threads) {
111 max_threads *= 2;
112 grow = true;
113 }
114
115 if (grow) {
116 struct thread_map *tmp;
117
118 tmp = realloc(threads, (sizeof(*threads) +
119 max_threads * sizeof(pid_t)));
120 if (tmp == NULL)
121 goto out_free_namelist;
122
123 threads = tmp;
124 }
125
Jiri Olsae13798c2015-06-23 00:36:02 +0200126 for (i = 0; i < items; i++) {
127 thread_map__set_pid(threads, threads->nr + i,
128 atoi(namelist[i]->d_name));
129 }
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -0200130
131 for (i = 0; i < items; i++)
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300132 zfree(&namelist[i]);
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -0200133 free(namelist);
134
135 threads->nr += items;
136 }
137
138out_closedir:
139 closedir(proc);
140out:
141 return threads;
142
143out_free_threads:
144 free(threads);
145 return NULL;
146
147out_free_namelist:
148 for (i = 0; i < items; i++)
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300149 zfree(&namelist[i]);
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -0200150 free(namelist);
151
152out_free_closedir:
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -0300153 zfree(&threads);
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -0200154 goto out_closedir;
155}
156
157struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -0200158{
159 if (pid != -1)
160 return thread_map__new_by_pid(pid);
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -0200161
162 if (tid == -1 && uid != UINT_MAX)
163 return thread_map__new_by_uid(uid);
164
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -0200165 return thread_map__new_by_tid(tid);
166}
167
David Ahernb52956c2012-02-08 09:32:52 -0700168static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
169{
170 struct thread_map *threads = NULL, *nt;
171 char name[256];
172 int items, total_tasks = 0;
173 struct dirent **namelist = NULL;
174 int i, j = 0;
175 pid_t pid, prev_pid = INT_MAX;
176 char *end_ptr;
177 struct str_node *pos;
178 struct strlist *slist = strlist__new(false, pid_str);
179
180 if (!slist)
181 return NULL;
182
183 strlist__for_each(pos, slist) {
184 pid = strtol(pos->s, &end_ptr, 10);
185
186 if (pid == INT_MIN || pid == INT_MAX ||
187 (*end_ptr != '\0' && *end_ptr != ','))
188 goto out_free_threads;
189
190 if (pid == prev_pid)
191 continue;
192
193 sprintf(name, "/proc/%d/task", pid);
194 items = scandir(name, &namelist, filter, NULL);
195 if (items <= 0)
196 goto out_free_threads;
197
198 total_tasks += items;
Jiri Olsa9d7e8c32015-06-14 10:19:17 +0200199 nt = thread_map__realloc(threads, total_tasks);
David Ahernb52956c2012-02-08 09:32:52 -0700200 if (nt == NULL)
Franck Bui-Huue8cdd942012-05-25 15:21:49 +0200201 goto out_free_namelist;
David Ahernb52956c2012-02-08 09:32:52 -0700202
203 threads = nt;
204
Franck Bui-Huue8cdd942012-05-25 15:21:49 +0200205 for (i = 0; i < items; i++) {
Jiri Olsae13798c2015-06-23 00:36:02 +0200206 thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300207 zfree(&namelist[i]);
Franck Bui-Huue8cdd942012-05-25 15:21:49 +0200208 }
209 threads->nr = total_tasks;
David Ahernb52956c2012-02-08 09:32:52 -0700210 free(namelist);
David Ahernb52956c2012-02-08 09:32:52 -0700211 }
212
213out:
214 strlist__delete(slist);
215 return threads;
216
Franck Bui-Huue8cdd942012-05-25 15:21:49 +0200217out_free_namelist:
218 for (i = 0; i < items; i++)
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300219 zfree(&namelist[i]);
Franck Bui-Huue8cdd942012-05-25 15:21:49 +0200220 free(namelist);
221
David Ahernb52956c2012-02-08 09:32:52 -0700222out_free_threads:
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -0300223 zfree(&threads);
David Ahernb52956c2012-02-08 09:32:52 -0700224 goto out;
225}
226
Arnaldo Carvalho de Melo641556c2014-10-10 12:03:46 -0300227struct thread_map *thread_map__new_dummy(void)
228{
Jiri Olsa9d7e8c32015-06-14 10:19:17 +0200229 struct thread_map *threads = thread_map__alloc(1);
Arnaldo Carvalho de Melo641556c2014-10-10 12:03:46 -0300230
231 if (threads != NULL) {
Jiri Olsae13798c2015-06-23 00:36:02 +0200232 thread_map__set_pid(threads, 0, -1);
233 threads->nr = 1;
Arnaldo Carvalho de Melo641556c2014-10-10 12:03:46 -0300234 }
235 return threads;
236}
237
David Ahernb52956c2012-02-08 09:32:52 -0700238static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
239{
240 struct thread_map *threads = NULL, *nt;
241 int ntasks = 0;
242 pid_t tid, prev_tid = INT_MAX;
243 char *end_ptr;
244 struct str_node *pos;
245 struct strlist *slist;
246
247 /* perf-stat expects threads to be generated even if tid not given */
Arnaldo Carvalho de Melo641556c2014-10-10 12:03:46 -0300248 if (!tid_str)
249 return thread_map__new_dummy();
David Ahernb52956c2012-02-08 09:32:52 -0700250
251 slist = strlist__new(false, tid_str);
252 if (!slist)
253 return NULL;
254
255 strlist__for_each(pos, slist) {
256 tid = strtol(pos->s, &end_ptr, 10);
257
258 if (tid == INT_MIN || tid == INT_MAX ||
259 (*end_ptr != '\0' && *end_ptr != ','))
260 goto out_free_threads;
261
262 if (tid == prev_tid)
263 continue;
264
265 ntasks++;
Jiri Olsa9d7e8c32015-06-14 10:19:17 +0200266 nt = thread_map__realloc(threads, ntasks);
David Ahernb52956c2012-02-08 09:32:52 -0700267
268 if (nt == NULL)
269 goto out_free_threads;
270
271 threads = nt;
Jiri Olsae13798c2015-06-23 00:36:02 +0200272 thread_map__set_pid(threads, ntasks - 1, tid);
273 threads->nr = ntasks;
David Ahernb52956c2012-02-08 09:32:52 -0700274 }
275out:
276 return threads;
277
278out_free_threads:
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -0300279 zfree(&threads);
David Ahernb52956c2012-02-08 09:32:52 -0700280 goto out;
281}
282
283struct thread_map *thread_map__new_str(const char *pid, const char *tid,
284 uid_t uid)
285{
286 if (pid)
287 return thread_map__new_by_pid_str(pid);
288
289 if (!tid && uid != UINT_MAX)
290 return thread_map__new_by_uid(uid);
291
292 return thread_map__new_by_tid_str(tid);
293}
294
Arnaldo Carvalho de Melofd782602011-01-18 15:15:24 -0200295void thread_map__delete(struct thread_map *threads)
296{
297 free(threads);
298}
Arnaldo Carvalho de Melo9ae7d332012-01-19 14:07:23 -0200299
300size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
301{
302 int i;
303 size_t printed = fprintf(fp, "%d thread%s: ",
304 threads->nr, threads->nr > 1 ? "s" : "");
305 for (i = 0; i < threads->nr; ++i)
Jiri Olsae13798c2015-06-23 00:36:02 +0200306 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
Arnaldo Carvalho de Melo9ae7d332012-01-19 14:07:23 -0200307
308 return printed + fprintf(fp, "\n");
309}