]> granicus.if.org Git - sysstat/blob - mpstat.c
iostat, mpstat: Color enhancements
[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", curr_string);
295                 cprintf_in(IS_INT, "  %3d", "", cpu - 1);
296
297                 for (j = 0; j < ic_nr; j++) {
298                         p0 = st_ic[curr] + j;   /* irq field set only for proc #0 */
299                         /*
300                          * An empty string for irq name means it is a remaining interrupt
301                          * which is no longer used, for example because the
302                          * number of interrupts has decreased in /proc/interrupts.
303                          */
304                         if (p0->irq_name[0] != '\0') {
305                                 q0 = st_ic[prev] + j;
306                                 offset = j;
307
308                                 /*
309                                  * If we want stats for the time since system startup,
310                                  * we have p0->irq != q0->irq, since q0 structure is
311                                  * completely set to zero.
312                                  */
313                                 if (strcmp(p0->irq_name, q0->irq_name) && interval) {
314                                         if (j)
315                                                 offset = j - 1;
316                                         q0 = st_ic[prev] + offset;
317                                         if (strcmp(p0->irq_name, q0->irq_name) && (j + 1 < ic_nr))
318                                                 offset = j + 1;
319                                         q0 = st_ic[prev] + offset;
320                                 }
321
322                                 if (!strcmp(p0->irq_name, q0->irq_name) || !interval) {
323                                         p = st_ic[curr] + (cpu - 1) * ic_nr + j;
324                                         q = st_ic[prev] + (cpu - 1) * ic_nr + offset;
325                                         cprintf_f(1, colwidth[j], 2,
326                                                   S_VALUE(q->interrupt, p->interrupt, itv));
327                                 }
328                                 else
329                                         printf("        N/A");
330                         }
331                 }
332                 printf("\n");
333         }
334 }
335
336 /*
337  ***************************************************************************
338  * Core function used to display statistics.
339  *
340  * IN:
341  * @prev        Position in array where statistics used as reference are.
342  *              Stats used as reference may be the previous ones read, or
343  *              the very first ones when calculating the average.
344  * @curr        Position in array where statistics for current sample are.
345  * @dis         TRUE if a header line must be printed.
346  * @prev_string String displayed at the beginning of a header line. This is
347  *              the timestamp of the previous sample, or "Average" when
348  *              displaying average stats.
349  * @curr_string String displayed at the beginning of current sample stats.
350  *              This is the timestamp of the current sample, or "Average"
351  *              when displaying average stats.
352  ***************************************************************************
353  */
354 void write_stats_core(int prev, int curr, int dis,
355                       char *prev_string, char *curr_string)
356 {
357         struct stats_cpu *scc, *scp;
358         unsigned long long itv, pc_itv, g_itv;
359         int cpu;
360
361         /* Test stdout */
362         TEST_STDOUT(STDOUT_FILENO);
363
364         /* Compute time interval */
365         g_itv = get_interval(uptime[prev], uptime[curr]);
366
367         /* Reduce interval value to one processor */
368         if (cpu_nr > 1) {
369                 itv = get_interval(uptime0[prev], uptime0[curr]);
370         }
371         else {
372                 itv = g_itv;
373         }
374
375         /* Print CPU stats */
376         if (DISPLAY_CPU(actflags)) {
377                 if (dis) {
378                         printf("\n%-11s  CPU    %%usr   %%nice    %%sys %%iowait    %%irq   "
379                                "%%soft  %%steal  %%guest  %%gnice   %%idle\n",
380                                prev_string);
381                 }
382
383                 /* Check if we want global stats among all proc */
384                 if (*cpu_bitmap & 1) {
385
386                         printf("%-11s", curr_string);
387                         cprintf_in(IS_STR, " %s", " all", 0);
388
389                         cprintf_pc(10, 7, 2,
390                                    (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) <
391                                    (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ?
392                                    0.0 :
393                                    ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest,
394                                                st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest,
395                                                g_itv),
396                                    (st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice) <
397                                    (st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice) ?
398                                    0.0 :
399                                    ll_sp_value(st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice,
400                                                st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice,
401                                                g_itv),
402                                    ll_sp_value(st_cpu[prev]->cpu_sys,
403                                                st_cpu[curr]->cpu_sys,
404                                                g_itv),
405                                    ll_sp_value(st_cpu[prev]->cpu_iowait,
406                                                st_cpu[curr]->cpu_iowait,
407                                                g_itv),
408                                    ll_sp_value(st_cpu[prev]->cpu_hardirq,
409                                                st_cpu[curr]->cpu_hardirq,
410                                                g_itv),
411                                    ll_sp_value(st_cpu[prev]->cpu_softirq,
412                                                st_cpu[curr]->cpu_softirq,
413                                                g_itv),
414                                    ll_sp_value(st_cpu[prev]->cpu_steal,
415                                                st_cpu[curr]->cpu_steal,
416                                                g_itv),
417                                    ll_sp_value(st_cpu[prev]->cpu_guest,
418                                                st_cpu[curr]->cpu_guest,
419                                                g_itv),
420                                    ll_sp_value(st_cpu[prev]->cpu_guest_nice,
421                                                st_cpu[curr]->cpu_guest_nice,
422                                                g_itv),
423                                    (st_cpu[curr]->cpu_idle < st_cpu[prev]->cpu_idle) ?
424                                    0.0 :
425                                    ll_sp_value(st_cpu[prev]->cpu_idle,
426                                                st_cpu[curr]->cpu_idle,
427                                                g_itv));
428                         printf("\n");
429                 }
430
431                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
432
433                         scc = st_cpu[curr] + cpu;
434                         scp = st_cpu[prev] + cpu;
435
436                         /* Check if we want stats about this proc */
437                         if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
438                                 continue;
439
440                         /*
441                          * If the CPU is offline then it is omited from /proc/stat
442                          * and the sum of all values is zero.
443                          * (Remember that guest/guest_nice times are already included in
444                          * user/nice modes.)
445                          */
446                         if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
447                              scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
448                              scc->cpu_hardirq + scc->cpu_softirq) == 0) {
449
450                                 if (!DISPLAY_ONLINE_CPU(flags)) {
451                                         printf("%-11s", curr_string);
452                                         cprintf_in(IS_INT, " %4d", "", cpu - 1);
453                                         cprintf_pc(10, 7, 2,
454                                                    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
455                                         printf("\n");
456                                 }
457                                 continue;
458                         }
459
460                         printf("%-11s", curr_string);
461                         cprintf_in(IS_INT, " %4d", "", cpu - 1);
462
463                         /* Recalculate itv for current proc */
464                         pc_itv = get_per_cpu_interval(scc, scp);
465
466                         if (!pc_itv) {
467                                 /*
468                                  * If the CPU is tickless then there is no change in CPU values
469                                  * but the sum of values is not zero.
470                                  */
471                                 cprintf_pc(10, 7, 2,
472                                            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
473                                 printf("\n");
474                         }
475
476                         else {
477                                 cprintf_pc(10, 7, 2,
478                                            (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
479                                            0.0 :
480                                            ll_sp_value(scp->cpu_user - scp->cpu_guest,
481                                                        scc->cpu_user - scc->cpu_guest,
482                                                        pc_itv),
483                                            (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
484                                            0.0 :
485                                            ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
486                                                        scc->cpu_nice - scc->cpu_guest_nice,
487                                                        pc_itv),
488                                            ll_sp_value(scp->cpu_sys,
489                                                        scc->cpu_sys,
490                                                        pc_itv),
491                                            ll_sp_value(scp->cpu_iowait,
492                                                        scc->cpu_iowait,
493                                                        pc_itv),
494                                            ll_sp_value(scp->cpu_hardirq,
495                                                        scc->cpu_hardirq,
496                                                        pc_itv),
497                                            ll_sp_value(scp->cpu_softirq,
498                                                        scc->cpu_softirq,
499                                                        pc_itv),
500                                            ll_sp_value(scp->cpu_steal,
501                                                        scc->cpu_steal,
502                                                        pc_itv),
503                                            ll_sp_value(scp->cpu_guest,
504                                                        scc->cpu_guest,
505                                                        pc_itv),
506                                            ll_sp_value(scp->cpu_guest_nice,
507                                                        scc->cpu_guest_nice,
508                                                        pc_itv),
509                                            (scc->cpu_idle < scp->cpu_idle) ?
510                                            0.0 :
511                                            ll_sp_value(scp->cpu_idle,
512                                                        scc->cpu_idle,
513                                                        pc_itv));
514                                 printf("\n");
515                         }
516                 }
517         }
518
519         /* Print total number of interrupts per processor */
520         if (DISPLAY_IRQ_SUM(actflags)) {
521                 struct stats_irq *sic, *sip;
522
523                 if (dis) {
524                         printf("\n%-11s  CPU    intr/s\n", prev_string);
525                 }
526
527                 if (*cpu_bitmap & 1) {
528                         printf("%-11s", curr_string);
529                         cprintf_in(IS_STR, " %s", " all", 0);
530                         cprintf_f(1, 9, 2,
531                                   S_VALUE(st_irq[prev]->irq_nr, st_irq[curr]->irq_nr, itv));
532                         printf("\n");
533                 }
534
535                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
536
537                         sic = st_irq[curr] + cpu;
538                         sip = st_irq[prev] + cpu;
539
540                         scc = st_cpu[curr] + cpu;
541                         scp = st_cpu[prev] + cpu;
542
543                         /* Check if we want stats about this proc */
544                         if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
545                                 continue;
546
547                         if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
548                              scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
549                              scc->cpu_hardirq + scc->cpu_softirq) == 0) {
550
551                                 /* This is an offline CPU */
552
553                                 if (!DISPLAY_ONLINE_CPU(flags)) {
554                                         printf("%-11s", curr_string);
555                                         cprintf_in(IS_INT, " %4d", "", cpu- 1);
556                                         cprintf_f(1, 9, 2, 0.0);
557                                         printf("\n");
558                                 }
559                                 continue;
560                         }
561
562                         printf("%-11s", curr_string);
563                         cprintf_in(IS_INT, " %4d", "", cpu- 1);
564
565                         /* Recalculate itv for current proc */
566                         pc_itv = get_per_cpu_interval(scc, scp);
567
568                         if (!pc_itv) {
569                                 /* This is a tickless CPU */
570                                 cprintf_f(1, 9, 2, 0.0);
571                                 printf("\n");
572                         }
573                         else {
574                                 cprintf_f(1, 9, 2,
575                                           S_VALUE(sip->irq_nr, sic->irq_nr, itv));
576                                 printf("\n");
577                         }
578                 }
579         }
580
581         if (DISPLAY_IRQ_CPU(actflags)) {
582                 write_irqcpu_stats(st_irqcpu, irqcpu_nr, dis, itv, prev, curr,
583                                    prev_string, curr_string);
584         }
585
586         if (DISPLAY_SOFTIRQS(actflags)) {
587                 write_irqcpu_stats(st_softirqcpu, softirqcpu_nr, dis, itv, prev, curr,
588                                    prev_string, curr_string);
589         }
590
591         /* Fix CPU counter values for every offline CPU */
592         for (cpu = 1; cpu <= cpu_nr; cpu++) {
593
594                 scc = st_cpu[curr] + cpu;
595                 scp = st_cpu[prev] + cpu;
596
597                 if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
598                      scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
599                      scc->cpu_hardirq + scc->cpu_softirq) == 0) {
600                         /*
601                          * Offline CPU found.
602                          * Set current struct fields (which have been set to zero)
603                          * to values from previous iteration. Hence their values won't
604                          * jump from zero when the CPU comes back online.
605                          */
606                         *scc = *scp;
607                 }
608         }
609 }
610
611 /*
612  ***************************************************************************
613  * Print statistics average.
614  *
615  * IN:
616  * @curr        Position in array where statistics for current sample are.
617  * @dis         TRUE if a header line must be printed.
618  ***************************************************************************
619  */
620 void write_stats_avg(int curr, int dis)
621 {
622         char string[16];
623
624         strncpy(string, _("Average:"), 16);
625         string[15] = '\0';
626         write_stats_core(2, curr, dis, string, string);
627 }
628
629 /*
630  ***************************************************************************
631  * Print statistics.
632  *
633  * IN:
634  * @curr        Position in array where statistics for current sample are.
635  * @dis         TRUE if a header line must be printed.
636  ***************************************************************************
637  */
638 void write_stats(int curr, int dis)
639 {
640         char cur_time[2][16];
641
642         /* Get previous timestamp */
643         strftime(cur_time[!curr], sizeof(cur_time[!curr]), "%X", &(mp_tstamp[!curr]));
644
645         /* Get current timestamp */
646         strftime(cur_time[curr], sizeof(cur_time[curr]), "%X", &(mp_tstamp[curr]));
647
648         write_stats_core(!curr, curr, dis, cur_time[!curr], cur_time[curr]);
649 }
650
651 /*
652  ***************************************************************************
653  * Read stats from /proc/interrupts or /proc/softirqs.
654  *
655  * IN:
656  * @file        /proc file to read (interrupts or softirqs).
657  * @ic_nr       Number of interrupts (hard or soft) per CPU.
658  * @curr        Position in array where current statistics will be saved.
659  *
660  * OUT:
661  * @st_ic       Array for per-CPU statistics.
662  ***************************************************************************
663  */
664 void read_interrupts_stat(char *file, struct stats_irqcpu *st_ic[], int ic_nr, int curr)
665 {
666         FILE *fp;
667         struct stats_irq *st_irq_i;
668         struct stats_irqcpu *p;
669         char *line = NULL, *li;
670         unsigned long irq = 0;
671         unsigned int cpu;
672         int cpu_index[cpu_nr], index = 0, len;
673         char *cp, *next;
674
675         for (cpu = 0; cpu < cpu_nr; cpu++) {
676                 st_irq_i = st_irq[curr] + cpu + 1;
677                 st_irq_i->irq_nr = 0;
678         }
679
680         if ((fp = fopen(file, "r")) != NULL) {
681
682                 SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr);
683
684                 /*
685                  * Parse header line to see which CPUs are online
686                  */
687                 while (fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) {
688                         next = line;
689                         while (((cp = strstr(next, "CPU")) != NULL) && (index < cpu_nr)) {
690                                 cpu = strtol(cp + 3, &next, 10);
691                                 cpu_index[index++] = cpu;
692                         }
693                         if (index)
694                                 /* Header line found */
695                                 break;
696                 }
697
698                 while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) &&
699                        (irq < ic_nr)) {
700
701                         /* Skip over "<irq>:" */
702                         if ((cp = strchr(line, ':')) == NULL)
703                                 /* Chr ':' not found */
704                                 continue;
705                         cp++;
706
707                         p = st_ic[curr] + irq;
708
709                         li = line;
710                         while (*li == ' ')
711                                 li++;
712
713                         len = strcspn(li, ":");
714                         if (len >= MAX_IRQ_LEN) {
715                                 len = MAX_IRQ_LEN - 1;
716                         }
717                         strncpy(p->irq_name, li, len);
718                         p->irq_name[len] = '\0';
719
720                         for (cpu = 0; cpu < index; cpu++) {
721                                 p = st_ic[curr] + cpu_index[cpu] * ic_nr + irq;
722                                 st_irq_i = st_irq[curr] + cpu_index[cpu] + 1;
723                                 /*
724                                  * No need to set (st_irqcpu + cpu * irqcpu_nr)->irq:
725                                  * This is the same as st_irqcpu->irq.
726                                  */
727                                 p->interrupt = strtoul(cp, &next, 10);
728                                 st_irq_i->irq_nr += p->interrupt;
729                                 cp = next;
730                         }
731                         irq++;
732                 }
733
734                 fclose(fp);
735
736                 free(line);
737         }
738
739         while (irq < ic_nr) {
740                 /* Nb of interrupts per processor has changed */
741                 p = st_ic[curr] + irq;
742                 p->irq_name[0] = '\0';  /* This value means this is a dummy interrupt */
743                 irq++;
744         }
745 }
746
747 /*
748  ***************************************************************************
749  * Main loop: Read stats from the relevant sources, and display them.
750  *
751  * IN:
752  * @dis_hdr     Set to TRUE if the header line must always be printed.
753  * @rows        Number of rows of screen.
754  ***************************************************************************
755  */
756 void rw_mpstat_loop(int dis_hdr, int rows)
757 {
758         struct stats_cpu *scc;
759         int cpu;
760         int curr = 1, dis = 1;
761         unsigned long lines = rows;
762
763         /* Dont buffer data if redirected to a pipe */
764         setbuf(stdout, NULL);
765
766         /* Read stats */
767         if (cpu_nr > 1) {
768                 /*
769                  * Init uptime0. So if /proc/uptime cannot fill it,
770                  * this will be done by /proc/stat.
771                  */
772                 uptime0[0] = 0;
773                 read_uptime(&(uptime0[0]));
774         }
775         read_stat_cpu(st_cpu[0], cpu_nr + 1, &(uptime[0]), &(uptime0[0]));
776
777         if (DISPLAY_IRQ_SUM(actflags)) {
778                 read_stat_irq(st_irq[0], 1);
779         }
780
781         if (DISPLAY_IRQ_SUM(actflags) || DISPLAY_IRQ_CPU(actflags)) {
782                 /* Read this file to display int per CPU or total nr of int per CPU */
783                 read_interrupts_stat(INTERRUPTS, st_irqcpu, irqcpu_nr, 0);
784         }
785
786         if (DISPLAY_SOFTIRQS(actflags)) {
787                 read_interrupts_stat(SOFTIRQS, st_softirqcpu, softirqcpu_nr, 0);
788         }
789
790         if (!interval) {
791                 /* Display since boot time */
792                 mp_tstamp[1] = mp_tstamp[0];
793                 memset(st_cpu[1], 0, STATS_CPU_SIZE * (cpu_nr + 1));
794                 memset(st_irq[1], 0, STATS_IRQ_SIZE * (cpu_nr + 1));
795                 memset(st_irqcpu[1], 0, STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
796                 if (DISPLAY_SOFTIRQS(actflags)) {
797                         memset(st_softirqcpu[1], 0, STATS_IRQCPU_SIZE * (cpu_nr + 1) * softirqcpu_nr);
798                 }
799                 write_stats(0, DISP_HDR);
800                 exit(0);
801         }
802
803         /* Set a handler for SIGALRM */
804         memset(&alrm_act, 0, sizeof(alrm_act));
805         alrm_act.sa_handler = alarm_handler;
806         sigaction(SIGALRM, &alrm_act, NULL);
807         alarm(interval);
808
809         /* Save the first stats collected. Will be used to compute the average */
810         mp_tstamp[2] = mp_tstamp[0];
811         uptime[2] = uptime[0];
812         uptime0[2] = uptime0[0];
813         memcpy(st_cpu[2], st_cpu[0], STATS_CPU_SIZE * (cpu_nr + 1));
814         memcpy(st_irq[2], st_irq[0], STATS_IRQ_SIZE * (cpu_nr + 1));
815         memcpy(st_irqcpu[2], st_irqcpu[0], STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
816         if (DISPLAY_SOFTIRQS(actflags)) {
817                 memcpy(st_softirqcpu[2], st_softirqcpu[0],
818                        STATS_IRQCPU_SIZE * (cpu_nr + 1) * softirqcpu_nr);
819         }
820
821         /* Set a handler for SIGINT */
822         memset(&int_act, 0, sizeof(int_act));
823         int_act.sa_handler = int_handler;
824         sigaction(SIGINT, &int_act, NULL);
825
826         pause();
827
828         if (sigint_caught)
829                 /* SIGINT signal caught during first interval: Exit immediately */
830                 return;
831
832         do {
833                 /*
834                  * Resetting the structure not needed since every fields will be set.
835                  * Exceptions are per-CPU structures: Some of them may not be filled
836                  * if corresponding processor is disabled (offline). We set them to zero
837                  * to be able to distinguish between offline and tickless CPUs.
838                  */
839                 for (cpu = 1; cpu <= cpu_nr; cpu++) {
840                         scc = st_cpu[curr] + cpu;
841                         memset(scc, 0, STATS_CPU_SIZE);
842                 }
843
844                 /* Get time */
845                 get_localtime(&(mp_tstamp[curr]), 0);
846
847                 /* Read stats */
848                 if (cpu_nr > 1) {
849                         uptime0[curr] = 0;
850                         read_uptime(&(uptime0[curr]));
851                 }
852                 read_stat_cpu(st_cpu[curr], cpu_nr + 1, &(uptime[curr]), &(uptime0[curr]));
853
854                 if (DISPLAY_IRQ_SUM(actflags)) {
855                         read_stat_irq(st_irq[curr], 1);
856                 }
857
858                 if (DISPLAY_IRQ_SUM(actflags) || DISPLAY_IRQ_CPU(actflags)) {
859                         read_interrupts_stat(INTERRUPTS, st_irqcpu, irqcpu_nr, curr);
860                 }
861
862                 if (DISPLAY_SOFTIRQS(actflags)) {
863                         read_interrupts_stat(SOFTIRQS, st_softirqcpu, softirqcpu_nr, curr);
864                 }
865
866                 /* Write stats */
867                 if (!dis_hdr) {
868                         dis = lines / rows;
869                         if (dis) {
870                                 lines %= rows;
871                         }
872                         lines++;
873                 }
874                 write_stats(curr, dis);
875
876                 if (count > 0) {
877                         count--;
878                 }
879
880                 if (count) {
881
882                         pause();
883
884                         if (sigint_caught) {
885                                 /* SIGINT signal caught => Display average stats */
886                                 count = 0;
887                                 printf("\n");   /* Skip "^C" displayed on screen */
888                         }
889                         else {
890                                 curr ^= 1;
891                         }
892                 }
893         }
894         while (count);
895
896         /* Write stats average */
897         write_stats_avg(curr, dis_hdr);
898 }
899
900 /*
901  ***************************************************************************
902  * Main entry to the program
903  ***************************************************************************
904  */
905 int main(int argc, char **argv)
906 {
907         int opt = 0, i, actset = FALSE;
908         struct utsname header;
909         int dis_hdr = -1;
910         int rows = 23;
911         char *t;
912
913 #ifdef USE_NLS
914         /* Init National Language Support */
915         init_nls();
916 #endif
917
918         /* Init color strings */
919         init_colors();
920
921         /* Get HZ */
922         get_HZ();
923
924         /* What is the highest processor number on this machine? */
925         cpu_nr = get_cpu_nr(~0, TRUE);
926
927         /* Calculate number of interrupts per processor */
928         irqcpu_nr = get_irqcpu_nr(INTERRUPTS, NR_IRQS, cpu_nr) +
929                     NR_IRQCPU_PREALLOC;
930         /* Calculate number of soft interrupts per processor */
931         softirqcpu_nr = get_irqcpu_nr(SOFTIRQS, NR_IRQS, cpu_nr) +
932                         NR_IRQCPU_PREALLOC;
933
934         /*
935          * cpu_nr: a value of 2 means there are 2 processors (0 and 1).
936          * In this case, we have to allocate 3 structures: global, proc0 and proc1.
937          */
938         salloc_mp_struct(cpu_nr + 1);
939
940         while (++opt < argc) {
941
942                 if (!strcmp(argv[opt], "-I")) {
943                         if (argv[++opt]) {
944                                 actset = TRUE;
945                                 if (!strcmp(argv[opt], K_SUM)) {
946                                         /* Display total number of interrupts per CPU */
947                                         actflags |= M_D_IRQ_SUM;
948                                 }
949                                 else if (!strcmp(argv[opt], K_CPU)) {
950                                         /* Display interrupts per CPU */
951                                         actflags |= M_D_IRQ_CPU;
952                                 }
953                                 else if (!strcmp(argv[opt], K_SCPU)) {
954                                         /* Display soft interrupts per CPU */
955                                         actflags |= M_D_SOFTIRQS;
956                                 }
957                                 else if (!strcmp(argv[opt], K_ALL)) {
958                                         actflags |= M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
959                                 }
960                                 else {
961                                         usage(argv[0]);
962                                 }
963                         }
964                         else {
965                                 usage(argv[0]);
966                         }
967                 }
968
969                 else if (!strcmp(argv[opt], "-P")) {
970                         /* '-P ALL' can be used on UP machines */
971                         if (argv[++opt]) {
972                                 flags |= F_P_OPTION;
973                                 dis_hdr++;
974
975                                 for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
976                                         if (!strcmp(t, K_ALL) || !strcmp(t, K_ON)) {
977                                                 if (cpu_nr) {
978                                                         dis_hdr = 9;
979                                                 }
980                                                 /*
981                                                  * Set bit for every processor.
982                                                  * Also indicate to display stats for CPU 'all'.
983                                                  */
984                                                 memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
985                                                 if (!strcmp(t, K_ON)) {
986                                                         /* Display stats only for online CPU */
987                                                         flags |= F_P_ON;
988                                                 }
989                                         }
990                                         else {
991                                                 if (strspn(t, DIGITS) != strlen(t)) {
992                                                         usage(argv[0]);
993                                                 }
994                                                 i = atoi(t);    /* Get cpu number */
995                                                 if (i >= cpu_nr) {
996                                                         fprintf(stderr, _("Not that many processors!\n"));
997                                                         exit(1);
998                                                 }
999                                                 i++;
1000                                                 *(cpu_bitmap + (i >> 3)) |= 1 << (i & 0x07);
1001                                         }
1002                                 }
1003                         }
1004                         else {
1005                                 usage(argv[0]);
1006                         }
1007                 }
1008
1009                 else if (!strncmp(argv[opt], "-", 1)) {
1010                         for (i = 1; *(argv[opt] + i); i++) {
1011
1012                                 switch (*(argv[opt] + i)) {
1013
1014                                 case 'A':
1015                                         actflags |= M_D_CPU + M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
1016                                         actset = TRUE;
1017                                         /* Select all processors */
1018                                         flags |= F_P_OPTION;
1019                                         memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
1020                                         break;
1021
1022                                 case 'u':
1023                                         /* Display CPU */
1024                                         actflags |= M_D_CPU;
1025                                         break;
1026
1027                                 case 'V':
1028                                         /* Print version number */
1029                                         print_version();
1030                                         break;
1031
1032                                 default:
1033                                         usage(argv[0]);
1034                                 }
1035                         }
1036                 }
1037
1038                 else if (interval < 0) {
1039                         /* Get interval */
1040                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1041                                 usage(argv[0]);
1042                         }
1043                         interval = atol(argv[opt]);
1044                         if (interval < 0) {
1045                                 usage(argv[0]);
1046                         }
1047                         count = -1;
1048                 }
1049
1050                 else if (count <= 0) {
1051                         /* Get count value */
1052                         if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1053                             !interval) {
1054                                 usage(argv[0]);
1055                         }
1056                         count = atol(argv[opt]);
1057                         if (count < 1) {
1058                                 usage(argv[0]);
1059                         }
1060                 }
1061
1062                 else {
1063                         usage(argv[0]);
1064                 }
1065         }
1066
1067         /* Default: Display CPU */
1068         if (!actset) {
1069                 actflags |= M_D_CPU;
1070         }
1071
1072         if (count_bits(&actflags, sizeof(unsigned int)) > 1) {
1073                 dis_hdr = 9;
1074         }
1075
1076         if (!USE_P_OPTION(flags)) {
1077                 /* Option -P not used: Set bit 0 (global stats among all proc) */
1078                 *cpu_bitmap = 1;
1079         }
1080         if (dis_hdr < 0) {
1081                 dis_hdr = 0;
1082         }
1083         if (!dis_hdr) {
1084                 /* Get window size */
1085                 rows = get_win_height();
1086         }
1087         if (interval < 0) {
1088                 /* Interval not set => display stats since boot time */
1089                 interval = 0;
1090         }
1091
1092         /* Get time */
1093         get_localtime(&(mp_tstamp[0]), 0);
1094
1095         /* Get system name, release number and hostname */
1096         uname(&header);
1097         print_gal_header(&(mp_tstamp[0]), header.sysname, header.release,
1098                          header.nodename, header.machine, get_cpu_nr(~0, FALSE));
1099
1100         /* Main loop */
1101         rw_mpstat_loop(dis_hdr, rows);
1102
1103         /* Free structures */
1104         sfree_mp_struct();
1105
1106         return 0;
1107 }