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 <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