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