linux/tools/perf/util/thread_map.c
<<
>>
Prefs
   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}
 490