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