Home | History | Annotate | Download | only in cpustats
      1 /*
      2  * Copyright (c) 2012, The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *  * Neither the name of Google, Inc. nor the names of its contributors
     15  *    may be used to endorse or promote products derived from this
     16  *    software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     22  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <unistd.h>
     36 
     37 #define MAX_BUF_SIZE 64
     38 
     39 struct freq_info {
     40     unsigned freq;
     41     long unsigned time;
     42 };
     43 
     44 struct cpu_info {
     45     long unsigned utime, ntime, stime, itime, iowtime, irqtime, sirqtime;
     46     struct freq_info *freqs;
     47     int freq_count;
     48 };
     49 
     50 #define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
     51 
     52 static struct cpu_info old_total_cpu, new_total_cpu, *old_cpus, *new_cpus;
     53 static int cpu_count, delay, iterations;
     54 static char minimal, aggregate_freq_stats;
     55 
     56 static int get_cpu_count();
     57 static int get_cpu_count_from_file(char *filename);
     58 static long unsigned get_cpu_total_time(struct cpu_info *cpu);
     59 static int get_freq_scales_count(int cpu);
     60 static void print_stats();
     61 static void print_cpu_stats(char *label, struct cpu_info *new_cpu, struct cpu_info *old_cpu,
     62         char print_freq);
     63 static void print_freq_stats(struct cpu_info *new_cpu, struct cpu_info *old_cpu);
     64 static void read_stats();
     65 static void read_freq_stats(int cpu);
     66 static char should_aggregate_freq_stats();
     67 static char should_print_freq_stats();
     68 static void usage(char *cmd);
     69 
     70 int main(int argc, char *argv[]) {
     71     struct cpu_info *tmp_cpus, tmp_total_cpu;
     72     int i, freq_count;
     73 
     74     delay = 3;
     75     iterations = -1;
     76     minimal = 0;
     77     aggregate_freq_stats = 0;
     78 
     79     for (i = 0; i < argc; i++) {
     80         if (!strcmp(argv[i], "-n")) {
     81             if (i + 1 >= argc) {
     82                 fprintf(stderr, "Option -n expects an argument.\n");
     83                 usage(argv[0]);
     84                 exit(EXIT_FAILURE);
     85             }
     86             iterations = atoi(argv[++i]);
     87             continue;
     88         }
     89         if (!strcmp(argv[i], "-d")) {
     90             if (i + 1 >= argc) {
     91                 fprintf(stderr, "Option -d expects an argument.\n");
     92                 usage(argv[0]);
     93                 exit(EXIT_FAILURE);
     94             }
     95             delay = atoi(argv[++i]);
     96             continue;
     97         }
     98         if (!strcmp(argv[i], "-m")) {
     99             minimal = 1;
    100         }
    101         if (!strcmp(argv[i], "-h")) {
    102             usage(argv[0]);
    103             exit(EXIT_SUCCESS);
    104         }
    105     }
    106 
    107     cpu_count = get_cpu_count();
    108     if (cpu_count < 1) die("Unexpected cpu count\n");
    109 
    110     old_cpus = malloc(sizeof(struct cpu_info) * cpu_count);
    111     if (!old_cpus) die("Could not allocate struct cpu_info\n");
    112     new_cpus = malloc(sizeof(struct cpu_info) * cpu_count);
    113     if (!new_cpus) die("Could not allocate struct cpu_info\n");
    114 
    115     for (i = 0; i < cpu_count; i++) {
    116         freq_count = get_freq_scales_count(i);
    117         if (freq_count < 1) die("Unexpected frequency scale count\n");
    118         old_cpus[i].freq_count = new_cpus[i].freq_count = freq_count;
    119         new_cpus[i].freqs = malloc(sizeof(struct freq_info) * new_cpus[i].freq_count);
    120         if (!new_cpus[i].freqs) die("Could not allocate struct freq_info\n");
    121         old_cpus[i].freqs = malloc(sizeof(struct freq_info) * old_cpus[i].freq_count);
    122         if (!old_cpus[i].freqs) die("Could not allocate struct freq_info\n");
    123     }
    124 
    125     // Read stats without aggregating freq stats in the total cpu
    126     read_stats();
    127 
    128     aggregate_freq_stats = should_aggregate_freq_stats();
    129     if (aggregate_freq_stats) {
    130         old_total_cpu.freq_count = new_total_cpu.freq_count = new_cpus[0].freq_count;
    131         new_total_cpu.freqs = malloc(sizeof(struct freq_info) * new_total_cpu.freq_count);
    132         if (!new_total_cpu.freqs) die("Could not allocate struct freq_info\n");
    133         old_total_cpu.freqs = malloc(sizeof(struct freq_info) * old_total_cpu.freq_count);
    134         if (!old_total_cpu.freqs) die("Could not allocate struct freq_info\n");
    135 
    136         // Read stats again with aggregating freq stats in the total cpu
    137         read_stats();
    138     }
    139 
    140     while ((iterations == -1) || (iterations-- > 0)) {
    141         // Swap new and old cpu buffers;
    142         tmp_total_cpu = old_total_cpu;
    143         old_total_cpu = new_total_cpu;
    144         new_total_cpu = tmp_total_cpu;
    145 
    146         tmp_cpus = old_cpus;
    147         old_cpus = new_cpus;
    148         new_cpus = tmp_cpus;
    149 
    150         sleep(delay);
    151         read_stats();
    152         print_stats();
    153     }
    154 
    155     // Clean up
    156     if (aggregate_freq_stats) {
    157         free(new_total_cpu.freqs);
    158         free(old_total_cpu.freqs);
    159     }
    160     for (i = 0; i < cpu_count; i++) {
    161         free(new_cpus[i].freqs);
    162         free(old_cpus[i].freqs);
    163     }
    164     free(new_cpus);
    165     free(old_cpus);
    166 
    167     return 0;
    168 }
    169 
    170 /*
    171  * Get the number of CPUs of the system.
    172  *
    173  * Uses the two files /sys/devices/system/cpu/present and
    174  * /sys/devices/system/cpu/online to determine the number of CPUs. Expects the
    175  * format of both files to be either 0 or 0-N where N+1 is the number of CPUs.
    176  *
    177  * Exits if the present CPUs is not equal to the online CPUs
    178  */
    179 static int get_cpu_count() {
    180     int cpu_count = get_cpu_count_from_file("/sys/devices/system/cpu/present");
    181     if (cpu_count != get_cpu_count_from_file("/sys/devices/system/cpu/online")) {
    182         die("present cpus != online cpus\n");
    183     }
    184     return cpu_count;
    185 }
    186 
    187 /*
    188  * Get the number of CPUs from a given filename.
    189  */
    190 static int get_cpu_count_from_file(char *filename) {
    191     FILE *file;
    192     char line[MAX_BUF_SIZE];
    193     int cpu_count;
    194 
    195     file = fopen(filename, "r");
    196     if (!file) die("Could not open %s\n", filename);
    197     if (!fgets(line, MAX_BUF_SIZE, file)) die("Could not get %s contents\n", filename);
    198     fclose(file);
    199 
    200     if (strcmp(line, "0\n") == 0) {
    201         return 1;
    202     }
    203 
    204     if (1 == sscanf(line, "0-%d\n", &cpu_count)) {
    205         return cpu_count + 1;
    206     }
    207 
    208     die("Unexpected input in file %s (%s).\n", filename, line);
    209     return -1;
    210 }
    211 
    212 /*
    213  * Get the number of frequency states a given CPU can be scaled to.
    214  */
    215 static int get_freq_scales_count(int cpu) {
    216     FILE *file;
    217     char filename[MAX_BUF_SIZE];
    218     long unsigned freq;
    219     int count = 0;
    220 
    221     sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
    222     file = fopen(filename, "r");
    223     if (!file) die("Could not open %s\n", filename);
    224     do {
    225         freq = 0;
    226         fscanf(file, "%lu %*d\n", &freq);
    227         if (freq) count++;
    228     } while(freq);
    229     fclose(file);
    230 
    231     return count;
    232 }
    233 
    234 /*
    235  * Read the CPU and frequency stats for all cpus.
    236  */
    237 static void read_stats() {
    238     FILE *file;
    239     char scanline[MAX_BUF_SIZE];
    240     int i;
    241 
    242     file = fopen("/proc/stat", "r");
    243     if (!file) die("Could not open /proc/stat.\n");
    244     fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu %*d %*d %*d\n",
    245            &new_total_cpu.utime, &new_total_cpu.ntime, &new_total_cpu.stime, &new_total_cpu.itime,
    246            &new_total_cpu.iowtime, &new_total_cpu.irqtime, &new_total_cpu.sirqtime);
    247     if (aggregate_freq_stats) {
    248         for (i = 0; i < new_total_cpu.freq_count; i++) {
    249             new_total_cpu.freqs[i].time = 0;
    250         }
    251     }
    252 
    253     for (i = 0; i < cpu_count; i++) {
    254         sprintf(scanline, "cpu%d %%lu %%lu %%lu %%lu %%lu %%lu %%lu %%*d %%*d %%*d\n", i);
    255         fscanf(file, scanline, &new_cpus[i].utime, &new_cpus[i].ntime, &new_cpus[i].stime,
    256                &new_cpus[i].itime, &new_cpus[i].iowtime, &new_cpus[i].irqtime,
    257                &new_cpus[i].sirqtime);
    258         read_freq_stats(i);
    259     }
    260     fclose(file);
    261 }
    262 
    263 /*
    264  * Read the frequency stats for a given cpu.
    265  */
    266 static void read_freq_stats(int cpu) {
    267     FILE *file;
    268     char filename[MAX_BUF_SIZE];
    269     int i;
    270 
    271     sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
    272     file = fopen(filename, "r");
    273     for (i = 0; i < new_cpus[cpu].freq_count; i++) {
    274         if (file) {
    275             fscanf(file, "%u %lu\n", &new_cpus[cpu].freqs[i].freq,
    276                &new_cpus[cpu].freqs[i].time);
    277         } else {
    278             /* The CPU has been off lined for some reason */
    279             new_cpus[cpu].freqs[i].freq = old_cpus[cpu].freqs[i].freq;
    280             new_cpus[cpu].freqs[i].time = old_cpus[cpu].freqs[i].time;
    281         }
    282         if (aggregate_freq_stats) {
    283             new_total_cpu.freqs[i].freq = new_cpus[cpu].freqs[i].freq;
    284             new_total_cpu.freqs[i].time += new_cpus[cpu].freqs[i].time;
    285         }
    286     }
    287     if (file)
    288         fclose(file);
    289 }
    290 
    291 /*
    292  * Get the sum of the cpu time from all categories.
    293  */
    294 static long unsigned get_cpu_total_time(struct cpu_info *cpu) {
    295     return (cpu->utime + cpu->ntime + cpu->stime + cpu->itime + cpu->iowtime + cpu->irqtime +
    296             cpu->sirqtime);
    297 }
    298 
    299 /*
    300  * Print the stats for all CPUs.
    301  */
    302 static void print_stats() {
    303     char label[8];
    304     int i, j;
    305     char print_freq;
    306 
    307     print_freq = should_print_freq_stats();
    308 
    309     print_cpu_stats("Total", &new_total_cpu, &old_total_cpu, 1);
    310     for (i = 0; i < cpu_count; i++) {
    311         sprintf(label, "cpu%d", i);
    312         print_cpu_stats(label, &new_cpus[i], &old_cpus[i], print_freq);
    313     }
    314     printf("\n");
    315 }
    316 
    317 /*
    318  * Print the stats for a single CPU.
    319  */
    320 static void print_cpu_stats(char *label, struct cpu_info *new_cpu, struct cpu_info *old_cpu,
    321         char print_freq) {
    322     long int total_delta_time;
    323 
    324     if (!minimal) {
    325         total_delta_time = get_cpu_total_time(new_cpu) - get_cpu_total_time(old_cpu);
    326         printf("%s: User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = "
    327                 "%ld\n", label,
    328                 new_cpu->utime - old_cpu->utime,
    329                 new_cpu->ntime - old_cpu->ntime,
    330                 new_cpu->stime - old_cpu->stime,
    331                 new_cpu->itime - old_cpu->itime,
    332                 new_cpu->iowtime - old_cpu->iowtime,
    333                 new_cpu->irqtime - old_cpu->irqtime,
    334                 new_cpu->sirqtime - old_cpu->sirqtime,
    335                 total_delta_time);
    336         if (print_freq) {
    337             print_freq_stats(new_cpu, old_cpu);
    338         }
    339     } else {
    340         printf("%s,%ld,%ld,%ld,%ld,%ld,%ld,%ld", label,
    341                 new_cpu->utime - old_cpu->utime,
    342                 new_cpu->ntime - old_cpu->ntime,
    343                 new_cpu->stime - old_cpu->stime,
    344                 new_cpu->itime - old_cpu->itime,
    345                 new_cpu->iowtime - old_cpu->iowtime,
    346                 new_cpu->irqtime - old_cpu->irqtime,
    347                 new_cpu->sirqtime - old_cpu->sirqtime);
    348         print_freq_stats(new_cpu, old_cpu);
    349         printf("\n");
    350     }
    351 }
    352 
    353 /*
    354  * Print the CPU stats for a single CPU.
    355  */
    356 static void print_freq_stats(struct cpu_info *new_cpu, struct cpu_info *old_cpu) {
    357     long int delta_time, total_delta_time;
    358     int i;
    359 
    360     if (new_cpu->freq_count > 0) {
    361         if (!minimal) {
    362             total_delta_time = 0;
    363             printf("  ");
    364             for (i = 0; i < new_cpu->freq_count; i++) {
    365                 delta_time = new_cpu->freqs[i].time - old_cpu->freqs[i].time;
    366                 total_delta_time += delta_time;
    367                 printf("%ukHz %ld", new_cpu->freqs[i].freq, delta_time);
    368                 if (i + 1 != new_cpu->freq_count) {
    369                     printf(" + \n  ");
    370                 } else {
    371                     printf(" = ");
    372                 }
    373             }
    374             printf("%ld\n", total_delta_time);
    375         } else {
    376             for (i = 0; i < new_cpu->freq_count; i++) {
    377                 printf(",%u,%ld", new_cpu->freqs[i].freq,
    378                         new_cpu->freqs[i].time - old_cpu->freqs[i].time);
    379             }
    380         }
    381     }
    382 }
    383 
    384 /*
    385  * Determine if frequency stats should be printed.
    386  *
    387  * If the frequency stats are different between CPUs, the stats should be
    388  * printed for each CPU, else only the aggregate frequency stats should be
    389  * printed.
    390  */
    391 static char should_print_freq_stats() {
    392     int i, j;
    393 
    394     for (i = 1; i < cpu_count; i++) {
    395         for (j = 0; j < new_cpus[i].freq_count; j++) {
    396             if (new_cpus[i].freqs[j].time - old_cpus[i].freqs[j].time !=
    397                     new_cpus[0].freqs[j].time - old_cpus[0].freqs[j].time) {
    398                 return 1;
    399             }
    400         }
    401     }
    402     return 0;
    403 }
    404 
    405 /*
    406  * Determine if the frequency stats should be aggregated.
    407  *
    408  * Only aggregate the frequency stats in the total cpu stats if the frequencies
    409  * reported by all CPUs are identical.  Must be called after read_stats() has
    410  * been called once.
    411  */
    412 static char should_aggregate_freq_stats() {
    413     int i, j;
    414 
    415     for (i = 1; i < cpu_count; i++) {
    416         if (new_cpus[i].freq_count != new_cpus[0].freq_count) {
    417             return 0;
    418         }
    419         for (j = 0; j < new_cpus[i].freq_count; j++) {
    420             if (new_cpus[i].freqs[j].freq != new_cpus[0].freqs[j].freq) {
    421                 return 0;
    422             }
    423         }
    424     }
    425 
    426     return 1;
    427 }
    428 
    429 /*
    430  * Print the usage message.
    431  */
    432 static void usage(char *cmd) {
    433     fprintf(stderr, "Usage %s [ -n iterations ] [ -d delay ] [ -c cpu ] [ -m ] [ -h ]\n"
    434             "    -n num  Updates to show before exiting.\n"
    435             "    -d num  Seconds to wait between updates.\n"
    436             "    -m      Display minimal output.\n"
    437             "    -h      Display this help screen.\n",
    438             cmd);
    439 }
    440