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