]> granicus.if.org Git - sysstat/blob - mpstat.c
mpstat: Initial color support
[sysstat] / mpstat.c
1 /*
2  * mpstat: per-processor statistics
3  * (C) 2000-2015 by Sebastien GODARD (sysstat <at> orange.fr)
4  *
5  ***************************************************************************
6  * This program is free software; you can redistribute it and/or modify it *
7  * under the terms of the GNU General Public License as published  by  the *
8  * Free Software Foundation; either version 2 of the License, or (at  your *
9  * option) any later version.                                              *
10  *                                                                         *
11  * This program is distributed in the hope that it  will  be  useful,  but *
12  * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
14  * for more details.                                                       *
15  *                                                                         *
16  * You should have received a copy of the GNU General Public License along *
17  * with this program; if not, write to the Free Software Foundation, Inc., *
18  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                   *
19  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <sys/utsname.h>
30
31 #include "version.h"
32 #include "mpstat.h"
33 #include "common.h"
34 #include "rd_stats.h"
35 #include "count.h"
36
37 #ifdef USE_NLS
38 #include <locale.h>
39 #include <libintl.h>
40 #define _(string) gettext(string)
41 #else
42 #define _(string) (string)
43 #endif
44
45 #define SCCSID "@(#)sysstat-" VERSION ": "  __FILE__ " compiled " __DATE__ " " __TIME__
46 char *sccsid(void) { return (SCCSID); }
47
48 unsigned long long uptime[3] = {0, 0, 0};
49 unsigned long long uptime0[3] = {0, 0, 0};
50
51 /* NOTE: Use array of _char_ for bitmaps to avoid endianness problems...*/
52 unsigned char *cpu_bitmap;      /* Bit 0: Global; Bit 1: 1st proc; etc. */
53
54 /* Structures used to store stats */
55 struct stats_cpu *st_cpu[3];
56 struct stats_irq *st_irq[3];
57 struct stats_irqcpu *st_irqcpu[3];
58 struct stats_irqcpu *st_softirqcpu[3];
59
60 struct tm mp_tstamp[3];
61
62 /* Activity flag */
63 unsigned int actflags = 0;
64
65 unsigned int flags = 0;
66
67 /* Interval and count parameters */
68 long interval = -1, count = 0;
69
70 /* Nb of processors on the machine */
71 int cpu_nr = 0;
72 /* Nb of interrupts per processor */
73 int irqcpu_nr = 0;
74 /* Nb of soft interrupts per processor */
75 int softirqcpu_nr = 0;
76
77 struct sigaction alrm_act, int_act;
78 int sigint_caught = 0;
79
80 /*
81  ***************************************************************************
82  * Print usage and exit
83  *
84  * IN:
85  * @progname    Name of sysstat command
86  ***************************************************************************
87  */
88 void usage(char *progname)
89 {
90         fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
91                 progname);
92
93         fprintf(stderr, _("Options are:\n"
94                           "[ -A ] [ -u ] [ -V ] [ -I { SUM | CPU | SCPU | ALL } ]\n"
95                           "[ -P { <cpu> [,...] | ON | ALL } ]\n"));
96         exit(1);
97 }
98
99 /*
100  ***************************************************************************
101  * SIGALRM signal handler. No need to reset the handler here.
102  *
103  * IN:
104  * @sig Signal number.
105  ***************************************************************************
106  */
107 void alarm_handler(int sig)
108 {
109         alarm(interval);
110 }
111
112 /*
113  ***************************************************************************
114  * SIGINT signal handler.
115  *
116  * IN:
117  * @sig Signal number.
118  **************************************************************************
119  */
120 void int_handler(int sig)
121 {
122         sigint_caught = 1;
123 }
124
125 /*
126  ***************************************************************************
127  * Allocate stats structures and cpu bitmap.
128  *
129  * IN:
130  * @nr_cpus     Number of CPUs. This is the real number of available CPUs + 1
131  *              because we also have to allocate a structure for CPU 'all'.
132  ***************************************************************************
133  */
134 void salloc_mp_struct(int nr_cpus)
135 {
136         int i;
137
138         for (i = 0; i < 3; i++) {
139
140                 if ((st_cpu[i] = (struct stats_cpu *) malloc(STATS_CPU_SIZE * nr_cpus))
141                     == NULL) {
142                         perror("malloc");
143                         exit(4);
144                 }
145                 memset(st_cpu[i], 0, STATS_CPU_SIZE * nr_cpus);
146
147                 if ((st_irq[i] = (struct stats_irq *) malloc(STATS_IRQ_SIZE * nr_cpus))
148                     == NULL) {
149                         perror("malloc");
150                         exit(4);
151                 }
152                 memset(st_irq[i], 0, STATS_IRQ_SIZE * nr_cpus);
153
154                 if ((st_irqcpu[i] = (struct stats_irqcpu *) malloc(STATS_IRQCPU_SIZE * nr_cpus * irqcpu_nr))
155                     == NULL) {
156                         perror("malloc");
157                         exit(4);
158                 }
159                 memset(st_irqcpu[i], 0, STATS_IRQCPU_SIZE * nr_cpus * irqcpu_nr);
160
161                 if ((st_softirqcpu[i] = (struct stats_irqcpu *) malloc(STATS_IRQCPU_SIZE * nr_cpus * softirqcpu_nr))
162                      == NULL) {
163                         perror("malloc");
164                         exit(4);
165                 }
166                 memset(st_softirqcpu[i], 0, STATS_IRQCPU_SIZE * nr_cpus * softirqcpu_nr);
167         }
168
169         if ((cpu_bitmap = (unsigned char *) malloc((nr_cpus >> 3) + 1)) == NULL) {
170                 perror("malloc");
171                 exit(4);
172         }
173         memset(cpu_bitmap, 0, (nr_cpus >> 3) + 1);
174 }
175
176 /*
177  ***************************************************************************
178  * Free structures and bitmap.
179  ***************************************************************************
180  */
181 void sfree_mp_struct(void)
182 {
183         int i;
184
185         for (i = 0; i < 3; i++) {
186                 free(st_cpu[i]);
187                 free(st_irq[i]);
188                 free(st_irqcpu[i]);
189                 free(st_softirqcpu[i]);
190         }
191
192         free(cpu_bitmap);
193 }
194
195 /*
196  ***************************************************************************
197  * Display per CPU statistics.
198  *
199  * IN:
200  * @st_ic       Array for per-CPU statistics.
201  * @ic_nr       Number of interrupts (hard or soft) per CPU.
202  * @dis         TRUE if a header line must be printed.
203  * @itv         Interval value.
204  * @prev        Position in array where statistics used as reference are.
205  *              Stats used as reference may be the previous ones read, or
206  *              the very first ones when calculating the average.
207  * @curr        Position in array where current statistics will be saved.
208  * @prev_string String displayed at the beginning of a header line. This is
209  *              the timestamp of the previous sample, or "Average" when
210  *              displaying average stats.
211  * @curr_string String displayed at the beginning of current sample stats.
212  *              This is the timestamp of the current sample, or "Average"
213  *              when displaying average stats.
214  ***************************************************************************
215  */
216 void write_irqcpu_stats(struct stats_irqcpu *st_ic[], int ic_nr, int dis,
217                         unsigned long long itv, int prev, int curr,
218                         char *prev_string, char *curr_string)
219 {
220         struct stats_cpu *scc;
221         int j = 0, offset, cpu, colwidth[NR_IRQS];
222         struct stats_irqcpu *p, *q, *p0, *q0;
223
224         /*
225         * Check if number of interrupts has changed.
226         * NB: A null interval value indicates that we are
227          * displaying statistics since system startup.
228          */
229         if (!dis && interval) {
230                 do {
231                         p0 = st_ic[curr] + j;
232                         if (p0->irq_name[0] != '\0') {
233                                 q0 = st_ic[prev] + j;
234                                 if (strcmp(p0->irq_name, q0->irq_name)) {
235                                         /* These are two different irq */
236                                         j = -2;
237                                 }
238                         }
239                         j++;
240                 }
241                 while ((j > 0) && (j <= ic_nr));
242         }
243
244         if (dis || (j < 0)) {
245                 /* Print header */
246                 printf("\n%-11s  CPU", prev_string);
247                 for (j = 0; j < ic_nr; j++) {
248                         p0 = st_ic[curr] + j;
249                         if (p0->irq_name[0] != '\0') {  /* Nb of irq per proc may have varied... */
250                                 printf(" %8s/s", p0->irq_name);
251                         }
252                 }
253                 printf("\n");
254         }
255
256         /* Calculate column widths */
257         for (j = 0; j < ic_nr; j++) {
258                 p0 = st_ic[curr] + j;
259                 if (p0->irq_name[0] != '\0') {
260                         /* Width is IRQ name + 2 for the trailing "/s" */
261                         colwidth[j] = strlen(p0->irq_name) + 2;
262                         /*
263                          * Normal space for printing a number is 11 chars
264                          * (space + 10 digits including the period).
265                          */
266                         if (colwidth[j] < 10) {
267                                 colwidth[j] = 10;
268                         }
269                 }
270         }
271
272         for (cpu = 1; cpu <= cpu_nr; cpu++) {
273
274                 scc = st_cpu[curr] + cpu;
275
276                 /*
277                  * Check if we want stats about this CPU.
278                  * CPU must have been explicitly selected using option -P,
279                  * else we display every CPU.
280                  */
281                 if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) && USE_P_OPTION(flags))
282                         continue;
283
284                 if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
285                      scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
286                      scc->cpu_hardirq + scc->cpu_softirq) == 0) {
287
288                         /* Offline CPU found */
289
290                         if (DISPLAY_ONLINE_CPU(flags))
291                                 continue;
292                 }
293
294                 printf("%-11s  %3d", curr_string, cpu - 1);
295
296                 for (j = 0; j < ic_nr; j++) {
297                         p0 = st_ic[curr] + j;   /* irq field set only for proc #0 */
298                         /*
299                          * An empty string for irq name means it is a remaining interrupt
300                          * which is no longer used, for example because the
301                          * number of interrupts has decreased in /proc/interrupts.
302                          */
303                         if (p0->irq_name[0] != '\0') {
304                                 q0 = st_ic[prev] + j;
305                                 offset = j;
306
307                                 /*
308                                  * If we want stats for the time since system startup,
309                                  * we have p0->irq != q0->irq, since q0 structure is
310                                  * completely set to zero.
311                                  */
312                                 if (strcmp(p0->irq_name, q0->irq_name) && interval) {
313                                         if (j)
314                                                 offset = j - 1;
315                                         q0 = st_ic[prev] + offset;
316                                         if (strcmp(p0->irq_name, q0->irq_name) && (j + 1 < ic_nr))
317                                                 offset = j + 1;
318                                         q0 = st_ic[prev] + offset;
319                                 }
320
321                                 if (!strcmp(p0->irq_name, q0->irq_name) || !interval) {
322                                         p = st_ic[curr] + (cpu - 1) * ic_nr + j;
323                                         q = st_ic[prev] + (cpu - 1) * ic_nr + offset;
324                                         cprintf_f(1, colwidth[j], 2,
325                                                   S_VALUE(q->interrupt, p->interrupt, itv));
326                                 }
327                                 else
328                                         printf("        N/A");
329                         }
330                 }
331                 printf("\n");
332         }
333 }
334
335 /*
336  ***************************************************************************
337  * Core function used to display statistics.
338  *
339  * IN:
340  * @prev        Position in array where statistics used as reference are.
341  *              Stats used as reference may be the previous ones read, or
342  *              the very first ones when calculating the average.
343  * @curr        Position in array where statistics for current sample are.
344  * @dis         TRUE if a header line must be printed.
345  * @prev_string String displayed at the beginning of a header line. This is
346  *              the timestamp of the previous sample, or "Average" when
347  *              displaying average stats.
348  * @curr_string String displayed at the beginning of current sample stats.
349  *              This is the timestamp of the current sample, or "Average"
350  *              when displaying average stats.
351  ***************************************************************************
352  */
353 void write_stats_core(int prev, int curr, int dis,
354                       char *prev_string, char *curr_string)
355 {
356         struct stats_cpu *scc, *scp;
357         unsigned long long itv, pc_itv, g_itv;
358         int cpu;
359
360         /* Test stdout */
361         TEST_STDOUT(STDOUT_FILENO);
362
363         /* Compute time interval */
364         g_itv = get_interval(uptime[prev], uptime[curr]);
365
366         /* Reduce interval value to one processor */
367         if (cpu_nr > 1) {
368                 itv = get_interval(uptime0[prev], uptime0[curr]);
369         }
370         else {
371                 itv = g_itv;
372         }
373
374         /* Print CPU stats */
375         if (DISPLAY_CPU(actflags)) {
376                 if (dis) {
377                         printf("\n%-11s  CPU    %%usr   %%nice    %%sys %%iowait    %%irq   "
378                                "%%soft  %%steal  %%guest  %%gnice   %%idle\n",
379                                prev_string);
380                 }
381
382                 /* Check if we want global stats among all proc */
383                 if (*cpu_bitmap & 1) {
384
385                         printf("%-11s  all", curr_string);
386
387                         cprintf_pc(10, 7, 2,
388                                    (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) <
389                                    (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ?
390                                    0.0 :
391                                    ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest,
392                                                st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest,
393                                                g_itv),
394                                    (st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice) <
395                                    (st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice) ?
396                                    0.0 :
397                                    ll_sp_value(st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice,
398                                                st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice,
399                                                g_itv),
400                                    ll_sp_value(st_cpu[prev]->cpu_sys,
401                                                st_cpu[curr]->cpu_sys,
402                                                g_itv),
403                                    ll_sp_value(st_cpu[prev]->cpu_iowait,
404                                                st_cpu[curr]->cpu_iowait,
405                                                g_itv),
406                                    ll_sp_value(st_cpu[prev]->cpu_hardirq,
407                                                st_cpu[curr]->cpu_hardirq,
408                                                g_itv),
409                                    ll_sp_value(st_cpu[prev]->cpu_softirq,
410                                                st_cpu[curr]->cpu_softirq,
411                                                g_itv),
412                                    ll_sp_value(st_cpu[prev]->cpu_steal,
413                                                st_cpu[curr]->cpu_steal,
414                                                g_itv),
415                                    ll_sp_value(st_cpu[prev]->cpu_guest,
416                                                st_cpu[curr]->cpu_guest,
417                                                g_itv),
418                                    ll_sp_value(st_cpu[prev]->cpu_guest_nice,
419                                                st_cpu[curr]->cpu_guest_nice,
420                                                g_itv),
421                                    (st_cpu[curr]->cpu_idle < st_cpu[prev]->cpu_idle) ?
422                                    0.0 :
423                                    ll_sp_value(st_cpu[prev]->cpu_idle,
424                                                st_cpu[curr]->cpu_idle,
425                                                g_itv));
426                         printf("\n");
427                 }
428
429                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
430
431                         scc = st_cpu[curr] + cpu;
432                         scp = st_cpu[prev] + cpu;
433
434                         /* Check if we want stats about this proc */
435                         if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
436                                 continue;
437
438                         /*
439                          * If the CPU is offline then it is omited from /proc/stat
440                          * and the sum of all values is zero.
441                          * (Remember that guest/guest_nice times are already included in
442                          * user/nice modes.)
443                          */
444                         if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
445                              scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
446                              scc->cpu_hardirq + scc->cpu_softirq) == 0) {
447
448                                 if (!DISPLAY_ONLINE_CPU(flags)) {
449                                         printf("%-11s %4d", curr_string, cpu - 1);
450                                         cprintf_pc(10, 7, 2,
451                                                    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
452                                         printf("\n");
453                                 }
454                                 continue;
455                         }
456
457                         printf("%-11s %4d", curr_string, cpu - 1);
458
459                         /* Recalculate itv for current proc */
460                         pc_itv = get_per_cpu_interval(scc, scp);
461
462                         if (!pc_itv) {
463                                 /*
464                                  * If the CPU is tickless then there is no change in CPU values
465                                  * but the sum of values is not zero.
466                                  */
467                                 cprintf_pc(10, 7, 2,
468                                            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
469                                 printf("\n");
470                         }
471
472                         else {
473                                 cprintf_pc(10, 7, 2,
474                                            (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
475                                            0.0 :
476                                            ll_sp_value(scp->cpu_user - scp->cpu_guest,
477                                                        scc->cpu_user - scc->cpu_guest,
478                                                        pc_itv),
479                                            (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
480                                            0.0 :
481                                            ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
482                                                        scc->cpu_nice - scc->cpu_guest_nice,
483                                                        pc_itv),
484                                            ll_sp_value(scp->cpu_sys,
485                                                        scc->cpu_sys,
486                                                        pc_itv),
487                                            ll_sp_value(scp->cpu_iowait,
488                                                        scc->cpu_iowait,
489                                                        pc_itv),
490                                            ll_sp_value(scp->cpu_hardirq,
491                                                        scc->cpu_hardirq,
492                                                        pc_itv),
493                                            ll_sp_value(scp->cpu_softirq,
494                                                        scc->cpu_softirq,
495                                                        pc_itv),
496                                            ll_sp_value(scp->cpu_steal,
497                                                        scc->cpu_steal,
498                                                        pc_itv),
499                                            ll_sp_value(scp->cpu_guest,
500                                                        scc->cpu_guest,
501                                                        pc_itv),
502                                            ll_sp_value(scp->cpu_guest_nice,
503                                                        scc->cpu_guest_nice,
504                                                        pc_itv),
505                                            (scc->cpu_idle < scp->cpu_idle) ?
506                                            0.0 :
507                                            ll_sp_value(scp->cpu_idle,
508                                                        scc->cpu_idle,
509                                                        pc_itv));
510                                 printf("\n");
511                         }
512                 }
513         }
514
515         /* Print total number of interrupts per processor */
516         if (DISPLAY_IRQ_SUM(actflags)) {
517                 struct stats_irq *sic, *sip;
518
519                 if (dis) {
520                         printf("\n%-11s  CPU    intr/s\n", prev_string);
521                 }
522
523                 if (*cpu_bitmap & 1) {
524                         printf("%-11s  all", curr_string);
525                         cprintf_f(1, 9, 2,
526                                   S_VALUE(st_irq[prev]->irq_nr, st_irq[curr]->irq_nr, itv));
527                         printf("\n");
528                 }
529
530                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
531
532                         sic = st_irq[curr] + cpu;
533                         sip = st_irq[prev] + cpu;
534
535                         scc = st_cpu[curr] + cpu;
536                         scp = st_cpu[prev] + cpu;
537
538                         /* Check if we want stats about this proc */
539                         if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
540                                 continue;
541
542                         if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
543                              scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
544                              scc->cpu_hardirq + scc->cpu_softirq) == 0) {
545
546                                 /* This is an offline CPU */
547
548                                 if (!DISPLAY_ONLINE_CPU(flags)) {
549                                         printf("%-11s %4d", curr_string, cpu - 1);
550                                         cprintf_f(1, 9, 2, 0.0);
551                                         printf("\n");
552                                 }
553                                 continue;
554                         }
555
556                         printf("%-11s %4d", curr_string, cpu - 1);
557
558                         /* Recalculate itv for current proc */
559                         pc_itv = get_per_cpu_interval(scc, scp);
560
561                         if (!pc_itv) {
562                                 /* This is a tickless CPU */
563                                 cprintf_f(1, 9, 2, 0.0);
564                                 printf("\n");
565                         }
566                         else {
567                                 cprintf_f(1, 9, 2,
568                                           S_VALUE(sip->irq_nr, sic->irq_nr, itv));
569                                 printf("\n");
570                         }
571                 }
572         }
573
574         if (DISPLAY_IRQ_CPU(actflags)) {
575                 write_irqcpu_stats(st_irqcpu, irqcpu_nr, dis, itv, prev, curr,
576                                    prev_string, curr_string);
577         }
578
579         if (DISPLAY_SOFTIRQS(actflags)) {
580                 write_irqcpu_stats(st_softirqcpu, softirqcpu_nr, dis, itv, prev, curr,
581                                    prev_string, curr_string);
582         }
583
584         /* Fix CPU counter values for every offline CPU */
585         for (cpu = 1; cpu <= cpu_nr; cpu++) {
586
587                 scc = st_cpu[curr] + cpu;
588                 scp = st_cpu[prev] + cpu;
589
590                 if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
591                      scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
592                      scc->cpu_hardirq + scc->cpu_softirq) == 0) {
593                         /*
594                          * Offline CPU found.
595                          * Set current struct fields (which have been set to zero)
596                          * to values from previous iteration. Hence their values won't
597                          * jump from zero when the CPU comes back online.
598                          */
599                         *scc = *scp;
600                 }
601         }
602 }
603
604 /*
605  ***************************************************************************
606  * Print statistics average.
607  *
608  * IN:
609  * @curr        Position in array where statistics for current sample are.
610  * @dis         TRUE if a header line must be printed.
611  ***************************************************************************
612  */
613 void write_stats_avg(int curr, int dis)
614 {
615         char string[16];
616
617         strncpy(string, _("Average:"), 16);
618         string[15] = '\0';
619         write_stats_core(2, curr, dis, string, string);
620 }
621
622 /*
623  ***************************************************************************
624  * Print statistics.
625  *
626  * IN:
627  * @curr        Position in array where statistics for current sample are.
628  * @dis         TRUE if a header line must be printed.
629  ***************************************************************************
630  */
631 void write_stats(int curr, int dis)
632 {
633         char cur_time[2][16];
634
635         /* Get previous timestamp */
636         strftime(cur_time[!curr], sizeof(cur_time[!curr]), "%X", &(mp_tstamp[!curr]));
637
638         /* Get current timestamp */
639         strftime(cur_time[curr], sizeof(cur_time[curr]), "%X", &(mp_tstamp[curr]));
640
641         write_stats_core(!curr, curr, dis, cur_time[!curr], cur_time[curr]);
642 }
643
644 /*
645  ***************************************************************************
646  * Read stats from /proc/interrupts or /proc/softirqs.
647  *
648  * IN:
649  * @file        /proc file to read (interrupts or softirqs).
650  * @ic_nr       Number of interrupts (hard or soft) per CPU.
651  * @curr        Position in array where current statistics will be saved.
652  *
653  * OUT:
654  * @st_ic       Array for per-CPU statistics.
655  ***************************************************************************
656  */
657 void read_interrupts_stat(char *file, struct stats_irqcpu *st_ic[], int ic_nr, int curr)
658 {
659         FILE *fp;
660         struct stats_irq *st_irq_i;
661         struct stats_irqcpu *p;
662         char *line = NULL, *li;
663         unsigned long irq = 0;
664         unsigned int cpu;
665         int cpu_index[cpu_nr], index = 0, len;
666         char *cp, *next;
667
668         for (cpu = 0; cpu < cpu_nr; cpu++) {
669                 st_irq_i = st_irq[curr] + cpu + 1;
670                 st_irq_i->irq_nr = 0;
671         }
672
673         if ((fp = fopen(file, "r")) != NULL) {
674
675                 SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr);
676
677                 /*
678                  * Parse header line to see which CPUs are online
679                  */
680                 while (fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) {
681                         next = line;
682                         while (((cp = strstr(next, "CPU")) != NULL) && (index < cpu_nr)) {
683                                 cpu = strtol(cp + 3, &next, 10);
684                                 cpu_index[index++] = cpu;
685                         }
686                         if (index)
687                                 /* Header line found */
688                                 break;
689                 }
690
691                 while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) &&
692                        (irq < ic_nr)) {
693
694                         /* Skip over "<irq>:" */
695                         if ((cp = strchr(line, ':')) == NULL)
696                                 /* Chr ':' not found */
697                                 continue;
698                         cp++;
699
700                         p = st_ic[curr] + irq;
701
702                         li = line;
703                         while (*li == ' ')
704                                 li++;
705
706                         len = strcspn(li, ":");
707                         if (len >= MAX_IRQ_LEN) {
708                                 len = MAX_IRQ_LEN - 1;
709                         }
710                         strncpy(p->irq_name, li, len);
711                         p->irq_name[len] = '\0';
712
713                         for (cpu = 0; cpu < index; cpu++) {
714                                 p = st_ic[curr] + cpu_index[cpu] * ic_nr + irq;
715                                 st_irq_i = st_irq[curr] + cpu_index[cpu] + 1;
716                                 /*
717                                  * No need to set (st_irqcpu + cpu * irqcpu_nr)->irq:
718                                  * This is the same as st_irqcpu->irq.
719                                  */
720                                 p->interrupt = strtoul(cp, &next, 10);
721                                 st_irq_i->irq_nr += p->interrupt;
722                                 cp = next;
723                         }
724                         irq++;
725                 }
726
727                 fclose(fp);
728
729                 free(line);
730         }
731
732         while (irq < ic_nr) {
733                 /* Nb of interrupts per processor has changed */
734                 p = st_ic[curr] + irq;
735                 p->irq_name[0] = '\0';  /* This value means this is a dummy interrupt */
736                 irq++;
737         }
738 }
739
740 /*
741  ***************************************************************************
742  * Main loop: Read stats from the relevant sources, and display them.
743  *
744  * IN:
745  * @dis_hdr     Set to TRUE if the header line must always be printed.
746  * @rows        Number of rows of screen.
747  ***************************************************************************
748  */
749 void rw_mpstat_loop(int dis_hdr, int rows)
750 {
751         struct stats_cpu *scc;
752         int cpu;
753         int curr = 1, dis = 1;
754         unsigned long lines = rows;
755
756         /* Dont buffer data if redirected to a pipe */
757         setbuf(stdout, NULL);
758
759         /* Read stats */
760         if (cpu_nr > 1) {
761                 /*
762                  * Init uptime0. So if /proc/uptime cannot fill it,
763                  * this will be done by /proc/stat.
764                  */
765                 uptime0[0] = 0;
766                 read_uptime(&(uptime0[0]));
767         }
768         read_stat_cpu(st_cpu[0], cpu_nr + 1, &(uptime[0]), &(uptime0[0]));
769
770         if (DISPLAY_IRQ_SUM(actflags)) {
771                 read_stat_irq(st_irq[0], 1);
772         }
773
774         if (DISPLAY_IRQ_SUM(actflags) || DISPLAY_IRQ_CPU(actflags)) {
775                 /* Read this file to display int per CPU or total nr of int per CPU */
776                 read_interrupts_stat(INTERRUPTS, st_irqcpu, irqcpu_nr, 0);
777         }
778
779         if (DISPLAY_SOFTIRQS(actflags)) {
780                 read_interrupts_stat(SOFTIRQS, st_softirqcpu, softirqcpu_nr, 0);
781         }
782
783         if (!interval) {
784                 /* Display since boot time */
785                 mp_tstamp[1] = mp_tstamp[0];
786                 memset(st_cpu[1], 0, STATS_CPU_SIZE * (cpu_nr + 1));
787                 memset(st_irq[1], 0, STATS_IRQ_SIZE * (cpu_nr + 1));
788                 memset(st_irqcpu[1], 0, STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
789                 if (DISPLAY_SOFTIRQS(actflags)) {
790                         memset(st_softirqcpu[1], 0, STATS_IRQCPU_SIZE * (cpu_nr + 1) * softirqcpu_nr);
791                 }
792                 write_stats(0, DISP_HDR);
793                 exit(0);
794         }
795
796         /* Set a handler for SIGALRM */
797         memset(&alrm_act, 0, sizeof(alrm_act));
798         alrm_act.sa_handler = alarm_handler;
799         sigaction(SIGALRM, &alrm_act, NULL);
800         alarm(interval);
801
802         /* Save the first stats collected. Will be used to compute the average */
803         mp_tstamp[2] = mp_tstamp[0];
804         uptime[2] = uptime[0];
805         uptime0[2] = uptime0[0];
806         memcpy(st_cpu[2], st_cpu[0], STATS_CPU_SIZE * (cpu_nr + 1));
807         memcpy(st_irq[2], st_irq[0], STATS_IRQ_SIZE * (cpu_nr + 1));
808         memcpy(st_irqcpu[2], st_irqcpu[0], STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
809         if (DISPLAY_SOFTIRQS(actflags)) {
810                 memcpy(st_softirqcpu[2], st_softirqcpu[0],
811                        STATS_IRQCPU_SIZE * (cpu_nr + 1) * softirqcpu_nr);
812         }
813
814         /* Set a handler for SIGINT */
815         memset(&int_act, 0, sizeof(int_act));
816         int_act.sa_handler = int_handler;
817         sigaction(SIGINT, &int_act, NULL);
818
819         pause();
820
821         if (sigint_caught)
822                 /* SIGINT signal caught during first interval: Exit immediately */
823                 return;
824
825         do {
826                 /*
827                  * Resetting the structure not needed since every fields will be set.
828                  * Exceptions are per-CPU structures: Some of them may not be filled
829                  * if corresponding processor is disabled (offline). We set them to zero
830                  * to be able to distinguish between offline and tickless CPUs.
831                  */
832                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
833                         scc = st_cpu[curr] + cpu;
834                         memset(scc, 0, STATS_CPU_SIZE);
835                 }
836
837                 /* Get time */
838                 get_localtime(&(mp_tstamp[curr]), 0);
839
840                 /* Read stats */
841                 if (cpu_nr > 1) {
842                         uptime0[curr] = 0;
843                         read_uptime(&(uptime0[curr]));
844                 }
845                 read_stat_cpu(st_cpu[curr], cpu_nr + 1, &(uptime[curr]), &(uptime0[curr]));
846
847                 if (DISPLAY_IRQ_SUM(actflags)) {
848                         read_stat_irq(st_irq[curr], 1);
849                 }
850
851                 if (DISPLAY_IRQ_SUM(actflags) || DISPLAY_IRQ_CPU(actflags)) {
852                         read_interrupts_stat(INTERRUPTS, st_irqcpu, irqcpu_nr, curr);
853                 }
854
855                 if (DISPLAY_SOFTIRQS(actflags)) {
856                         read_interrupts_stat(SOFTIRQS, st_softirqcpu, softirqcpu_nr, curr);
857                 }
858
859                 /* Write stats */
860                 if (!dis_hdr) {
861                         dis = lines / rows;
862                         if (dis) {
863                                 lines %= rows;
864                         }
865                         lines++;
866                 }
867                 write_stats(curr, dis);
868
869                 if (count > 0) {
870                         count--;
871                 }
872
873                 if (count) {
874
875                         pause();
876
877                         if (sigint_caught) {
878                                 /* SIGINT signal caught => Display average stats */
879                                 count = 0;
880                                 printf("\n");   /* Skip "^C" displayed on screen */
881                         }
882                         else {
883                                 curr ^= 1;
884                         }
885                 }
886         }
887         while (count);
888
889         /* Write stats average */
890         write_stats_avg(curr, dis_hdr);
891 }
892
893 /*
894  ***************************************************************************
895  * Main entry to the program
896  ***************************************************************************
897  */
898 int main(int argc, char **argv)
899 {
900         int opt = 0, i, actset = FALSE;
901         struct utsname header;
902         int dis_hdr = -1;
903         int rows = 23;
904         char *t;
905
906 #ifdef USE_NLS
907         /* Init National Language Support */
908         init_nls();
909 #endif
910
911         /* Init color strings */
912         init_colors();
913
914         /* Get HZ */
915         get_HZ();
916
917         /* What is the highest processor number on this machine? */
918         cpu_nr = get_cpu_nr(~0, TRUE);
919
920         /* Calculate number of interrupts per processor */
921         irqcpu_nr = get_irqcpu_nr(INTERRUPTS, NR_IRQS, cpu_nr) +
922                     NR_IRQCPU_PREALLOC;
923         /* Calculate number of soft interrupts per processor */
924         softirqcpu_nr = get_irqcpu_nr(SOFTIRQS, NR_IRQS, cpu_nr) +
925                         NR_IRQCPU_PREALLOC;
926
927         /*
928          * cpu_nr: a value of 2 means there are 2 processors (0 and 1).
929          * In this case, we have to allocate 3 structures: global, proc0 and proc1.
930          */
931         salloc_mp_struct(cpu_nr + 1);
932
933         while (++opt < argc) {
934
935                 if (!strcmp(argv[opt], "-I")) {
936                         if (argv[++opt]) {
937                                 actset = TRUE;
938                                 if (!strcmp(argv[opt], K_SUM)) {
939                                         /* Display total number of interrupts per CPU */
940                                         actflags |= M_D_IRQ_SUM;
941                                 }
942                                 else if (!strcmp(argv[opt], K_CPU)) {
943                                         /* Display interrupts per CPU */
944                                         actflags |= M_D_IRQ_CPU;
945                                 }
946                                 else if (!strcmp(argv[opt], K_SCPU)) {
947                                         /* Display soft interrupts per CPU */
948                                         actflags |= M_D_SOFTIRQS;
949                                 }
950                                 else if (!strcmp(argv[opt], K_ALL)) {
951                                         actflags |= M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
952                                 }
953                                 else {
954                                         usage(argv[0]);
955                                 }
956                         }
957                         else {
958                                 usage(argv[0]);
959                         }
960                 }
961
962                 else if (!strcmp(argv[opt], "-P")) {
963                         /* '-P ALL' can be used on UP machines */
964                         if (argv[++opt]) {
965                                 flags |= F_P_OPTION;
966                                 dis_hdr++;
967
968                                 for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
969                                         if (!strcmp(t, K_ALL) || !strcmp(t, K_ON)) {
970                                                 if (cpu_nr) {
971                                                         dis_hdr = 9;
972                                                 }
973                                                 /*
974                                                  * Set bit for every processor.
975                                                  * Also indicate to display stats for CPU 'all'.
976                                                  */
977                                                 memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
978                                                 if (!strcmp(t, K_ON)) {
979                                                         /* Display stats only for online CPU */
980                                                         flags |= F_P_ON;
981                                                 }
982                                         }
983                                         else {
984                                                 if (strspn(t, DIGITS) != strlen(t)) {
985                                                         usage(argv[0]);
986                                                 }
987                                                 i = atoi(t);    /* Get cpu number */
988                                                 if (i >= cpu_nr) {
989                                                         fprintf(stderr, _("Not that many processors!\n"));
990                                                         exit(1);
991                                                 }
992                                                 i++;
993                                                 *(cpu_bitmap + (i >> 3)) |= 1 << (i & 0x07);
994                                         }
995                                 }
996                         }
997                         else {
998                                 usage(argv[0]);
999                         }
1000                 }
1001
1002                 else if (!strncmp(argv[opt], "-", 1)) {
1003                         for (i = 1; *(argv[opt] + i); i++) {
1004
1005                                 switch (*(argv[opt] + i)) {
1006
1007                                 case 'A':
1008                                         actflags |= M_D_CPU + M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
1009                                         actset = TRUE;
1010                                         /* Select all processors */
1011                                         flags |= F_P_OPTION;
1012                                         memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
1013                                         break;
1014
1015                                 case 'u':
1016                                         /* Display CPU */
1017                                         actflags |= M_D_CPU;
1018                                         break;
1019
1020                                 case 'V':
1021                                         /* Print version number */
1022                                         print_version();
1023                                         break;
1024
1025                                 default:
1026                                         usage(argv[0]);
1027                                 }
1028                         }
1029                 }
1030
1031                 else if (interval < 0) {
1032                         /* Get interval */
1033                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1034                                 usage(argv[0]);
1035                         }
1036                         interval = atol(argv[opt]);
1037                         if (interval < 0) {
1038                                 usage(argv[0]);
1039                         }
1040                         count = -1;
1041                 }
1042
1043                 else if (count <= 0) {
1044                         /* Get count value */
1045                         if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1046                             !interval) {
1047                                 usage(argv[0]);
1048                         }
1049                         count = atol(argv[opt]);
1050                         if (count < 1) {
1051                                 usage(argv[0]);
1052                         }
1053                 }
1054
1055                 else {
1056                         usage(argv[0]);
1057                 }
1058         }
1059
1060         /* Default: Display CPU */
1061         if (!actset) {
1062                 actflags |= M_D_CPU;
1063         }
1064
1065         if (count_bits(&actflags, sizeof(unsigned int)) > 1) {
1066                 dis_hdr = 9;
1067         }
1068
1069         if (!USE_P_OPTION(flags)) {
1070                 /* Option -P not used: Set bit 0 (global stats among all proc) */
1071                 *cpu_bitmap = 1;
1072         }
1073         if (dis_hdr < 0) {
1074                 dis_hdr = 0;
1075         }
1076         if (!dis_hdr) {
1077                 /* Get window size */
1078                 rows = get_win_height();
1079         }
1080         if (interval < 0) {
1081                 /* Interval not set => display stats since boot time */
1082                 interval = 0;
1083         }
1084
1085         /* Get time */
1086         get_localtime(&(mp_tstamp[0]), 0);
1087
1088         /* Get system name, release number and hostname */
1089         uname(&header);
1090         print_gal_header(&(mp_tstamp[0]), header.sysname, header.release,
1091                          header.nodename, header.machine, get_cpu_nr(~0, FALSE));
1092
1093         /* Main loop */
1094         rw_mpstat_loop(dis_hdr, rows);
1095
1096         /* Free structures */
1097         sfree_mp_struct();
1098
1099         return 0;
1100 }