linux/tools/perf/util/thread_map.c
<<
>>
Prefs
   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 */
  19static int filter(const struct dirent *dir)
  20{
  21        if (dir->d_name[0] == '.')
  22                return 0;
  23        else
  24                return 1;
  25}
  26
  27static 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
  34static 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
  51struct 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
  79struct 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
  92struct 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
 159out_closedir:
 160        closedir(proc);
 161out:
 162        return threads;
 163
 164out_free_threads:
 165        free(threads);
 166        return NULL;
 167
 168out_free_namelist:
 169        for (i = 0; i < items; i++)
 170                zfree(&namelist[i]);
 171        free(namelist);
 172
 173out_free_closedir:
 174        zfree(&threads);
 175        goto out_closedir;
 176}
 177
 178struct 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
 189static 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
 235out:
 236        strlist__delete(slist);
 237        if (threads)
 238                atomic_set(&threads->refcnt, 1);
 239        return threads;
 240
 241out_free_namelist:
 242        for (i = 0; i < items; i++)
 243                zfree(&namelist[i]);
 244        free(namelist);
 245
 246out_free_threads:
 247        zfree(&threads);
 248        goto out;
 249}
 250
 251struct 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
 263struct 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        }
 301out:
 302        if (threads)
 303                atomic_set(&threads->refcnt, 1);
 304        return threads;
 305
 306out_free_threads:
 307        zfree(&threads);
 308        strlist__delete(slist);
 309        goto out;
 310}
 311
 312struct 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
 324static 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
 337struct thread_map *thread_map__get(struct thread_map *map)
 338{
 339        if (map)
 340                atomic_inc(&map->refcnt);
 341        return map;
 342}
 343
 344void thread_map__put(struct thread_map *map)
 345{
 346        if (map && atomic_dec_and_test(&map->refcnt))
 347                thread_map__delete(map);
 348}
 349
 350size_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
 361static 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
 385static 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
 406void 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
 414static 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
 429struct 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
 440bool 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