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