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