Merge branch 'for-4.2/sg' of git://git.kernel.dk/linux-block
[firefly-linux-kernel-4.4.55.git] / tools / perf / util / thread_map.c
1 #include <dirent.h>
2 #include <limits.h>
3 #include <stdbool.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include "strlist.h"
10 #include <string.h>
11 #include "thread_map.h"
12 #include "util.h"
13
14 /* Skip "." and ".." directories */
15 static int filter(const struct dirent *dir)
16 {
17         if (dir->d_name[0] == '.')
18                 return 0;
19         else
20                 return 1;
21 }
22
23 static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
24 {
25         size_t size = sizeof(*map) + sizeof(pid_t) * nr;
26
27         return realloc(map, size);
28 }
29
30 #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
31
32 struct 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)
43                 return NULL;
44
45         threads = thread_map__alloc(items);
46         if (threads != NULL) {
47                 for (i = 0; i < items; i++)
48                         threads->map[i] = atoi(namelist[i]->d_name);
49                 threads->nr = items;
50         }
51
52         for (i=0; i<items; i++)
53                 zfree(&namelist[i]);
54         free(namelist);
55
56         return threads;
57 }
58
59 struct thread_map *thread_map__new_by_tid(pid_t tid)
60 {
61         struct thread_map *threads = thread_map__alloc(1);
62
63         if (threads != NULL) {
64                 threads->map[0] = tid;
65                 threads->nr     = 1;
66         }
67
68         return threads;
69 }
70
71 struct 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;
77         struct thread_map *threads = thread_map__alloc(max_threads);
78
79         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
126                 for (i = 0; i < items; i++)
127                         threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
128
129                 for (i = 0; i < items; i++)
130                         zfree(&namelist[i]);
131                 free(namelist);
132
133                 threads->nr += items;
134         }
135
136 out_closedir:
137         closedir(proc);
138 out:
139         return threads;
140
141 out_free_threads:
142         free(threads);
143         return NULL;
144
145 out_free_namelist:
146         for (i = 0; i < items; i++)
147                 zfree(&namelist[i]);
148         free(namelist);
149
150 out_free_closedir:
151         zfree(&threads);
152         goto out_closedir;
153 }
154
155 struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
156 {
157         if (pid != -1)
158                 return thread_map__new_by_pid(pid);
159
160         if (tid == -1 && uid != UINT_MAX)
161                 return thread_map__new_by_uid(uid);
162
163         return thread_map__new_by_tid(tid);
164 }
165
166 static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
167 {
168         struct thread_map *threads = NULL, *nt;
169         char name[256];
170         int items, total_tasks = 0;
171         struct dirent **namelist = NULL;
172         int i, j = 0;
173         pid_t pid, prev_pid = INT_MAX;
174         char *end_ptr;
175         struct str_node *pos;
176         struct strlist *slist = strlist__new(false, pid_str);
177
178         if (!slist)
179                 return NULL;
180
181         strlist__for_each(pos, slist) {
182                 pid = strtol(pos->s, &end_ptr, 10);
183
184                 if (pid == INT_MIN || pid == INT_MAX ||
185                     (*end_ptr != '\0' && *end_ptr != ','))
186                         goto out_free_threads;
187
188                 if (pid == prev_pid)
189                         continue;
190
191                 sprintf(name, "/proc/%d/task", pid);
192                 items = scandir(name, &namelist, filter, NULL);
193                 if (items <= 0)
194                         goto out_free_threads;
195
196                 total_tasks += items;
197                 nt = thread_map__realloc(threads, total_tasks);
198                 if (nt == NULL)
199                         goto out_free_namelist;
200
201                 threads = nt;
202
203                 for (i = 0; i < items; i++) {
204                         threads->map[j++] = atoi(namelist[i]->d_name);
205                         zfree(&namelist[i]);
206                 }
207                 threads->nr = total_tasks;
208                 free(namelist);
209         }
210
211 out:
212         strlist__delete(slist);
213         return threads;
214
215 out_free_namelist:
216         for (i = 0; i < items; i++)
217                 zfree(&namelist[i]);
218         free(namelist);
219
220 out_free_threads:
221         zfree(&threads);
222         goto out;
223 }
224
225 struct thread_map *thread_map__new_dummy(void)
226 {
227         struct thread_map *threads = thread_map__alloc(1);
228
229         if (threads != NULL) {
230                 threads->map[0] = -1;
231                 threads->nr     = 1;
232         }
233         return threads;
234 }
235
236 static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
237 {
238         struct thread_map *threads = NULL, *nt;
239         int ntasks = 0;
240         pid_t tid, prev_tid = INT_MAX;
241         char *end_ptr;
242         struct str_node *pos;
243         struct strlist *slist;
244
245         /* perf-stat expects threads to be generated even if tid not given */
246         if (!tid_str)
247                 return thread_map__new_dummy();
248
249         slist = strlist__new(false, tid_str);
250         if (!slist)
251                 return NULL;
252
253         strlist__for_each(pos, slist) {
254                 tid = strtol(pos->s, &end_ptr, 10);
255
256                 if (tid == INT_MIN || tid == INT_MAX ||
257                     (*end_ptr != '\0' && *end_ptr != ','))
258                         goto out_free_threads;
259
260                 if (tid == prev_tid)
261                         continue;
262
263                 ntasks++;
264                 nt = thread_map__realloc(threads, ntasks);
265
266                 if (nt == NULL)
267                         goto out_free_threads;
268
269                 threads = nt;
270                 threads->map[ntasks - 1] = tid;
271                 threads->nr              = ntasks;
272         }
273 out:
274         return threads;
275
276 out_free_threads:
277         zfree(&threads);
278         goto out;
279 }
280
281 struct thread_map *thread_map__new_str(const char *pid, const char *tid,
282                                        uid_t uid)
283 {
284         if (pid)
285                 return thread_map__new_by_pid_str(pid);
286
287         if (!tid && uid != UINT_MAX)
288                 return thread_map__new_by_uid(uid);
289
290         return thread_map__new_by_tid_str(tid);
291 }
292
293 void thread_map__delete(struct thread_map *threads)
294 {
295         free(threads);
296 }
297
298 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
299 {
300         int i;
301         size_t printed = fprintf(fp, "%d thread%s: ",
302                                  threads->nr, threads->nr > 1 ? "s" : "");
303         for (i = 0; i < threads->nr; ++i)
304                 printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
305
306         return printed + fprintf(fp, "\n");
307 }