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 | ||
5128492b | 75 | static struct perf_thread_map *thread_map__new_all_cpus(void) |
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 | ||
0d37aa34 ACM |
101 | snprintf(path, sizeof(path), "/proc/%d/task", pid); |
102 | items = scandir(path, &namelist, filter, NULL); | |
510e5287 IR |
103 | if (items <= 0) { |
104 | pr_debug("scandir for %d returned empty, skipping\n", pid); | |
105 | continue; | |
106 | } | |
0d37aa34 ACM |
107 | while (threads->nr + items >= max_threads) { |
108 | max_threads *= 2; | |
109 | grow = true; | |
110 | } | |
111 | ||
112 | if (grow) { | |
9749b90e | 113 | struct perf_thread_map *tmp; |
0d37aa34 | 114 | |
4b49cce2 | 115 | tmp = perf_thread_map__realloc(threads, max_threads); |
0d37aa34 ACM |
116 | if (tmp == NULL) |
117 | goto out_free_namelist; | |
118 | ||
119 | threads = tmp; | |
120 | } | |
121 | ||
e13798c7 | 122 | for (i = 0; i < items; i++) { |
4b49cce2 JO |
123 | perf_thread_map__set_pid(threads, threads->nr + i, |
124 | atoi(namelist[i]->d_name)); | |
e13798c7 | 125 | } |
0d37aa34 ACM |
126 | |
127 | for (i = 0; i < items; i++) | |
74cf249d | 128 | zfree(&namelist[i]); |
0d37aa34 ACM |
129 | free(namelist); |
130 | ||
131 | threads->nr += items; | |
132 | } | |
133 | ||
134 | out_closedir: | |
135 | closedir(proc); | |
136 | out: | |
137 | return threads; | |
138 | ||
139 | out_free_threads: | |
140 | free(threads); | |
141 | return NULL; | |
142 | ||
143 | out_free_namelist: | |
144 | for (i = 0; i < items; i++) | |
74cf249d | 145 | zfree(&namelist[i]); |
0d37aa34 | 146 | free(namelist); |
04662523 | 147 | zfree(&threads); |
0d37aa34 ACM |
148 | goto out_closedir; |
149 | } | |
150 | ||
5128492b | 151 | struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid) |
fd78260b ACM |
152 | { |
153 | if (pid != -1) | |
154 | return thread_map__new_by_pid(pid); | |
0d37aa34 | 155 | |
fd78260b ACM |
156 | return thread_map__new_by_tid(tid); |
157 | } | |
158 | ||
9749b90e | 159 | static struct perf_thread_map *thread_map__new_by_pid_str(const char *pid_str) |
b52956c9 | 160 | { |
9749b90e | 161 | struct perf_thread_map *threads = NULL, *nt; |
b52956c9 DA |
162 | char name[256]; |
163 | int items, total_tasks = 0; | |
164 | struct dirent **namelist = NULL; | |
165 | int i, j = 0; | |
166 | pid_t pid, prev_pid = INT_MAX; | |
167 | char *end_ptr; | |
168 | struct str_node *pos; | |
4a77e218 ACM |
169 | struct strlist_config slist_config = { .dont_dupstr = true, }; |
170 | struct strlist *slist = strlist__new(pid_str, &slist_config); | |
b52956c9 DA |
171 | |
172 | if (!slist) | |
173 | return NULL; | |
174 | ||
602a1f4d | 175 | strlist__for_each_entry(pos, slist) { |
b52956c9 DA |
176 | pid = strtol(pos->s, &end_ptr, 10); |
177 | ||
178 | if (pid == INT_MIN || pid == INT_MAX || | |
179 | (*end_ptr != '\0' && *end_ptr != ',')) | |
180 | goto out_free_threads; | |
181 | ||
182 | if (pid == prev_pid) | |
183 | continue; | |
184 | ||
185 | sprintf(name, "/proc/%d/task", pid); | |
186 | items = scandir(name, &namelist, filter, NULL); | |
187 | if (items <= 0) | |
188 | goto out_free_threads; | |
189 | ||
190 | total_tasks += items; | |
4b49cce2 | 191 | nt = perf_thread_map__realloc(threads, total_tasks); |
b52956c9 | 192 | if (nt == NULL) |
e8cdd947 | 193 | goto out_free_namelist; |
b52956c9 DA |
194 | |
195 | threads = nt; | |
196 | ||
e8cdd947 | 197 | for (i = 0; i < items; i++) { |
4b49cce2 | 198 | perf_thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name)); |
74cf249d | 199 | zfree(&namelist[i]); |
e8cdd947 FBH |
200 | } |
201 | threads->nr = total_tasks; | |
b52956c9 | 202 | free(namelist); |
b52956c9 DA |
203 | } |
204 | ||
205 | out: | |
206 | strlist__delete(slist); | |
186fbb74 | 207 | if (threads) |
364fed35 | 208 | refcount_set(&threads->refcnt, 1); |
b52956c9 DA |
209 | return threads; |
210 | ||
e8cdd947 FBH |
211 | out_free_namelist: |
212 | for (i = 0; i < items; i++) | |
74cf249d | 213 | zfree(&namelist[i]); |
e8cdd947 FBH |
214 | free(namelist); |
215 | ||
b52956c9 | 216 | out_free_threads: |
04662523 | 217 | zfree(&threads); |
b52956c9 DA |
218 | goto out; |
219 | } | |
220 | ||
9749b90e | 221 | struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str) |
b52956c9 | 222 | { |
9749b90e | 223 | struct perf_thread_map *threads = NULL, *nt; |
b52956c9 DA |
224 | int ntasks = 0; |
225 | pid_t tid, prev_tid = INT_MAX; | |
226 | char *end_ptr; | |
227 | struct str_node *pos; | |
4a77e218 | 228 | struct strlist_config slist_config = { .dont_dupstr = true, }; |
b52956c9 DA |
229 | struct strlist *slist; |
230 | ||
231 | /* perf-stat expects threads to be generated even if tid not given */ | |
641556c9 | 232 | if (!tid_str) |
4b49cce2 | 233 | return perf_thread_map__new_dummy(); |
b52956c9 | 234 | |
4a77e218 | 235 | slist = strlist__new(tid_str, &slist_config); |
b52956c9 DA |
236 | if (!slist) |
237 | return NULL; | |
238 | ||
602a1f4d | 239 | strlist__for_each_entry(pos, slist) { |
b52956c9 DA |
240 | tid = strtol(pos->s, &end_ptr, 10); |
241 | ||
242 | if (tid == INT_MIN || tid == INT_MAX || | |
243 | (*end_ptr != '\0' && *end_ptr != ',')) | |
244 | goto out_free_threads; | |
245 | ||
246 | if (tid == prev_tid) | |
247 | continue; | |
248 | ||
249 | ntasks++; | |
4b49cce2 | 250 | nt = perf_thread_map__realloc(threads, ntasks); |
b52956c9 DA |
251 | |
252 | if (nt == NULL) | |
253 | goto out_free_threads; | |
254 | ||
255 | threads = nt; | |
4b49cce2 | 256 | perf_thread_map__set_pid(threads, ntasks - 1, tid); |
e13798c7 | 257 | threads->nr = ntasks; |
b52956c9 DA |
258 | } |
259 | out: | |
1eb3d924 | 260 | strlist__delete(slist); |
186fbb74 | 261 | if (threads) |
364fed35 | 262 | refcount_set(&threads->refcnt, 1); |
b52956c9 DA |
263 | return threads; |
264 | ||
265 | out_free_threads: | |
04662523 | 266 | zfree(&threads); |
b52956c9 DA |
267 | goto out; |
268 | } | |
269 | ||
5128492b | 270 | struct perf_thread_map *thread_map__new_str(const char *pid, const char *tid, bool all_threads) |
b52956c9 DA |
271 | { |
272 | if (pid) | |
273 | return thread_map__new_by_pid_str(pid); | |
274 | ||
147c508f | 275 | if (all_threads) |
73c0ca1e JY |
276 | return thread_map__new_all_cpus(); |
277 | ||
b52956c9 DA |
278 | return thread_map__new_by_tid_str(tid); |
279 | } | |
280 | ||
9749b90e | 281 | size_t thread_map__fprintf(struct perf_thread_map *threads, FILE *fp) |
9ae7d335 ACM |
282 | { |
283 | int i; | |
284 | size_t printed = fprintf(fp, "%d thread%s: ", | |
285 | threads->nr, threads->nr > 1 ? "s" : ""); | |
286 | for (i = 0; i < threads->nr; ++i) | |
a2f354e3 | 287 | printed += fprintf(fp, "%s%d", i ? ", " : "", perf_thread_map__pid(threads, i)); |
9ae7d335 ACM |
288 | |
289 | return printed + fprintf(fp, "\n"); | |
290 | } | |
792402fd JO |
291 | |
292 | static int get_comm(char **comm, pid_t pid) | |
293 | { | |
294 | char *path; | |
295 | size_t size; | |
296 | int err; | |
297 | ||
298 | if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1) | |
299 | return -ENOMEM; | |
300 | ||
301 | err = filename__read_str(path, comm, &size); | |
302 | if (!err) { | |
303 | /* | |
304 | * We're reading 16 bytes, while filename__read_str | |
305 | * allocates data per BUFSIZ bytes, so we can safely | |
306 | * mark the end of the string. | |
307 | */ | |
308 | (*comm)[size] = 0; | |
13c230ab | 309 | strim(*comm); |
792402fd JO |
310 | } |
311 | ||
312 | free(path); | |
313 | return err; | |
314 | } | |
315 | ||
9749b90e | 316 | static void comm_init(struct perf_thread_map *map, int i) |
792402fd | 317 | { |
a2f354e3 | 318 | pid_t pid = perf_thread_map__pid(map, i); |
792402fd JO |
319 | char *comm = NULL; |
320 | ||
321 | /* dummy pid comm initialization */ | |
322 | if (pid == -1) { | |
323 | map->map[i].comm = strdup("dummy"); | |
324 | return; | |
325 | } | |
326 | ||
327 | /* | |
328 | * The comm name is like extra bonus ;-), | |
329 | * so just warn if we fail for any reason. | |
330 | */ | |
331 | if (get_comm(&comm, pid)) | |
332 | pr_warning("Couldn't resolve comm name for pid %d\n", pid); | |
333 | ||
334 | map->map[i].comm = comm; | |
335 | } | |
336 | ||
9749b90e | 337 | void thread_map__read_comms(struct perf_thread_map *threads) |
792402fd JO |
338 | { |
339 | int i; | |
340 | ||
341 | for (i = 0; i < threads->nr; ++i) | |
342 | comm_init(threads, i); | |
343 | } | |
59660942 | 344 | |
9749b90e | 345 | static void thread_map__copy_event(struct perf_thread_map *threads, |
72932371 | 346 | struct perf_record_thread_map *event) |
59660942 JO |
347 | { |
348 | unsigned i; | |
349 | ||
350 | threads->nr = (int) event->nr; | |
351 | ||
352 | for (i = 0; i < event->nr; i++) { | |
4b49cce2 | 353 | perf_thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid); |
59660942 JO |
354 | threads->map[i].comm = strndup(event->entries[i].comm, 16); |
355 | } | |
356 | ||
364fed35 | 357 | refcount_set(&threads->refcnt, 1); |
59660942 JO |
358 | } |
359 | ||
72932371 | 360 | struct perf_thread_map *thread_map__new_event(struct perf_record_thread_map *event) |
59660942 | 361 | { |
9749b90e | 362 | struct perf_thread_map *threads; |
59660942 JO |
363 | |
364 | threads = thread_map__alloc(event->nr); | |
365 | if (threads) | |
366 | thread_map__copy_event(threads, event); | |
367 | ||
368 | return threads; | |
369 | } | |
3407df8b | 370 | |
9749b90e | 371 | bool thread_map__has(struct perf_thread_map *threads, pid_t pid) |
3407df8b JO |
372 | { |
373 | int i; | |
374 | ||
375 | for (i = 0; i < threads->nr; ++i) { | |
376 | if (threads->map[i].pid == pid) | |
377 | return true; | |
378 | } | |
379 | ||
380 | return false; | |
381 | } | |
38af91f0 | 382 | |
9749b90e | 383 | int thread_map__remove(struct perf_thread_map *threads, int idx) |
38af91f0 JO |
384 | { |
385 | int i; | |
386 | ||
387 | if (threads->nr < 1) | |
388 | return -EINVAL; | |
389 | ||
390 | if (idx >= threads->nr) | |
391 | return -EINVAL; | |
392 | ||
393 | /* | |
394 | * Free the 'idx' item and shift the rest up. | |
395 | */ | |
d8f9da24 | 396 | zfree(&threads->map[idx].comm); |
38af91f0 JO |
397 | |
398 | for (i = idx; i < threads->nr - 1; i++) | |
399 | threads->map[i] = threads->map[i + 1]; | |
400 | ||
401 | threads->nr--; | |
402 | return 0; | |
403 | } |