thread_map.c 6.7 KB
Newer Older
1
#include <dirent.h>
2 3
#include <limits.h>
#include <stdbool.h>
4 5
#include <stdlib.h>
#include <stdio.h>
6 7 8
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
9 10
#include "strlist.h"
#include <string.h>
11
#include "asm/bug.h"
12
#include "thread_map.h"
13
#include "util.h"
14 15 16 17 18 19 20 21 22 23

/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
{
	if (dir->d_name[0] == '.')
		return 0;
	else
		return 1;
}

24 25
static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
{
26
	size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
27 28 29 30 31 32

	return realloc(map, size);
}

#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)

33 34 35 36 37 38 39 40 41 42 43
struct thread_map *thread_map__new_by_pid(pid_t pid)
{
	struct thread_map *threads;
	char name[256];
	int items;
	struct dirent **namelist = NULL;
	int i;

	sprintf(name, "/proc/%d/task", pid);
	items = scandir(name, &namelist, filter, NULL);
	if (items <= 0)
44
		return NULL;
45

46
	threads = thread_map__alloc(items);
47 48
	if (threads != NULL) {
		for (i = 0; i < items; i++)
49
			thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
50
		threads->nr = items;
51
		atomic_set(&threads->refcnt, 1);
52 53 54
	}

	for (i=0; i<items; i++)
55
		zfree(&namelist[i]);
56 57 58 59 60 61 62
	free(namelist);

	return threads;
}

struct thread_map *thread_map__new_by_tid(pid_t tid)
{
63
	struct thread_map *threads = thread_map__alloc(1);
64 65

	if (threads != NULL) {
66 67
		thread_map__set_pid(threads, 0, tid);
		threads->nr = 1;
68
		atomic_set(&threads->refcnt, 1);
69 70 71 72 73
	}

	return threads;
}

74 75 76 77 78 79
struct thread_map *thread_map__new_by_uid(uid_t uid)
{
	DIR *proc;
	int max_threads = 32, items, i;
	char path[256];
	struct dirent dirent, *next, **namelist = NULL;
80 81
	struct thread_map *threads = thread_map__alloc(max_threads);

82 83 84 85 86 87 88 89
	if (threads == NULL)
		goto out;

	proc = opendir("/proc");
	if (proc == NULL)
		goto out_free_threads;

	threads->nr = 0;
90
	atomic_set(&threads->refcnt, 1);
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

	while (!readdir_r(proc, &dirent, &next) && next) {
		char *end;
		bool grow = false;
		struct stat st;
		pid_t pid = strtol(dirent.d_name, &end, 10);

		if (*end) /* only interested in proper numerical dirents */
			continue;

		snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);

		if (stat(path, &st) != 0)
			continue;

		if (st.st_uid != uid)
			continue;

		snprintf(path, sizeof(path), "/proc/%d/task", pid);
		items = scandir(path, &namelist, filter, NULL);
		if (items <= 0)
			goto out_free_closedir;

		while (threads->nr + items >= max_threads) {
			max_threads *= 2;
			grow = true;
		}

		if (grow) {
			struct thread_map *tmp;

			tmp = realloc(threads, (sizeof(*threads) +
						max_threads * sizeof(pid_t)));
			if (tmp == NULL)
				goto out_free_namelist;

			threads = tmp;
		}

130 131 132 133
		for (i = 0; i < items; i++) {
			thread_map__set_pid(threads, threads->nr + i,
					    atoi(namelist[i]->d_name));
		}
134 135

		for (i = 0; i < items; i++)
136
			zfree(&namelist[i]);
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
		free(namelist);

		threads->nr += items;
	}

out_closedir:
	closedir(proc);
out:
	return threads;

out_free_threads:
	free(threads);
	return NULL;

out_free_namelist:
	for (i = 0; i < items; i++)
153
		zfree(&namelist[i]);
154 155 156
	free(namelist);

out_free_closedir:
157
	zfree(&threads);
158 159 160 161
	goto out_closedir;
}

struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
162 163 164
{
	if (pid != -1)
		return thread_map__new_by_pid(pid);
165 166 167 168

	if (tid == -1 && uid != UINT_MAX)
		return thread_map__new_by_uid(uid);

169 170 171
	return thread_map__new_by_tid(tid);
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
{
	struct thread_map *threads = NULL, *nt;
	char name[256];
	int items, total_tasks = 0;
	struct dirent **namelist = NULL;
	int i, j = 0;
	pid_t pid, prev_pid = INT_MAX;
	char *end_ptr;
	struct str_node *pos;
	struct strlist *slist = strlist__new(false, pid_str);

	if (!slist)
		return NULL;

	strlist__for_each(pos, slist) {
		pid = strtol(pos->s, &end_ptr, 10);

		if (pid == INT_MIN || pid == INT_MAX ||
		    (*end_ptr != '\0' && *end_ptr != ','))
			goto out_free_threads;

		if (pid == prev_pid)
			continue;

		sprintf(name, "/proc/%d/task", pid);
		items = scandir(name, &namelist, filter, NULL);
		if (items <= 0)
			goto out_free_threads;

		total_tasks += items;
203
		nt = thread_map__realloc(threads, total_tasks);
204
		if (nt == NULL)
205
			goto out_free_namelist;
206 207 208

		threads = nt;

209
		for (i = 0; i < items; i++) {
210
			thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
211
			zfree(&namelist[i]);
212 213
		}
		threads->nr = total_tasks;
214 215 216 217 218
		free(namelist);
	}

out:
	strlist__delete(slist);
219 220
	if (threads)
		atomic_set(&threads->refcnt, 1);
221 222
	return threads;

223 224
out_free_namelist:
	for (i = 0; i < items; i++)
225
		zfree(&namelist[i]);
226 227
	free(namelist);

228
out_free_threads:
229
	zfree(&threads);
230 231 232
	goto out;
}

233 234
struct thread_map *thread_map__new_dummy(void)
{
235
	struct thread_map *threads = thread_map__alloc(1);
236 237

	if (threads != NULL) {
238 239
		thread_map__set_pid(threads, 0, -1);
		threads->nr = 1;
240
		atomic_set(&threads->refcnt, 1);
241 242 243 244
	}
	return threads;
}

245 246 247 248 249 250 251 252 253 254
static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
{
	struct thread_map *threads = NULL, *nt;
	int ntasks = 0;
	pid_t tid, prev_tid = INT_MAX;
	char *end_ptr;
	struct str_node *pos;
	struct strlist *slist;

	/* perf-stat expects threads to be generated even if tid not given */
255 256
	if (!tid_str)
		return thread_map__new_dummy();
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

	slist = strlist__new(false, tid_str);
	if (!slist)
		return NULL;

	strlist__for_each(pos, slist) {
		tid = strtol(pos->s, &end_ptr, 10);

		if (tid == INT_MIN || tid == INT_MAX ||
		    (*end_ptr != '\0' && *end_ptr != ','))
			goto out_free_threads;

		if (tid == prev_tid)
			continue;

		ntasks++;
273
		nt = thread_map__realloc(threads, ntasks);
274 275 276 277 278

		if (nt == NULL)
			goto out_free_threads;

		threads = nt;
279 280
		thread_map__set_pid(threads, ntasks - 1, tid);
		threads->nr = ntasks;
281 282
	}
out:
283 284
	if (threads)
		atomic_set(&threads->refcnt, 1);
285 286 287
	return threads;

out_free_threads:
288
	zfree(&threads);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	goto out;
}

struct thread_map *thread_map__new_str(const char *pid, const char *tid,
				       uid_t uid)
{
	if (pid)
		return thread_map__new_by_pid_str(pid);

	if (!tid && uid != UINT_MAX)
		return thread_map__new_by_uid(uid);

	return thread_map__new_by_tid_str(tid);
}

304
static void thread_map__delete(struct thread_map *threads)
305
{
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
	if (threads) {
		WARN_ONCE(atomic_read(&threads->refcnt) != 0,
			  "thread map refcnt unbalanced\n");
		free(threads);
	}
}

struct thread_map *thread_map__get(struct thread_map *map)
{
	if (map)
		atomic_inc(&map->refcnt);
	return map;
}

void thread_map__put(struct thread_map *map)
{
	if (map && atomic_dec_and_test(&map->refcnt))
		thread_map__delete(map);
324
}
325 326 327 328 329 330 331

size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
{
	int i;
	size_t printed = fprintf(fp, "%d thread%s: ",
				 threads->nr, threads->nr > 1 ? "s" : "");
	for (i = 0; i < threads->nr; ++i)
332
		printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
333 334 335

	return printed + fprintf(fp, "\n");
}