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