source: MondoRescue/branches/3.3/mindi-busybox/procps/iostat.c@ 3909

Last change on this file since 3909 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Report CPU and I/O stats, 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 source tree.
8 */
9
10//config:config IOSTAT
11//config: bool "iostat"
12//config: default y
13//config: help
14//config: Report CPU and I/O statistics
15
16//applet:IF_IOSTAT(APPLET(iostat, BB_DIR_BIN, BB_SUID_DROP))
17
18//kbuild:lib-$(CONFIG_IOSTAT) += iostat.o
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#define MAX_DEVICE_NAME 12
27#define MAX_DEVICE_NAME_STR "12"
28
29#if 1
30typedef unsigned long long cputime_t;
31typedef long long icputime_t;
32# define FMT_DATA "ll"
33# define CPUTIME_MAX (~0ULL)
34#else
35typedef unsigned long cputime_t;
36typedef long icputime_t;
37# define FMT_DATA "l"
38# define CPUTIME_MAX (~0UL)
39#endif
40
41enum {
42 STATS_CPU_USER,
43 STATS_CPU_NICE,
44 STATS_CPU_SYSTEM,
45 STATS_CPU_IDLE,
46 STATS_CPU_IOWAIT,
47 STATS_CPU_IRQ,
48 STATS_CPU_SOFTIRQ,
49 STATS_CPU_STEAL,
50 STATS_CPU_GUEST,
51
52 GLOBAL_UPTIME,
53 SMP_UPTIME,
54
55 N_STATS_CPU,
56};
57
58typedef struct {
59 cputime_t vector[N_STATS_CPU];
60} stats_cpu_t;
61
62typedef struct {
63 stats_cpu_t *prev;
64 stats_cpu_t *curr;
65 cputime_t itv;
66} stats_cpu_pair_t;
67
68typedef struct {
69 unsigned long long rd_sectors;
70 unsigned long long wr_sectors;
71 unsigned long rd_ops;
72 unsigned long wr_ops;
73} stats_dev_data_t;
74
75typedef struct stats_dev {
76 struct stats_dev *next;
77 char dname[MAX_DEVICE_NAME + 1];
78 stats_dev_data_t prev_data;
79 stats_dev_data_t curr_data;
80} stats_dev_t;
81
82/* Globals. Sort by size and access frequency. */
83struct globals {
84 smallint show_all;
85 unsigned total_cpus; /* Number of CPUs */
86 unsigned clk_tck; /* Number of clock ticks per second */
87 llist_t *dev_name_list; /* List of devices entered on the command line */
88 stats_dev_t *stats_dev_list;
89 struct tm tmtime;
90 struct {
91 const char *str;
92 unsigned div;
93 } unit;
94};
95#define G (*ptr_to_globals)
96#define INIT_G() do { \
97 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
98 G.unit.str = "Blk"; \
99 G.unit.div = 1; \
100} while (0)
101
102/* Must match option string! */
103enum {
104 OPT_c = 1 << 0,
105 OPT_d = 1 << 1,
106 OPT_t = 1 << 2,
107 OPT_z = 1 << 3,
108 OPT_k = 1 << 4,
109 OPT_m = 1 << 5,
110};
111
112static ALWAYS_INLINE int this_is_smp(void)
113{
114 return (G.total_cpus > 1);
115}
116
117static void print_header(void)
118{
119 char buf[32];
120 struct utsname uts;
121
122 uname(&uts); /* never fails */
123
124 /* Date representation for the current locale */
125 strftime(buf, sizeof(buf), "%x", &G.tmtime);
126
127 printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
128 uts.sysname, uts.release, uts.nodename,
129 buf, uts.machine, G.total_cpus);
130}
131
132static void get_localtime(struct tm *ptm)
133{
134 time_t timer;
135 time(&timer);
136 localtime_r(&timer, ptm);
137}
138
139static void print_timestamp(void)
140{
141 char buf[64];
142 /* %x: date representation for the current locale */
143 /* %X: time representation for the current locale */
144 strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
145 puts(buf);
146}
147
148static cputime_t get_smp_uptime(void)
149{
150 FILE *fp;
151 unsigned long sec, dec;
152
153 fp = xfopen_for_read("/proc/uptime");
154
155 if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
156 bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
157
158 fclose(fp);
159
160 return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
161}
162
163/* Fetch CPU statistics from /proc/stat */
164static void get_cpu_statistics(stats_cpu_t *sc)
165{
166 FILE *fp;
167 char buf[1024];
168
169 fp = xfopen_for_read("/proc/stat");
170
171 memset(sc, 0, sizeof(*sc));
172
173 while (fgets(buf, sizeof(buf), fp)) {
174 int i;
175 char *ibuf;
176
177 /* Does the line start with "cpu "? */
178 if (!starts_with_cpu(buf) || buf[3] != ' ') {
179 continue;
180 }
181 ibuf = buf + 4;
182 for (i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
183 ibuf = skip_whitespace(ibuf);
184 sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
185 if (i != STATS_CPU_GUEST) {
186 sc->vector[GLOBAL_UPTIME] += sc->vector[i];
187 }
188 ibuf = skip_non_whitespace(ibuf);
189 }
190 break;
191 }
192
193 if (this_is_smp()) {
194 sc->vector[SMP_UPTIME] = get_smp_uptime();
195 }
196
197 fclose(fp);
198}
199
200static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
201{
202 cputime_t itv = new - old;
203
204 return (itv == 0) ? 1 : itv;
205}
206
207#if CPUTIME_MAX > 0xffffffff
208/*
209 * Handle overflow conditions properly for counters which can have
210 * less bits than cputime_t, depending on the kernel version.
211 */
212/* Surprisingly, on 32bit inlining is a size win */
213static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
214{
215 cputime_t v = curr - prev;
216
217 if ((icputime_t)v < 0 /* curr < prev - counter overflow? */
218 && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
219 ) {
220 /* Add 33th bit set to 1 to curr, compensating for the overflow */
221 /* double shift defeats "warning: left shift count >= width of type" */
222 v += ((cputime_t)1 << 16) << 16;
223 }
224 return v;
225}
226#else
227static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
228{
229 return curr - prev;
230}
231#endif
232
233static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
234{
235 return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
236}
237
238static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
239{
240 cputime_t *p = stats->prev->vector;
241 cputime_t *c = stats->curr->vector;
242 printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
243 percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv),
244 percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv),
245 percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
246 c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
247 percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
248 percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
249 percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv)
250 );
251}
252
253static void cpu_report(stats_cpu_pair_t *stats)
254{
255 /* Always print a header */
256 puts("avg-cpu: %user %nice %system %iowait %steal %idle");
257
258 /* Print current statistics */
259 print_stats_cpu_struct(stats);
260}
261
262static void print_stats_dev_struct(stats_dev_t *stats_dev, cputime_t itv)
263{
264 stats_dev_data_t *p = &stats_dev->prev_data;
265 stats_dev_data_t *c = &stats_dev->curr_data;
266 if (option_mask32 & OPT_z)
267 if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
268 return;
269
270 printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu\n",
271 stats_dev->dname,
272 percent_value(p->rd_ops + p->wr_ops, c->rd_ops + c->wr_ops, itv),
273 percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
274 percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
275 (c->rd_sectors - p->rd_sectors) / G.unit.div,
276 (c->wr_sectors - p->wr_sectors) / G.unit.div
277 );
278}
279
280static void print_devstat_header(void)
281{
282 printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n",
283 "tps",
284 G.unit.str, "_read", G.unit.str, "_wrtn",
285 G.unit.str, "_read", G.unit.str, "_wrtn"
286 );
287}
288
289/*
290 * Is input partition of format [sdaN]?
291 */
292static int is_partition(const char *dev)
293{
294 /* Ok, this is naive... */
295 return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
296}
297
298static stats_dev_t *stats_dev_find_or_new(const char *dev_name)
299{
300 stats_dev_t **curr = &G.stats_dev_list;
301
302 while (*curr != NULL) {
303 if (strcmp((*curr)->dname, dev_name) == 0)
304 return *curr;
305 curr = &(*curr)->next;
306 }
307
308 *curr = xzalloc(sizeof(stats_dev_t));
309 strncpy((*curr)->dname, dev_name, MAX_DEVICE_NAME);
310 return *curr;
311}
312
313static void stats_dev_free(stats_dev_t *stats_dev)
314{
315 if (stats_dev) {
316 stats_dev_free(stats_dev->next);
317 free(stats_dev);
318 }
319}
320
321static void do_disk_statistics(cputime_t itv)
322{
323 char buf[128];
324 char dev_name[MAX_DEVICE_NAME + 1];
325 unsigned long long rd_sec_or_dummy;
326 unsigned long long wr_sec_or_dummy;
327 stats_dev_data_t *curr_data;
328 stats_dev_t *stats_dev;
329 FILE *fp;
330 int rc;
331
332 fp = xfopen_for_read("/proc/diskstats");
333 /* Read and possibly print stats from /proc/diskstats */
334 while (fgets(buf, sizeof(buf), fp)) {
335 sscanf(buf, "%*s %*s %"MAX_DEVICE_NAME_STR"s", dev_name);
336 if (G.dev_name_list) {
337 /* Is device name in list? */
338 if (!llist_find_str(G.dev_name_list, dev_name))
339 continue;
340 } else if (is_partition(dev_name)) {
341 continue;
342 }
343
344 stats_dev = stats_dev_find_or_new(dev_name);
345 curr_data = &stats_dev->curr_data;
346
347 rc = sscanf(buf, "%*s %*s %*s %lu %llu %llu %llu %lu %*s %llu",
348 &curr_data->rd_ops,
349 &rd_sec_or_dummy,
350 &curr_data->rd_sectors,
351 &wr_sec_or_dummy,
352 &curr_data->wr_ops,
353 &curr_data->wr_sectors);
354 if (rc != 6) {
355 curr_data->rd_sectors = rd_sec_or_dummy;
356 curr_data->wr_sectors = wr_sec_or_dummy;
357 //curr_data->rd_ops = ;
358 curr_data->wr_ops = (unsigned long)curr_data->rd_sectors;
359 }
360
361 if (!G.dev_name_list /* User didn't specify device */
362 && !G.show_all
363 && curr_data->rd_ops == 0
364 && curr_data->wr_ops == 0
365 ) {
366 /* Don't print unused device */
367 continue;
368 }
369
370 /* Print current statistics */
371 print_stats_dev_struct(stats_dev, itv);
372 stats_dev->prev_data = *curr_data;
373 }
374
375 fclose(fp);
376}
377
378static void dev_report(cputime_t itv)
379{
380 /* Always print a header */
381 print_devstat_header();
382
383 /* Fetch current disk statistics */
384 do_disk_statistics(itv);
385}
386
387//usage:#define iostat_trivial_usage
388//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
389//usage:#define iostat_full_usage "\n\n"
390//usage: "Report CPU and I/O statistics\n"
391//usage: "\n -c Show CPU utilization"
392//usage: "\n -d Show device utilization"
393//usage: "\n -t Print current time"
394//usage: "\n -z Omit devices with no activity"
395//usage: "\n -k Use kb/s"
396//usage: "\n -m Use Mb/s"
397
398int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
399int iostat_main(int argc UNUSED_PARAM, char **argv)
400{
401 int opt;
402 unsigned interval;
403 int count;
404 stats_cpu_t stats_data[2];
405 smallint current_stats;
406
407 INIT_G();
408
409 memset(&stats_data, 0, sizeof(stats_data));
410
411 /* Get number of clock ticks per sec */
412 G.clk_tck = bb_clk_tck();
413
414 /* Determine number of CPUs */
415 G.total_cpus = get_cpu_count();
416 if (G.total_cpus == 0)
417 G.total_cpus = 1;
418
419 /* Parse and process arguments */
420 /* -k and -m are mutually exclusive */
421 opt_complementary = "k--m:m--k";
422 opt = getopt32(argv, "cdtzkm");
423 if (!(opt & (OPT_c + OPT_d)))
424 /* Default is -cd */
425 opt |= OPT_c + OPT_d;
426
427 argv += optind;
428
429 /* Store device names into device list */
430 while (*argv && !isdigit(*argv[0])) {
431 if (strcmp(*argv, "ALL") != 0) {
432 /* If not ALL, save device name */
433 char *dev_name = skip_dev_pfx(*argv);
434 if (!llist_find_str(G.dev_name_list, dev_name)) {
435 llist_add_to(&G.dev_name_list, dev_name);
436 }
437 } else {
438 G.show_all = 1;
439 }
440 argv++;
441 }
442
443 interval = 0;
444 count = 1;
445 if (*argv) {
446 /* Get interval */
447 interval = xatoi_positive(*argv);
448 count = (interval != 0 ? -1 : 1);
449 argv++;
450 if (*argv)
451 /* Get count value */
452 count = xatoi_positive(*argv);
453 }
454
455 if (opt & OPT_m) {
456 G.unit.str = " MB";
457 G.unit.div = 2048;
458 }
459
460 if (opt & OPT_k) {
461 G.unit.str = " kB";
462 G.unit.div = 2;
463 }
464
465 get_localtime(&G.tmtime);
466
467 /* Display header */
468 print_header();
469
470 current_stats = 0;
471 /* Main loop */
472 for (;;) {
473 stats_cpu_pair_t stats;
474
475 stats.prev = &stats_data[current_stats ^ 1];
476 stats.curr = &stats_data[current_stats];
477
478 /* Fill the time structure */
479 get_localtime(&G.tmtime);
480
481 /* Fetch current CPU statistics */
482 get_cpu_statistics(stats.curr);
483
484 /* Get interval */
485 stats.itv = get_interval(
486 stats.prev->vector[GLOBAL_UPTIME],
487 stats.curr->vector[GLOBAL_UPTIME]
488 );
489
490 if (opt & OPT_t)
491 print_timestamp();
492
493 if (opt & OPT_c) {
494 cpu_report(&stats);
495 if (opt & OPT_d)
496 /* Separate outputs by a newline */
497 bb_putchar('\n');
498 }
499
500 if (opt & OPT_d) {
501 if (this_is_smp()) {
502 stats.itv = get_interval(
503 stats.prev->vector[SMP_UPTIME],
504 stats.curr->vector[SMP_UPTIME]
505 );
506 }
507 dev_report(stats.itv);
508 }
509
510 bb_putchar('\n');
511
512 if (count > 0) {
513 if (--count == 0)
514 break;
515 }
516
517 /* Swap stats */
518 current_stats ^= 1;
519
520 sleep(interval);
521 }
522
523 if (ENABLE_FEATURE_CLEAN_UP) {
524 llist_free(G.dev_name_list, NULL);
525 stats_dev_free(G.stats_dev_list);
526 free(&G);
527 }
528
529 return EXIT_SUCCESS;
530}
Note: See TracBrowser for help on using the repository browser.