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 = 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 158 out_closedir: 159 closedir(proc); 160 out: 161 return threads; 162 163 out_free_threads: 164 free(threads); 165 return NULL; 166 167 out_free_namelist: 168 for (i = 0; i < items; i++) 169 zfree(&namelist[i]); 170 free(namelist); 171 172 out_free_closedir: 173 zfree(&threads); 174 goto out_closedir; 175 } 176 177 struct 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 188 static 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 233 out: 234 strlist__delete(slist); 235 if (threads) 236 atomic_set(&threads->refcnt, 1); 237 return threads; 238 239 out_free_namelist: 240 for (i = 0; i < items; i++) 241 zfree(&namelist[i]); 242 free(namelist); 243 244 out_free_threads: 245 zfree(&threads); 246 goto out; 247 } 248 249 struct 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 261 static 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 } 298 out: 299 if (threads) 300 atomic_set(&threads->refcnt, 1); 301 return threads; 302 303 out_free_threads: 304 zfree(&threads); 305 goto out; 306 } 307 308 struct 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 320 static 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 333 struct thread_map *thread_map__get(struct thread_map *map) 334 { 335 if (map) 336 atomic_inc(&map->refcnt); 337 return map; 338 } 339 340 void thread_map__put(struct thread_map *map) 341 { 342 if (map && atomic_dec_and_test(&map->refcnt)) 343 thread_map__delete(map); 344 } 345 346 size_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 357 static 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 381 static 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 402 void 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