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