1 /* vi: set sw=4 ts=4: */
3 * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard
5 * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
7 * Licensed under GPLv2, see file LICENSE in this source tree.
10 //applet:IF_MPSTAT(APPLET(mpstat, BB_DIR_BIN, BB_SUID_DROP))
12 //kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o
14 //config:config MPSTAT
15 //config: bool "mpstat"
18 //config: Per-processor statistics
21 #include <sys/utsname.h> /* struct utsname */
23 //#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
24 #define debug(fmt, ...) ((void)0)
26 /* Size of /proc/interrupts line, CPU data excluded */
27 #define INTERRUPTS_LINE 64
28 /* Maximum number of interrupts */
30 #define NR_IRQCPU_PREALLOC 3
31 #define MAX_IRQNAME_LEN 16
32 #define MAX_PF_NAME 512
33 /* sysstat 9.0.6 uses width 8, but newer code which also prints /proc/softirqs
34 * data needs more: "interrupts" in /proc/softirqs have longer names,
35 * most are up to 8 chars, one (BLOCK_IOPOLL) is even longer.
36 * We are printing headers in the " IRQNAME/s" form, experimentally
37 * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
39 #define INTRATE_SCRWIDTH 10
40 #define INTRATE_SCRWIDTH_STR "10"
43 #define PROCFS_STAT "/proc/stat"
44 #define PROCFS_INTERRUPTS "/proc/interrupts"
45 #define PROCFS_SOFTIRQS "/proc/softirqs"
46 #define PROCFS_UPTIME "/proc/uptime"
50 typedef unsigned long long data_t
;
51 typedef long long idata_t
;
53 #define DATA_MAX ULLONG_MAX
55 typedef unsigned long data_t
;
58 #define DATA_MAX ULONG_MAX
64 char irq_name
[MAX_IRQNAME_LEN
];
84 /* Globals. Sort by size and access frequency. */
88 unsigned cpu_nr
; /* Number of CPUs */
89 unsigned irqcpu_nr
; /* Number of interrupts per CPU */
90 unsigned softirqcpu_nr
; /* Number of soft interrupts per CPU */
93 unsigned cpu_bitmap_len
;
95 // 9.0.6 does not do it. Try "mpstat -A 1 2" - headers are repeated!
96 //smallint header_done;
97 //smallint avg_header_done;
98 unsigned char *cpu_bitmap
; /* Bit 0: global, bit 1: 1st proc... */
99 data_t global_uptime
[3];
100 data_t per_cpu_uptime
[3];
101 struct stats_cpu
*st_cpu
[3];
102 struct stats_irq
*st_irq
[3];
103 struct stats_irqcpu
*st_irqcpu
[3];
104 struct stats_irqcpu
*st_softirqcpu
[3];
105 struct tm timestamp
[3];
107 #define G (*ptr_to_globals)
108 #define INIT_G() do { \
109 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
112 /* The selected interrupts statistics (bits in G.options) */
122 static ALWAYS_INLINE
int display_opt(int opt
)
124 return (opt
& G
.options
);
127 #if DATA_MAX > 0xffffffff
129 * Handle overflow conditions properly for counters which can have
130 * less bits than data_t, depending on the kernel version.
132 /* Surprisingly, on 32bit inlining is a size win */
133 static ALWAYS_INLINE data_t
overflow_safe_sub(data_t prev
, data_t curr
)
135 data_t v
= curr
- prev
;
137 if ((idata_t
)v
< 0 /* curr < prev - counter overflow? */
138 && prev
<= 0xffffffff /* kernel uses 32bit value for the counter? */
140 /* Add 33th bit set to 1 to curr, compensating for the overflow */
141 /* double shift defeats "warning: left shift count >= width of type" */
142 v
+= ((data_t
)1 << 16) << 16;
147 static ALWAYS_INLINE data_t
overflow_safe_sub(data_t prev
, data_t curr
)
153 static double percent_value(data_t prev
, data_t curr
, data_t itv
)
155 return ((double)overflow_safe_sub(prev
, curr
)) / itv
* 100;
158 static double hz_value(data_t prev
, data_t curr
, data_t itv
)
160 //bb_error_msg("curr:%lld prev:%lld G.hz:%u", curr, prev, G.hz);
161 return ((double)overflow_safe_sub(prev
, curr
)) / itv
* G
.hz
;
164 static ALWAYS_INLINE data_t
jiffies_diff(data_t old
, data_t
new)
166 data_t diff
= new - old
;
167 return (diff
== 0) ? 1 : diff
;
170 static int is_cpu_in_bitmap(unsigned cpu
)
172 return G
.cpu_bitmap
[cpu
>> 3] & (1 << (cpu
& 7));
175 static void write_irqcpu_stats(struct stats_irqcpu
*per_cpu_stats
[],
178 int prev
, int current
,
179 const char *prev_str
, const char *current_str
)
183 struct stats_irqcpu
*p0
, *q0
;
185 /* Check if number of IRQs has changed */
186 if (G
.interval
!= 0) {
187 for (j
= 0; j
<= total_irqs
; j
++) {
188 p0
= &per_cpu_stats
[current
][j
];
189 if (p0
->irq_name
[0] != '\0') {
190 q0
= &per_cpu_stats
[prev
][j
];
191 if (strcmp(p0
->irq_name
, q0
->irq_name
) != 0) {
192 /* Strings are different */
200 printf("\n%-11s CPU", prev_str
);
202 /* A bit complex code to "buy back" space if one header is too wide.
203 * Here's how it looks like. BLOCK_IOPOLL eats too much space,
204 * and latter headers use smaller width to compensate:
205 * ...BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s RCU/s
206 * ... 2.32 0.00 0.01 17.58 0.14 141.96
208 int expected_len
= 0;
210 for (j
= 0; j
< total_irqs
; j
++) {
211 p0
= &per_cpu_stats
[current
][j
];
212 if (p0
->irq_name
[0] != '\0') {
213 int n
= (INTRATE_SCRWIDTH
-3) - (printed_len
- expected_len
);
214 printed_len
+= printf(" %*s/s", n
> 0 ? n
: 0, skip_whitespace(p0
->irq_name
));
215 expected_len
+= INTRATE_SCRWIDTH
;
221 for (cpu
= 1; cpu
<= G
.cpu_nr
; cpu
++) {
222 /* Check if we want stats about this CPU */
223 if (!is_cpu_in_bitmap(cpu
) && G
.p_option
) {
227 printf("%-11s %4u", current_str
, cpu
- 1);
229 for (j
= 0; j
< total_irqs
; j
++) {
230 /* IRQ field set only for proc 0 */
231 p0
= &per_cpu_stats
[current
][j
];
234 * An empty string for irq name means that
235 * interrupt is no longer used.
237 if (p0
->irq_name
[0] != '\0') {
239 q0
= &per_cpu_stats
[prev
][offset
];
242 * If we want stats for the time since boot
243 * we have p0->irq != q0->irq.
245 if (strcmp(p0
->irq_name
, q0
->irq_name
) != 0
250 q0
= &per_cpu_stats
[prev
][offset
];
252 if (strcmp(p0
->irq_name
, q0
->irq_name
) != 0
253 && (j
+ 1 < total_irqs
)
256 q0
= &per_cpu_stats
[prev
][offset
];
260 if (strcmp(p0
->irq_name
, q0
->irq_name
) == 0
263 struct stats_irqcpu
*p
, *q
;
264 p
= &per_cpu_stats
[current
][(cpu
- 1) * total_irqs
+ j
];
265 q
= &per_cpu_stats
[prev
][(cpu
- 1) * total_irqs
+ offset
];
266 printf("%"INTRATE_SCRWIDTH_STR
".2f",
267 (double)(p
->interrupts
- q
->interrupts
) / itv
* G
.hz
);
277 static data_t
get_per_cpu_interval(const struct stats_cpu
*scc
,
278 const struct stats_cpu
*scp
)
280 return ((scc
->cpu_user
+ scc
->cpu_nice
+
281 scc
->cpu_system
+ scc
->cpu_iowait
+
282 scc
->cpu_idle
+ scc
->cpu_steal
+
283 scc
->cpu_irq
+ scc
->cpu_softirq
) -
284 (scp
->cpu_user
+ scp
->cpu_nice
+
285 scp
->cpu_system
+ scp
->cpu_iowait
+
286 scp
->cpu_idle
+ scp
->cpu_steal
+
287 scp
->cpu_irq
+ scp
->cpu_softirq
));
290 static void print_stats_cpu_struct(const struct stats_cpu
*p
,
291 const struct stats_cpu
*c
,
294 printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
295 percent_value(p
->cpu_user
- p
->cpu_guest
,
296 /**/ c
->cpu_user
- c
->cpu_guest
, itv
),
297 percent_value(p
->cpu_nice
, c
->cpu_nice
, itv
),
298 percent_value(p
->cpu_system
, c
->cpu_system
, itv
),
299 percent_value(p
->cpu_iowait
, c
->cpu_iowait
, itv
),
300 percent_value(p
->cpu_irq
, c
->cpu_irq
, itv
),
301 percent_value(p
->cpu_softirq
, c
->cpu_softirq
, itv
),
302 percent_value(p
->cpu_steal
, c
->cpu_steal
, itv
),
303 percent_value(p
->cpu_guest
, c
->cpu_guest
, itv
),
304 percent_value(p
->cpu_idle
, c
->cpu_idle
, itv
)
308 static void write_stats_core(int prev
, int current
,
309 const char *prev_str
, const char *current_str
)
311 struct stats_cpu
*scc
, *scp
;
312 data_t itv
, global_itv
;
315 /* Compute time interval */
316 itv
= global_itv
= jiffies_diff(G
.global_uptime
[prev
], G
.global_uptime
[current
]);
318 /* Reduce interval to one CPU */
320 itv
= jiffies_diff(G
.per_cpu_uptime
[prev
], G
.per_cpu_uptime
[current
]);
322 /* Print CPU stats */
323 if (display_opt(D_CPU
)) {
325 ///* This is done exactly once */
326 //if (!G.header_done) {
327 printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq %%soft %%steal %%guest %%idle\n",
330 // G.header_done = 1;
333 for (cpu
= 0; cpu
<= G
.cpu_nr
; cpu
++) {
336 /* Print stats about this particular CPU? */
337 if (!is_cpu_in_bitmap(cpu
))
340 scc
= &G
.st_cpu
[current
][cpu
];
341 scp
= &G
.st_cpu
[prev
][cpu
];
342 per_cpu_itv
= global_itv
;
344 printf((cpu
? "%-11s %4u" : "%-11s all"), current_str
, cpu
- 1);
348 * If the CPU is offline, then it isn't in /proc/stat,
349 * so all values are 0.
350 * NB: Guest time is already included in user time.
352 if ((scc
->cpu_user
| scc
->cpu_nice
| scc
->cpu_system
|
353 scc
->cpu_iowait
| scc
->cpu_idle
| scc
->cpu_steal
|
354 scc
->cpu_irq
| scc
->cpu_softirq
) == 0
357 * Set current struct fields to values from prev.
358 * iteration. Then their values won't jump from
359 * zero, when the CPU comes back online.
365 /* Compute interval again for current proc */
366 per_cpu_itv
= get_per_cpu_interval(scc
, scp
);
367 if (per_cpu_itv
== 0) {
369 * If the CPU is tickless then there is no change in CPU values
370 * but the sum of values is not zero.
374 printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
375 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle
);
379 print_stats_cpu_struct(scp
, scc
, per_cpu_itv
);
383 /* Print total number of IRQs per CPU */
384 if (display_opt(D_IRQ_SUM
)) {
386 ///* Print average header, this is done exactly once */
387 //if (!G.avg_header_done) {
388 printf("\n%-11s CPU intr/s\n", prev_str
);
389 // G.avg_header_done = 1;
392 for (cpu
= 0; cpu
<= G
.cpu_nr
; cpu
++) {
395 /* Print stats about this CPU? */
396 if (!is_cpu_in_bitmap(cpu
))
400 printf((cpu
? "%-11s %4u" : "%-11s all"), current_str
, cpu
- 1);
402 scc
= &G
.st_cpu
[current
][cpu
];
403 scp
= &G
.st_cpu
[prev
][cpu
];
404 /* Compute interval again for current proc */
405 per_cpu_itv
= get_per_cpu_interval(scc
, scp
);
406 if (per_cpu_itv
== 0) {
407 printf(" %9.2f\n", 0.0);
411 //bb_error_msg("G.st_irq[%u][%u].irq_nr:%lld - G.st_irq[%u][%u].irq_nr:%lld",
412 // current, cpu, G.st_irq[prev][cpu].irq_nr, prev, cpu, G.st_irq[current][cpu].irq_nr);
413 printf(" %9.2f\n", hz_value(G
.st_irq
[prev
][cpu
].irq_nr
, G
.st_irq
[current
][cpu
].irq_nr
, per_cpu_itv
));
417 if (display_opt(D_IRQ_CPU
)) {
418 write_irqcpu_stats(G
.st_irqcpu
, G
.irqcpu_nr
,
421 prev_str
, current_str
425 if (display_opt(D_SOFTIRQS
)) {
426 write_irqcpu_stats(G
.st_softirqcpu
, G
.softirqcpu_nr
,
429 prev_str
, current_str
435 * Print the statistics
437 static void write_stats(int current
)
442 strftime(prev_time
, sizeof(prev_time
), "%X", &G
.timestamp
[!current
]);
443 strftime(curr_time
, sizeof(curr_time
), "%X", &G
.timestamp
[current
]);
445 write_stats_core(!current
, current
, prev_time
, curr_time
);
448 static void write_stats_avg(int current
)
450 write_stats_core(2, current
, "Average:", "Average:");
454 * Read CPU statistics
456 static void get_cpu_statistics(struct stats_cpu
*cpu
, data_t
*up
, data_t
*up0
)
461 fp
= xfopen_for_read(PROCFS_STAT
);
463 while (fgets(buf
, sizeof(buf
), fp
)) {
466 struct stats_cpu
*cp
;
468 if (!starts_with_cpu(buf
))
469 continue; /* not "cpu" */
471 cp
= cpu
; /* for "cpu " case */
475 || sscanf(buf
+ 3, "%u ", &cpu_number
) != 1
476 || cpu_number
>= G
.cpu_nr
480 cp
= &cpu
[cpu_number
+ 1];
483 /* Read the counters, save them */
484 /* Not all fields have to be present */
485 memset(cp
, 0, sizeof(*cp
));
487 " %"FMT_DATA
"u %"FMT_DATA
"u %"FMT_DATA
"u"
488 " %"FMT_DATA
"u %"FMT_DATA
"u %"FMT_DATA
"u"
489 " %"FMT_DATA
"u %"FMT_DATA
"u %"FMT_DATA
"u",
490 &cp
->cpu_user
, &cp
->cpu_nice
, &cp
->cpu_system
,
491 &cp
->cpu_idle
, &cp
->cpu_iowait
, &cp
->cpu_irq
,
492 &cp
->cpu_softirq
, &cp
->cpu_steal
, &cp
->cpu_guest
495 * Compute uptime in jiffies (1/HZ), it'll be the sum of
496 * individual CPU's uptimes.
497 * NB: We have to omit cpu_guest, because cpu_user includes it.
499 sum
= cp
->cpu_user
+ cp
->cpu_nice
+ cp
->cpu_system
+
500 cp
->cpu_idle
+ cp
->cpu_iowait
+ cp
->cpu_irq
+
501 cp
->cpu_softirq
+ cp
->cpu_steal
;
508 if (cpu_number
== 0 && *up0
!= 0) {
509 /* Compute uptime of single CPU */
518 * Read IRQs from /proc/stat
520 static void get_irqs_from_stat(struct stats_irq
*irq
)
525 fp
= xfopen_for_read(PROCFS_STAT
);
527 while (fgets(buf
, sizeof(buf
), fp
)) {
528 //bb_error_msg("/proc/stat:'%s'", buf);
529 if (is_prefixed_with(buf
, "intr ")) {
530 /* Read total number of IRQs since system boot */
531 sscanf(buf
+ 5, "%"FMT_DATA
"u", &irq
->irq_nr
);
539 * Read stats from /proc/interrupts or /proc/softirqs
541 static void get_irqs_from_interrupts(const char *fname
,
542 struct stats_irqcpu
*per_cpu_stats
[],
543 int irqs_per_cpu
, int current
)
546 struct stats_irq
*irq_i
;
547 struct stats_irqcpu
*ic
;
552 int cpu_index
[G
.cpu_nr
];
556 // Otherwise reading of /proc/softirqs
557 // was resetting counts to 0 after we painstakingly collected them from
558 // /proc/interrupts. Which resulted in:
559 // 01:32:47 PM CPU intr/s
560 // 01:32:47 PM all 591.47
561 // 01:32:47 PM 0 0.00 <= ???
562 // 01:32:47 PM 1 0.00 <= ???
563 // for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
564 // G.st_irq[current][cpu].irq_nr = 0;
565 // //bb_error_msg("G.st_irq[%u][%u].irq_nr=0", current, cpu);
568 fp
= fopen_for_read(fname
);
572 buflen
= INTERRUPTS_LINE
+ 16 * G
.cpu_nr
;
573 buf
= xmalloc(buflen
);
575 /* Parse header and determine, which CPUs are online */
577 while (fgets(buf
, buflen
, fp
)) {
580 while ((cp
= strstr(next
, "CPU")) != NULL
583 cpu
= strtoul(cp
+ 3, &next
, 10);
584 cpu_index
[iindex
++] = cpu
;
586 if (iindex
) /* We found header */
591 while (fgets(buf
, buflen
, fp
)
592 && irq
< irqs_per_cpu
598 /* Skip over "IRQNAME:" */
599 cp
= strchr(buf
, ':');
604 ic
= &per_cpu_stats
[current
][irq
];
606 if (len
>= sizeof(ic
->irq_name
)) {
607 len
= sizeof(ic
->irq_name
) - 1;
609 safe_strncpy(ic
->irq_name
, buf
, len
+ 1);
610 //bb_error_msg("%s: irq%d:'%s' buf:'%s'", fname, irq, ic->irq_name, buf);
613 for (cpu
= 0; cpu
< iindex
; cpu
++) {
615 ic
= &per_cpu_stats
[current
][cpu_index
[cpu
] * irqs_per_cpu
+ irq
];
616 irq_i
= &G
.st_irq
[current
][cpu_index
[cpu
] + 1];
617 ic
->interrupts
= strtoul(cp
, &next
, 10);
618 /* Count only numerical IRQs */
619 if (isdigit(last_char
)) {
620 irq_i
->irq_nr
+= ic
->interrupts
;
621 //bb_error_msg("G.st_irq[%u][%u].irq_nr + %u = %lld",
622 // current, cpu_index[cpu] + 1, ic->interrupts, irq_i->irq_nr);
631 while (irq
< irqs_per_cpu
) {
632 /* Number of interrupts per CPU has changed */
633 ic
= &per_cpu_stats
[current
][irq
];
634 ic
->irq_name
[0] = '\0'; /* False interrupt */
639 static void get_uptime(data_t
*uptime
)
642 char buf
[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
643 unsigned long uptime_sec
, decimal
;
645 fp
= xfopen_for_read(PROCFS_UPTIME
);
646 if (fgets(buf
, sizeof(buf
), fp
)) {
647 if (sscanf(buf
, "%lu.%lu", &uptime_sec
, &decimal
) == 2) {
648 *uptime
= (data_t
)uptime_sec
* G
.hz
+ decimal
* G
.hz
/ 100;
655 static void get_localtime(struct tm
*tm
)
659 localtime_r(&timer
, tm
);
662 static void alarm_handler(int sig UNUSED_PARAM
)
664 signal(SIGALRM
, alarm_handler
);
668 static void main_loop(void)
675 G
.per_cpu_uptime
[0] = 0;
676 get_uptime(&G
.per_cpu_uptime
[0]);
679 get_cpu_statistics(G
.st_cpu
[0], &G
.global_uptime
[0], &G
.per_cpu_uptime
[0]);
681 if (display_opt(D_IRQ_SUM
))
682 get_irqs_from_stat(G
.st_irq
[0]);
684 if (display_opt(D_IRQ_SUM
| D_IRQ_CPU
))
685 get_irqs_from_interrupts(PROCFS_INTERRUPTS
, G
.st_irqcpu
,
688 if (display_opt(D_SOFTIRQS
))
689 get_irqs_from_interrupts(PROCFS_SOFTIRQS
, G
.st_softirqcpu
,
692 if (G
.interval
== 0) {
693 /* Display since boot time */
695 G
.timestamp
[1] = G
.timestamp
[0];
696 memset(G
.st_cpu
[1], 0, sizeof(G
.st_cpu
[1][0]) * cpus
);
697 memset(G
.st_irq
[1], 0, sizeof(G
.st_irq
[1][0]) * cpus
);
698 memset(G
.st_irqcpu
[1], 0, sizeof(G
.st_irqcpu
[1][0]) * cpus
* G
.irqcpu_nr
);
699 memset(G
.st_softirqcpu
[1], 0, sizeof(G
.st_softirqcpu
[1][0]) * cpus
* G
.softirqcpu_nr
);
707 /* Set a handler for SIGALRM */
710 /* Save the stats we already have. We need them to compute the average */
711 G
.timestamp
[2] = G
.timestamp
[0];
712 G
.global_uptime
[2] = G
.global_uptime
[0];
713 G
.per_cpu_uptime
[2] = G
.per_cpu_uptime
[0];
715 memcpy(G
.st_cpu
[2], G
.st_cpu
[0], sizeof(G
.st_cpu
[0][0]) * cpus
);
716 memcpy(G
.st_irq
[2], G
.st_irq
[0], sizeof(G
.st_irq
[0][0]) * cpus
);
717 memcpy(G
.st_irqcpu
[2], G
.st_irqcpu
[0], sizeof(G
.st_irqcpu
[0][0]) * cpus
* G
.irqcpu_nr
);
718 if (display_opt(D_SOFTIRQS
)) {
719 memcpy(G
.st_softirqcpu
[2], G
.st_softirqcpu
[0],
720 sizeof(G
.st_softirqcpu
[0][0]) * cpus
* G
.softirqcpu_nr
);
725 /* Suspend until a signal is received */
728 /* Set structures to 0 to distinguish off/online CPUs */
729 memset(&G
.st_cpu
[current
][/*cpu:*/ 1], 0, sizeof(G
.st_cpu
[0][0]) * G
.cpu_nr
);
731 get_localtime(&G
.timestamp
[current
]);
735 G
.per_cpu_uptime
[current
] = 0;
736 get_uptime(&G
.per_cpu_uptime
[current
]);
738 get_cpu_statistics(G
.st_cpu
[current
], &G
.global_uptime
[current
], &G
.per_cpu_uptime
[current
]);
740 if (display_opt(D_IRQ_SUM
))
741 get_irqs_from_stat(G
.st_irq
[current
]);
743 if (display_opt(D_IRQ_SUM
| D_IRQ_CPU
)) {
745 for (cpu
= 1; cpu
<= G
.cpu_nr
; cpu
++) {
746 G
.st_irq
[current
][cpu
].irq_nr
= 0;
748 /* accumulates .irq_nr */
749 get_irqs_from_interrupts(PROCFS_INTERRUPTS
, G
.st_irqcpu
,
750 G
.irqcpu_nr
, current
);
753 if (display_opt(D_SOFTIRQS
))
754 get_irqs_from_interrupts(PROCFS_SOFTIRQS
,
756 G
.softirqcpu_nr
, current
);
758 write_stats(current
);
768 /* Print average statistics */
769 write_stats_avg(current
);
774 static void alloc_struct(int cpus
)
777 for (i
= 0; i
< 3; i
++) {
778 G
.st_cpu
[i
] = xzalloc(sizeof(G
.st_cpu
[i
][0]) * cpus
);
779 G
.st_irq
[i
] = xzalloc(sizeof(G
.st_irq
[i
][0]) * cpus
);
780 G
.st_irqcpu
[i
] = xzalloc(sizeof(G
.st_irqcpu
[i
][0]) * cpus
* G
.irqcpu_nr
);
781 G
.st_softirqcpu
[i
] = xzalloc(sizeof(G
.st_softirqcpu
[i
][0]) * cpus
* G
.softirqcpu_nr
);
783 G
.cpu_bitmap_len
= (cpus
>> 3) + 1;
784 G
.cpu_bitmap
= xzalloc(G
.cpu_bitmap_len
);
787 static void print_header(struct tm
*t
)
792 /* Get system name, release number and hostname */
795 strftime(cur_date
, sizeof(cur_date
), "%x", t
);
797 printf("%s %s (%s)\t%s\t_%s_\t(%u CPU)\n",
798 uts
.sysname
, uts
.release
, uts
.nodename
, cur_date
, uts
.machine
, G
.cpu_nr
);
802 * Get number of interrupts available per processor
804 static int get_irqcpu_nr(const char *f
, int max_irqs
)
811 fp
= fopen_for_read(f
);
812 if (!fp
) /* No interrupts file */
815 linelen
= INTERRUPTS_LINE
+ 16 * G
.cpu_nr
;
816 line
= xmalloc(linelen
);
819 while (fgets(line
, linelen
, fp
)
822 int p
= strcspn(line
, ":");
823 if ((p
> 0) && (p
< 16))
833 //usage:#define mpstat_trivial_usage
834 //usage: "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]"
835 //usage:#define mpstat_full_usage "\n\n"
836 //usage: "Per-processor statistics\n"
837 //usage: "\n -A Same as -I ALL -u -P ALL"
838 //usage: "\n -I SUM|CPU|ALL|SCPU Report interrupt statistics"
839 //usage: "\n -P num|ALL Processor to monitor"
840 //usage: "\n -u Report CPU utilization"
842 int mpstat_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
843 int mpstat_main(int UNUSED_PARAM argc
, char **argv
)
849 OPT_ALL
= 1 << 0, /* -A */
850 OPT_INTS
= 1 << 1, /* -I */
851 OPT_SETCPU
= 1 << 2, /* -P */
852 OPT_UTIL
= 1 << 3, /* -u */
855 /* Dont buffer data if redirected to a pipe */
856 setbuf(stdout
, NULL
);
862 /* Get number of processors */
863 G
.cpu_nr
= get_cpu_count();
865 /* Get number of clock ticks per sec */
868 /* Calculate number of interrupts per processor */
869 G
.irqcpu_nr
= get_irqcpu_nr(PROCFS_INTERRUPTS
, NR_IRQS
) + NR_IRQCPU_PREALLOC
;
871 /* Calculate number of soft interrupts per processor */
872 G
.softirqcpu_nr
= get_irqcpu_nr(PROCFS_SOFTIRQS
, NR_IRQS
) + NR_IRQCPU_PREALLOC
;
874 /* Allocate space for structures. + 1 for global structure. */
875 alloc_struct(G
.cpu_nr
+ 1);
877 /* Parse and process arguments */
878 opt
= getopt32(argv
, "AI:P:u", &opt_irq_fmt
, &opt_set_cpu
);
883 G
.interval
= xatoi_positive(*argv
);
887 /* Get count value */
890 G
.count
= xatoi_positive(*argv
);
900 G
.options
|= D_CPU
+ D_IRQ_SUM
+ D_IRQ_CPU
+ D_SOFTIRQS
;
901 /* Select every CPU */
902 memset(G
.cpu_bitmap
, 0xff, G
.cpu_bitmap_len
);
905 if (opt
& OPT_INTS
) {
906 static const char v
[] = {
907 D_IRQ_CPU
, D_IRQ_SUM
, D_SOFTIRQS
,
908 D_IRQ_SUM
+ D_IRQ_CPU
+ D_SOFTIRQS
910 i
= index_in_strings("CPU\0SUM\0SCPU\0ALL\0", opt_irq_fmt
);
916 if ((opt
& OPT_UTIL
) /* -u? */
917 || G
.options
== 0 /* nothing? (use default then) */
922 if (opt
& OPT_SETCPU
) {
926 for (t
= strtok(opt_set_cpu
, ","); t
; t
= strtok(NULL
, ",")) {
927 if (strcmp(t
, "ALL") == 0) {
928 /* Select every CPU */
929 memset(G
.cpu_bitmap
, 0xff, G
.cpu_bitmap_len
);
932 unsigned n
= xatoi_positive(t
);
934 bb_error_msg_and_die("not that many processors");
936 G
.cpu_bitmap
[n
>> 3] |= 1 << (n
& 7);
942 /* Display global stats */
946 get_localtime(&G
.timestamp
[0]);
949 print_header(&G
.timestamp
[0]);
954 if (ENABLE_FEATURE_CLEAN_UP
) {
956 for (i
= 0; i
< 3; i
++) {
959 free(G
.st_irqcpu
[i
]);
960 free(G
.st_softirqcpu
[i
]);