xref: /linux/tools/perf/util/thread_map.c (revision 641556c98c821922a9a2121247b5fa25e4407d11)
1fd78260bSArnaldo Carvalho de Melo #include <dirent.h>
20d37aa34SArnaldo Carvalho de Melo #include <limits.h>
30d37aa34SArnaldo Carvalho de Melo #include <stdbool.h>
4fd78260bSArnaldo Carvalho de Melo #include <stdlib.h>
5fd78260bSArnaldo Carvalho de Melo #include <stdio.h>
60d37aa34SArnaldo Carvalho de Melo #include <sys/types.h>
70d37aa34SArnaldo Carvalho de Melo #include <sys/stat.h>
80d37aa34SArnaldo Carvalho de Melo #include <unistd.h>
9b52956c9SDavid Ahern #include "strlist.h"
10b52956c9SDavid Ahern #include <string.h>
11fd78260bSArnaldo Carvalho de Melo #include "thread_map.h"
1204662523SArnaldo Carvalho de Melo #include "util.h"
13fd78260bSArnaldo Carvalho de Melo 
14fd78260bSArnaldo Carvalho de Melo /* Skip "." and ".." directories */
15fd78260bSArnaldo Carvalho de Melo static int filter(const struct dirent *dir)
16fd78260bSArnaldo Carvalho de Melo {
17fd78260bSArnaldo Carvalho de Melo 	if (dir->d_name[0] == '.')
18fd78260bSArnaldo Carvalho de Melo 		return 0;
19fd78260bSArnaldo Carvalho de Melo 	else
20fd78260bSArnaldo Carvalho de Melo 		return 1;
21fd78260bSArnaldo Carvalho de Melo }
22fd78260bSArnaldo Carvalho de Melo 
23fd78260bSArnaldo Carvalho de Melo struct thread_map *thread_map__new_by_pid(pid_t pid)
24fd78260bSArnaldo Carvalho de Melo {
25fd78260bSArnaldo Carvalho de Melo 	struct thread_map *threads;
26fd78260bSArnaldo Carvalho de Melo 	char name[256];
27fd78260bSArnaldo Carvalho de Melo 	int items;
28fd78260bSArnaldo Carvalho de Melo 	struct dirent **namelist = NULL;
29fd78260bSArnaldo Carvalho de Melo 	int i;
30fd78260bSArnaldo Carvalho de Melo 
31fd78260bSArnaldo Carvalho de Melo 	sprintf(name, "/proc/%d/task", pid);
32fd78260bSArnaldo Carvalho de Melo 	items = scandir(name, &namelist, filter, NULL);
33fd78260bSArnaldo Carvalho de Melo 	if (items <= 0)
34fd78260bSArnaldo Carvalho de Melo 		return NULL;
35fd78260bSArnaldo Carvalho de Melo 
36fd78260bSArnaldo Carvalho de Melo 	threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
37fd78260bSArnaldo Carvalho de Melo 	if (threads != NULL) {
38fd78260bSArnaldo Carvalho de Melo 		for (i = 0; i < items; i++)
39fd78260bSArnaldo Carvalho de Melo 			threads->map[i] = atoi(namelist[i]->d_name);
40fd78260bSArnaldo Carvalho de Melo 		threads->nr = items;
41fd78260bSArnaldo Carvalho de Melo 	}
42fd78260bSArnaldo Carvalho de Melo 
43fd78260bSArnaldo Carvalho de Melo 	for (i=0; i<items; i++)
4474cf249dSArnaldo Carvalho de Melo 		zfree(&namelist[i]);
45fd78260bSArnaldo Carvalho de Melo 	free(namelist);
46fd78260bSArnaldo Carvalho de Melo 
47fd78260bSArnaldo Carvalho de Melo 	return threads;
48fd78260bSArnaldo Carvalho de Melo }
49fd78260bSArnaldo Carvalho de Melo 
50fd78260bSArnaldo Carvalho de Melo struct thread_map *thread_map__new_by_tid(pid_t tid)
51fd78260bSArnaldo Carvalho de Melo {
52fd78260bSArnaldo Carvalho de Melo 	struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
53fd78260bSArnaldo Carvalho de Melo 
54fd78260bSArnaldo Carvalho de Melo 	if (threads != NULL) {
55fd78260bSArnaldo Carvalho de Melo 		threads->map[0] = tid;
56fd78260bSArnaldo Carvalho de Melo 		threads->nr	= 1;
57fd78260bSArnaldo Carvalho de Melo 	}
58fd78260bSArnaldo Carvalho de Melo 
59fd78260bSArnaldo Carvalho de Melo 	return threads;
60fd78260bSArnaldo Carvalho de Melo }
61fd78260bSArnaldo Carvalho de Melo 
620d37aa34SArnaldo Carvalho de Melo struct thread_map *thread_map__new_by_uid(uid_t uid)
630d37aa34SArnaldo Carvalho de Melo {
640d37aa34SArnaldo Carvalho de Melo 	DIR *proc;
650d37aa34SArnaldo Carvalho de Melo 	int max_threads = 32, items, i;
660d37aa34SArnaldo Carvalho de Melo 	char path[256];
670d37aa34SArnaldo Carvalho de Melo 	struct dirent dirent, *next, **namelist = NULL;
680d37aa34SArnaldo Carvalho de Melo 	struct thread_map *threads = malloc(sizeof(*threads) +
690d37aa34SArnaldo Carvalho de Melo 					    max_threads * sizeof(pid_t));
700d37aa34SArnaldo Carvalho de Melo 	if (threads == NULL)
710d37aa34SArnaldo Carvalho de Melo 		goto out;
720d37aa34SArnaldo Carvalho de Melo 
730d37aa34SArnaldo Carvalho de Melo 	proc = opendir("/proc");
740d37aa34SArnaldo Carvalho de Melo 	if (proc == NULL)
750d37aa34SArnaldo Carvalho de Melo 		goto out_free_threads;
760d37aa34SArnaldo Carvalho de Melo 
770d37aa34SArnaldo Carvalho de Melo 	threads->nr = 0;
780d37aa34SArnaldo Carvalho de Melo 
790d37aa34SArnaldo Carvalho de Melo 	while (!readdir_r(proc, &dirent, &next) && next) {
800d37aa34SArnaldo Carvalho de Melo 		char *end;
810d37aa34SArnaldo Carvalho de Melo 		bool grow = false;
820d37aa34SArnaldo Carvalho de Melo 		struct stat st;
830d37aa34SArnaldo Carvalho de Melo 		pid_t pid = strtol(dirent.d_name, &end, 10);
840d37aa34SArnaldo Carvalho de Melo 
850d37aa34SArnaldo Carvalho de Melo 		if (*end) /* only interested in proper numerical dirents */
860d37aa34SArnaldo Carvalho de Melo 			continue;
870d37aa34SArnaldo Carvalho de Melo 
880d37aa34SArnaldo Carvalho de Melo 		snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
890d37aa34SArnaldo Carvalho de Melo 
900d37aa34SArnaldo Carvalho de Melo 		if (stat(path, &st) != 0)
910d37aa34SArnaldo Carvalho de Melo 			continue;
920d37aa34SArnaldo Carvalho de Melo 
930d37aa34SArnaldo Carvalho de Melo 		if (st.st_uid != uid)
940d37aa34SArnaldo Carvalho de Melo 			continue;
950d37aa34SArnaldo Carvalho de Melo 
960d37aa34SArnaldo Carvalho de Melo 		snprintf(path, sizeof(path), "/proc/%d/task", pid);
970d37aa34SArnaldo Carvalho de Melo 		items = scandir(path, &namelist, filter, NULL);
980d37aa34SArnaldo Carvalho de Melo 		if (items <= 0)
990d37aa34SArnaldo Carvalho de Melo 			goto out_free_closedir;
1000d37aa34SArnaldo Carvalho de Melo 
1010d37aa34SArnaldo Carvalho de Melo 		while (threads->nr + items >= max_threads) {
1020d37aa34SArnaldo Carvalho de Melo 			max_threads *= 2;
1030d37aa34SArnaldo Carvalho de Melo 			grow = true;
1040d37aa34SArnaldo Carvalho de Melo 		}
1050d37aa34SArnaldo Carvalho de Melo 
1060d37aa34SArnaldo Carvalho de Melo 		if (grow) {
1070d37aa34SArnaldo Carvalho de Melo 			struct thread_map *tmp;
1080d37aa34SArnaldo Carvalho de Melo 
1090d37aa34SArnaldo Carvalho de Melo 			tmp = realloc(threads, (sizeof(*threads) +
1100d37aa34SArnaldo Carvalho de Melo 						max_threads * sizeof(pid_t)));
1110d37aa34SArnaldo Carvalho de Melo 			if (tmp == NULL)
1120d37aa34SArnaldo Carvalho de Melo 				goto out_free_namelist;
1130d37aa34SArnaldo Carvalho de Melo 
1140d37aa34SArnaldo Carvalho de Melo 			threads = tmp;
1150d37aa34SArnaldo Carvalho de Melo 		}
1160d37aa34SArnaldo Carvalho de Melo 
1170d37aa34SArnaldo Carvalho de Melo 		for (i = 0; i < items; i++)
1180d37aa34SArnaldo Carvalho de Melo 			threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
1190d37aa34SArnaldo Carvalho de Melo 
1200d37aa34SArnaldo Carvalho de Melo 		for (i = 0; i < items; i++)
12174cf249dSArnaldo Carvalho de Melo 			zfree(&namelist[i]);
1220d37aa34SArnaldo Carvalho de Melo 		free(namelist);
1230d37aa34SArnaldo Carvalho de Melo 
1240d37aa34SArnaldo Carvalho de Melo 		threads->nr += items;
1250d37aa34SArnaldo Carvalho de Melo 	}
1260d37aa34SArnaldo Carvalho de Melo 
1270d37aa34SArnaldo Carvalho de Melo out_closedir:
1280d37aa34SArnaldo Carvalho de Melo 	closedir(proc);
1290d37aa34SArnaldo Carvalho de Melo out:
1300d37aa34SArnaldo Carvalho de Melo 	return threads;
1310d37aa34SArnaldo Carvalho de Melo 
1320d37aa34SArnaldo Carvalho de Melo out_free_threads:
1330d37aa34SArnaldo Carvalho de Melo 	free(threads);
1340d37aa34SArnaldo Carvalho de Melo 	return NULL;
1350d37aa34SArnaldo Carvalho de Melo 
1360d37aa34SArnaldo Carvalho de Melo out_free_namelist:
1370d37aa34SArnaldo Carvalho de Melo 	for (i = 0; i < items; i++)
13874cf249dSArnaldo Carvalho de Melo 		zfree(&namelist[i]);
1390d37aa34SArnaldo Carvalho de Melo 	free(namelist);
1400d37aa34SArnaldo Carvalho de Melo 
1410d37aa34SArnaldo Carvalho de Melo out_free_closedir:
14204662523SArnaldo Carvalho de Melo 	zfree(&threads);
1430d37aa34SArnaldo Carvalho de Melo 	goto out_closedir;
1440d37aa34SArnaldo Carvalho de Melo }
1450d37aa34SArnaldo Carvalho de Melo 
1460d37aa34SArnaldo Carvalho de Melo struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
147fd78260bSArnaldo Carvalho de Melo {
148fd78260bSArnaldo Carvalho de Melo 	if (pid != -1)
149fd78260bSArnaldo Carvalho de Melo 		return thread_map__new_by_pid(pid);
1500d37aa34SArnaldo Carvalho de Melo 
1510d37aa34SArnaldo Carvalho de Melo 	if (tid == -1 && uid != UINT_MAX)
1520d37aa34SArnaldo Carvalho de Melo 		return thread_map__new_by_uid(uid);
1530d37aa34SArnaldo Carvalho de Melo 
154fd78260bSArnaldo Carvalho de Melo 	return thread_map__new_by_tid(tid);
155fd78260bSArnaldo Carvalho de Melo }
156fd78260bSArnaldo Carvalho de Melo 
157b52956c9SDavid Ahern static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
158b52956c9SDavid Ahern {
159b52956c9SDavid Ahern 	struct thread_map *threads = NULL, *nt;
160b52956c9SDavid Ahern 	char name[256];
161b52956c9SDavid Ahern 	int items, total_tasks = 0;
162b52956c9SDavid Ahern 	struct dirent **namelist = NULL;
163b52956c9SDavid Ahern 	int i, j = 0;
164b52956c9SDavid Ahern 	pid_t pid, prev_pid = INT_MAX;
165b52956c9SDavid Ahern 	char *end_ptr;
166b52956c9SDavid Ahern 	struct str_node *pos;
167b52956c9SDavid Ahern 	struct strlist *slist = strlist__new(false, pid_str);
168b52956c9SDavid Ahern 
169b52956c9SDavid Ahern 	if (!slist)
170b52956c9SDavid Ahern 		return NULL;
171b52956c9SDavid Ahern 
172b52956c9SDavid Ahern 	strlist__for_each(pos, slist) {
173b52956c9SDavid Ahern 		pid = strtol(pos->s, &end_ptr, 10);
174b52956c9SDavid Ahern 
175b52956c9SDavid Ahern 		if (pid == INT_MIN || pid == INT_MAX ||
176b52956c9SDavid Ahern 		    (*end_ptr != '\0' && *end_ptr != ','))
177b52956c9SDavid Ahern 			goto out_free_threads;
178b52956c9SDavid Ahern 
179b52956c9SDavid Ahern 		if (pid == prev_pid)
180b52956c9SDavid Ahern 			continue;
181b52956c9SDavid Ahern 
182b52956c9SDavid Ahern 		sprintf(name, "/proc/%d/task", pid);
183b52956c9SDavid Ahern 		items = scandir(name, &namelist, filter, NULL);
184b52956c9SDavid Ahern 		if (items <= 0)
185b52956c9SDavid Ahern 			goto out_free_threads;
186b52956c9SDavid Ahern 
187b52956c9SDavid Ahern 		total_tasks += items;
188b52956c9SDavid Ahern 		nt = realloc(threads, (sizeof(*threads) +
189b52956c9SDavid Ahern 				       sizeof(pid_t) * total_tasks));
190b52956c9SDavid Ahern 		if (nt == NULL)
191e8cdd947SFranck Bui-Huu 			goto out_free_namelist;
192b52956c9SDavid Ahern 
193b52956c9SDavid Ahern 		threads = nt;
194b52956c9SDavid Ahern 
195e8cdd947SFranck Bui-Huu 		for (i = 0; i < items; i++) {
196b52956c9SDavid Ahern 			threads->map[j++] = atoi(namelist[i]->d_name);
19774cf249dSArnaldo Carvalho de Melo 			zfree(&namelist[i]);
198e8cdd947SFranck Bui-Huu 		}
199e8cdd947SFranck Bui-Huu 		threads->nr = total_tasks;
200b52956c9SDavid Ahern 		free(namelist);
201b52956c9SDavid Ahern 	}
202b52956c9SDavid Ahern 
203b52956c9SDavid Ahern out:
204b52956c9SDavid Ahern 	strlist__delete(slist);
205b52956c9SDavid Ahern 	return threads;
206b52956c9SDavid Ahern 
207e8cdd947SFranck Bui-Huu out_free_namelist:
208e8cdd947SFranck Bui-Huu 	for (i = 0; i < items; i++)
20974cf249dSArnaldo Carvalho de Melo 		zfree(&namelist[i]);
210e8cdd947SFranck Bui-Huu 	free(namelist);
211e8cdd947SFranck Bui-Huu 
212b52956c9SDavid Ahern out_free_threads:
21304662523SArnaldo Carvalho de Melo 	zfree(&threads);
214b52956c9SDavid Ahern 	goto out;
215b52956c9SDavid Ahern }
216b52956c9SDavid Ahern 
217*641556c9SArnaldo Carvalho de Melo struct thread_map *thread_map__new_dummy(void)
218*641556c9SArnaldo Carvalho de Melo {
219*641556c9SArnaldo Carvalho de Melo 	struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
220*641556c9SArnaldo Carvalho de Melo 
221*641556c9SArnaldo Carvalho de Melo 	if (threads != NULL) {
222*641556c9SArnaldo Carvalho de Melo 		threads->map[0]	= -1;
223*641556c9SArnaldo Carvalho de Melo 		threads->nr	= 1;
224*641556c9SArnaldo Carvalho de Melo 	}
225*641556c9SArnaldo Carvalho de Melo 	return threads;
226*641556c9SArnaldo Carvalho de Melo }
227*641556c9SArnaldo Carvalho de Melo 
228b52956c9SDavid Ahern static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
229b52956c9SDavid Ahern {
230b52956c9SDavid Ahern 	struct thread_map *threads = NULL, *nt;
231b52956c9SDavid Ahern 	int ntasks = 0;
232b52956c9SDavid Ahern 	pid_t tid, prev_tid = INT_MAX;
233b52956c9SDavid Ahern 	char *end_ptr;
234b52956c9SDavid Ahern 	struct str_node *pos;
235b52956c9SDavid Ahern 	struct strlist *slist;
236b52956c9SDavid Ahern 
237b52956c9SDavid Ahern 	/* perf-stat expects threads to be generated even if tid not given */
238*641556c9SArnaldo Carvalho de Melo 	if (!tid_str)
239*641556c9SArnaldo Carvalho de Melo 		return thread_map__new_dummy();
240b52956c9SDavid Ahern 
241b52956c9SDavid Ahern 	slist = strlist__new(false, tid_str);
242b52956c9SDavid Ahern 	if (!slist)
243b52956c9SDavid Ahern 		return NULL;
244b52956c9SDavid Ahern 
245b52956c9SDavid Ahern 	strlist__for_each(pos, slist) {
246b52956c9SDavid Ahern 		tid = strtol(pos->s, &end_ptr, 10);
247b52956c9SDavid Ahern 
248b52956c9SDavid Ahern 		if (tid == INT_MIN || tid == INT_MAX ||
249b52956c9SDavid Ahern 		    (*end_ptr != '\0' && *end_ptr != ','))
250b52956c9SDavid Ahern 			goto out_free_threads;
251b52956c9SDavid Ahern 
252b52956c9SDavid Ahern 		if (tid == prev_tid)
253b52956c9SDavid Ahern 			continue;
254b52956c9SDavid Ahern 
255b52956c9SDavid Ahern 		ntasks++;
256b52956c9SDavid Ahern 		nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
257b52956c9SDavid Ahern 
258b52956c9SDavid Ahern 		if (nt == NULL)
259b52956c9SDavid Ahern 			goto out_free_threads;
260b52956c9SDavid Ahern 
261b52956c9SDavid Ahern 		threads = nt;
262b52956c9SDavid Ahern 		threads->map[ntasks - 1] = tid;
263b52956c9SDavid Ahern 		threads->nr		 = ntasks;
264b52956c9SDavid Ahern 	}
265b52956c9SDavid Ahern out:
266b52956c9SDavid Ahern 	return threads;
267b52956c9SDavid Ahern 
268b52956c9SDavid Ahern out_free_threads:
26904662523SArnaldo Carvalho de Melo 	zfree(&threads);
270b52956c9SDavid Ahern 	goto out;
271b52956c9SDavid Ahern }
272b52956c9SDavid Ahern 
273b52956c9SDavid Ahern struct thread_map *thread_map__new_str(const char *pid, const char *tid,
274b52956c9SDavid Ahern 				       uid_t uid)
275b52956c9SDavid Ahern {
276b52956c9SDavid Ahern 	if (pid)
277b52956c9SDavid Ahern 		return thread_map__new_by_pid_str(pid);
278b52956c9SDavid Ahern 
279b52956c9SDavid Ahern 	if (!tid && uid != UINT_MAX)
280b52956c9SDavid Ahern 		return thread_map__new_by_uid(uid);
281b52956c9SDavid Ahern 
282b52956c9SDavid Ahern 	return thread_map__new_by_tid_str(tid);
283b52956c9SDavid Ahern }
284b52956c9SDavid Ahern 
285fd78260bSArnaldo Carvalho de Melo void thread_map__delete(struct thread_map *threads)
286fd78260bSArnaldo Carvalho de Melo {
287fd78260bSArnaldo Carvalho de Melo 	free(threads);
288fd78260bSArnaldo Carvalho de Melo }
2899ae7d335SArnaldo Carvalho de Melo 
2909ae7d335SArnaldo Carvalho de Melo size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
2919ae7d335SArnaldo Carvalho de Melo {
2929ae7d335SArnaldo Carvalho de Melo 	int i;
2939ae7d335SArnaldo Carvalho de Melo 	size_t printed = fprintf(fp, "%d thread%s: ",
2949ae7d335SArnaldo Carvalho de Melo 				 threads->nr, threads->nr > 1 ? "s" : "");
2959ae7d335SArnaldo Carvalho de Melo 	for (i = 0; i < threads->nr; ++i)
2969ae7d335SArnaldo Carvalho de Melo 		printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
2979ae7d335SArnaldo Carvalho de Melo 
2989ae7d335SArnaldo Carvalho de Melo 	return printed + fprintf(fp, "\n");
2999ae7d335SArnaldo Carvalho de Melo }
300