diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:19:57 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:19:57 +1000 |
commit | ec71cb6575290eb6ad716e4f620db445d8e1bcd3 (patch) | |
tree | 219a5dba000e0ad98ff563bc6f7d45d274d3a178 /procps | |
parent | b5139d7cd8982d9b683cb1babf0bd759076aaab0 (diff) | |
parent | 6814cbc9288601840aedb372e2bd84dab76ffa43 (diff) | |
download | busybox-w32-ec71cb6575290eb6ad716e4f620db445d8e1bcd3.tar.gz busybox-w32-ec71cb6575290eb6ad716e4f620db445d8e1bcd3.tar.bz2 busybox-w32-ec71cb6575290eb6ad716e4f620db445d8e1bcd3.zip |
Merge branch 'origin/master' (early part)
Diffstat (limited to 'procps')
-rw-r--r-- | procps/Config.src | 4 | ||||
-rw-r--r-- | procps/mpstat.c | 1017 |
2 files changed, 1020 insertions, 1 deletions
diff --git a/procps/Config.src b/procps/Config.src index e61de5614..1ff6dfd30 100644 --- a/procps/Config.src +++ b/procps/Config.src | |||
@@ -10,6 +10,7 @@ INSERT | |||
10 | config FREE | 10 | config FREE |
11 | bool "free" | 11 | bool "free" |
12 | default y | 12 | default y |
13 | depends on PLATFORM_LINUX #sysinfo() | ||
13 | help | 14 | help |
14 | free displays the total amount of free and used physical and swap | 15 | free displays the total amount of free and used physical and swap |
15 | memory in the system, as well as the buffers used by the kernel. | 16 | memory in the system, as well as the buffers used by the kernel. |
@@ -104,7 +105,7 @@ config FEATURE_PS_WIDE | |||
104 | config FEATURE_PS_TIME | 105 | config FEATURE_PS_TIME |
105 | bool "Enable time and elapsed time output" | 106 | bool "Enable time and elapsed time output" |
106 | default y | 107 | default y |
107 | depends on PS && DESKTOP | 108 | depends on PS && DESKTOP && PLATFORM_LINUX #sysinfo() |
108 | help | 109 | help |
109 | Support -o time and -o etime output specifiers. | 110 | Support -o time and -o etime output specifiers. |
110 | 111 | ||
@@ -200,6 +201,7 @@ config FEATURE_SHOW_THREADS | |||
200 | config UPTIME | 201 | config UPTIME |
201 | bool "uptime" | 202 | bool "uptime" |
202 | default y | 203 | default y |
204 | depends on PLATFORM_LINUX #sysinfo() | ||
203 | help | 205 | help |
204 | uptime gives a one line display of the current time, how long | 206 | uptime gives a one line display of the current time, how long |
205 | the system has been running, how many users are currently logged | 207 | the system has been running, how many users are currently logged |
diff --git a/procps/mpstat.c b/procps/mpstat.c new file mode 100644 index 000000000..7610a68fb --- /dev/null +++ b/procps/mpstat.c | |||
@@ -0,0 +1,1017 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard | ||
4 | * | ||
5 | * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com> | ||
6 | * | ||
7 | * Licensed under GPLv2, see file License in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | //applet:IF_MPSTAT(APPLET(mpstat, _BB_DIR_BIN, _BB_SUID_DROP)) | ||
11 | |||
12 | //kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o | ||
13 | |||
14 | //config:config MPSTAT | ||
15 | //config: bool "mpstat" | ||
16 | //config: default y | ||
17 | //config: help | ||
18 | //config: Per-processor statistics | ||
19 | |||
20 | #include "libbb.h" | ||
21 | #include <sys/utsname.h> /* struct utsname */ | ||
22 | |||
23 | //#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) | ||
24 | #define debug(fmt, ...) ((void)0) | ||
25 | |||
26 | /* Size of /proc/interrupts line, CPU data excluded */ | ||
27 | #define INTERRUPTS_LINE 64 | ||
28 | /* Maximum number of interrupts */ | ||
29 | #define NR_IRQS 256 | ||
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. | ||
38 | */ | ||
39 | #define INTRATE_SCRWIDTH 10 | ||
40 | #define INTRATE_SCRWIDTH_STR "10" | ||
41 | |||
42 | /* System files */ | ||
43 | #define SYSFS_DEVCPU "/sys/devices/system/cpu" | ||
44 | #define PROCFS_STAT "/proc/stat" | ||
45 | #define PROCFS_INTERRUPTS "/proc/interrupts" | ||
46 | #define PROCFS_SOFTIRQS "/proc/softirqs" | ||
47 | #define PROCFS_UPTIME "/proc/uptime" | ||
48 | |||
49 | |||
50 | #if 1 | ||
51 | typedef unsigned long long data_t; | ||
52 | typedef long long idata_t; | ||
53 | #define FMT_DATA "ll" | ||
54 | #define DATA_MAX ULLONG_MAX | ||
55 | #else | ||
56 | typedef unsigned long data_t; | ||
57 | typedef long idata_t; | ||
58 | #define FMT_DATA "l" | ||
59 | #define DATA_MAX ULONG_MAX | ||
60 | #endif | ||
61 | |||
62 | |||
63 | struct stats_irqcpu { | ||
64 | unsigned interrupt; | ||
65 | char irq_name[MAX_IRQNAME_LEN]; | ||
66 | }; | ||
67 | |||
68 | struct stats_cpu { | ||
69 | data_t cpu_user; | ||
70 | data_t cpu_nice; | ||
71 | data_t cpu_system; | ||
72 | data_t cpu_idle; | ||
73 | data_t cpu_iowait; | ||
74 | data_t cpu_steal; | ||
75 | data_t cpu_irq; | ||
76 | data_t cpu_softirq; | ||
77 | data_t cpu_guest; | ||
78 | }; | ||
79 | |||
80 | struct stats_irq { | ||
81 | data_t irq_nr; | ||
82 | }; | ||
83 | |||
84 | |||
85 | /* Globals. Sort by size and access frequency. */ | ||
86 | struct globals { | ||
87 | int interval; | ||
88 | int count; | ||
89 | unsigned cpu_nr; /* Number of CPUs */ | ||
90 | unsigned irqcpu_nr; /* Number of interrupts per CPU */ | ||
91 | unsigned softirqcpu_nr; /* Number of soft interrupts per CPU */ | ||
92 | unsigned options; | ||
93 | unsigned hz; | ||
94 | unsigned cpu_bitmap_len; | ||
95 | smallint p_option; | ||
96 | // 9.0.6 does not do it. Try "mpstat -A 1 2" - headers are repeated! | ||
97 | //smallint header_done; | ||
98 | //smallint avg_header_done; | ||
99 | unsigned char *cpu_bitmap; /* Bit 0: global, bit 1: 1st proc... */ | ||
100 | data_t global_uptime[3]; | ||
101 | data_t per_cpu_uptime[3]; | ||
102 | struct stats_cpu *st_cpu[3]; | ||
103 | struct stats_irq *st_irq[3]; | ||
104 | struct stats_irqcpu *st_irqcpu[3]; | ||
105 | struct stats_irqcpu *st_softirqcpu[3]; | ||
106 | struct tm timestamp[3]; | ||
107 | }; | ||
108 | #define G (*ptr_to_globals) | ||
109 | #define INIT_G() do { \ | ||
110 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
111 | } while (0) | ||
112 | |||
113 | /* The selected interrupts statistics (bits in G.options) */ | ||
114 | enum { | ||
115 | D_CPU = 1 << 0, | ||
116 | D_IRQ_SUM = 1 << 1, | ||
117 | D_IRQ_CPU = 1 << 2, | ||
118 | D_SOFTIRQS = 1 << 3, | ||
119 | }; | ||
120 | |||
121 | |||
122 | /* Does str start with "cpu"? */ | ||
123 | static int starts_with_cpu(const char *str) | ||
124 | { | ||
125 | return !((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')); | ||
126 | } | ||
127 | |||
128 | /* Is option on? */ | ||
129 | static ALWAYS_INLINE int display_opt(int opt) | ||
130 | { | ||
131 | return (opt & G.options); | ||
132 | } | ||
133 | |||
134 | #if DATA_MAX > 0xffffffff | ||
135 | /* | ||
136 | * Handle overflow conditions properly for counters which can have | ||
137 | * less bits than data_t, depending on the kernel version. | ||
138 | */ | ||
139 | /* Surprisingly, on 32bit inlining is a size win */ | ||
140 | static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr) | ||
141 | { | ||
142 | data_t v = curr - prev; | ||
143 | |||
144 | if ((idata_t)v < 0 /* curr < prev - counter overflow? */ | ||
145 | && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */ | ||
146 | ) { | ||
147 | /* Add 33th bit set to 1 to curr, compensating for the overflow */ | ||
148 | /* double shift defeats "warning: left shift count >= width of type" */ | ||
149 | v += ((data_t)1 << 16) << 16; | ||
150 | } | ||
151 | return v; | ||
152 | } | ||
153 | #else | ||
154 | static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr) | ||
155 | { | ||
156 | return curr - prev; | ||
157 | } | ||
158 | #endif | ||
159 | |||
160 | static double percent_value(data_t prev, data_t curr, data_t itv) | ||
161 | { | ||
162 | return ((double)overflow_safe_sub(prev, curr)) / itv * 100; | ||
163 | } | ||
164 | |||
165 | static double hz_value(data_t prev, data_t curr, data_t itv) | ||
166 | { | ||
167 | //bb_error_msg("curr:%lld prev:%lld G.hz:%u", curr, prev, G.hz); | ||
168 | return ((double)overflow_safe_sub(prev, curr)) / itv * G.hz; | ||
169 | } | ||
170 | |||
171 | static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new) | ||
172 | { | ||
173 | data_t diff = new - old; | ||
174 | return (diff == 0) ? 1 : diff; | ||
175 | } | ||
176 | |||
177 | static int is_cpu_in_bitmap(unsigned cpu) | ||
178 | { | ||
179 | return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7)); | ||
180 | } | ||
181 | |||
182 | static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[], | ||
183 | int total_irqs, | ||
184 | data_t itv, | ||
185 | int prev, int current, | ||
186 | const char *prev_str, const char *current_str) | ||
187 | { | ||
188 | int j; | ||
189 | int offset, cpu; | ||
190 | struct stats_irqcpu *p0, *q0; | ||
191 | |||
192 | /* Check if number of IRQs has changed */ | ||
193 | if (G.interval != 0) { | ||
194 | for (j = 0; j <= total_irqs; j++) { | ||
195 | p0 = &per_cpu_stats[current][j]; | ||
196 | if (p0->irq_name[0] != '\0') { | ||
197 | q0 = &per_cpu_stats[prev][j]; | ||
198 | if (strcmp(p0->irq_name, q0->irq_name) != 0) { | ||
199 | /* Strings are different */ | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* Print header */ | ||
207 | printf("\n%-11s CPU", prev_str); | ||
208 | { | ||
209 | /* A bit complex code to "buy back" space if one header is too wide. | ||
210 | * Here's how it looks like. BLOCK_IOPOLL eats too much space, | ||
211 | * and latter headers use smaller width to compensate: | ||
212 | * ...BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s RCU/s | ||
213 | * ... 2.32 0.00 0.01 17.58 0.14 141.96 | ||
214 | */ | ||
215 | int expected_len = 0; | ||
216 | int printed_len = 0; | ||
217 | for (j = 0; j < total_irqs; j++) { | ||
218 | p0 = &per_cpu_stats[current][j]; | ||
219 | if (p0->irq_name[0] != '\0') { | ||
220 | int n = (INTRATE_SCRWIDTH-3) - (printed_len - expected_len); | ||
221 | printed_len += printf(" %*s/s", n > 0 ? n : 0, skip_whitespace(p0->irq_name)); | ||
222 | expected_len += INTRATE_SCRWIDTH; | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | bb_putchar('\n'); | ||
227 | |||
228 | for (cpu = 1; cpu <= G.cpu_nr; cpu++) { | ||
229 | /* Check if we want stats about this CPU */ | ||
230 | if (!is_cpu_in_bitmap(cpu) && G.p_option) { | ||
231 | continue; | ||
232 | } | ||
233 | |||
234 | printf("%-11s %4u", current_str, cpu - 1); | ||
235 | |||
236 | for (j = 0; j < total_irqs; j++) { | ||
237 | /* IRQ field set only for proc 0 */ | ||
238 | p0 = &per_cpu_stats[current][j]; | ||
239 | |||
240 | /* | ||
241 | * An empty string for irq name means that | ||
242 | * interrupt is no longer used. | ||
243 | */ | ||
244 | if (p0->irq_name[0] != '\0') { | ||
245 | offset = j; | ||
246 | q0 = &per_cpu_stats[prev][offset]; | ||
247 | |||
248 | /* | ||
249 | * If we want stats for the time since boot | ||
250 | * we have p0->irq != q0->irq. | ||
251 | */ | ||
252 | if (strcmp(p0->irq_name, q0->irq_name) != 0 | ||
253 | && G.interval != 0 | ||
254 | ) { | ||
255 | if (j) { | ||
256 | offset = j - 1; | ||
257 | q0 = &per_cpu_stats[prev][offset]; | ||
258 | } | ||
259 | if (strcmp(p0->irq_name, q0->irq_name) != 0 | ||
260 | && (j + 1 < total_irqs) | ||
261 | ) { | ||
262 | offset = j + 1; | ||
263 | q0 = &per_cpu_stats[prev][offset]; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if (strcmp(p0->irq_name, q0->irq_name) == 0 | ||
268 | || G.interval == 0 | ||
269 | ) { | ||
270 | struct stats_irqcpu *p, *q; | ||
271 | p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j]; | ||
272 | q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset]; | ||
273 | printf("%"INTRATE_SCRWIDTH_STR".2f", | ||
274 | (double)(p->interrupt - q->interrupt) / itv * G.hz); | ||
275 | } else { | ||
276 | printf(" N/A"); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | bb_putchar('\n'); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static data_t get_per_cpu_interval(const struct stats_cpu *scc, | ||
285 | const struct stats_cpu *scp) | ||
286 | { | ||
287 | return ((scc->cpu_user + scc->cpu_nice + | ||
288 | scc->cpu_system + scc->cpu_iowait + | ||
289 | scc->cpu_idle + scc->cpu_steal + | ||
290 | scc->cpu_irq + scc->cpu_softirq) - | ||
291 | (scp->cpu_user + scp->cpu_nice + | ||
292 | scp->cpu_system + scp->cpu_iowait + | ||
293 | scp->cpu_idle + scp->cpu_steal + | ||
294 | scp->cpu_irq + scp->cpu_softirq)); | ||
295 | } | ||
296 | |||
297 | static void print_stats_cpu_struct(const struct stats_cpu *p, | ||
298 | const struct stats_cpu *c, | ||
299 | data_t itv) | ||
300 | { | ||
301 | printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", | ||
302 | percent_value(p->cpu_user - p->cpu_guest, | ||
303 | /**/ c->cpu_user - c->cpu_guest, itv), | ||
304 | percent_value(p->cpu_nice , c->cpu_nice , itv), | ||
305 | percent_value(p->cpu_system , c->cpu_system , itv), | ||
306 | percent_value(p->cpu_iowait , c->cpu_iowait , itv), | ||
307 | percent_value(p->cpu_irq , c->cpu_irq , itv), | ||
308 | percent_value(p->cpu_softirq, c->cpu_softirq, itv), | ||
309 | percent_value(p->cpu_steal , c->cpu_steal , itv), | ||
310 | percent_value(p->cpu_guest , c->cpu_guest , itv), | ||
311 | percent_value(p->cpu_idle , c->cpu_idle , itv) | ||
312 | ); | ||
313 | } | ||
314 | |||
315 | static void write_stats_core(int prev, int current, | ||
316 | const char *prev_str, const char *current_str) | ||
317 | { | ||
318 | struct stats_cpu *scc, *scp; | ||
319 | data_t itv, global_itv; | ||
320 | int cpu; | ||
321 | |||
322 | /* Compute time interval */ | ||
323 | itv = global_itv = jiffies_diff(G.global_uptime[prev], G.global_uptime[current]); | ||
324 | |||
325 | /* Reduce interval to one CPU */ | ||
326 | if (G.cpu_nr > 1) | ||
327 | itv = jiffies_diff(G.per_cpu_uptime[prev], G.per_cpu_uptime[current]); | ||
328 | |||
329 | /* Print CPU stats */ | ||
330 | if (display_opt(D_CPU)) { | ||
331 | |||
332 | ///* This is done exactly once */ | ||
333 | //if (!G.header_done) { | ||
334 | printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq %%soft %%steal %%guest %%idle\n", | ||
335 | prev_str | ||
336 | ); | ||
337 | // G.header_done = 1; | ||
338 | //} | ||
339 | |||
340 | for (cpu = 0; cpu <= G.cpu_nr; cpu++) { | ||
341 | data_t per_cpu_itv; | ||
342 | |||
343 | /* Print stats about this particular CPU? */ | ||
344 | if (!is_cpu_in_bitmap(cpu)) | ||
345 | continue; | ||
346 | |||
347 | scc = &G.st_cpu[current][cpu]; | ||
348 | scp = &G.st_cpu[prev][cpu]; | ||
349 | per_cpu_itv = global_itv; | ||
350 | |||
351 | printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); | ||
352 | if (cpu) { | ||
353 | double idle; | ||
354 | /* | ||
355 | * If the CPU is offline, then it isn't in /proc/stat, | ||
356 | * so all values are 0. | ||
357 | * NB: Guest time is already included in user time. | ||
358 | */ | ||
359 | if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system | | ||
360 | scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal | | ||
361 | scc->cpu_irq | scc->cpu_softirq) == 0 | ||
362 | ) { | ||
363 | /* | ||
364 | * Set current struct fields to values from prev. | ||
365 | * iteration. Then their values won't jump from | ||
366 | * zero, when the CPU comes back online. | ||
367 | */ | ||
368 | *scc = *scp; | ||
369 | idle = 0.0; | ||
370 | goto print_zeros; | ||
371 | } | ||
372 | /* Compute interval again for current proc */ | ||
373 | per_cpu_itv = get_per_cpu_interval(scc, scp); | ||
374 | if (per_cpu_itv == 0) { | ||
375 | /* | ||
376 | * If the CPU is tickless then there is no change in CPU values | ||
377 | * but the sum of values is not zero. | ||
378 | */ | ||
379 | idle = 100.0; | ||
380 | print_zeros: | ||
381 | printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", | ||
382 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle); | ||
383 | continue; | ||
384 | } | ||
385 | } | ||
386 | print_stats_cpu_struct(scp, scc, per_cpu_itv); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | /* Print total number of IRQs per CPU */ | ||
391 | if (display_opt(D_IRQ_SUM)) { | ||
392 | |||
393 | ///* Print average header, this is done exactly once */ | ||
394 | //if (!G.avg_header_done) { | ||
395 | printf("\n%-11s CPU intr/s\n", prev_str); | ||
396 | // G.avg_header_done = 1; | ||
397 | //} | ||
398 | |||
399 | for (cpu = 0; cpu <= G.cpu_nr; cpu++) { | ||
400 | data_t per_cpu_itv; | ||
401 | |||
402 | /* Print stats about this CPU? */ | ||
403 | if (!is_cpu_in_bitmap(cpu)) | ||
404 | continue; | ||
405 | |||
406 | per_cpu_itv = itv; | ||
407 | printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); | ||
408 | if (cpu) { | ||
409 | scc = &G.st_cpu[current][cpu]; | ||
410 | scp = &G.st_cpu[prev][cpu]; | ||
411 | /* Compute interval again for current proc */ | ||
412 | per_cpu_itv = get_per_cpu_interval(scc, scp); | ||
413 | if (per_cpu_itv == 0) { | ||
414 | printf(" %9.2f\n", 0.0); | ||
415 | continue; | ||
416 | } | ||
417 | } | ||
418 | //bb_error_msg("G.st_irq[%u][%u].irq_nr:%lld - G.st_irq[%u][%u].irq_nr:%lld", | ||
419 | // current, cpu, G.st_irq[prev][cpu].irq_nr, prev, cpu, G.st_irq[current][cpu].irq_nr); | ||
420 | printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, per_cpu_itv)); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | if (display_opt(D_IRQ_CPU)) { | ||
425 | write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr, | ||
426 | itv, | ||
427 | prev, current, | ||
428 | prev_str, current_str | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | if (display_opt(D_SOFTIRQS)) { | ||
433 | write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr, | ||
434 | itv, | ||
435 | prev, current, | ||
436 | prev_str, current_str | ||
437 | ); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * Print the statistics | ||
443 | */ | ||
444 | static void write_stats(int current) | ||
445 | { | ||
446 | char prev_time[16]; | ||
447 | char curr_time[16]; | ||
448 | |||
449 | strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]); | ||
450 | strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]); | ||
451 | |||
452 | write_stats_core(!current, current, prev_time, curr_time); | ||
453 | } | ||
454 | |||
455 | static void write_stats_avg(int current) | ||
456 | { | ||
457 | write_stats_core(2, current, "Average:", "Average:"); | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | * Read CPU statistics | ||
462 | */ | ||
463 | static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0) | ||
464 | { | ||
465 | FILE *fp; | ||
466 | char buf[1024]; | ||
467 | |||
468 | fp = xfopen_for_read(PROCFS_STAT); | ||
469 | |||
470 | while (fgets(buf, sizeof(buf), fp)) { | ||
471 | data_t sum; | ||
472 | unsigned cpu_number; | ||
473 | struct stats_cpu *cp; | ||
474 | |||
475 | if (!starts_with_cpu(buf)) | ||
476 | continue; /* not "cpu" */ | ||
477 | |||
478 | cp = cpu; /* for "cpu " case */ | ||
479 | if (buf[3] != ' ') { | ||
480 | /* "cpuN " */ | ||
481 | if (G.cpu_nr == 0 | ||
482 | || sscanf(buf + 3, "%u ", &cpu_number) != 1 | ||
483 | || cpu_number >= G.cpu_nr | ||
484 | ) { | ||
485 | continue; | ||
486 | } | ||
487 | cp = &cpu[cpu_number + 1]; | ||
488 | } | ||
489 | |||
490 | /* Read the counters, save them */ | ||
491 | /* Not all fields have to be present */ | ||
492 | memset(cp, 0, sizeof(*cp)); | ||
493 | sscanf(buf, "%*s" | ||
494 | " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u" | ||
495 | " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u" | ||
496 | " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u", | ||
497 | &cp->cpu_user, &cp->cpu_nice, &cp->cpu_system, | ||
498 | &cp->cpu_idle, &cp->cpu_iowait, &cp->cpu_irq, | ||
499 | &cp->cpu_softirq, &cp->cpu_steal, &cp->cpu_guest | ||
500 | ); | ||
501 | /* | ||
502 | * Compute uptime in jiffies (1/HZ), it'll be the sum of | ||
503 | * individual CPU's uptimes. | ||
504 | * NB: We have to omit cpu_guest, because cpu_user includes it. | ||
505 | */ | ||
506 | sum = cp->cpu_user + cp->cpu_nice + cp->cpu_system + | ||
507 | cp->cpu_idle + cp->cpu_iowait + cp->cpu_irq + | ||
508 | cp->cpu_softirq + cp->cpu_steal; | ||
509 | |||
510 | if (buf[3] == ' ') { | ||
511 | /* "cpu " */ | ||
512 | *up = sum; | ||
513 | } else { | ||
514 | /* "cpuN " */ | ||
515 | if (cpu_number == 0 && *up0 != 0) { | ||
516 | /* Compute uptime of single CPU */ | ||
517 | *up0 = sum; | ||
518 | } | ||
519 | } | ||
520 | } | ||
521 | fclose(fp); | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * Read IRQs from /proc/stat | ||
526 | */ | ||
527 | static void get_irqs_from_stat(struct stats_irq *irq) | ||
528 | { | ||
529 | FILE *fp; | ||
530 | char buf[1024]; | ||
531 | |||
532 | fp = fopen_for_read(PROCFS_STAT); | ||
533 | if (!fp) | ||
534 | return; | ||
535 | |||
536 | while (fgets(buf, sizeof(buf), fp)) { | ||
537 | //bb_error_msg("/proc/stat:'%s'", buf); | ||
538 | if (strncmp(buf, "intr ", 5) == 0) { | ||
539 | /* Read total number of IRQs since system boot */ | ||
540 | sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | fclose(fp); | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Read stats from /proc/interrupts or /proc/softirqs | ||
549 | */ | ||
550 | static void get_irqs_from_interrupts(const char *fname, | ||
551 | struct stats_irqcpu *per_cpu_stats[], | ||
552 | int irqs_per_cpu, int current) | ||
553 | { | ||
554 | FILE *fp; | ||
555 | struct stats_irq *irq_i; | ||
556 | struct stats_irqcpu *ic; | ||
557 | char *buf; | ||
558 | unsigned buflen; | ||
559 | unsigned cpu; | ||
560 | unsigned irq; | ||
561 | int cpu_index[G.cpu_nr]; | ||
562 | int iindex; | ||
563 | |||
564 | // Moved to caller. | ||
565 | // Otherwise reading of /proc/softirqs | ||
566 | // was resetting counts to 0 after we painstakingly collected them from | ||
567 | // /proc/interrupts. Which resulted in: | ||
568 | // 01:32:47 PM CPU intr/s | ||
569 | // 01:32:47 PM all 591.47 | ||
570 | // 01:32:47 PM 0 0.00 <= ??? | ||
571 | // 01:32:47 PM 1 0.00 <= ??? | ||
572 | // for (cpu = 1; cpu <= G.cpu_nr; cpu++) { | ||
573 | // G.st_irq[current][cpu].irq_nr = 0; | ||
574 | // //bb_error_msg("G.st_irq[%u][%u].irq_nr=0", current, cpu); | ||
575 | // } | ||
576 | |||
577 | fp = fopen_for_read(fname); | ||
578 | if (!fp) | ||
579 | return; | ||
580 | |||
581 | buflen = INTERRUPTS_LINE + 16 * G.cpu_nr; | ||
582 | buf = xmalloc(buflen); | ||
583 | |||
584 | /* Parse header and determine, which CPUs are online */ | ||
585 | iindex = 0; | ||
586 | while (fgets(buf, buflen, fp)) { | ||
587 | char *cp, *next; | ||
588 | next = buf; | ||
589 | while ((cp = strstr(next, "CPU")) != NULL | ||
590 | && iindex < G.cpu_nr | ||
591 | ) { | ||
592 | cpu = strtoul(cp + 3, &next, 10); | ||
593 | cpu_index[iindex++] = cpu; | ||
594 | } | ||
595 | if (iindex) /* We found header */ | ||
596 | break; | ||
597 | } | ||
598 | |||
599 | irq = 0; | ||
600 | while (fgets(buf, buflen, fp) | ||
601 | && irq < irqs_per_cpu | ||
602 | ) { | ||
603 | int len; | ||
604 | char last_char; | ||
605 | char *cp; | ||
606 | |||
607 | /* Skip over "IRQNAME:" */ | ||
608 | cp = strchr(buf, ':'); | ||
609 | if (!cp) | ||
610 | continue; | ||
611 | last_char = cp[-1]; | ||
612 | |||
613 | ic = &per_cpu_stats[current][irq]; | ||
614 | len = cp - buf; | ||
615 | if (len >= sizeof(ic->irq_name)) { | ||
616 | len = sizeof(ic->irq_name) - 1; | ||
617 | } | ||
618 | safe_strncpy(ic->irq_name, buf, len + 1); | ||
619 | //bb_error_msg("%s: irq%d:'%s' buf:'%s'", fname, irq, ic->irq_name, buf); | ||
620 | cp++; | ||
621 | |||
622 | for (cpu = 0; cpu < iindex; cpu++) { | ||
623 | char *next; | ||
624 | ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq]; | ||
625 | irq_i = &G.st_irq[current][cpu_index[cpu] + 1]; | ||
626 | ic->interrupt = strtoul(cp, &next, 10); | ||
627 | /* Count only numerical IRQs */ | ||
628 | if (isdigit(last_char)) { | ||
629 | irq_i->irq_nr += ic->interrupt; | ||
630 | //bb_error_msg("G.st_irq[%u][%u].irq_nr + %u = %lld", | ||
631 | // current, cpu_index[cpu] + 1, ic->interrupt, irq_i->irq_nr); | ||
632 | } | ||
633 | cp = next; | ||
634 | } | ||
635 | irq++; | ||
636 | } | ||
637 | fclose(fp); | ||
638 | free(buf); | ||
639 | |||
640 | while (irq < irqs_per_cpu) { | ||
641 | /* Number of interrupts per CPU has changed */ | ||
642 | ic = &per_cpu_stats[current][irq]; | ||
643 | ic->irq_name[0] = '\0'; /* False interrupt */ | ||
644 | irq++; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | static void get_uptime(data_t *uptime) | ||
649 | { | ||
650 | FILE *fp; | ||
651 | char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */ | ||
652 | unsigned long uptime_sec, decimal; | ||
653 | |||
654 | fp = fopen_for_read(PROCFS_UPTIME); | ||
655 | if (!fp) | ||
656 | return; | ||
657 | if (fgets(buf, sizeof(buf), fp)) { | ||
658 | if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) { | ||
659 | *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | fclose(fp); | ||
664 | } | ||
665 | |||
666 | static void get_localtime(struct tm *tm) | ||
667 | { | ||
668 | time_t timer; | ||
669 | time(&timer); | ||
670 | localtime_r(&timer, tm); | ||
671 | } | ||
672 | |||
673 | static void alarm_handler(int sig UNUSED_PARAM) | ||
674 | { | ||
675 | signal(SIGALRM, alarm_handler); | ||
676 | alarm(G.interval); | ||
677 | } | ||
678 | |||
679 | static void main_loop(void) | ||
680 | { | ||
681 | unsigned current; | ||
682 | unsigned cpus; | ||
683 | |||
684 | /* Read the stats */ | ||
685 | if (G.cpu_nr > 1) { | ||
686 | G.per_cpu_uptime[0] = 0; | ||
687 | get_uptime(&G.per_cpu_uptime[0]); | ||
688 | } | ||
689 | |||
690 | get_cpu_statistics(G.st_cpu[0], &G.global_uptime[0], &G.per_cpu_uptime[0]); | ||
691 | |||
692 | if (display_opt(D_IRQ_SUM)) | ||
693 | get_irqs_from_stat(G.st_irq[0]); | ||
694 | |||
695 | if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) | ||
696 | get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu, | ||
697 | G.irqcpu_nr, 0); | ||
698 | |||
699 | if (display_opt(D_SOFTIRQS)) | ||
700 | get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu, | ||
701 | G.softirqcpu_nr, 0); | ||
702 | |||
703 | if (G.interval == 0) { | ||
704 | /* Display since boot time */ | ||
705 | cpus = G.cpu_nr + 1; | ||
706 | G.timestamp[1] = G.timestamp[0]; | ||
707 | memset(G.st_cpu[1], 0, sizeof(G.st_cpu[1][0]) * cpus); | ||
708 | memset(G.st_irq[1], 0, sizeof(G.st_irq[1][0]) * cpus); | ||
709 | memset(G.st_irqcpu[1], 0, sizeof(G.st_irqcpu[1][0]) * cpus * G.irqcpu_nr); | ||
710 | memset(G.st_softirqcpu[1], 0, sizeof(G.st_softirqcpu[1][0]) * cpus * G.softirqcpu_nr); | ||
711 | |||
712 | write_stats(0); | ||
713 | |||
714 | /* And we're done */ | ||
715 | return; | ||
716 | } | ||
717 | |||
718 | /* Set a handler for SIGALRM */ | ||
719 | alarm_handler(0); | ||
720 | |||
721 | /* Save the stats we already have. We need them to compute the average */ | ||
722 | G.timestamp[2] = G.timestamp[0]; | ||
723 | G.global_uptime[2] = G.global_uptime[0]; | ||
724 | G.per_cpu_uptime[2] = G.per_cpu_uptime[0]; | ||
725 | cpus = G.cpu_nr + 1; | ||
726 | memcpy(G.st_cpu[2], G.st_cpu[0], sizeof(G.st_cpu[0][0]) * cpus); | ||
727 | memcpy(G.st_irq[2], G.st_irq[0], sizeof(G.st_irq[0][0]) * cpus); | ||
728 | memcpy(G.st_irqcpu[2], G.st_irqcpu[0], sizeof(G.st_irqcpu[0][0]) * cpus * G.irqcpu_nr); | ||
729 | if (display_opt(D_SOFTIRQS)) { | ||
730 | memcpy(G.st_softirqcpu[2], G.st_softirqcpu[0], | ||
731 | sizeof(G.st_softirqcpu[0][0]) * cpus * G.softirqcpu_nr); | ||
732 | } | ||
733 | |||
734 | current = 1; | ||
735 | while (1) { | ||
736 | /* Suspend until a signal is received */ | ||
737 | pause(); | ||
738 | |||
739 | /* Set structures to 0 to distinguish off/online CPUs */ | ||
740 | memset(&G.st_cpu[current][/*cpu:*/ 1], 0, sizeof(G.st_cpu[0][0]) * G.cpu_nr); | ||
741 | |||
742 | get_localtime(&G.timestamp[current]); | ||
743 | |||
744 | /* Read stats */ | ||
745 | if (G.cpu_nr > 1) { | ||
746 | G.per_cpu_uptime[current] = 0; | ||
747 | get_uptime(&G.per_cpu_uptime[current]); | ||
748 | } | ||
749 | get_cpu_statistics(G.st_cpu[current], &G.global_uptime[current], &G.per_cpu_uptime[current]); | ||
750 | |||
751 | if (display_opt(D_IRQ_SUM)) | ||
752 | get_irqs_from_stat(G.st_irq[current]); | ||
753 | |||
754 | if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) { | ||
755 | int cpu; | ||
756 | for (cpu = 1; cpu <= G.cpu_nr; cpu++) { | ||
757 | G.st_irq[current][cpu].irq_nr = 0; | ||
758 | } | ||
759 | /* accumulates .irq_nr */ | ||
760 | get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu, | ||
761 | G.irqcpu_nr, current); | ||
762 | } | ||
763 | |||
764 | if (display_opt(D_SOFTIRQS)) | ||
765 | get_irqs_from_interrupts(PROCFS_SOFTIRQS, | ||
766 | G.st_softirqcpu, | ||
767 | G.softirqcpu_nr, current); | ||
768 | |||
769 | write_stats(current); | ||
770 | |||
771 | if (G.count > 0) { | ||
772 | if (--G.count == 0) | ||
773 | break; | ||
774 | } | ||
775 | |||
776 | current ^= 1; | ||
777 | } | ||
778 | |||
779 | /* Print average statistics */ | ||
780 | write_stats_avg(current); | ||
781 | } | ||
782 | |||
783 | /* Initialization */ | ||
784 | |||
785 | /* Get number of clock ticks per sec */ | ||
786 | static ALWAYS_INLINE unsigned get_hz(void) | ||
787 | { | ||
788 | return sysconf(_SC_CLK_TCK); | ||
789 | } | ||
790 | |||
791 | static void alloc_struct(int cpus) | ||
792 | { | ||
793 | int i; | ||
794 | for (i = 0; i < 3; i++) { | ||
795 | G.st_cpu[i] = xzalloc(sizeof(G.st_cpu[i][0]) * cpus); | ||
796 | G.st_irq[i] = xzalloc(sizeof(G.st_irq[i][0]) * cpus); | ||
797 | G.st_irqcpu[i] = xzalloc(sizeof(G.st_irqcpu[i][0]) * cpus * G.irqcpu_nr); | ||
798 | G.st_softirqcpu[i] = xzalloc(sizeof(G.st_softirqcpu[i][0]) * cpus * G.softirqcpu_nr); | ||
799 | } | ||
800 | G.cpu_bitmap_len = (cpus >> 3) + 1; | ||
801 | G.cpu_bitmap = xzalloc(G.cpu_bitmap_len); | ||
802 | } | ||
803 | |||
804 | static void print_header(struct tm *t) | ||
805 | { | ||
806 | char cur_date[16]; | ||
807 | struct utsname uts; | ||
808 | |||
809 | /* Get system name, release number and hostname */ | ||
810 | uname(&uts); | ||
811 | |||
812 | strftime(cur_date, sizeof(cur_date), "%x", t); | ||
813 | |||
814 | printf("%s %s (%s)\t%s\t_%s_\t(%u CPU)\n", | ||
815 | uts.sysname, uts.release, uts.nodename, cur_date, uts.machine, G.cpu_nr); | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * Get number of processors in /proc/stat | ||
820 | * Return value '0' means one CPU and non SMP kernel. | ||
821 | * Otherwise N means N processor(s) and SMP kernel. | ||
822 | */ | ||
823 | static int get_cpu_nr(void) | ||
824 | { | ||
825 | FILE *fp; | ||
826 | char line[256]; | ||
827 | int proc_nr = -1; | ||
828 | |||
829 | fp = xfopen_for_read(PROCFS_STAT); | ||
830 | while (fgets(line, sizeof(line), fp)) { | ||
831 | if (!starts_with_cpu(line)) { | ||
832 | if (proc_nr >= 0) | ||
833 | break; /* we are past "cpuN..." lines */ | ||
834 | continue; | ||
835 | } | ||
836 | if (line[3] != ' ') { /* "cpuN" */ | ||
837 | int num_proc; | ||
838 | if (sscanf(line + 3, "%u", &num_proc) == 1 | ||
839 | && num_proc > proc_nr | ||
840 | ) { | ||
841 | proc_nr = num_proc; | ||
842 | } | ||
843 | } | ||
844 | } | ||
845 | |||
846 | fclose(fp); | ||
847 | return proc_nr + 1; | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * Get number of interrupts available per processor | ||
852 | */ | ||
853 | static int get_irqcpu_nr(const char *f, int max_irqs) | ||
854 | { | ||
855 | FILE *fp; | ||
856 | char *line; | ||
857 | unsigned linelen; | ||
858 | unsigned irq; | ||
859 | |||
860 | fp = fopen_for_read(f); | ||
861 | if (!fp) /* No interrupts file */ | ||
862 | return 0; | ||
863 | |||
864 | linelen = INTERRUPTS_LINE + 16 * G.cpu_nr; | ||
865 | line = xmalloc(linelen); | ||
866 | |||
867 | irq = 0; | ||
868 | while (fgets(line, linelen, fp) | ||
869 | && irq < max_irqs | ||
870 | ) { | ||
871 | int p = strcspn(line, ":"); | ||
872 | if ((p > 0) && (p < 16)) | ||
873 | irq++; | ||
874 | } | ||
875 | |||
876 | fclose(fp); | ||
877 | free(line); | ||
878 | |||
879 | return irq; | ||
880 | } | ||
881 | |||
882 | //usage:#define mpstat_trivial_usage | ||
883 | //usage: "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]" | ||
884 | //usage:#define mpstat_full_usage "\n\n" | ||
885 | //usage: "Per-processor statistics\n" | ||
886 | //usage: "\nOptions:" | ||
887 | //usage: "\n -A Same as -I ALL -u -P ALL" | ||
888 | //usage: "\n -I SUM|CPU|ALL|SCPU Report interrupt statistics" | ||
889 | //usage: "\n -P num|ALL Processor to monitor" | ||
890 | //usage: "\n -u Report CPU utilization" | ||
891 | |||
892 | int mpstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
893 | int mpstat_main(int UNUSED_PARAM argc, char **argv) | ||
894 | { | ||
895 | char *opt_irq_fmt; | ||
896 | char *opt_set_cpu; | ||
897 | int i, opt; | ||
898 | enum { | ||
899 | OPT_ALL = 1 << 0, /* -A */ | ||
900 | OPT_INTS = 1 << 1, /* -I */ | ||
901 | OPT_SETCPU = 1 << 2, /* -P */ | ||
902 | OPT_UTIL = 1 << 3, /* -u */ | ||
903 | }; | ||
904 | |||
905 | /* Dont buffer data if redirected to a pipe */ | ||
906 | setbuf(stdout, NULL); | ||
907 | |||
908 | INIT_G(); | ||
909 | |||
910 | G.interval = -1; | ||
911 | |||
912 | /* Get number of processors */ | ||
913 | G.cpu_nr = get_cpu_nr(); | ||
914 | |||
915 | /* Get number of clock ticks per sec */ | ||
916 | G.hz = get_hz(); | ||
917 | |||
918 | /* Calculate number of interrupts per processor */ | ||
919 | G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC; | ||
920 | |||
921 | /* Calculate number of soft interrupts per processor */ | ||
922 | G.softirqcpu_nr = get_irqcpu_nr(PROCFS_SOFTIRQS, NR_IRQS) + NR_IRQCPU_PREALLOC; | ||
923 | |||
924 | /* Allocate space for structures. + 1 for global structure. */ | ||
925 | alloc_struct(G.cpu_nr + 1); | ||
926 | |||
927 | /* Parse and process arguments */ | ||
928 | opt = getopt32(argv, "AI:P:u", &opt_irq_fmt, &opt_set_cpu); | ||
929 | argv += optind; | ||
930 | |||
931 | if (*argv) { | ||
932 | /* Get interval */ | ||
933 | G.interval = xatoi_u(*argv); | ||
934 | G.count = -1; | ||
935 | argv++; | ||
936 | if (*argv) { | ||
937 | /* Get count value */ | ||
938 | if (G.interval == 0) | ||
939 | bb_show_usage(); | ||
940 | G.count = xatoi_u(*argv); | ||
941 | //if (*++argv) | ||
942 | // bb_show_usage(); | ||
943 | } | ||
944 | } | ||
945 | if (G.interval < 0) | ||
946 | G.interval = 0; | ||
947 | |||
948 | if (opt & OPT_ALL) { | ||
949 | G.p_option = 1; | ||
950 | G.options |= D_CPU + D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS; | ||
951 | /* Select every CPU */ | ||
952 | memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); | ||
953 | } | ||
954 | |||
955 | if (opt & OPT_INTS) { | ||
956 | static const char v[] = { | ||
957 | D_IRQ_CPU, D_IRQ_SUM, D_SOFTIRQS, | ||
958 | D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS | ||
959 | }; | ||
960 | i = index_in_strings("CPU\0SUM\0SCPU\0ALL\0", opt_irq_fmt); | ||
961 | if (i == -1) | ||
962 | bb_show_usage(); | ||
963 | G.options |= v[i]; | ||
964 | } | ||
965 | |||
966 | if ((opt & OPT_UTIL) /* -u? */ | ||
967 | || G.options == 0 /* nothing? (use default then) */ | ||
968 | ) { | ||
969 | G.options |= D_CPU; | ||
970 | } | ||
971 | |||
972 | if (opt & OPT_SETCPU) { | ||
973 | char *t; | ||
974 | G.p_option = 1; | ||
975 | |||
976 | for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) { | ||
977 | if (strcmp(t, "ALL") == 0) { | ||
978 | /* Select every CPU */ | ||
979 | memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); | ||
980 | } else { | ||
981 | /* Get CPU number */ | ||
982 | unsigned n = xatoi_u(t); | ||
983 | if (n >= G.cpu_nr) | ||
984 | bb_error_msg_and_die("not that many processors"); | ||
985 | n++; | ||
986 | G.cpu_bitmap[n >> 3] |= 1 << (n & 7); | ||
987 | } | ||
988 | } | ||
989 | } | ||
990 | |||
991 | if (!G.p_option) | ||
992 | /* Display global stats */ | ||
993 | G.cpu_bitmap[0] = 1; | ||
994 | |||
995 | /* Get time */ | ||
996 | get_localtime(&G.timestamp[0]); | ||
997 | |||
998 | /* Display header */ | ||
999 | print_header(&G.timestamp[0]); | ||
1000 | |||
1001 | /* The main loop */ | ||
1002 | main_loop(); | ||
1003 | |||
1004 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
1005 | /* Clean up */ | ||
1006 | for (i = 0; i < 3; i++) { | ||
1007 | free(G.st_cpu[i]); | ||
1008 | free(G.st_irq[i]); | ||
1009 | free(G.st_irqcpu[i]); | ||
1010 | free(G.st_softirqcpu[i]); | ||
1011 | } | ||
1012 | free(G.cpu_bitmap); | ||
1013 | free(&G); | ||
1014 | } | ||
1015 | |||
1016 | return EXIT_SUCCESS; | ||
1017 | } | ||