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