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 */ 15 static int filter(const struct dirent *dir) 16 { 17 if (dir->d_name[0] == '.') 18 return 0; 19 else 20 return 1; 21 } 22 23 struct 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 50 struct 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 62 struct 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 127 out_closedir: 128 closedir(proc); 129 out: 130 return threads; 131 132 out_free_threads: 133 free(threads); 134 return NULL; 135 136 out_free_namelist: 137 for (i = 0; i < items; i++) 138 zfree(&namelist[i]); 139 free(namelist); 140 141 out_free_closedir: 142 zfree(&threads); 143 goto out_closedir; 144 } 145 146 struct 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 157 static 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 203 out: 204 strlist__delete(slist); 205 return threads; 206 207 out_free_namelist: 208 for (i = 0; i < items; i++) 209 zfree(&namelist[i]); 210 free(namelist); 211 212 out_free_threads: 213 zfree(&threads); 214 goto out; 215 } 216 217 struct thread_map *thread_map__new_dummy(void) 218 { 219 struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); 220 221 if (threads != NULL) { 222 threads->map[0] = -1; 223 threads->nr = 1; 224 } 225 return threads; 226 } 227 228 static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) 229 { 230 struct thread_map *threads = NULL, *nt; 231 int ntasks = 0; 232 pid_t tid, prev_tid = INT_MAX; 233 char *end_ptr; 234 struct str_node *pos; 235 struct strlist *slist; 236 237 /* perf-stat expects threads to be generated even if tid not given */ 238 if (!tid_str) 239 return thread_map__new_dummy(); 240 241 slist = strlist__new(false, tid_str); 242 if (!slist) 243 return NULL; 244 245 strlist__for_each(pos, slist) { 246 tid = strtol(pos->s, &end_ptr, 10); 247 248 if (tid == INT_MIN || tid == INT_MAX || 249 (*end_ptr != '\0' && *end_ptr != ',')) 250 goto out_free_threads; 251 252 if (tid == prev_tid) 253 continue; 254 255 ntasks++; 256 nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks); 257 258 if (nt == NULL) 259 goto out_free_threads; 260 261 threads = nt; 262 threads->map[ntasks - 1] = tid; 263 threads->nr = ntasks; 264 } 265 out: 266 return threads; 267 268 out_free_threads: 269 zfree(&threads); 270 goto out; 271 } 272 273 struct thread_map *thread_map__new_str(const char *pid, const char *tid, 274 uid_t uid) 275 { 276 if (pid) 277 return thread_map__new_by_pid_str(pid); 278 279 if (!tid && uid != UINT_MAX) 280 return thread_map__new_by_uid(uid); 281 282 return thread_map__new_by_tid_str(tid); 283 } 284 285 void thread_map__delete(struct thread_map *threads) 286 { 287 free(threads); 288 } 289 290 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) 291 { 292 int i; 293 size_t printed = fprintf(fp, "%d thread%s: ", 294 threads->nr, threads->nr > 1 ? "s" : ""); 295 for (i = 0; i < threads->nr; ++i) 296 printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]); 297 298 return printed + fprintf(fp, "\n"); 299 } 300