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 static void free_latency_entry(struct latency_entry *e);
     50 
     51 static void set_latencytop(int on);
     52 static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list);
     53 static void erase_latency_file(FILE *f);
     54 
     55 static struct latency_entry *find_latency_entry(struct latency_entry *e, char *reason);
     56 static void print_latency_entries(struct latency_entry *head);
     57 
     58 static void signal_handler(int sig);
     59 static void disable_latencytop(void);
     60 
     61 static int numcmp(const long long a, const long long b);
     62 static int lat_cmp(const void *a, const void *b);
     63 
     64 static void clear_screen(void);
     65 static void usage(const char *cmd);
     66 
     67 struct latency_entry *free_entries;
     68 
     69 int 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 
    175 static 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 
    202 static 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 
    232 static 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 
    272 static 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 
    289 static void free_latency_entry(struct latency_entry *e) {
    290     e->next = free_entries;
    291     free_entries = e;
    292 }
    293 
    294 static 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 
    308 static 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 
    322 static void erase_latency_file(FILE *f) {
    323     fprintf(f, "erase\n");
    324 }
    325 
    326 static 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 
    369 static 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 
    408 static void signal_handler(int sig) {
    409     exit(EXIT_SUCCESS);
    410 }
    411 
    412 static void disable_latencytop(void) {
    413     set_latencytop(0);
    414 }
    415 
    416 static void clear_screen(void) {
    417     printf("\n\n");
    418 }
    419 
    420 static 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 
    430 static 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 
    436 static 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