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 static 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 } 260 out: 261 return threads; 262 263 out_free_threads: 264 zfree(&threads); 265 goto out; 266 } 267 268 struct 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 280 void thread_map__delete(struct thread_map *threads) 281 { 282 free(threads); 283 } 284 285 size_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