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 #include "event.h" 17 18 /* Skip "." and ".." directories */ 19 static int filter(const struct dirent *dir) 20 { 21 if (dir->d_name[0] == '.') 22 return 0; 23 else 24 return 1; 25 } 26 27 static void thread_map__reset(struct thread_map *map, int start, int nr) 28 { 29 size_t size = (nr - start) * sizeof(map->map[0]); 30 31 memset(&map->map[start], 0, size); 32 } 33 34 static struct thread_map *thread_map__realloc(struct thread_map *map, int nr) 35 { 36 size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; 37 int start = map ? map->nr : 0; 38 39 map = realloc(map, size); 40 /* 41 * We only realloc to add more items, let's reset new items. 42 */ 43 if (map) 44 thread_map__reset(map, start, nr); 45 46 return map; 47 } 48 49 #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr) 50 51 struct thread_map *thread_map__new_by_pid(pid_t pid) 52 { 53 struct thread_map *threads; 54 char name[256]; 55 int items; 56 struct dirent **namelist = NULL; 57 int i; 58 59 sprintf(name, "/proc/%d/task", pid); 60 items = scandir(name, &namelist, filter, NULL); 61 if (items <= 0) 62 return NULL; 63 64 threads = thread_map__alloc(items); 65 if (threads != NULL) { 66 for (i = 0; i < items; i++) 67 thread_map__set_pid(threads, i, atoi(namelist[i]->d_name)); 68 threads->nr = items; 69 atomic_set(&threads->refcnt, 1); 70 } 71 72 for (i=0; i<items; i++) 73 zfree(&namelist[i]); 74 free(namelist); 75 76 return threads; 77 } 78 79 struct thread_map *thread_map__new_by_tid(pid_t tid) 80 { 81 struct thread_map *threads = thread_map__alloc(1); 82 83 if (threads != NULL) { 84 thread_map__set_pid(threads, 0, tid); 85 threads->nr = 1; 86 atomic_set(&threads->refcnt, 1); 87 } 88 89 return threads; 90 } 91 92 struct thread_map *thread_map__new_by_uid(uid_t uid) 93 { 94 DIR *proc; 95 int max_threads = 32, items, i; 96 char path[256]; 97 struct dirent *dirent, **namelist = NULL; 98 struct thread_map *threads = thread_map__alloc(max_threads); 99 100 if (threads == NULL) 101 goto out; 102 103 proc = opendir("/proc"); 104 if (proc == NULL) 105 goto out_free_threads; 106 107 threads->nr = 0; 108 atomic_set(&threads->refcnt, 1); 109 110 while ((dirent = readdir(proc)) != NULL) { 111 char *end; 112 bool grow = false; 113 struct stat st; 114 pid_t pid = strtol(dirent->d_name, &end, 10); 115 116 if (*end) /* only interested in proper numerical dirents */ 117 continue; 118 119 snprintf(path, sizeof(path), "/proc/%s", dirent->d_name); 120 121 if (stat(path, &st) != 0) 122 continue; 123 124 if (st.st_uid != uid) 125 continue; 126 127 snprintf(path, sizeof(path), "/proc/%d/task", pid); 128 items = scandir(path, &namelist, filter, NULL); 129 if (items <= 0) 130 goto out_free_closedir; 131 132 while (threads->nr + items >= max_threads) { 133 max_threads *= 2; 134 grow = true; 135 } 136 137 if (grow) { 138 struct thread_map *tmp; 139 140 tmp = thread_map__realloc(threads, max_threads); 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_entry(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 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_entry(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 strlist__delete(slist); 309 goto out; 310 } 311 312 struct thread_map *thread_map__new_str(const char *pid, const char *tid, 313 uid_t uid) 314 { 315 if (pid) 316 return thread_map__new_by_pid_str(pid); 317 318 if (!tid && uid != UINT_MAX) 319 return thread_map__new_by_uid(uid); 320 321 return thread_map__new_by_tid_str(tid); 322 } 323 324 static void thread_map__delete(struct thread_map *threads) 325 { 326 if (threads) { 327 int i; 328 329 WARN_ONCE(atomic_read(&threads->refcnt) != 0, 330 "thread map refcnt unbalanced\n"); 331 for (i = 0; i < threads->nr; i++) 332 free(thread_map__comm(threads, i)); 333 free(threads); 334 } 335 } 336 337 struct thread_map *thread_map__get(struct thread_map *map) 338 { 339 if (map) 340 atomic_inc(&map->refcnt); 341 return map; 342 } 343 344 void thread_map__put(struct thread_map *map) 345 { 346 if (map && atomic_dec_and_test(&map->refcnt)) 347 thread_map__delete(map); 348 } 349 350 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) 351 { 352 int i; 353 size_t printed = fprintf(fp, "%d thread%s: ", 354 threads->nr, threads->nr > 1 ? "s" : ""); 355 for (i = 0; i < threads->nr; ++i) 356 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i)); 357 358 return printed + fprintf(fp, "\n"); 359 } 360 361 static int get_comm(char **comm, pid_t pid) 362 { 363 char *path; 364 size_t size; 365 int err; 366 367 if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1) 368 return -ENOMEM; 369 370 err = filename__read_str(path, comm, &size); 371 if (!err) { 372 /* 373 * We're reading 16 bytes, while filename__read_str 374 * allocates data per BUFSIZ bytes, so we can safely 375 * mark the end of the string. 376 */ 377 (*comm)[size] = 0; 378 rtrim(*comm); 379 } 380 381 free(path); 382 return err; 383 } 384 385 static void comm_init(struct thread_map *map, int i) 386 { 387 pid_t pid = thread_map__pid(map, i); 388 char *comm = NULL; 389 390 /* dummy pid comm initialization */ 391 if (pid == -1) { 392 map->map[i].comm = strdup("dummy"); 393 return; 394 } 395 396 /* 397 * The comm name is like extra bonus ;-), 398 * so just warn if we fail for any reason. 399 */ 400 if (get_comm(&comm, pid)) 401 pr_warning("Couldn't resolve comm name for pid %d\n", pid); 402 403 map->map[i].comm = comm; 404 } 405 406 void thread_map__read_comms(struct thread_map *threads) 407 { 408 int i; 409 410 for (i = 0; i < threads->nr; ++i) 411 comm_init(threads, i); 412 } 413 414 static void thread_map__copy_event(struct thread_map *threads, 415 struct thread_map_event *event) 416 { 417 unsigned i; 418 419 threads->nr = (int) event->nr; 420 421 for (i = 0; i < event->nr; i++) { 422 thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid); 423 threads->map[i].comm = strndup(event->entries[i].comm, 16); 424 } 425 426 atomic_set(&threads->refcnt, 1); 427 } 428 429 struct thread_map *thread_map__new_event(struct thread_map_event *event) 430 { 431 struct thread_map *threads; 432 433 threads = thread_map__alloc(event->nr); 434 if (threads) 435 thread_map__copy_event(threads, event); 436 437 return threads; 438 } 439 440 bool thread_map__has(struct thread_map *threads, pid_t pid) 441 { 442 int i; 443 444 for (i = 0; i < threads->nr; ++i) { 445 if (threads->map[i].pid == pid) 446 return true; 447 } 448 449 return false; 450 } 451 452 int thread_map__remove(struct thread_map *threads, int idx) 453 { 454 int i; 455 456 if (threads->nr < 1) 457 return -EINVAL; 458 459 if (idx >= threads->nr) 460 return -EINVAL; 461 462 /* 463 * Free the 'idx' item and shift the rest up. 464 */ 465 free(threads->map[idx].comm); 466 467 for (i = idx; i < threads->nr - 1; i++) 468 threads->map[i] = threads->map[i + 1]; 469 470 threads->nr--; 471 return 0; 472 } 473