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 <api/fs/fs.h> 12 #include "asm/bug.h" 13 #include "thread_map.h" 14 #include "util.h" 15 #include "debug.h" 16 17 /* Skip "." and ".." directories */ 18 static int filter(const struct dirent *dir) 19 { 20 if (dir->d_name[0] == '.') 21 return 0; 22 else 23 return 1; 24 } 25 26 static void thread_map__reset(struct thread_map *map, int start, int nr) 27 { 28 size_t size = (nr - start) * sizeof(map->map[0]); 29 30 memset(&map->map[start], 0, size); 31 } 32 33 static struct thread_map *thread_map__realloc(struct thread_map *map, int nr) 34 { 35 size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; 36 int start = map ? map->nr : 0; 37 38 map = realloc(map, size); 39 /* 40 * We only realloc to add more items, let's reset new items. 41 */ 42 if (map) 43 thread_map__reset(map, start, nr); 44 45 return map; 46 } 47 48 #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr) 49 50 struct thread_map *thread_map__new_by_pid(pid_t pid) 51 { 52 struct thread_map *threads; 53 char name[256]; 54 int items; 55 struct dirent **namelist = NULL; 56 int i; 57 58 sprintf(name, "/proc/%d/task", pid); 59 items = scandir(name, &namelist, filter, NULL); 60 if (items <= 0) 61 return NULL; 62 63 threads = thread_map__alloc(items); 64 if (threads != NULL) { 65 for (i = 0; i < items; i++) 66 thread_map__set_pid(threads, i, atoi(namelist[i]->d_name)); 67 threads->nr = items; 68 atomic_set(&threads->refcnt, 1); 69 } 70 71 for (i=0; i<items; i++) 72 zfree(&namelist[i]); 73 free(namelist); 74 75 return threads; 76 } 77 78 struct thread_map *thread_map__new_by_tid(pid_t tid) 79 { 80 struct thread_map *threads = thread_map__alloc(1); 81 82 if (threads != NULL) { 83 thread_map__set_pid(threads, 0, tid); 84 threads->nr = 1; 85 atomic_set(&threads->refcnt, 1); 86 } 87 88 return threads; 89 } 90 91 struct thread_map *thread_map__new_by_uid(uid_t uid) 92 { 93 DIR *proc; 94 int max_threads = 32, items, i; 95 char path[256]; 96 struct dirent dirent, *next, **namelist = NULL; 97 struct thread_map *threads = thread_map__alloc(max_threads); 98 99 if (threads == NULL) 100 goto out; 101 102 proc = opendir("/proc"); 103 if (proc == NULL) 104 goto out_free_threads; 105 106 threads->nr = 0; 107 atomic_set(&threads->refcnt, 1); 108 109 while (!readdir_r(proc, &dirent, &next) && next) { 110 char *end; 111 bool grow = false; 112 struct stat st; 113 pid_t pid = strtol(dirent.d_name, &end, 10); 114 115 if (*end) /* only interested in proper numerical dirents */ 116 continue; 117 118 snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); 119 120 if (stat(path, &st) != 0) 121 continue; 122 123 if (st.st_uid != uid) 124 continue; 125 126 snprintf(path, sizeof(path), "/proc/%d/task", pid); 127 items = scandir(path, &namelist, filter, NULL); 128 if (items <= 0) 129 goto out_free_closedir; 130 131 while (threads->nr + items >= max_threads) { 132 max_threads *= 2; 133 grow = true; 134 } 135 136 if (grow) { 137 struct thread_map *tmp; 138 139 tmp = realloc(threads, (sizeof(*threads) + 140 max_threads * sizeof(pid_t))); 141 if (tmp == NULL) 142 goto out_free_namelist; 143 144 threads = tmp; 145 } 146 147 for (i = 0; i < items; i++) { 148 thread_map__set_pid(threads, threads->nr + i, 149 atoi(namelist[i]->d_name)); 150 } 151 152 for (i = 0; i < items; i++) 153 zfree(&namelist[i]); 154 free(namelist); 155 156 threads->nr += items; 157 } 158 159 out_closedir: 160 closedir(proc); 161 out: 162 return threads; 163 164 out_free_threads: 165 free(threads); 166 return NULL; 167 168 out_free_namelist: 169 for (i = 0; i < items; i++) 170 zfree(&namelist[i]); 171 free(namelist); 172 173 out_free_closedir: 174 zfree(&threads); 175 goto out_closedir; 176 } 177 178 struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) 179 { 180 if (pid != -1) 181 return thread_map__new_by_pid(pid); 182 183 if (tid == -1 && uid != UINT_MAX) 184 return thread_map__new_by_uid(uid); 185 186 return thread_map__new_by_tid(tid); 187 } 188 189 static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) 190 { 191 struct thread_map *threads = NULL, *nt; 192 char name[256]; 193 int items, total_tasks = 0; 194 struct dirent **namelist = NULL; 195 int i, j = 0; 196 pid_t pid, prev_pid = INT_MAX; 197 char *end_ptr; 198 struct str_node *pos; 199 struct strlist_config slist_config = { .dont_dupstr = true, }; 200 struct strlist *slist = strlist__new(pid_str, &slist_config); 201 202 if (!slist) 203 return NULL; 204 205 strlist__for_each(pos, slist) { 206 pid = strtol(pos->s, &end_ptr, 10); 207 208 if (pid == INT_MIN || pid == INT_MAX || 209 (*end_ptr != '\0' && *end_ptr != ',')) 210 goto out_free_threads; 211 212 if (pid == prev_pid) 213 continue; 214 215 sprintf(name, "/proc/%d/task", pid); 216 items = scandir(name, &namelist, filter, NULL); 217 if (items <= 0) 218 goto out_free_threads; 219 220 total_tasks += items; 221 nt = thread_map__realloc(threads, total_tasks); 222 if (nt == NULL) 223 goto out_free_namelist; 224 225 threads = nt; 226 227 for (i = 0; i < items; i++) { 228 thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name)); 229 zfree(&namelist[i]); 230 } 231 threads->nr = total_tasks; 232 free(namelist); 233 } 234 235 out: 236 strlist__delete(slist); 237 if (threads) 238 atomic_set(&threads->refcnt, 1); 239 return threads; 240 241 out_free_namelist: 242 for (i = 0; i < items; i++) 243 zfree(&namelist[i]); 244 free(namelist); 245 246 out_free_threads: 247 zfree(&threads); 248 goto out; 249 } 250 251 struct thread_map *thread_map__new_dummy(void) 252 { 253 struct thread_map *threads = thread_map__alloc(1); 254 255 if (threads != NULL) { 256 thread_map__set_pid(threads, 0, -1); 257 threads->nr = 1; 258 atomic_set(&threads->refcnt, 1); 259 } 260 return threads; 261 } 262 263 static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) 264 { 265 struct thread_map *threads = NULL, *nt; 266 int ntasks = 0; 267 pid_t tid, prev_tid = INT_MAX; 268 char *end_ptr; 269 struct str_node *pos; 270 struct strlist_config slist_config = { .dont_dupstr = true, }; 271 struct strlist *slist; 272 273 /* perf-stat expects threads to be generated even if tid not given */ 274 if (!tid_str) 275 return thread_map__new_dummy(); 276 277 slist = strlist__new(tid_str, &slist_config); 278 if (!slist) 279 return NULL; 280 281 strlist__for_each(pos, slist) { 282 tid = strtol(pos->s, &end_ptr, 10); 283 284 if (tid == INT_MIN || tid == INT_MAX || 285 (*end_ptr != '\0' && *end_ptr != ',')) 286 goto out_free_threads; 287 288 if (tid == prev_tid) 289 continue; 290 291 ntasks++; 292 nt = thread_map__realloc(threads, ntasks); 293 294 if (nt == NULL) 295 goto out_free_threads; 296 297 threads = nt; 298 thread_map__set_pid(threads, ntasks - 1, tid); 299 threads->nr = ntasks; 300 } 301 out: 302 if (threads) 303 atomic_set(&threads->refcnt, 1); 304 return threads; 305 306 out_free_threads: 307 zfree(&threads); 308 goto out; 309 } 310 311 struct thread_map *thread_map__new_str(const char *pid, const char *tid, 312 uid_t uid) 313 { 314 if (pid) 315 return thread_map__new_by_pid_str(pid); 316 317 if (!tid && uid != UINT_MAX) 318 return thread_map__new_by_uid(uid); 319 320 return thread_map__new_by_tid_str(tid); 321 } 322 323 static void thread_map__delete(struct thread_map *threads) 324 { 325 if (threads) { 326 int i; 327 328 WARN_ONCE(atomic_read(&threads->refcnt) != 0, 329 "thread map refcnt unbalanced\n"); 330 for (i = 0; i < threads->nr; i++) 331 free(thread_map__comm(threads, i)); 332 free(threads); 333 } 334 } 335 336 struct thread_map *thread_map__get(struct thread_map *map) 337 { 338 if (map) 339 atomic_inc(&map->refcnt); 340 return map; 341 } 342 343 void thread_map__put(struct thread_map *map) 344 { 345 if (map && atomic_dec_and_test(&map->refcnt)) 346 thread_map__delete(map); 347 } 348 349 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) 350 { 351 int i; 352 size_t printed = fprintf(fp, "%d thread%s: ", 353 threads->nr, threads->nr > 1 ? "s" : ""); 354 for (i = 0; i < threads->nr; ++i) 355 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i)); 356 357 return printed + fprintf(fp, "\n"); 358 } 359 360 static int get_comm(char **comm, pid_t pid) 361 { 362 char *path; 363 size_t size; 364 int err; 365 366 if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1) 367 return -ENOMEM; 368 369 err = filename__read_str(path, comm, &size); 370 if (!err) { 371 /* 372 * We're reading 16 bytes, while filename__read_str 373 * allocates data per BUFSIZ bytes, so we can safely 374 * mark the end of the string. 375 */ 376 (*comm)[size] = 0; 377 rtrim(*comm); 378 } 379 380 free(path); 381 return err; 382 } 383 384 static void comm_init(struct thread_map *map, int i) 385 { 386 pid_t pid = thread_map__pid(map, i); 387 char *comm = NULL; 388 389 /* dummy pid comm initialization */ 390 if (pid == -1) { 391 map->map[i].comm = strdup("dummy"); 392 return; 393 } 394 395 /* 396 * The comm name is like extra bonus ;-), 397 * so just warn if we fail for any reason. 398 */ 399 if (get_comm(&comm, pid)) 400 pr_warning("Couldn't resolve comm name for pid %d\n", pid); 401 402 map->map[i].comm = comm; 403 } 404 405 void thread_map__read_comms(struct thread_map *threads) 406 { 407 int i; 408 409 for (i = 0; i < threads->nr; ++i) 410 comm_init(threads, i); 411 } 412