mpstat: small code shrink
[platform/upstream/busybox.git] / procps / mpstat.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard
4  *
5  * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
6  *
7  * Licensed under GPLv2, see file License in this tarball for details.
8  */
9
10 //applet:IF_MPSTAT(APPLET(mpstat, _BB_DIR_BIN, _BB_SUID_DROP))
11
12 //kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o
13
14 //config:config MPSTAT
15 //config:       bool "mpstat"
16 //config:       default y
17 //config:       help
18 //config:         Per-processor statistics
19
20 #include "libbb.h"
21 #include <sys/utsname.h>        /* struct utsname */
22
23 //#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
24 #define debug(fmt, ...) ((void)0)
25
26 /* Size of /proc/interrupts line, CPU data excluded */
27 #define INTERRUPTS_LINE    64
28 /* Maximum number of interrupts */
29 #define NR_IRQS            256
30 #define NR_IRQCPU_PREALLOC 3
31 #define MAX_IRQ_LEN        16
32 #define MAX_PF_NAME        512
33
34 /* System files */
35 #define SYSFS_DEVCPU      "/sys/devices/system/cpu"
36 #define PROCFS_STAT       "/proc/stat"
37 #define PROCFS_INTERRUPTS "/proc/interrupts"
38 #define PROCFS_SOFTIRQS   "/proc/softirqs"
39 #define PROCFS_UPTIME     "/proc/uptime"
40
41
42 #if 1
43 typedef unsigned long long data_t;
44 typedef long long idata_t;
45 #define FMT_DATA "ll"
46 #define DATA_MAX ULLONG_MAX
47 #else
48 typedef unsigned long data_t;
49 typedef long idata_t;
50 #define FMT_DATA "l"
51 #define DATA_MAX ULONG_MAX
52 #endif
53
54
55 struct stats_irqcpu {
56         unsigned interrupt;
57         char irq_name[MAX_IRQ_LEN];
58 };
59
60 struct stats_cpu {
61         data_t cpu_user;
62         data_t cpu_nice;
63         data_t cpu_system;
64         data_t cpu_idle;
65         data_t cpu_iowait;
66         data_t cpu_steal;
67         data_t cpu_irq;
68         data_t cpu_softirq;
69         data_t cpu_guest;
70 };
71
72 struct stats_irq {
73         data_t irq_nr;
74 };
75
76
77 /* Globals. Sort by size and access frequency. */
78 struct globals {
79         int interval;
80         int count;
81         unsigned cpu_nr;                /* Number of CPUs */
82         unsigned irqcpu_nr;             /* Number of interrupts per CPU */
83         unsigned softirqcpu_nr;         /* Number of soft interrupts per CPU */
84         unsigned options;
85         unsigned hz;
86         unsigned cpu_bitmap_len;
87         smallint p_option;
88         smallint header_done;
89         smallint avg_header_done;
90         unsigned char *cpu_bitmap;      /* Bit 0: global, bit 1: 1st proc... */
91         data_t global_uptime[3];
92         data_t per_cpu_uptime[3];
93         struct stats_cpu *st_cpu[3];
94         struct stats_irq *st_irq[3];
95         struct stats_irqcpu *st_irqcpu[3];
96         struct stats_irqcpu *st_softirqcpu[3];
97         struct tm timestamp[3];
98 };
99 #define G (*ptr_to_globals)
100 #define INIT_G() do { \
101         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
102 } while (0)
103
104 /* The selected interrupts statistics (bits in G.options) */
105 enum {
106         D_CPU      = 1 << 0,
107         D_IRQ_SUM  = 1 << 1,
108         D_IRQ_CPU  = 1 << 2,
109         D_SOFTIRQS = 1 << 3,
110 };
111
112
113 /* Does str start with "cpu"? */
114 static int starts_with_cpu(const char *str)
115 {
116         return !((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u'));
117 }
118
119 /* Is option on? */
120 static ALWAYS_INLINE int display_opt(int opt)
121 {
122         return (opt & G.options);
123 }
124
125 #if DATA_MAX > 0xffffffff
126 /*
127  * Handle overflow conditions properly for counters which can have
128  * less bits than data_t, depending on the kernel version.
129  */
130 /* Surprisingly, on 32bit inlining is a size win */
131 static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
132 {
133         data_t v = curr - prev;
134
135         if ((idata_t)v < 0     /* curr < prev - counter overflow? */
136          && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
137         ) {
138                 /* Add 33th bit set to 1 to curr, compensating for the overflow */
139                 /* double shift defeats "warning: left shift count >= width of type" */
140                 v += ((data_t)1 << 16) << 16;
141         }
142         return v;
143 }
144 #else
145 static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
146 {
147         return curr - prev;
148 }
149 #endif
150
151 static double percent_value(data_t prev, data_t curr, data_t itv)
152 {
153         return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
154 }
155
156 static double hz_value(data_t prev, data_t curr, data_t itv)
157 {
158         return ((double)overflow_safe_sub(prev, curr)) / itv * G.hz;
159 }
160
161 static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new)
162 {
163         data_t diff = new - old;
164         return (diff == 0) ? 1 : diff;
165 }
166
167 static int is_cpu_in_bitmap(unsigned cpu)
168 {
169         return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7));
170 }
171
172 static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[],
173                 int total_irqs,
174                 data_t itv,
175                 int prev, int current,
176                 const char *prev_str, const char *current_str)
177 {
178         int j;
179         int offset, cpu;
180         struct stats_irqcpu *p0, *q0;
181
182         /* Check if number of IRQs has changed */
183         if (G.interval != 0) {
184                 for (j = 0; j <= total_irqs; j++) {
185                         p0 = &per_cpu_stats[current][j];
186                         if (p0->irq_name[0] != '\0') {
187                                 q0 = &per_cpu_stats[prev][j];
188                                 if (strcmp(p0->irq_name, q0->irq_name) != 0) {
189                                         /* Strings are different */
190                                         break;
191                                 }
192                         }
193                 }
194         }
195
196         /* Print header */
197         printf("\n%-11s  CPU", prev_str);
198         for (j = 0; j < total_irqs; j++) {
199                 p0 = &per_cpu_stats[current][j];
200                 if (p0->irq_name[0] != '\0')
201                         printf(" %8s/s", p0->irq_name);
202         }
203         bb_putchar('\n');
204
205         for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
206                 /* Check if we want stats about this CPU */
207                 if (!is_cpu_in_bitmap(cpu) && G.p_option) {
208                         continue;
209                 }
210
211                 printf("%-11s %4u", current_str, cpu - 1);
212
213                 for (j = 0; j < total_irqs; j++) {
214                         /* IRQ field set only for proc 0 */
215                         p0 = &per_cpu_stats[current][j];
216
217                         /*
218                          * An empty string for irq name means that
219                          * interrupt is no longer used.
220                          */
221                         if (p0->irq_name[0] != '\0') {
222                                 offset = j;
223                                 q0 = &per_cpu_stats[prev][offset];
224
225                                 /*
226                                  * If we want stats for the time since boot
227                                  * we have p0->irq != q0->irq.
228                                  */
229                                 if (strcmp(p0->irq_name, q0->irq_name) != 0
230                                  && G.interval != 0
231                                 ) {
232                                         if (j) {
233                                                 offset = j - 1;
234                                                 q0 = &per_cpu_stats[prev][offset];
235                                         }
236                                         if (strcmp(p0->irq_name, q0->irq_name) != 0
237                                          && (j + 1 < total_irqs)
238                                         ) {
239                                                 offset = j + 1;
240                                                 q0 = &per_cpu_stats[prev][offset];
241                                         }
242                                 }
243
244                                 if (strcmp(p0->irq_name, q0->irq_name) == 0
245                                  || G.interval == 0
246                                 ) {
247                                         struct stats_irqcpu *p, *q;
248                                         p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j];
249                                         q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset];
250                                         printf(" %10.2f",
251                                                 (double)(p->interrupt - q->interrupt) / itv * G.hz);
252                                 } else {
253                                         printf("        N/A");
254                                 }
255                         }
256                 }
257                 bb_putchar('\n');
258         }
259 }
260
261 static data_t get_per_cpu_interval(const struct stats_cpu *scc,
262                 const struct stats_cpu *scp)
263 {
264         return ((scc->cpu_user + scc->cpu_nice +
265                  scc->cpu_system + scc->cpu_iowait +
266                  scc->cpu_idle + scc->cpu_steal +
267                  scc->cpu_irq + scc->cpu_softirq) -
268                 (scp->cpu_user + scp->cpu_nice +
269                  scp->cpu_system + scp->cpu_iowait +
270                  scp->cpu_idle + scp->cpu_steal +
271                  scp->cpu_irq + scp->cpu_softirq));
272 }
273
274 static void print_stats_cpu_struct(const struct stats_cpu *p,
275                 const struct stats_cpu *c,
276                 data_t itv)
277 {
278         printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
279                 percent_value(p->cpu_user - p->cpu_guest,
280                 /**/                          c->cpu_user - c->cpu_guest, itv),
281                 percent_value(p->cpu_nice   , c->cpu_nice   , itv),
282                 percent_value(p->cpu_system , c->cpu_system , itv),
283                 percent_value(p->cpu_iowait , c->cpu_iowait , itv),
284                 percent_value(p->cpu_irq    , c->cpu_irq    , itv),
285                 percent_value(p->cpu_softirq, c->cpu_softirq, itv),
286                 percent_value(p->cpu_steal  , c->cpu_steal  , itv),
287                 percent_value(p->cpu_guest  , c->cpu_guest  , itv),
288                 percent_value(p->cpu_idle   , c->cpu_idle   , itv)
289         );
290 }
291
292 static void write_stats_core(int prev, int current,
293                 const char *prev_str, const char *current_str)
294 {
295         struct stats_cpu *scc, *scp;
296         data_t itv, global_itv;
297         int cpu;
298
299         /* Compute time interval */
300         itv = global_itv = jiffies_diff(G.global_uptime[prev], G.global_uptime[current]);
301
302         /* Reduce interval to one CPU */
303         if (G.cpu_nr > 1)
304                 itv = jiffies_diff(G.per_cpu_uptime[prev], G.per_cpu_uptime[current]);
305
306         /* Print CPU stats */
307         if (display_opt(D_CPU)) {
308
309                 /* This is done exactly once */
310                 if (!G.header_done) {
311                         printf("\n%-11s  CPU    %%usr   %%nice    %%sys %%iowait    %%irq   %%soft  %%steal  %%guest   %%idle\n",
312                                 prev_str
313                         );
314                         G.header_done = 1;
315                 }
316
317                 for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
318                         data_t per_cpu_itv;
319
320                         /* Print stats about this particular CPU? */
321                         if (!is_cpu_in_bitmap(cpu))
322                                 continue;
323
324                         scc = &G.st_cpu[current][cpu];
325                         scp = &G.st_cpu[prev][cpu];
326                         per_cpu_itv = global_itv;
327
328                         printf((cpu ? "%-11s %4u" : "%-11s  all"), current_str, cpu - 1);
329                         if (cpu) {
330                                 double idle;
331                                 /*
332                                  * If the CPU is offline, then it isn't in /proc/stat,
333                                  * so all values are 0.
334                                  * NB: Guest time is already included in user time.
335                                  */
336                                 if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system |
337                                      scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal |
338                                      scc->cpu_irq | scc->cpu_softirq) == 0
339                                 ) {
340                                         /*
341                                          * Set current struct fields to values from prev.
342                                          * iteration. Then their values won't jump from
343                                          * zero, when the CPU comes back online.
344                                          */
345                                         *scc = *scp;
346                                         idle = 0.0;
347                                         goto print_zeros;
348                                 }
349                                 /* Compute interval again for current proc */
350                                 per_cpu_itv = get_per_cpu_interval(scc, scp);
351                                 if (per_cpu_itv == 0) {
352                                         /*
353                                          * If the CPU is tickless then there is no change in CPU values
354                                          * but the sum of values is not zero.
355                                          */
356                                         idle = 100.0;
357  print_zeros:
358                                         printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
359                                                 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle);
360                                         continue;
361                                 }
362                         }
363                         print_stats_cpu_struct(scp, scc, per_cpu_itv);
364                 }
365         }
366
367         /* Print total number of IRQs per CPU */
368         if (display_opt(D_IRQ_SUM)) {
369
370                 /* Print average header, this is done exactly once */
371                 if (!G.avg_header_done) {
372                         printf("\n%-11s  CPU    intr/s\n", prev_str);
373                         G.avg_header_done = 1;
374                 }
375
376                 for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
377                         data_t per_cpu_itv;
378
379                         /* Print stats about this CPU? */
380                         if (!is_cpu_in_bitmap(cpu))
381                                 continue;
382
383                         per_cpu_itv = itv;
384                         printf((cpu ? "%-11s %4u" : "%-11s  all"), current_str, cpu - 1);
385                         if (cpu) {
386                                 scc = &G.st_cpu[current][cpu];
387                                 scp = &G.st_cpu[prev][cpu];
388                                 /* Compute interval again for current proc */
389                                 per_cpu_itv = get_per_cpu_interval(scc, scp);
390                                 if (per_cpu_itv == 0) {
391                                         printf(" %9.2f\n", 0.0);
392                                         continue;
393                                 }
394                         }
395                         printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, per_cpu_itv));
396                 }
397         }
398
399         if (display_opt(D_IRQ_CPU)) {
400                 write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr,
401                                 itv,
402                                 prev, current,
403                                 prev_str, current_str
404                 );
405         }
406
407         if (display_opt(D_SOFTIRQS)) {
408                 write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr,
409                                 itv,
410                                 prev, current,
411                                 prev_str, current_str
412                 );
413         }
414 }
415
416 /*
417  * Print the statistics
418  */
419 static void write_stats(int current)
420 {
421         char prev_time[16];
422         char curr_time[16];
423
424         strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]);
425         strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]);
426
427         write_stats_core(!current, current, prev_time, curr_time);
428 }
429
430 static void write_stats_avg(int current)
431 {
432         write_stats_core(2, current, "Average:", "Average:");
433 }
434
435 /*
436  * Read CPU statistics
437  */
438 static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0)
439 {
440         FILE *fp;
441         char buf[1024];
442
443         fp = xfopen_for_read(PROCFS_STAT);
444
445         while (fgets(buf, sizeof(buf), fp)) {
446                 data_t sum;
447                 unsigned cpu_number;
448                 struct stats_cpu *cp;
449
450                 if (!starts_with_cpu(buf))
451                         continue; /* not "cpu" */
452
453                 cp = cpu; /* for "cpu " case */
454                 if (buf[3] != ' ') {
455                         /* "cpuN " */
456                         if (G.cpu_nr == 0
457                          || sscanf(buf + 3, "%u ", &cpu_number) != 1
458                          || cpu_number >= G.cpu_nr
459                         ) {
460                                 continue;
461                         }
462                         cp = &cpu[cpu_number + 1];
463                 }
464
465                 /* Read the counters, save them */
466                 /* Not all fields have to be present */
467                 memset(cp, 0, sizeof(*cp));
468                 sscanf(buf, "%*s"
469                         " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
470                         " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
471                         " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
472                         &cp->cpu_user, &cp->cpu_nice, &cp->cpu_system,
473                         &cp->cpu_idle, &cp->cpu_iowait, &cp->cpu_irq,
474                         &cp->cpu_softirq, &cp->cpu_steal, &cp->cpu_guest
475                 );
476                 /*
477                  * Compute uptime in jiffies (1/HZ), it'll be the sum of
478                  * individual CPU's uptimes.
479                  * NB: We have to omit cpu_guest, because cpu_user includes it.
480                  */
481                 sum = cp->cpu_user + cp->cpu_nice + cp->cpu_system +
482                         cp->cpu_idle + cp->cpu_iowait + cp->cpu_irq +
483                         cp->cpu_softirq + cp->cpu_steal;
484
485                 if (buf[3] == ' ') {
486                         /* "cpu " */
487                         *up = sum;
488                 } else {
489                         /* "cpuN " */
490                         if (cpu_number == 0 && *up0 != 0) {
491                                 /* Compute uptime of single CPU */
492                                 *up0 = sum;
493                         }
494                 }
495         }
496         fclose(fp);
497 }
498
499 /*
500  * Read IRQs from /proc/stat
501  */
502 static void get_irqs_from_stat(struct stats_irq *irq)
503 {
504         FILE *fp;
505         char buf[1024];
506
507         fp = fopen_for_read(PROCFS_STAT);
508         if (!fp)
509                 return;
510
511         while (fgets(buf, sizeof(buf), fp)) {
512                 if (strncmp(buf, "intr ", 5) == 0)
513                         /* Read total number of IRQs since system boot */
514                         sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr);
515         }
516
517         fclose(fp);
518 }
519
520 /*
521  * Read stats from /proc/interrupts or /proc/softirqs
522  */
523 static void get_irqs_from_interrupts(const char *fname,
524                 struct stats_irqcpu *per_cpu_stats[],
525                 int irqs_per_cpu, int current)
526 {
527         FILE *fp;
528         struct stats_irq *irq_i;
529         struct stats_irqcpu *ic;
530         char *buf;
531         unsigned buflen;
532         unsigned cpu;
533         unsigned irq;
534         int cpu_index[G.cpu_nr];
535         int iindex;
536         int len, digit;
537
538         for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
539                 irq_i = &G.st_irq[current][cpu];
540                 irq_i->irq_nr = 0;
541         }
542
543         fp = fopen_for_read(fname);
544         if (!fp)
545                 return;
546
547         buflen = INTERRUPTS_LINE + 16 * G.cpu_nr;
548         buf = xmalloc(buflen);
549
550         /* Parse header and determine, which CPUs are online */
551         iindex = 0;
552         while (fgets(buf, buflen, fp)) {
553                 char *cp, *next;
554                 next = buf;
555                 while ((cp = strstr(next, "CPU")) != NULL
556                  && iindex < G.cpu_nr
557                 ) {
558                         cpu = strtoul(cp + 3, &next, 10);
559                         cpu_index[iindex++] = cpu;
560                 }
561                 if (iindex) /* We found header */
562                         break;
563         }
564
565         irq = 0;
566         while (fgets(buf, buflen, fp)
567          && irq < irqs_per_cpu
568         ) {
569                 char *cp;
570                 /* Skip over "<irq>:" */
571                 cp = strchr(buf, ':');
572                 if (!cp)
573                         continue;
574
575                 ic = &per_cpu_stats[current][irq];
576                 len = cp - buf;
577                 if (len > sizeof(ic->irq_name)) {
578                         len = sizeof(ic->irq_name);
579                 }
580                 safe_strncpy(ic->irq_name, buf, len);
581                 digit = isdigit(buf[len - 1]);
582                 cp++;
583
584                 for (cpu = 0; cpu < iindex; cpu++) {
585                         char *next;
586                         ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq];
587                         irq_i = &G.st_irq[current][cpu_index[cpu] + 1];
588                         ic->interrupt = strtoul(cp, &next, 10);
589                         if (digit) {
590                                 /* Do not count non-numerical IRQs */
591                                 irq_i->irq_nr += ic->interrupt;
592                         }
593                         cp = next;
594                 }
595                 irq++;
596         }
597         fclose(fp);
598         free(buf);
599
600         while (irq < irqs_per_cpu) {
601                 /* Number of interrupts per CPU has changed */
602                 ic = &per_cpu_stats[current][irq];
603                 ic->irq_name[0] = '\0'; /* False interrupt */
604                 irq++;
605         }
606 }
607
608 static void get_uptime(data_t *uptime)
609 {
610         FILE *fp;
611         char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
612         unsigned long uptime_sec, decimal;
613
614         fp = fopen_for_read(PROCFS_UPTIME);
615         if (!fp)
616                 return;
617         if (fgets(buf, sizeof(buf), fp)) {
618                 if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) {
619                         *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100;
620                 }
621         }
622
623         fclose(fp);
624 }
625
626 static void get_localtime(struct tm *tm)
627 {
628         time_t timer;
629         time(&timer);
630         localtime_r(&timer, tm);
631 }
632
633 static void alarm_handler(int sig UNUSED_PARAM)
634 {
635         signal(SIGALRM, alarm_handler);
636         alarm(G.interval);
637 }
638
639 static void main_loop(void)
640 {
641         unsigned current;
642         unsigned cpus;
643
644         /* Read the stats */
645         if (G.cpu_nr > 1) {
646                 G.per_cpu_uptime[0] = 0;
647                 get_uptime(&G.per_cpu_uptime[0]);
648         }
649
650         get_cpu_statistics(G.st_cpu[0], &G.global_uptime[0], &G.per_cpu_uptime[0]);
651
652         if (display_opt(D_IRQ_SUM))
653                 get_irqs_from_stat(G.st_irq[0]);
654
655         if (display_opt(D_IRQ_SUM | D_IRQ_CPU))
656                 get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
657                                         G.irqcpu_nr, 0);
658
659         if (display_opt(D_SOFTIRQS))
660                 get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu,
661                                         G.softirqcpu_nr, 0);
662
663         if (G.interval == 0) {
664                 /* Display since boot time */
665                 cpus = G.cpu_nr + 1;
666                 G.timestamp[1] = G.timestamp[0];
667                 memset(G.st_cpu[1], 0, sizeof(G.st_cpu[1][0]) * cpus);
668                 memset(G.st_irq[1], 0, sizeof(G.st_irq[1][0]) * cpus);
669                 memset(G.st_irqcpu[1], 0, sizeof(G.st_irqcpu[1][0]) * cpus * G.irqcpu_nr);
670                 memset(G.st_softirqcpu[1], 0, sizeof(G.st_softirqcpu[1][0]) * cpus * G.softirqcpu_nr);
671
672                 write_stats(0);
673
674                 /* And we're done */
675                 return;
676         }
677
678         /* Set a handler for SIGALRM */
679         alarm_handler(0);
680
681         /* Save the stats we already have. We need them to compute the average */
682         G.timestamp[2] = G.timestamp[0];
683         G.global_uptime[2] = G.global_uptime[0];
684         G.per_cpu_uptime[2] = G.per_cpu_uptime[0];
685         cpus = G.cpu_nr + 1;
686         memcpy(G.st_cpu[2], G.st_cpu[0], sizeof(G.st_cpu[0][0]) * cpus);
687         memcpy(G.st_irq[2], G.st_irq[0], sizeof(G.st_irq[0][0]) * cpus);
688         memcpy(G.st_irqcpu[2], G.st_irqcpu[0], sizeof(G.st_irqcpu[0][0]) * cpus * G.irqcpu_nr);
689         if (display_opt(D_SOFTIRQS)) {
690                 memcpy(G.st_softirqcpu[2], G.st_softirqcpu[0],
691                         sizeof(G.st_softirqcpu[0][0]) * cpus * G.softirqcpu_nr);
692         }
693
694         current = 1;
695         while (1) {
696                 /* Suspend until a signal is received */
697                 pause();
698
699                 /* Set structures to 0 to distinguish off/online CPUs */
700                 memset(&G.st_cpu[current][/*cpu:*/ 1], 0, sizeof(G.st_cpu[0][0]) * G.cpu_nr);
701
702                 get_localtime(&G.timestamp[current]);
703
704                 /* Read stats */
705                 if (G.cpu_nr > 1) {
706                         G.per_cpu_uptime[current] = 0;
707                         get_uptime(&G.per_cpu_uptime[current]);
708                 }
709                 get_cpu_statistics(G.st_cpu[current], &G.global_uptime[current], &G.per_cpu_uptime[current]);
710
711                 if (display_opt(D_IRQ_SUM))
712                         get_irqs_from_stat(G.st_irq[current]);
713
714                 if (display_opt(D_IRQ_SUM | D_IRQ_CPU))
715                         get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
716                                         G.irqcpu_nr, current);
717
718                 if (display_opt(D_SOFTIRQS))
719                         get_irqs_from_interrupts(PROCFS_SOFTIRQS,
720                                         G.st_softirqcpu,
721                                         G.softirqcpu_nr, current);
722
723                 write_stats(current);
724
725                 if (G.count > 0) {
726                         if (--G.count == 0)
727                                 break;
728                 }
729
730                 current ^= 1;
731         }
732
733         /* Print average statistics */
734         write_stats_avg(current);
735 }
736
737 /* Initialization */
738
739 /* Get number of clock ticks per sec */
740 static ALWAYS_INLINE unsigned get_hz(void)
741 {
742         return sysconf(_SC_CLK_TCK);
743 }
744
745 static void alloc_struct(int cpus)
746 {
747         int i;
748         for (i = 0; i < 3; i++) {
749                 G.st_cpu[i] = xzalloc(sizeof(G.st_cpu[i][0]) * cpus);
750                 G.st_irq[i] = xzalloc(sizeof(G.st_irq[i][0]) * cpus);
751                 G.st_irqcpu[i] = xzalloc(sizeof(G.st_irqcpu[i][0]) * cpus * G.irqcpu_nr);
752                 G.st_softirqcpu[i] = xzalloc(sizeof(G.st_softirqcpu[i][0]) * cpus * G.softirqcpu_nr);
753         }
754         G.cpu_bitmap_len = (cpus >> 3) + 1;
755         G.cpu_bitmap = xzalloc(G.cpu_bitmap_len);
756 }
757
758 static void print_header(struct tm *t)
759 {
760         char cur_date[16];
761         struct utsname uts;
762
763         /* Get system name, release number and hostname */
764         uname(&uts);
765
766         strftime(cur_date, sizeof(cur_date), "%x", t);
767
768         printf("%s %s (%s)\t%s\t_%s_\t(%u CPU)\n",
769                 uts.sysname, uts.release, uts.nodename, cur_date, uts.machine, G.cpu_nr);
770 }
771
772 /*
773  * Get number of processors in /sys
774  */
775 static int get_sys_cpu_nr(void)
776 {
777         DIR *dir;
778         struct dirent *d;
779         struct stat buf;
780         char line[MAX_PF_NAME];
781         int proc_nr = 0;
782
783         dir = opendir(SYSFS_DEVCPU);
784         if (!dir)
785                 return 0;       /* /sys not mounted */
786
787         /* Get current file entry */
788         while ((d = readdir(dir)) != NULL) {
789                 if (starts_with_cpu(d->d_name) && isdigit(d->d_name[3])) {
790                         snprintf(line, MAX_PF_NAME, "%s/%s", SYSFS_DEVCPU,
791                                  d->d_name);
792                         line[MAX_PF_NAME - 1] = '\0';
793                         /* Get information about file */
794                         if (stat(line, &buf) < 0)
795                                 continue;
796                         /* If found 'cpuN', we have one more processor */
797                         if (S_ISDIR(buf.st_mode))
798                                 proc_nr++;
799                 }
800         }
801
802         closedir(dir);
803         return proc_nr;
804 }
805
806 /*
807  * Get number of processors in /proc/stat
808  * Return value '0' means one CPU and non SMP kernel.
809  * Otherwise N means N processor(s) and SMP kernel.
810  */
811 static int get_proc_cpu_nr(void)
812 {
813         FILE *fp;
814         char line[256];
815         int proc_nr = -1;
816
817         fp = xfopen_for_read(PROCFS_STAT);
818         while (fgets(line, sizeof(line), fp)) {
819                 if (!starts_with_cpu(line)) {
820                         if (proc_nr >= 0)
821                                 break; /* we are past "cpuN..." lines */
822                         continue;
823                 }
824                 if (line[3] != ' ') { /* "cpuN" */
825                         int num_proc;
826                         if (sscanf(line + 3, "%u", &num_proc) == 1
827                          && num_proc > proc_nr
828                         ) {
829                                 proc_nr = num_proc;
830                         }
831                 }
832         }
833
834         fclose(fp);
835         return proc_nr + 1;
836 }
837
838 static int get_cpu_nr(void)
839 {
840         int n;
841
842         /* Try to use /sys, if possible */
843         n = get_sys_cpu_nr();
844         if (n == 0)
845                 /* Otherwise use /proc/stat */
846                 n = get_proc_cpu_nr();
847
848         return n;
849 }
850
851 /*
852  * Get number of interrupts available per processor
853  */
854 static int get_irqcpu_nr(const char *f, int max_irqs)
855 {
856         FILE *fp;
857         char *line;
858         unsigned linelen;
859         unsigned irq;
860
861         fp = fopen_for_read(f);
862         if (!fp)                /* No interrupts file */
863                 return 0;
864
865         linelen = INTERRUPTS_LINE + 16 * G.cpu_nr;
866         line = xmalloc(linelen);
867
868         irq = 0;
869         while (fgets(line, linelen, fp)
870          && irq < max_irqs
871         ) {
872                 int p = strcspn(line, ":");
873                 if ((p > 0) && (p < 16))
874                         irq++;
875         }
876
877         fclose(fp);
878         free(line);
879
880         return irq;
881 }
882
883 //usage:#define mpstat_trivial_usage
884 //usage:       "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]"
885 //usage:#define mpstat_full_usage "\n\n"
886 //usage:       "Per-processor statistics\n"
887 //usage:     "\nOptions:"
888 //usage:     "\n        -A                      Same as -I ALL -u -P ALL"
889 //usage:     "\n        -I SUM|CPU|ALL|SCPU     Report interrupt statistics"
890 //usage:     "\n        -P num|ALL              Processor to monitor"
891 //usage:     "\n        -u                      Report CPU utilization"
892
893 int mpstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
894 int mpstat_main(int UNUSED_PARAM argc, char **argv)
895 {
896         char *opt_irq_fmt;
897         char *opt_set_cpu;
898         int i, opt;
899         enum {
900                 OPT_ALL    = 1 << 0,    /* -A */
901                 OPT_INTS   = 1 << 1,    /* -I */
902                 OPT_SETCPU = 1 << 2,    /* -P */
903                 OPT_UTIL   = 1 << 3,    /* -u */
904         };
905
906         /* Dont buffer data if redirected to a pipe */
907         setbuf(stdout, NULL);
908
909         INIT_G();
910
911         G.interval = -1;
912
913         /* Get number of processors */
914         G.cpu_nr = get_cpu_nr();
915
916         /* Get number of clock ticks per sec */
917         G.hz = get_hz();
918
919         /* Calculate number of interrupts per processor */
920         G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC;
921
922         /* Calculate number of soft interrupts per processor */
923         G.softirqcpu_nr = get_irqcpu_nr(PROCFS_SOFTIRQS, NR_IRQS) + NR_IRQCPU_PREALLOC;
924
925         /* Allocate space for structures. + 1 for global structure. */
926         alloc_struct(G.cpu_nr + 1);
927
928         /* Parse and process arguments */
929         opt = getopt32(argv, "AI:P:u", &opt_irq_fmt, &opt_set_cpu);
930         argv += optind;
931
932         if (*argv) {
933                 /* Get interval */
934                 G.interval = xatoi_u(*argv);
935                 G.count = -1;
936                 argv++;
937                 if (*argv) {
938                         /* Get count value */
939                         if (G.interval == 0)
940                                 bb_show_usage();
941                         G.count = xatoi_u(*argv);
942                         //if (*++argv)
943                         //      bb_show_usage();
944                 }
945         }
946         if (G.interval < 0)
947                 G.interval = 0;
948
949         if (opt & OPT_ALL) {
950                 G.p_option = 1;
951                 G.options |= D_CPU + D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS;
952                 /* Select every CPU */
953                 memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
954         }
955
956         if (opt & OPT_INTS) {
957                 static const char v[] = {
958                         D_IRQ_CPU, D_IRQ_SUM, D_SOFTIRQS,
959                         D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS
960                 };
961                 i = index_in_strings("CPU\0SUM\0SCPU\0ALL\0", opt_irq_fmt);
962                 if (i == -1)
963                         bb_show_usage();
964                 G.options |= v[i];
965         }
966
967         if ((opt & OPT_UTIL) /* -u? */
968          || G.options == 0  /* nothing? (use default then) */
969         ) {
970                 G.options |= D_CPU;
971         }
972
973         if (opt & OPT_SETCPU) {
974                 char *t;
975                 G.p_option = 1;
976
977                 for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) {
978                         if (strcmp(t, "ALL") == 0) {
979                                 /* Select every CPU */
980                                 memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
981                         } else {
982                                 /* Get CPU number */
983                                 unsigned n = xatoi_u(t);
984                                 if (n >= G.cpu_nr)
985                                         bb_error_msg_and_die("not that many processors");
986                                 n++;
987                                 G.cpu_bitmap[n >> 3] |= 1 << (n & 7);
988                         }
989                 }
990         }
991
992         if (!G.p_option)
993                 /* Display global stats */
994                 G.cpu_bitmap[0] = 1;
995
996         /* Get time */
997         get_localtime(&G.timestamp[0]);
998
999         /* Display header */
1000         print_header(&G.timestamp[0]);
1001
1002         /* The main loop */
1003         main_loop();
1004
1005         if (ENABLE_FEATURE_CLEAN_UP) {
1006                 /* Clean up */
1007                 for (i = 0; i < 3; i++) {
1008                         free(G.st_cpu[i]);
1009                         free(G.st_irq[i]);
1010                         free(G.st_irqcpu[i]);
1011                         free(G.st_softirqcpu[i]);
1012                 }
1013                 free(G.cpu_bitmap);
1014                 free(&G);
1015         }
1016
1017         return EXIT_SUCCESS;
1018 }