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