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