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