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