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