1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <ctype.h> 18 #include <dirent.h> 19 #include <errno.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 25 #define MAX_LINE 512 26 #define MAX_FILENAME 64 27 28 const char *EXPECTED_VERSION = "Latency Top version : v0.1\n"; 29 const char *SYSCTL_FILE = "/proc/sys/kernel/latencytop"; 30 const char *GLOBAL_STATS_FILE = "/proc/latency_stats"; 31 const char *THREAD_STATS_FILE_FORMAT = "/proc/%d/task/%d/latency"; 32 33 struct latency_entry { 34 struct latency_entry *next; 35 unsigned long count; 36 unsigned long max; 37 unsigned long total; 38 char reason[MAX_LINE]; 39 }; 40 41 static inline void check_latencytop() { } 42 43 static struct latency_entry *read_global_stats(struct latency_entry *list, int erase); 44 static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid); 45 static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal); 46 47 static struct latency_entry *alloc_latency_entry(void); 48 static void free_latency_entry(struct latency_entry *e); 49 50 static void set_latencytop(int on); 51 static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list); 52 static void erase_latency_file(FILE *f); 53 54 static struct latency_entry *find_latency_entry(struct latency_entry *e, char *reason); 55 static void print_latency_entries(struct latency_entry *head); 56 57 static void signal_handler(int sig); 58 static void disable_latencytop(void); 59 60 static int numcmp(const long long a, const long long b); 61 static int lat_cmp(const void *a, const void *b); 62 63 static void clear_screen(void); 64 static void usage(const char *cmd); 65 66 struct latency_entry *free_entries; 67 68 int main(int argc, char *argv[]) { 69 struct latency_entry *e; 70 int delay, iterations; 71 int pid, tid; 72 int count, erase; 73 int i; 74 75 delay = 1; 76 iterations = 0; 77 pid = tid = 0; 78 79 for (i = 1; i < argc; i++) { 80 if (!strcmp(argv[i], "-d")) { 81 if (i >= argc - 1) { 82 fprintf(stderr, "Option -d expects an argument.\n"); 83 exit(EXIT_FAILURE); 84 } 85 delay = atoi(argv[++i]); 86 continue; 87 } 88 if (!strcmp(argv[i], "-n")) { 89 if (i >= argc - 1) { 90 fprintf(stderr, "Option -n expects an argument.\n"); 91 exit(EXIT_FAILURE); 92 } 93 iterations = atoi(argv[++i]); 94 continue; 95 } 96 if (!strcmp(argv[i], "-h")) { 97 usage(argv[0]); 98 exit(EXIT_SUCCESS); 99 } 100 if (!strcmp(argv[i], "-p")) { 101 if (i >= argc - 1) { 102 fprintf(stderr, "Option -p expects an argument.\n"); 103 exit(EXIT_FAILURE); 104 } 105 pid = atoi(argv[++i]); 106 continue; 107 } 108 if (!strcmp(argv[i], "-t")) { 109 if (i >= argc - 1) { 110 fprintf(stderr, "Option -t expects an argument.\n"); 111 exit(EXIT_FAILURE); 112 } 113 tid = atoi(argv[++i]); 114 continue; 115 } 116 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); 117 usage(argv[0]); 118 exit(EXIT_FAILURE); 119 } 120 121 if (tid && !pid) { 122 fprintf(stderr, "If you provide a thread ID with -t, you must provide a process ID with -p.\n"); 123 exit(EXIT_FAILURE); 124 } 125 126 check_latencytop(); 127 128 free_entries = NULL; 129 130 signal(SIGINT, &signal_handler); 131 signal(SIGTERM, &signal_handler); 132 133 atexit(&disable_latencytop); 134 135 set_latencytop(1); 136 137 count = 0; 138 erase = 1; 139 140 while ((iterations == 0) || (count++ < iterations)) { 141 142 sleep(delay); 143 144 e = NULL; 145 if (pid) { 146 if (tid) { 147 e = read_thread_stats(e, erase, pid, tid, 1); 148 } else { 149 e = read_process_stats(e, erase, pid); 150 } 151 } else { 152 e = read_global_stats(e, erase); 153 } 154 erase = 0; 155 156 clear_screen(); 157 if (pid) { 158 if (tid) { 159 printf("Latencies for thread %d in process %d:\n", tid, pid); 160 } else { 161 printf("Latencies for process %d:\n", pid); 162 } 163 } else { 164 printf("Latencies across all processes:\n"); 165 } 166 print_latency_entries(e); 167 } 168 169 set_latencytop(0); 170 171 return 0; 172 } 173 174 static struct latency_entry *read_global_stats(struct latency_entry *list, int erase) { 175 FILE *f; 176 struct latency_entry *e; 177 178 if (erase) { 179 f = fopen(GLOBAL_STATS_FILE, "w"); 180 if (!f) { 181 fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno)); 182 exit(EXIT_FAILURE); 183 } 184 fprintf(f, "erase\n"); 185 fclose(f); 186 } 187 188 f = fopen(GLOBAL_STATS_FILE, "r"); 189 if (!f) { 190 fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno)); 191 exit(EXIT_FAILURE); 192 } 193 194 e = read_latency_file(f, list); 195 196 fclose(f); 197 198 return e; 199 } 200 201 static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid) { 202 char dirname[MAX_FILENAME]; 203 DIR *dir; 204 struct dirent *ent; 205 struct latency_entry *e; 206 int tid; 207 208 sprintf(dirname, "/proc/%d/task", pid); 209 dir = opendir(dirname); 210 if (!dir) { 211 fprintf(stderr, "Could not open task dir for process %d.\n", pid); 212 fprintf(stderr, "Perhaps the process has terminated?\n"); 213 exit(EXIT_FAILURE); 214 } 215 216 e = list; 217 while ((ent = readdir(dir))) { 218 if (!isdigit(ent->d_name[0])) 219 continue; 220 221 tid = atoi(ent->d_name); 222 223 e = read_thread_stats(e, erase, pid, tid, 0); 224 } 225 226 closedir(dir); 227 228 return e; 229 } 230 231 static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal) { 232 char filename[MAX_FILENAME]; 233 FILE *f; 234 struct latency_entry *e; 235 236 sprintf(filename, THREAD_STATS_FILE_FORMAT, pid, tid); 237 238 if (erase) { 239 f = fopen(filename, "w"); 240 if (!f) { 241 if (fatal) { 242 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno)); 243 fprintf(stderr, "Perhaps the process or thread has terminated?\n"); 244 exit(EXIT_FAILURE); 245 } else { 246 return list; 247 } 248 } 249 fprintf(f, "erase\n"); 250 fclose(f); 251 } 252 253 f = fopen(GLOBAL_STATS_FILE, "r"); 254 if (!f) { 255 if (fatal) { 256 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno)); 257 fprintf(stderr, "Perhaps the process or thread has terminated?\n"); 258 exit(EXIT_FAILURE); 259 } else { 260 return list; 261 } 262 } 263 264 e = read_latency_file(f, list); 265 266 fclose(f); 267 268 return e; 269 } 270 271 static struct latency_entry *alloc_latency_entry(void) { 272 struct latency_entry *e; 273 274 if (free_entries) { 275 e = free_entries; 276 free_entries = free_entries->next; 277 } else { 278 e = calloc(1, sizeof(struct latency_entry)); 279 if (!e) { 280 fprintf(stderr, "Could not allocate latency entry: %s\n", strerror(errno)); 281 exit(EXIT_FAILURE); 282 } 283 } 284 285 return e; 286 } 287 288 static void free_latency_entry(struct latency_entry *e) { 289 e->next = free_entries; 290 free_entries = e; 291 } 292 293 static struct latency_entry *find_latency_entry(struct latency_entry *head, char *reason) { 294 struct latency_entry *e; 295 296 e = head; 297 298 while (e) { 299 if (!strcmp(e->reason, reason)) 300 return e; 301 e = e->next; 302 } 303 304 return NULL; 305 } 306 307 static void set_latencytop(int on) { 308 FILE *f; 309 310 f = fopen(SYSCTL_FILE, "w"); 311 if (!f) { 312 fprintf(stderr, "Could not open %s: %s\n", SYSCTL_FILE, strerror(errno)); 313 exit(EXIT_FAILURE); 314 } 315 316 fprintf(f, "%d\n", on); 317 318 fclose(f); 319 } 320 321 static void erase_latency_file(FILE *f) { 322 fprintf(f, "erase\n"); 323 } 324 325 static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list) { 326 struct latency_entry *e, *head; 327 char line[MAX_LINE]; 328 unsigned long count, max, total; 329 char reason[MAX_LINE]; 330 331 head = list; 332 333 if (!fgets(line, MAX_LINE, f)) { 334 fprintf(stderr, "Could not read latency file version: %s\n", strerror(errno)); 335 exit(EXIT_FAILURE); 336 } 337 338 if (strcmp(line, EXPECTED_VERSION) != 0) { 339 fprintf(stderr, "Expected version: %s\n", EXPECTED_VERSION); 340 fprintf(stderr, "But got version: %s", line); 341 exit(EXIT_FAILURE); 342 } 343 344 while (fgets(line, MAX_LINE, f)) { 345 sscanf(line, "%ld %ld %ld %s", &count, &total, &max, reason); 346 if (max > 0 || total > 0) { 347 e = find_latency_entry(head, reason); 348 if (e) { 349 e->count += count; 350 if (max > e->max) 351 e->max = max; 352 e->total += total; 353 } else { 354 e = alloc_latency_entry(); 355 e->count = count; 356 e->max = max; 357 e->total = total; 358 strcpy(e->reason, reason); 359 e->next = head; 360 head = e; 361 } 362 } 363 } 364 365 return head; 366 } 367 368 static void print_latency_entries(struct latency_entry *head) { 369 struct latency_entry *e, **array; 370 unsigned long average; 371 int i, count; 372 373 e = head; 374 count = 0; 375 while (e) { 376 count++; 377 e = e->next; 378 } 379 380 e = head; 381 array = calloc(count, sizeof(struct latency_entry *)); 382 if (!array) { 383 fprintf(stderr, "Error allocating array: %s\n", strerror(errno)); 384 exit(EXIT_FAILURE); 385 } 386 for (i = 0; i < count; i++) { 387 array[i] = e; 388 e = e->next; 389 } 390 391 qsort(array, count, sizeof(struct latency_entry *), &lat_cmp); 392 393 printf("%10s %10s %7s %s\n", "Maximum", "Average", "Count", "Reason"); 394 for (i = 0; i < count; i++) { 395 e = array[i]; 396 average = e->total / e->count; 397 printf("%4lu.%02lu ms %4lu.%02lu ms %7ld %s\n", 398 e->max / 1000, (e->max % 1000) / 10, 399 average / 1000, (average % 1000) / 10, 400 e->count, 401 e->reason); 402 } 403 404 free(array); 405 } 406 407 static void signal_handler(int sig) { 408 exit(EXIT_SUCCESS); 409 } 410 411 static void disable_latencytop(void) { 412 set_latencytop(0); 413 } 414 415 static void clear_screen(void) { 416 printf("\n\n"); 417 } 418 419 static void usage(const char *cmd) { 420 fprintf(stderr, "Usage: %s [ -d delay ] [ -n iterations ] [ -p pid [ -t tid ] ] [ -h ]\n" 421 " -d delay Time to sleep between updates.\n" 422 " -n iterations Number of updates to show (0 = infinite).\n" 423 " -p pid Process to monitor (default is all).\n" 424 " -t tid Thread (within specified process) to monitor (default is all).\n" 425 " -h Display this help screen.\n", 426 cmd); 427 } 428 429 static int numcmp(const long long a, const long long b) { 430 if (a < b) return -1; 431 if (a > b) return 1; 432 return 0; 433 } 434 435 static int lat_cmp(const void *a, const void *b) { 436 const struct latency_entry *pa, *pb; 437 438 pa = (*((struct latency_entry **)a)); 439 pb = (*((struct latency_entry **)b)); 440 441 return numcmp(pb->max, pa->max); 442 } 443