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