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