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 "thread_map.h"
  12#include "util.h"
  13
  14/* Skip "." and ".." directories */
  15static int filter(const struct dirent *dir)
  16{
  17        if (dir->d_name[0] == '.')
  18                return 0;
  19        else
  20                return 1;
  21}
  22
  23struct thread_map *thread_map__new_by_pid(pid_t pid)
  24{
  25        struct thread_map *threads;
  26        char name[256];
  27        int items;
  28        struct dirent **namelist = NULL;
  29        int i;
  30
  31        sprintf(name, "/proc/%d/task", pid);
  32        items = scandir(name, &namelist, filter, NULL);
  33        if (items <= 0)
  34                return NULL;
  35
  36        threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
  37        if (threads != NULL) {
  38                for (i = 0; i < items; i++)
  39                        threads->map[i] = atoi(namelist[i]->d_name);
  40                threads->nr = items;
  41        }
  42
  43        for (i=0; i<items; i++)
  44                zfree(&namelist[i]);
  45        free(namelist);
  46
  47        return threads;
  48}
  49
  50struct thread_map *thread_map__new_by_tid(pid_t tid)
  51{
  52        struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
  53
  54        if (threads != NULL) {
  55                threads->map[0] = tid;
  56                threads->nr     = 1;
  57        }
  58
  59        return threads;
  60}
  61
  62struct thread_map *thread_map__new_by_uid(uid_t uid)
  63{
  64        DIR *proc;
  65        int max_threads = 32, items, i;
  66        char path[256];
  67        struct dirent dirent, *next, **namelist = NULL;
  68        struct thread_map *threads = malloc(sizeof(*threads) +
  69                                            max_threads * sizeof(pid_t));
  70        if (threads == NULL)
  71                goto out;
  72
  73        proc = opendir("/proc");
  74        if (proc == NULL)
  75                goto out_free_threads;
  76
  77        threads->nr = 0;
  78
  79        while (!readdir_r(proc, &dirent, &next) && next) {
  80                char *end;
  81                bool grow = false;
  82                struct stat st;
  83                pid_t pid = strtol(dirent.d_name, &end, 10);
  84
  85                if (*end) /* only interested in proper numerical dirents */
  86                        continue;
  87
  88                snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
  89
  90                if (stat(path, &st) != 0)
  91                        continue;
  92
  93                if (st.st_uid != uid)
  94                        continue;
  95
  96                snprintf(path, sizeof(path), "/proc/%d/task", pid);
  97                items = scandir(path, &namelist, filter, NULL);
  98                if (items <= 0)
  99                        goto out_free_closedir;
 100
 101                while (threads->nr + items >= max_threads) {
 102                        max_threads *= 2;
 103                        grow = true;
 104                }
 105
 106                if (grow) {
 107                        struct thread_map *tmp;
 108
 109                        tmp = realloc(threads, (sizeof(*threads) +
 110                                                max_threads * sizeof(pid_t)));
 111                        if (tmp == NULL)
 112                                goto out_free_namelist;
 113
 114                        threads = tmp;
 115                }
 116
 117                for (i = 0; i < items; i++)
 118                        threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
 119
 120                for (i = 0; i < items; i++)
 121                        zfree(&namelist[i]);
 122                free(namelist);
 123
 124                threads->nr += items;
 125        }
 126
 127out_closedir:
 128        closedir(proc);
 129out:
 130        return threads;
 131
 132out_free_threads:
 133        free(threads);
 134        return NULL;
 135
 136out_free_namelist:
 137        for (i = 0; i < items; i++)
 138                zfree(&namelist[i]);
 139        free(namelist);
 140
 141out_free_closedir:
 142        zfree(&threads);
 143        goto out_closedir;
 144}
 145
 146struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
 147{
 148        if (pid != -1)
 149                return thread_map__new_by_pid(pid);
 150
 151        if (tid == -1 && uid != UINT_MAX)
 152                return thread_map__new_by_uid(uid);
 153
 154        return thread_map__new_by_tid(tid);
 155}
 156
 157static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
 158{
 159        struct thread_map *threads = NULL, *nt;
 160        char name[256];
 161        int items, total_tasks = 0;
 162        struct dirent **namelist = NULL;
 163        int i, j = 0;
 164        pid_t pid, prev_pid = INT_MAX;
 165        char *end_ptr;
 166        struct str_node *pos;
 167        struct strlist *slist = strlist__new(false, pid_str);
 168
 169        if (!slist)
 170                return NULL;
 171
 172        strlist__for_each(pos, slist) {
 173                pid = strtol(pos->s, &end_ptr, 10);
 174
 175                if (pid == INT_MIN || pid == INT_MAX ||
 176                    (*end_ptr != '\0' && *end_ptr != ','))
 177                        goto out_free_threads;
 178
 179                if (pid == prev_pid)
 180                        continue;
 181
 182                sprintf(name, "/proc/%d/task", pid);
 183                items = scandir(name, &namelist, filter, NULL);
 184                if (items <= 0)
 185                        goto out_free_threads;
 186
 187                total_tasks += items;
 188                nt = realloc(threads, (sizeof(*threads) +
 189                                       sizeof(pid_t) * total_tasks));
 190                if (nt == NULL)
 191                        goto out_free_namelist;
 192
 193                threads = nt;
 194
 195                for (i = 0; i < items; i++) {
 196                        threads->map[j++] = atoi(namelist[i]->d_name);
 197                        zfree(&namelist[i]);
 198                }
 199                threads->nr = total_tasks;
 200                free(namelist);
 201        }
 202
 203out:
 204        strlist__delete(slist);
 205        return threads;
 206
 207out_free_namelist:
 208        for (i = 0; i < items; i++)
 209                zfree(&namelist[i]);
 210        free(namelist);
 211
 212out_free_threads:
 213        zfree(&threads);
 214        goto out;
 215}
 216
 217static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
 218{
 219        struct thread_map *threads = NULL, *nt;
 220        int ntasks = 0;
 221        pid_t tid, prev_tid = INT_MAX;
 222        char *end_ptr;
 223        struct str_node *pos;
 224        struct strlist *slist;
 225
 226        /* perf-stat expects threads to be generated even if tid not given */
 227        if (!tid_str) {
 228                threads = malloc(sizeof(*threads) + sizeof(pid_t));
 229                if (threads != NULL) {
 230                        threads->map[0] = -1;
 231                        threads->nr     = 1;
 232                }
 233                return threads;
 234        }
 235
 236        slist = strlist__new(false, tid_str);
 237        if (!slist)
 238                return NULL;
 239
 240        strlist__for_each(pos, slist) {
 241                tid = strtol(pos->s, &end_ptr, 10);
 242
 243                if (tid == INT_MIN || tid == INT_MAX ||
 244                    (*end_ptr != '\0' && *end_ptr != ','))
 245                        goto out_free_threads;
 246
 247                if (tid == prev_tid)
 248                        continue;
 249
 250                ntasks++;
 251                nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
 252
 253                if (nt == NULL)
 254                        goto out_free_threads;
 255
 256                threads = nt;
 257                threads->map[ntasks - 1] = tid;
 258                threads->nr              = ntasks;
 259        }
 260out:
 261        return threads;
 262
 263out_free_threads:
 264        zfree(&threads);
 265        goto out;
 266}
 267
 268struct thread_map *thread_map__new_str(const char *pid, const char *tid,
 269                                       uid_t uid)
 270{
 271        if (pid)
 272                return thread_map__new_by_pid_str(pid);
 273
 274        if (!tid && uid != UINT_MAX)
 275                return thread_map__new_by_uid(uid);
 276
 277        return thread_map__new_by_tid_str(tid);
 278}
 279
 280void thread_map__delete(struct thread_map *threads)
 281{
 282        free(threads);
 283}
 284
 285size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
 286{
 287        int i;
 288        size_t printed = fprintf(fp, "%d thread%s: ",
 289                                 threads->nr, threads->nr > 1 ? "s" : "");
 290        for (i = 0; i < threads->nr; ++i)
 291                printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
 292
 293        return printed + fprintf(fp, "\n");
 294}
 295