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