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