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