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