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