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 
    109     old_cpus = malloc(sizeof(struct cpu_info) * cpu_count);
    110     if (!old_cpus) die("Could not allocate struct cpu_info\n");
    111     new_cpus = malloc(sizeof(struct cpu_info) * cpu_count);
    112     if (!new_cpus) die("Could not allocate struct cpu_info\n");
    113 
    114     for (i = 0; i < cpu_count; i++) {
    115         old_cpus[i].freq_count = new_cpus[i].freq_count = get_freq_scales_count(i);
    116         new_cpus[i].freqs = malloc(sizeof(struct freq_info) * new_cpus[i].freq_count);
    117         if (!new_cpus[i].freqs) die("Could not allocate struct freq_info\n");
    118         old_cpus[i].freqs = malloc(sizeof(struct freq_info) * old_cpus[i].freq_count);
    119         if (!old_cpus[i].freqs) die("Could not allocate struct freq_info\n");
    120     }
    121 
    122     // Read stats without aggregating freq stats in the total cpu
    123     read_stats();
    124 
    125     aggregate_freq_stats = should_aggregate_freq_stats();
    126     if (aggregate_freq_stats) {
    127         old_total_cpu.freq_count = new_total_cpu.freq_count = new_cpus[0].freq_count;
    128         new_total_cpu.freqs = malloc(sizeof(struct freq_info) * new_total_cpu.freq_count);
    129         if (!new_total_cpu.freqs) die("Could not allocate struct freq_info\n");
    130         old_total_cpu.freqs = malloc(sizeof(struct freq_info) * old_total_cpu.freq_count);
    131         if (!old_total_cpu.freqs) die("Could not allocate struct freq_info\n");
    132 
    133         // Read stats again with aggregating freq stats in the total cpu
    134         read_stats();
    135     }
    136 
    137     while ((iterations == -1) || (iterations-- > 0)) {
    138         // Swap new and old cpu buffers;
    139         tmp_total_cpu = old_total_cpu;
    140         old_total_cpu = new_total_cpu;
    141         new_total_cpu = tmp_total_cpu;
    142 
    143         tmp_cpus = old_cpus;
    144         old_cpus = new_cpus;
    145         new_cpus = tmp_cpus;
    146 
    147         sleep(delay);
    148         read_stats();
    149         print_stats();
    150     }
    151 
    152     // Clean up
    153     if (aggregate_freq_stats) {
    154         free(new_total_cpu.freqs);
    155         free(old_total_cpu.freqs);
    156     }
    157     for (i = 0; i < cpu_count; i++) {
    158         free(new_cpus[i].freqs);
    159         free(old_cpus[i].freqs);
    160     }
    161     free(new_cpus);
    162     free(old_cpus);
    163 
    164     return 0;
    165 }
    166 
    167 /*
    168  * Get the number of CPUs of the system.
    169  *
    170  * Uses the two files /sys/devices/system/cpu/present and
    171  * /sys/devices/system/cpu/online to determine the number of CPUs. Expects the
    172  * format of both files to be either 0 or 0-N where N+1 is the number of CPUs.
    173  *
    174  * Exits if the present CPUs is not equal to the online CPUs
    175  */
    176 static int get_cpu_count() {
    177     int cpu_count = get_cpu_count_from_file("/sys/devices/system/cpu/present");
    178     if (cpu_count != get_cpu_count_from_file("/sys/devices/system/cpu/online")) {
    179         die("present cpus != online cpus\n");
    180     }
    181     return cpu_count;
    182 }
    183 
    184 /*
    185  * Get the number of CPUs from a given filename.
    186  */
    187 static int get_cpu_count_from_file(char *filename) {
    188     FILE *file;
    189     char line[MAX_BUF_SIZE];
    190     int cpu_count;
    191 
    192     file = fopen(filename, "r");
    193     if (!file) die("Could not open %s\n", filename);
    194     if (!fgets(line, MAX_BUF_SIZE, file)) die("Could not get %s contents\n", filename);
    195     fclose(file);
    196 
    197     if (strcmp(line, "0\n") == 0) {
    198         return 1;
    199     }
    200 
    201     if (1 == sscanf(line, "0-%d\n", &cpu_count)) {
    202         return cpu_count + 1;
    203     }
    204 
    205     die("Unexpected input in file %s (%s).\n", filename, line);
    206     return -1;
    207 }
    208 
    209 /*
    210  * Get the number of frequency states a given CPU can be scaled to.
    211  */
    212 static int get_freq_scales_count(int cpu) {
    213     FILE *file;
    214     char filename[MAX_BUF_SIZE];
    215     long unsigned freq;
    216     int count = 0;
    217 
    218     sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
    219     file = fopen(filename, "r");
    220     if (!file) die("Could not open %s\n", filename);
    221     do {
    222         freq = 0;
    223         fscanf(file, "%lu %*d\n", &freq);
    224         if (freq) count++;
    225     } while(freq);
    226     fclose(file);
    227 
    228     return count;
    229 }
    230 
    231 /*
    232  * Read the CPU and frequency stats for all cpus.
    233  */
    234 static void read_stats() {
    235     FILE *file;
    236     char scanline[MAX_BUF_SIZE];
    237     int i;
    238 
    239     file = fopen("/proc/stat", "r");
    240     if (!file) die("Could not open /proc/stat.\n");
    241     fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu %*d %*d %*d\n",
    242            &new_total_cpu.utime, &new_total_cpu.ntime, &new_total_cpu.stime, &new_total_cpu.itime,
    243            &new_total_cpu.iowtime, &new_total_cpu.irqtime, &new_total_cpu.sirqtime);
    244     if (aggregate_freq_stats) {
    245         for (i = 0; i < new_total_cpu.freq_count; i++) {
    246             new_total_cpu.freqs[i].time = 0;
    247         }
    248     }
    249 
    250     for (i = 0; i < cpu_count; i++) {
    251         sprintf(scanline, "cpu%d %%lu %%lu %%lu %%lu %%lu %%lu %%lu %%*d %%*d %%*d\n", i);
    252         fscanf(file, scanline, &new_cpus[i].utime, &new_cpus[i].ntime, &new_cpus[i].stime,
    253                &new_cpus[i].itime, &new_cpus[i].iowtime, &new_cpus[i].irqtime,
    254                &new_cpus[i].sirqtime);
    255         read_freq_stats(i);
    256     }
    257     fclose(file);
    258 }
    259 
    260 /*
    261  * Read the frequency stats for a given cpu.
    262  */
    263 static void read_freq_stats(int cpu) {
    264     FILE *file;
    265     char filename[MAX_BUF_SIZE];
    266     int i;
    267 
    268     sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
    269     file = fopen(filename, "r");
    270     for (i = 0; i < new_cpus[cpu].freq_count; i++) {
    271         if (file) {
    272             fscanf(file, "%u %lu\n", &new_cpus[cpu].freqs[i].freq,
    273                &new_cpus[cpu].freqs[i].time);
    274         } else {
    275             /* The CPU has been off lined for some reason */
    276             new_cpus[cpu].freqs[i].freq = old_cpus[cpu].freqs[i].freq;
    277             new_cpus[cpu].freqs[i].time = old_cpus[cpu].freqs[i].time;
    278         }
    279         if (aggregate_freq_stats) {
    280             new_total_cpu.freqs[i].freq = new_cpus[cpu].freqs[i].freq;
    281             new_total_cpu.freqs[i].time += new_cpus[cpu].freqs[i].time;
    282         }
    283     }
    284     if (file)
    285         fclose(file);
    286 }
    287 
    288 /*
    289  * Get the sum of the cpu time from all categories.
    290  */
    291 static long unsigned get_cpu_total_time(struct cpu_info *cpu) {
    292     return (cpu->utime + cpu->ntime + cpu->stime + cpu->itime + cpu->iowtime + cpu->irqtime +
    293             cpu->sirqtime);
    294 }
    295 
    296 /*
    297  * Print the stats for all CPUs.
    298  */
    299 static void print_stats() {
    300     char label[8];
    301     int i, j;
    302     char print_freq;
    303 
    304     print_freq = should_print_freq_stats();
    305 
    306     print_cpu_stats("Total", &new_total_cpu, &old_total_cpu, 1);
    307     for (i = 0; i < cpu_count; i++) {
    308         sprintf(label, "cpu%d", i);
    309         print_cpu_stats(label, &new_cpus[i], &old_cpus[i], print_freq);
    310     }
    311     printf("\n");
    312 }
    313 
    314 /*
    315  * Print the stats for a single CPU.
    316  */
    317 static void print_cpu_stats(char *label, struct cpu_info *new_cpu, struct cpu_info *old_cpu,
    318         char print_freq) {
    319     long int total_delta_time;
    320 
    321     if (!minimal) {
    322         total_delta_time = get_cpu_total_time(new_cpu) - get_cpu_total_time(old_cpu);
    323         printf("%s: User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = "
    324                 "%ld\n", label,
    325                 new_cpu->utime - old_cpu->utime,
    326                 new_cpu->ntime - old_cpu->ntime,
    327                 new_cpu->stime - old_cpu->stime,
    328                 new_cpu->itime - old_cpu->itime,
    329                 new_cpu->iowtime - old_cpu->iowtime,
    330                 new_cpu->irqtime - old_cpu->irqtime,
    331                 new_cpu->sirqtime - old_cpu->sirqtime,
    332                 total_delta_time);
    333         if (print_freq) {
    334             print_freq_stats(new_cpu, old_cpu);
    335         }
    336     } else {
    337         printf("%s,%ld,%ld,%ld,%ld,%ld,%ld,%ld", label,
    338                 new_cpu->utime - old_cpu->utime,
    339                 new_cpu->ntime - old_cpu->ntime,
    340                 new_cpu->stime - old_cpu->stime,
    341                 new_cpu->itime - old_cpu->itime,
    342                 new_cpu->iowtime - old_cpu->iowtime,
    343                 new_cpu->irqtime - old_cpu->irqtime,
    344                 new_cpu->sirqtime - old_cpu->sirqtime);
    345         print_freq_stats(new_cpu, old_cpu);
    346         printf("\n");
    347     }
    348 }
    349 
    350 /*
    351  * Print the CPU stats for a single CPU.
    352  */
    353 static void print_freq_stats(struct cpu_info *new_cpu, struct cpu_info *old_cpu) {
    354     long int delta_time, total_delta_time;
    355     int i;
    356 
    357     if (new_cpu->freq_count > 0) {
    358         if (!minimal) {
    359             total_delta_time = 0;
    360             printf("  ");
    361             for (i = 0; i < new_cpu->freq_count; i++) {
    362                 delta_time = new_cpu->freqs[i].time - old_cpu->freqs[i].time;
    363                 total_delta_time += delta_time;
    364                 printf("%ukHz %ld", new_cpu->freqs[i].freq, delta_time);
    365                 if (i + 1 != new_cpu->freq_count) {
    366                     printf(" + \n  ");
    367                 } else {
    368                     printf(" = ");
    369                 }
    370             }
    371             printf("%ld\n", total_delta_time);
    372         } else {
    373             for (i = 0; i < new_cpu->freq_count; i++) {
    374                 printf(",%u,%ld", new_cpu->freqs[i].freq,
    375                         new_cpu->freqs[i].time - old_cpu->freqs[i].time);
    376             }
    377         }
    378     }
    379 }
    380 
    381 /*
    382  * Determine if frequency stats should be printed.
    383  *
    384  * If the frequency stats are different between CPUs, the stats should be
    385  * printed for each CPU, else only the aggregate frequency stats should be
    386  * printed.
    387  */
    388 static char should_print_freq_stats() {
    389     int i, j;
    390 
    391     for (i = 1; i < cpu_count; i++) {
    392         for (j = 0; j < new_cpus[i].freq_count; j++) {
    393             if (new_cpus[i].freqs[j].time - old_cpus[i].freqs[j].time !=
    394                     new_cpus[0].freqs[j].time - old_cpus[0].freqs[j].time) {
    395                 return 1;
    396             }
    397         }
    398     }
    399     return 0;
    400 }
    401 
    402 /*
    403  * Determine if the frequency stats should be aggregated.
    404  *
    405  * Only aggregate the frequency stats in the total cpu stats if the frequencies
    406  * reported by all CPUs are identical.  Must be called after read_stats() has
    407  * been called once.
    408  */
    409 static char should_aggregate_freq_stats() {
    410     int i, j;
    411 
    412     for (i = 1; i < cpu_count; i++) {
    413         if (new_cpus[i].freq_count != new_cpus[0].freq_count) {
    414             return 0;
    415         }
    416         for (j = 0; j < new_cpus[i].freq_count; j++) {
    417             if (new_cpus[i].freqs[j].freq != new_cpus[0].freqs[j].freq) {
    418                 return 0;
    419             }
    420         }
    421     }
    422 
    423     return 1;
    424 }
    425 
    426 /*
    427  * Print the usage message.
    428  */
    429 static void usage(char *cmd) {
    430     fprintf(stderr, "Usage %s [ -n iterations ] [ -d delay ] [ -c cpu ] [ -m ] [ -h ]\n"
    431             "    -n num  Updates to show before exiting.\n"
    432             "    -d num  Seconds to wait between updates.\n"
    433             "    -m      Display minimal output.\n"
    434             "    -h      Display this help screen.\n",
    435             cmd);
    436 }
    437