Home | History | Annotate | Download | only in latencytop
      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