]>
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" |
fd78260b ACM |
21 | |
22 | /* Skip "." and ".." directories */ | |
23 | static int filter(const struct dirent *dir) | |
24 | { | |
25 | if (dir->d_name[0] == '.') | |
26 | return 0; | |
27 | else | |
28 | return 1; | |
29 | } | |
30 | ||
4b49cce2 | 31 | #define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr) |
9d7e8c3a | 32 | |
9749b90e | 33 | struct perf_thread_map *thread_map__new_by_pid(pid_t pid) |
fd78260b | 34 | { |
9749b90e | 35 | struct perf_thread_map *threads; |
fd78260b ACM |
36 | char name[256]; |
37 | int items; | |
38 | struct dirent **namelist = NULL; | |
39 | int i; | |
40 | ||
41 | sprintf(name, "/proc/%d/task", pid); | |
42 | items = scandir(name, &namelist, filter, NULL); | |
43 | if (items <= 0) | |
0d37aa34 | 44 | return NULL; |
fd78260b | 45 | |
9d7e8c3a | 46 | threads = thread_map__alloc(items); |
fd78260b ACM |
47 | if (threads != NULL) { |
48 | for (i = 0; i < items; i++) | |
4b49cce2 | 49 | perf_thread_map__set_pid(threads, i, atoi(namelist[i]->d_name)); |
fd78260b | 50 | threads->nr = items; |
364fed35 | 51 | refcount_set(&threads->refcnt, 1); |
fd78260b ACM |
52 | } |
53 | ||
54 | for (i=0; i<items; i++) | |
74cf249d | 55 | zfree(&namelist[i]); |
fd78260b ACM |
56 | free(namelist); |
57 | ||
58 | return threads; | |
59 | } | |
60 | ||
9749b90e | 61 | struct perf_thread_map *thread_map__new_by_tid(pid_t tid) |
fd78260b | 62 | { |
9749b90e | 63 | struct perf_thread_map *threads = thread_map__alloc(1); |
fd78260b ACM |
64 | |
65 | if (threads != NULL) { | |
4b49cce2 | 66 | perf_thread_map__set_pid(threads, 0, tid); |
e13798c7 | 67 | threads->nr = 1; |
364fed35 | 68 | refcount_set(&threads->refcnt, 1); |
fd78260b ACM |
69 | } |
70 | ||
71 | return threads; | |
72 | } | |
73 | ||
9749b90e | 74 | static struct perf_thread_map *__thread_map__new_all_cpus(uid_t uid) |
0d37aa34 ACM |
75 | { |
76 | DIR *proc; | |
77 | int max_threads = 32, items, i; | |
bdf23a9a | 78 | char path[NAME_MAX + 1 + 6]; |
3354cf71 | 79 | struct dirent *dirent, **namelist = NULL; |
9749b90e | 80 | struct perf_thread_map *threads = thread_map__alloc(max_threads); |
9d7e8c3a | 81 | |
0d37aa34 ACM |
82 | if (threads == NULL) |
83 | goto out; | |
84 | ||
85 | proc = opendir("/proc"); | |
86 | if (proc == NULL) | |
87 | goto out_free_threads; | |
88 | ||
89 | threads->nr = 0; | |
364fed35 | 90 | refcount_set(&threads->refcnt, 1); |
0d37aa34 | 91 | |
3354cf71 | 92 | while ((dirent = readdir(proc)) != NULL) { |
0d37aa34 ACM |
93 | char *end; |
94 | bool grow = false; | |
3354cf71 | 95 | pid_t pid = strtol(dirent->d_name, &end, 10); |
0d37aa34 ACM |
96 | |
97 | if (*end) /* only interested in proper numerical dirents */ | |
98 | continue; | |
99 | ||
3354cf71 | 100 | snprintf(path, sizeof(path), "/proc/%s", dirent->d_name); |
0d37aa34 | 101 | |
8d3cd4c3 ACM |
102 | if (uid != UINT_MAX) { |
103 | struct stat st; | |
0d37aa34 | 104 | |
8d3cd4c3 ACM |
105 | if (stat(path, &st) != 0 || st.st_uid != uid) |
106 | continue; | |
107 | } | |
0d37aa34 ACM |
108 | |
109 | snprintf(path, sizeof(path), "/proc/%d/task", pid); | |
110 | items = scandir(path, &namelist, filter, NULL); | |
111 | if (items <= 0) | |
112 | goto out_free_closedir; | |
113 | ||
114 | while (threads->nr + items >= max_threads) { | |
115 | max_threads *= 2; | |
116 | grow = true; | |
117 | } | |
118 | ||
119 | if (grow) { | |
9749b90e | 120 | struct perf_thread_map *tmp; |
0d37aa34 | 121 | |
4b49cce2 | 122 | tmp = perf_thread_map__realloc(threads, max_threads); |
0d37aa34 ACM |
123 | if (tmp == NULL) |
124 | goto out_free_namelist; | |
125 | ||
126 | threads = tmp; | |
127 | } | |
128 | ||
e13798c7 | 129 | for (i = 0; i < items; i++) { |
4b49cce2 JO |
130 | perf_thread_map__set_pid(threads, threads->nr + i, |
131 | atoi(namelist[i]->d_name)); | |
e13798c7 | 132 | } |
0d37aa34 ACM |
133 | |
134 | for (i = 0; i < items; i++) | |
74cf249d | 135 | zfree(&namelist[i]); |
0d37aa34 ACM |
136 | free(namelist); |
137 | ||
138 | threads->nr += items; | |
139 | } | |
140 | ||
141 | out_closedir: | |
142 | closedir(proc); | |
143 | out: | |
144 | return threads; | |
145 | ||
146 | out_free_threads: | |
147 | free(threads); | |
148 | return NULL; | |
149 | ||
150 | out_free_namelist: | |
151 | for (i = 0; i < items; i++) | |
74cf249d | 152 | zfree(&namelist[i]); |
0d37aa34 ACM |
153 | free(namelist); |
154 | ||
155 | out_free_closedir: | |
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: | |
186fbb74 | 282 | if (threads) |
364fed35 | 283 | refcount_set(&threads->refcnt, 1); |
b52956c9 DA |
284 | return threads; |
285 | ||
286 | out_free_threads: | |
04662523 | 287 | zfree(&threads); |
7ecb48fd | 288 | strlist__delete(slist); |
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 | } |