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 */ 18 static int filter(const struct dirent *dir) 19 { 20 if (dir->d_name[0] == '.') 21 return 0; 22 else 23 return 1; 24 } 25 26 static 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 33 static 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 50 struct 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 78 struct 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 91 struct 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 = realloc(threads, (sizeof(*threads) + 140 max_threads * sizeof(pid_t))); 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 159 out_closedir: 160 closedir(proc); 161 out: 162 return threads; 163 164 out_free_threads: 165 free(threads); 166 return NULL; 167 168 out_free_namelist: 169 for (i = 0; i < items; i++) 170 zfree(&namelist[i]); 171 free(namelist); 172 173 out_free_closedir: 174 zfree(&threads); 175 goto out_closedir; 176 } 177 178 struct 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 189 static 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 *slist = strlist__new(false, pid_str); 200 201 if (!slist) 202 return NULL; 203 204 strlist__for_each(pos, slist) { 205 pid = strtol(pos->s, &end_ptr, 10); 206 207 if (pid == INT_MIN || pid == INT_MAX || 208 (*end_ptr != '\0' && *end_ptr != ',')) 209 goto out_free_threads; 210 211 if (pid == prev_pid) 212 continue; 213 214 sprintf(name, "/proc/%d/task", pid); 215 items = scandir(name, &namelist, filter, NULL); 216 if (items <= 0) 217 goto out_free_threads; 218 219 total_tasks += items; 220 nt = thread_map__realloc(threads, total_tasks); 221 if (nt == NULL) 222 goto out_free_namelist; 223 224 threads = nt; 225 226 for (i = 0; i < items; i++) { 227 thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name)); 228 zfree(&namelist[i]); 229 } 230 threads->nr = total_tasks; 231 free(namelist); 232 } 233 234 out: 235 strlist__delete(slist); 236 if (threads) 237 atomic_set(&threads->refcnt, 1); 238 return threads; 239 240 out_free_namelist: 241 for (i = 0; i < items; i++) 242 zfree(&namelist[i]); 243 free(namelist); 244 245 out_free_threads: 246 zfree(&threads); 247 goto out; 248 } 249 250 struct thread_map *thread_map__new_dummy(void) 251 { 252 struct thread_map *threads = thread_map__alloc(1); 253 254 if (threads != NULL) { 255 thread_map__set_pid(threads, 0, -1); 256 threads->nr = 1; 257 atomic_set(&threads->refcnt, 1); 258 } 259 return threads; 260 } 261 262 static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) 263 { 264 struct thread_map *threads = NULL, *nt; 265 int ntasks = 0; 266 pid_t tid, prev_tid = INT_MAX; 267 char *end_ptr; 268 struct str_node *pos; 269 struct strlist *slist; 270 271 /* perf-stat expects threads to be generated even if tid not given */ 272 if (!tid_str) 273 return thread_map__new_dummy(); 274 275 slist = strlist__new(false, tid_str); 276 if (!slist) 277 return NULL; 278 279 strlist__for_each(pos, slist) { 280 tid = strtol(pos->s, &end_ptr, 10); 281 282 if (tid == INT_MIN || tid == INT_MAX || 283 (*end_ptr != '\0' && *end_ptr != ',')) 284 goto out_free_threads; 285 286 if (tid == prev_tid) 287 continue; 288 289 ntasks++; 290 nt = thread_map__realloc(threads, ntasks); 291 292 if (nt == NULL) 293 goto out_free_threads; 294 295 threads = nt; 296 thread_map__set_pid(threads, ntasks - 1, tid); 297 threads->nr = ntasks; 298 } 299 out: 300 if (threads) 301 atomic_set(&threads->refcnt, 1); 302 return threads; 303 304 out_free_threads: 305 zfree(&threads); 306 goto out; 307 } 308 309 struct thread_map *thread_map__new_str(const char *pid, const char *tid, 310 uid_t uid) 311 { 312 if (pid) 313 return thread_map__new_by_pid_str(pid); 314 315 if (!tid && uid != UINT_MAX) 316 return thread_map__new_by_uid(uid); 317 318 return thread_map__new_by_tid_str(tid); 319 } 320 321 static void thread_map__delete(struct thread_map *threads) 322 { 323 if (threads) { 324 int i; 325 326 WARN_ONCE(atomic_read(&threads->refcnt) != 0, 327 "thread map refcnt unbalanced\n"); 328 for (i = 0; i < threads->nr; i++) 329 free(thread_map__comm(threads, i)); 330 free(threads); 331 } 332 } 333 334 struct thread_map *thread_map__get(struct thread_map *map) 335 { 336 if (map) 337 atomic_inc(&map->refcnt); 338 return map; 339 } 340 341 void thread_map__put(struct thread_map *map) 342 { 343 if (map && atomic_dec_and_test(&map->refcnt)) 344 thread_map__delete(map); 345 } 346 347 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) 348 { 349 int i; 350 size_t printed = fprintf(fp, "%d thread%s: ", 351 threads->nr, threads->nr > 1 ? "s" : ""); 352 for (i = 0; i < threads->nr; ++i) 353 printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i)); 354 355 return printed + fprintf(fp, "\n"); 356 } 357 358 static int get_comm(char **comm, pid_t pid) 359 { 360 char *path; 361 size_t size; 362 int err; 363 364 if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1) 365 return -ENOMEM; 366 367 err = filename__read_str(path, comm, &size); 368 if (!err) { 369 /* 370 * We're reading 16 bytes, while filename__read_str 371 * allocates data per BUFSIZ bytes, so we can safely 372 * mark the end of the string. 373 */ 374 (*comm)[size] = 0; 375 rtrim(*comm); 376 } 377 378 free(path); 379 return err; 380 } 381 382 static void comm_init(struct thread_map *map, int i) 383 { 384 pid_t pid = thread_map__pid(map, i); 385 char *comm = NULL; 386 387 /* dummy pid comm initialization */ 388 if (pid == -1) { 389 map->map[i].comm = strdup("dummy"); 390 return; 391 } 392 393 /* 394 * The comm name is like extra bonus ;-), 395 * so just warn if we fail for any reason. 396 */ 397 if (get_comm(&comm, pid)) 398 pr_warning("Couldn't resolve comm name for pid %d\n", pid); 399 400 map->map[i].comm = comm; 401 } 402 403 void thread_map__read_comms(struct thread_map *threads) 404 { 405 int i; 406 407 for (i = 0; i < threads->nr; ++i) 408 comm_init(threads, i); 409 } 410