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