xref: /openbmc/linux/tools/perf/util/thread_map.c (revision 45471cd98decae5fced8b38e46c223f54a924814)
1 #include <dirent.h>
2 #include <limits.h>
3 #include <stdbool.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include "strlist.h"
10 #include <string.h>
11 #include "thread_map.h"
12 #include "util.h"
13 
14 /* Skip "." and ".." directories */
15 static int filter(const struct dirent *dir)
16 {
17 	if (dir->d_name[0] == '.')
18 		return 0;
19 	else
20 		return 1;
21 }
22 
23 static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
24 {
25 	size_t size = sizeof(*map) + sizeof(pid_t) * nr;
26 
27 	return realloc(map, size);
28 }
29 
30 #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
31 
32 struct thread_map *thread_map__new_by_pid(pid_t pid)
33 {
34 	struct thread_map *threads;
35 	char name[256];
36 	int items;
37 	struct dirent **namelist = NULL;
38 	int i;
39 
40 	sprintf(name, "/proc/%d/task", pid);
41 	items = scandir(name, &namelist, filter, NULL);
42 	if (items <= 0)
43 		return NULL;
44 
45 	threads = thread_map__alloc(items);
46 	if (threads != NULL) {
47 		for (i = 0; i < items; i++)
48 			threads->map[i] = atoi(namelist[i]->d_name);
49 		threads->nr = items;
50 	}
51 
52 	for (i=0; i<items; i++)
53 		zfree(&namelist[i]);
54 	free(namelist);
55 
56 	return threads;
57 }
58 
59 struct thread_map *thread_map__new_by_tid(pid_t tid)
60 {
61 	struct thread_map *threads = thread_map__alloc(1);
62 
63 	if (threads != NULL) {
64 		threads->map[0] = tid;
65 		threads->nr	= 1;
66 	}
67 
68 	return threads;
69 }
70 
71 struct thread_map *thread_map__new_by_uid(uid_t uid)
72 {
73 	DIR *proc;
74 	int max_threads = 32, items, i;
75 	char path[256];
76 	struct dirent dirent, *next, **namelist = NULL;
77 	struct thread_map *threads = thread_map__alloc(max_threads);
78 
79 	if (threads == NULL)
80 		goto out;
81 
82 	proc = opendir("/proc");
83 	if (proc == NULL)
84 		goto out_free_threads;
85 
86 	threads->nr = 0;
87 
88 	while (!readdir_r(proc, &dirent, &next) && next) {
89 		char *end;
90 		bool grow = false;
91 		struct stat st;
92 		pid_t pid = strtol(dirent.d_name, &end, 10);
93 
94 		if (*end) /* only interested in proper numerical dirents */
95 			continue;
96 
97 		snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
98 
99 		if (stat(path, &st) != 0)
100 			continue;
101 
102 		if (st.st_uid != uid)
103 			continue;
104 
105 		snprintf(path, sizeof(path), "/proc/%d/task", pid);
106 		items = scandir(path, &namelist, filter, NULL);
107 		if (items <= 0)
108 			goto out_free_closedir;
109 
110 		while (threads->nr + items >= max_threads) {
111 			max_threads *= 2;
112 			grow = true;
113 		}
114 
115 		if (grow) {
116 			struct thread_map *tmp;
117 
118 			tmp = realloc(threads, (sizeof(*threads) +
119 						max_threads * sizeof(pid_t)));
120 			if (tmp == NULL)
121 				goto out_free_namelist;
122 
123 			threads = tmp;
124 		}
125 
126 		for (i = 0; i < items; i++)
127 			threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
128 
129 		for (i = 0; i < items; i++)
130 			zfree(&namelist[i]);
131 		free(namelist);
132 
133 		threads->nr += items;
134 	}
135 
136 out_closedir:
137 	closedir(proc);
138 out:
139 	return threads;
140 
141 out_free_threads:
142 	free(threads);
143 	return NULL;
144 
145 out_free_namelist:
146 	for (i = 0; i < items; i++)
147 		zfree(&namelist[i]);
148 	free(namelist);
149 
150 out_free_closedir:
151 	zfree(&threads);
152 	goto out_closedir;
153 }
154 
155 struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
156 {
157 	if (pid != -1)
158 		return thread_map__new_by_pid(pid);
159 
160 	if (tid == -1 && uid != UINT_MAX)
161 		return thread_map__new_by_uid(uid);
162 
163 	return thread_map__new_by_tid(tid);
164 }
165 
166 static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
167 {
168 	struct thread_map *threads = NULL, *nt;
169 	char name[256];
170 	int items, total_tasks = 0;
171 	struct dirent **namelist = NULL;
172 	int i, j = 0;
173 	pid_t pid, prev_pid = INT_MAX;
174 	char *end_ptr;
175 	struct str_node *pos;
176 	struct strlist *slist = strlist__new(false, pid_str);
177 
178 	if (!slist)
179 		return NULL;
180 
181 	strlist__for_each(pos, slist) {
182 		pid = strtol(pos->s, &end_ptr, 10);
183 
184 		if (pid == INT_MIN || pid == INT_MAX ||
185 		    (*end_ptr != '\0' && *end_ptr != ','))
186 			goto out_free_threads;
187 
188 		if (pid == prev_pid)
189 			continue;
190 
191 		sprintf(name, "/proc/%d/task", pid);
192 		items = scandir(name, &namelist, filter, NULL);
193 		if (items <= 0)
194 			goto out_free_threads;
195 
196 		total_tasks += items;
197 		nt = thread_map__realloc(threads, total_tasks);
198 		if (nt == NULL)
199 			goto out_free_namelist;
200 
201 		threads = nt;
202 
203 		for (i = 0; i < items; i++) {
204 			threads->map[j++] = atoi(namelist[i]->d_name);
205 			zfree(&namelist[i]);
206 		}
207 		threads->nr = total_tasks;
208 		free(namelist);
209 	}
210 
211 out:
212 	strlist__delete(slist);
213 	return threads;
214 
215 out_free_namelist:
216 	for (i = 0; i < items; i++)
217 		zfree(&namelist[i]);
218 	free(namelist);
219 
220 out_free_threads:
221 	zfree(&threads);
222 	goto out;
223 }
224 
225 struct thread_map *thread_map__new_dummy(void)
226 {
227 	struct thread_map *threads = thread_map__alloc(1);
228 
229 	if (threads != NULL) {
230 		threads->map[0]	= -1;
231 		threads->nr	= 1;
232 	}
233 	return threads;
234 }
235 
236 static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
237 {
238 	struct thread_map *threads = NULL, *nt;
239 	int ntasks = 0;
240 	pid_t tid, prev_tid = INT_MAX;
241 	char *end_ptr;
242 	struct str_node *pos;
243 	struct strlist *slist;
244 
245 	/* perf-stat expects threads to be generated even if tid not given */
246 	if (!tid_str)
247 		return thread_map__new_dummy();
248 
249 	slist = strlist__new(false, tid_str);
250 	if (!slist)
251 		return NULL;
252 
253 	strlist__for_each(pos, slist) {
254 		tid = strtol(pos->s, &end_ptr, 10);
255 
256 		if (tid == INT_MIN || tid == INT_MAX ||
257 		    (*end_ptr != '\0' && *end_ptr != ','))
258 			goto out_free_threads;
259 
260 		if (tid == prev_tid)
261 			continue;
262 
263 		ntasks++;
264 		nt = thread_map__realloc(threads, ntasks);
265 
266 		if (nt == NULL)
267 			goto out_free_threads;
268 
269 		threads = nt;
270 		threads->map[ntasks - 1] = tid;
271 		threads->nr		 = ntasks;
272 	}
273 out:
274 	return threads;
275 
276 out_free_threads:
277 	zfree(&threads);
278 	goto out;
279 }
280 
281 struct thread_map *thread_map__new_str(const char *pid, const char *tid,
282 				       uid_t uid)
283 {
284 	if (pid)
285 		return thread_map__new_by_pid_str(pid);
286 
287 	if (!tid && uid != UINT_MAX)
288 		return thread_map__new_by_uid(uid);
289 
290 	return thread_map__new_by_tid_str(tid);
291 }
292 
293 void thread_map__delete(struct thread_map *threads)
294 {
295 	free(threads);
296 }
297 
298 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
299 {
300 	int i;
301 	size_t printed = fprintf(fp, "%d thread%s: ",
302 				 threads->nr, threads->nr > 1 ? "s" : "");
303 	for (i = 0; i < threads->nr; ++i)
304 		printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
305 
306 	return printed + fprintf(fp, "\n");
307 }
308