perf thread_map: Introduce thread_map__reset function
[linux-2.6-block.git] / tools / perf / util / thread_map.c
CommitLineData
fd78260b 1#include <dirent.h>
0d37aa34
ACM
2#include <limits.h>
3#include <stdbool.h>
fd78260b
ACM
4#include <stdlib.h>
5#include <stdio.h>
0d37aa34
ACM
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
b52956c9
DA
9#include "strlist.h"
10#include <string.h>
186fbb74 11#include "asm/bug.h"
fd78260b 12#include "thread_map.h"
04662523 13#include "util.h"
fd78260b
ACM
14
15/* Skip "." and ".." directories */
16static int filter(const struct dirent *dir)
17{
18 if (dir->d_name[0] == '.')
19 return 0;
20 else
21 return 1;
22}
23
62eea464
JO
24static void thread_map__reset(struct thread_map *map, int start, int nr)
25{
26 size_t size = (nr - start) * sizeof(map->map[0]);
27
28 memset(&map->map[start], 0, size);
29}
30
9d7e8c3a
JO
31static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
32{
060664f3 33 size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
62eea464
JO
34 int start = map ? map->nr : 0;
35
36 map = realloc(map, size);
37 /*
38 * We only realloc to add more items, let's reset new items.
39 */
40 if (map)
41 thread_map__reset(map, start, nr);
9d7e8c3a 42
62eea464 43 return map;
9d7e8c3a
JO
44}
45
46#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
47
fd78260b
ACM
48struct thread_map *thread_map__new_by_pid(pid_t pid)
49{
50 struct thread_map *threads;
51 char name[256];
52 int items;
53 struct dirent **namelist = NULL;
54 int i;
55
56 sprintf(name, "/proc/%d/task", pid);
57 items = scandir(name, &namelist, filter, NULL);
58 if (items <= 0)
0d37aa34 59 return NULL;
fd78260b 60
9d7e8c3a 61 threads = thread_map__alloc(items);
fd78260b
ACM
62 if (threads != NULL) {
63 for (i = 0; i < items; i++)
e13798c7 64 thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
fd78260b 65 threads->nr = items;
186fbb74 66 atomic_set(&threads->refcnt, 1);
fd78260b
ACM
67 }
68
69 for (i=0; i<items; i++)
74cf249d 70 zfree(&namelist[i]);
fd78260b
ACM
71 free(namelist);
72
73 return threads;
74}
75
76struct thread_map *thread_map__new_by_tid(pid_t tid)
77{
9d7e8c3a 78 struct thread_map *threads = thread_map__alloc(1);
fd78260b
ACM
79
80 if (threads != NULL) {
e13798c7
JO
81 thread_map__set_pid(threads, 0, tid);
82 threads->nr = 1;
186fbb74 83 atomic_set(&threads->refcnt, 1);
fd78260b
ACM
84 }
85
86 return threads;
87}
88
0d37aa34
ACM
89struct thread_map *thread_map__new_by_uid(uid_t uid)
90{
91 DIR *proc;
92 int max_threads = 32, items, i;
93 char path[256];
94 struct dirent dirent, *next, **namelist = NULL;
9d7e8c3a
JO
95 struct thread_map *threads = thread_map__alloc(max_threads);
96
0d37aa34
ACM
97 if (threads == NULL)
98 goto out;
99
100 proc = opendir("/proc");
101 if (proc == NULL)
102 goto out_free_threads;
103
104 threads->nr = 0;
186fbb74 105 atomic_set(&threads->refcnt, 1);
0d37aa34
ACM
106
107 while (!readdir_r(proc, &dirent, &next) && next) {
108 char *end;
109 bool grow = false;
110 struct stat st;
111 pid_t pid = strtol(dirent.d_name, &end, 10);
112
113 if (*end) /* only interested in proper numerical dirents */
114 continue;
115
116 snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
117
118 if (stat(path, &st) != 0)
119 continue;
120
121 if (st.st_uid != uid)
122 continue;
123
124 snprintf(path, sizeof(path), "/proc/%d/task", pid);
125 items = scandir(path, &namelist, filter, NULL);
126 if (items <= 0)
127 goto out_free_closedir;
128
129 while (threads->nr + items >= max_threads) {
130 max_threads *= 2;
131 grow = true;
132 }
133
134 if (grow) {
135 struct thread_map *tmp;
136
137 tmp = realloc(threads, (sizeof(*threads) +
138 max_threads * sizeof(pid_t)));
139 if (tmp == NULL)
140 goto out_free_namelist;
141
142 threads = tmp;
143 }
144
e13798c7
JO
145 for (i = 0; i < items; i++) {
146 thread_map__set_pid(threads, threads->nr + i,
147 atoi(namelist[i]->d_name));
148 }
0d37aa34
ACM
149
150 for (i = 0; i < items; i++)
74cf249d 151 zfree(&namelist[i]);
0d37aa34
ACM
152 free(namelist);
153
154 threads->nr += items;
155 }
156
157out_closedir:
158 closedir(proc);
159out:
160 return threads;
161
162out_free_threads:
163 free(threads);
164 return NULL;
165
166out_free_namelist:
167 for (i = 0; i < items; i++)
74cf249d 168 zfree(&namelist[i]);
0d37aa34
ACM
169 free(namelist);
170
171out_free_closedir:
04662523 172 zfree(&threads);
0d37aa34
ACM
173 goto out_closedir;
174}
175
176struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
fd78260b
ACM
177{
178 if (pid != -1)
179 return thread_map__new_by_pid(pid);
0d37aa34
ACM
180
181 if (tid == -1 && uid != UINT_MAX)
182 return thread_map__new_by_uid(uid);
183
fd78260b
ACM
184 return thread_map__new_by_tid(tid);
185}
186
b52956c9
DA
187static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
188{
189 struct thread_map *threads = NULL, *nt;
190 char name[256];
191 int items, total_tasks = 0;
192 struct dirent **namelist = NULL;
193 int i, j = 0;
194 pid_t pid, prev_pid = INT_MAX;
195 char *end_ptr;
196 struct str_node *pos;
197 struct strlist *slist = strlist__new(false, pid_str);
198
199 if (!slist)
200 return NULL;
201
202 strlist__for_each(pos, slist) {
203 pid = strtol(pos->s, &end_ptr, 10);
204
205 if (pid == INT_MIN || pid == INT_MAX ||
206 (*end_ptr != '\0' && *end_ptr != ','))
207 goto out_free_threads;
208
209 if (pid == prev_pid)
210 continue;
211
212 sprintf(name, "/proc/%d/task", pid);
213 items = scandir(name, &namelist, filter, NULL);
214 if (items <= 0)
215 goto out_free_threads;
216
217 total_tasks += items;
9d7e8c3a 218 nt = thread_map__realloc(threads, total_tasks);
b52956c9 219 if (nt == NULL)
e8cdd947 220 goto out_free_namelist;
b52956c9
DA
221
222 threads = nt;
223
e8cdd947 224 for (i = 0; i < items; i++) {
e13798c7 225 thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
74cf249d 226 zfree(&namelist[i]);
e8cdd947
FBH
227 }
228 threads->nr = total_tasks;
b52956c9 229 free(namelist);
b52956c9
DA
230 }
231
232out:
233 strlist__delete(slist);
186fbb74
JO
234 if (threads)
235 atomic_set(&threads->refcnt, 1);
b52956c9
DA
236 return threads;
237
e8cdd947
FBH
238out_free_namelist:
239 for (i = 0; i < items; i++)
74cf249d 240 zfree(&namelist[i]);
e8cdd947
FBH
241 free(namelist);
242
b52956c9 243out_free_threads:
04662523 244 zfree(&threads);
b52956c9
DA
245 goto out;
246}
247
641556c9
ACM
248struct thread_map *thread_map__new_dummy(void)
249{
9d7e8c3a 250 struct thread_map *threads = thread_map__alloc(1);
641556c9
ACM
251
252 if (threads != NULL) {
e13798c7
JO
253 thread_map__set_pid(threads, 0, -1);
254 threads->nr = 1;
186fbb74 255 atomic_set(&threads->refcnt, 1);
641556c9
ACM
256 }
257 return threads;
258}
259
b52956c9
DA
260static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
261{
262 struct thread_map *threads = NULL, *nt;
263 int ntasks = 0;
264 pid_t tid, prev_tid = INT_MAX;
265 char *end_ptr;
266 struct str_node *pos;
267 struct strlist *slist;
268
269 /* perf-stat expects threads to be generated even if tid not given */
641556c9
ACM
270 if (!tid_str)
271 return thread_map__new_dummy();
b52956c9
DA
272
273 slist = strlist__new(false, tid_str);
274 if (!slist)
275 return NULL;
276
277 strlist__for_each(pos, slist) {
278 tid = strtol(pos->s, &end_ptr, 10);
279
280 if (tid == INT_MIN || tid == INT_MAX ||
281 (*end_ptr != '\0' && *end_ptr != ','))
282 goto out_free_threads;
283
284 if (tid == prev_tid)
285 continue;
286
287 ntasks++;
9d7e8c3a 288 nt = thread_map__realloc(threads, ntasks);
b52956c9
DA
289
290 if (nt == NULL)
291 goto out_free_threads;
292
293 threads = nt;
e13798c7
JO
294 thread_map__set_pid(threads, ntasks - 1, tid);
295 threads->nr = ntasks;
b52956c9
DA
296 }
297out:
186fbb74
JO
298 if (threads)
299 atomic_set(&threads->refcnt, 1);
b52956c9
DA
300 return threads;
301
302out_free_threads:
04662523 303 zfree(&threads);
b52956c9
DA
304 goto out;
305}
306
307struct thread_map *thread_map__new_str(const char *pid, const char *tid,
308 uid_t uid)
309{
310 if (pid)
311 return thread_map__new_by_pid_str(pid);
312
313 if (!tid && uid != UINT_MAX)
314 return thread_map__new_by_uid(uid);
315
316 return thread_map__new_by_tid_str(tid);
317}
318
186fbb74 319static void thread_map__delete(struct thread_map *threads)
fd78260b 320{
186fbb74
JO
321 if (threads) {
322 WARN_ONCE(atomic_read(&threads->refcnt) != 0,
323 "thread map refcnt unbalanced\n");
324 free(threads);
325 }
326}
327
328struct thread_map *thread_map__get(struct thread_map *map)
329{
330 if (map)
331 atomic_inc(&map->refcnt);
332 return map;
333}
334
335void thread_map__put(struct thread_map *map)
336{
337 if (map && atomic_dec_and_test(&map->refcnt))
338 thread_map__delete(map);
fd78260b 339}
9ae7d335
ACM
340
341size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
342{
343 int i;
344 size_t printed = fprintf(fp, "%d thread%s: ",
345 threads->nr, threads->nr > 1 ? "s" : "");
346 for (i = 0; i < threads->nr; ++i)
e13798c7 347 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
9ae7d335
ACM
348
349 return printed + fprintf(fp, "\n");
350}