perfmonger 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +15 -0
  2. data/.dir-locals.el +2 -0
  3. data/.gitignore +4 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +12 -0
  6. data/COPYING +674 -0
  7. data/Gemfile +5 -0
  8. data/HOWTO.md +15 -0
  9. data/NEWS +115 -0
  10. data/README.md +61 -0
  11. data/Rakefile +8 -0
  12. data/bin/perfmonger +6 -0
  13. data/data/NOTICE +8 -0
  14. data/data/Twitter_Bootstrap_LICENSE.txt +176 -0
  15. data/data/assets/css/bootstrap-responsive.css +1109 -0
  16. data/data/assets/css/bootstrap.css +6167 -0
  17. data/data/assets/css/perfmonger.css +17 -0
  18. data/data/assets/dashboard.erb +319 -0
  19. data/data/assets/img/glyphicons-halflings-white.png +0 -0
  20. data/data/assets/img/glyphicons-halflings.png +0 -0
  21. data/data/assets/js/bootstrap.js +2280 -0
  22. data/data/assets/js/bootstrap.min.js +6 -0
  23. data/data/assets/js/canvasjs.js +9042 -0
  24. data/data/assets/js/canvasjs.min.js +271 -0
  25. data/data/sysstat.ioconf +268 -0
  26. data/ext/perfmonger/extconf.rb +19 -0
  27. data/ext/perfmonger/perfmonger.h +58 -0
  28. data/ext/perfmonger/perfmonger_record.c +754 -0
  29. data/ext/perfmonger/sysstat/common.c +627 -0
  30. data/ext/perfmonger/sysstat/common.h +207 -0
  31. data/ext/perfmonger/sysstat/ioconf.c +515 -0
  32. data/ext/perfmonger/sysstat/ioconf.h +84 -0
  33. data/ext/perfmonger/sysstat/iostat.c +1100 -0
  34. data/ext/perfmonger/sysstat/iostat.h +121 -0
  35. data/ext/perfmonger/sysstat/libsysstat.h +19 -0
  36. data/ext/perfmonger/sysstat/mpstat.c +953 -0
  37. data/ext/perfmonger/sysstat/mpstat.h +79 -0
  38. data/ext/perfmonger/sysstat/rd_stats.c +2388 -0
  39. data/ext/perfmonger/sysstat/rd_stats.h +651 -0
  40. data/ext/perfmonger/sysstat/sysconfig.h +13 -0
  41. data/lib/perfmonger/cli.rb +115 -0
  42. data/lib/perfmonger/command/base_command.rb +39 -0
  43. data/lib/perfmonger/command/fingerprint.rb +453 -0
  44. data/lib/perfmonger/command/plot.rb +429 -0
  45. data/lib/perfmonger/command/record.rb +32 -0
  46. data/lib/perfmonger/command/record_option.rb +149 -0
  47. data/lib/perfmonger/command/server.rb +294 -0
  48. data/lib/perfmonger/command/stat.rb +60 -0
  49. data/lib/perfmonger/command/stat_option.rb +29 -0
  50. data/lib/perfmonger/command/summary.rb +402 -0
  51. data/lib/perfmonger/config.rb +6 -0
  52. data/lib/perfmonger/version.rb +5 -0
  53. data/lib/perfmonger.rb +12 -0
  54. data/misc/release-howto.txt +17 -0
  55. data/misc/sample-cpu.png +0 -0
  56. data/misc/sample-read-iops.png +0 -0
  57. data/perfmonger.gemspec +44 -0
  58. data/test/run-test.sh +39 -0
  59. data/test/spec/bin_spec.rb +37 -0
  60. data/test/spec/data/2devices.expected +42 -0
  61. data/test/spec/data/2devices.output +42 -0
  62. data/test/spec/spec_helper.rb +20 -0
  63. data/test/spec/summary_spec.rb +193 -0
  64. data/test/test-perfmonger.c +145 -0
  65. data/test/test.h +9 -0
  66. metadata +154 -0
@@ -0,0 +1,1100 @@
1
+ /*
2
+ * (C) 2011 Modified by Yuto HAYAMIZU (haya <at> tkl.iis.u-tokyo.ac.jp)
3
+ */
4
+
5
+ /*
6
+ * iostat: report CPU and I/O statistics
7
+ * (C) 1998-2010 by Sebastien GODARD (sysstat <at> orange.fr)
8
+ *
9
+ ***************************************************************************
10
+ * This program is free software; you can redistribute it and/or modify it *
11
+ * under the terms of the GNU General Public License as published by the *
12
+ * Free Software Foundation; either version 2 of the License, or (at your *
13
+ * option) any later version. *
14
+ * *
15
+ * This program is distributed in the hope that it will be useful, but *
16
+ * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
17
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18
+ * for more details. *
19
+ * *
20
+ * You should have received a copy of the GNU General Public License along *
21
+ * with this program; if not, write to the Free Software Foundation, Inc., *
22
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
23
+ ***************************************************************************
24
+ */
25
+
26
+ #include <stdio.h>
27
+ #include <string.h>
28
+ #include <stdlib.h>
29
+ #include <unistd.h>
30
+ #include <signal.h>
31
+ #include <fcntl.h>
32
+ #include <errno.h>
33
+ #include <time.h>
34
+ #include <ctype.h>
35
+ #include <dirent.h>
36
+ #include <sys/types.h>
37
+ #include <sys/stat.h>
38
+ #include <sys/utsname.h>
39
+
40
+ #include "iostat.h"
41
+ #include "common.h"
42
+ #include "ioconf.h"
43
+ #include "rd_stats.h"
44
+
45
+ #ifdef USE_NLS
46
+ #include <locale.h>
47
+ #include <libintl.h>
48
+ #define _(string) gettext(string)
49
+ #else
50
+ #define _(string) (string)
51
+ #endif
52
+
53
+ struct stats_cpu *st_cpu[2];
54
+ extern unsigned long long uptime[3]; /* defined in mpstat.c */
55
+ extern unsigned long long uptime0[3]; /* defined in mpstat.c */
56
+ struct io_stats *st_iodev[2];
57
+ struct io_hdr_stats *st_hdr_iodev;
58
+ struct io_dlist *st_dev_list;
59
+
60
+ int iodev_nr = 0; /* Nb of devices and partitions found */
61
+ int cpu_nr = 0; /* Nb of processors on the machine */
62
+ int dlist_idx = 0; /* Nb of devices entered on the command line */
63
+ int flags = 0; /* Flag for common options and system state */
64
+ unsigned int dm_major; /* Device-mapper major number */
65
+
66
+ long interval = 0;
67
+ char timestamp[64];
68
+
69
+
70
+ /*
71
+ ***************************************************************************
72
+ * Print usage and exit.
73
+ *
74
+ * IN:
75
+ * @progname Name of sysstat command.
76
+ ***************************************************************************
77
+ */
78
+ void usage(char *progname)
79
+ {
80
+ fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
81
+ progname);
82
+ #ifdef DEBUG
83
+ fprintf(stderr, _("Options are:\n"
84
+ "[ -c ] [ -d ] [ -N ] [ -k | -m ] [ -t ] [ -V ] [ -x ] [ -z ]\n"
85
+ "[ <device> [...] | ALL ] [ -p [ <device> [,...] | ALL ] ] [ --debuginfo ]\n"));
86
+ #else
87
+ fprintf(stderr, _("Options are:\n"
88
+ "[ -c ] [ -d ] [ -N ] [ -k | -m ] [ -t ] [ -V ] [ -x ] [ -z ]\n"
89
+ "[ <device> [...] | ALL ] [ -p [ <device> [,...] | ALL ] ]\n"));
90
+ #endif
91
+ exit(1);
92
+ }
93
+
94
+ /*
95
+ ***************************************************************************
96
+ * Set disk output unit. Unit will be kB/s unless POSIXLY_CORRECT
97
+ * environment variable has been set, in which case the output will be
98
+ * expressed in blocks/s.
99
+ ***************************************************************************
100
+ */
101
+ void set_disk_output_unit(void)
102
+ {
103
+ char *e;
104
+
105
+ if (DISPLAY_KILOBYTES(flags) || DISPLAY_MEGABYTES(flags))
106
+ return;
107
+
108
+ /* Check POSIXLY_CORRECT environment variable */
109
+ if ((e = getenv(ENV_POSIXLY_CORRECT)) == NULL) {
110
+ /* Variable not set: Unit is kB/s and not blocks/s */
111
+ flags |= I_D_KILOBYTES;
112
+ }
113
+ }
114
+
115
+ /*
116
+ ***************************************************************************
117
+ * Initialize stats common structures.
118
+ ***************************************************************************
119
+ */
120
+ void init_stats(void)
121
+ {
122
+ int i;
123
+
124
+ /* Allocate structures for CPUs "all" and 0 */
125
+ for (i = 0; i < 2; i++) {
126
+ if ((st_cpu[i] = (struct stats_cpu *) malloc(STATS_CPU_SIZE * 2)) == NULL) {
127
+ perror("malloc");
128
+ exit(4);
129
+ }
130
+ memset(st_cpu[i], 0, STATS_CPU_SIZE * 2);
131
+ }
132
+ }
133
+
134
+ /*
135
+ ***************************************************************************
136
+ * Set every disk_io entry to inactive state (unregistered).
137
+ *
138
+ * IN:
139
+ * @ioln_nr Number of devices and partitions.
140
+ * @st_hdr_ioln Pointer on first structure describing a device/partition.
141
+ ***************************************************************************
142
+ */
143
+ void set_entries_inactive(int ioln_nr, struct io_hdr_stats *st_hdr_ioln)
144
+ {
145
+ int i;
146
+ struct io_hdr_stats *shi = st_hdr_ioln;
147
+
148
+ for (i = 0; i < ioln_nr; i++, shi++) {
149
+ shi->active = FALSE;
150
+ }
151
+ }
152
+
153
+ /*
154
+ ***************************************************************************
155
+ * Free inactive entries (mark them as unused).
156
+ *
157
+ * IN:
158
+ * @ioln_nr Number of devices and partitions.
159
+ * @st_hdr_ioln Pointer on first structure describing a device/partition.
160
+ ***************************************************************************
161
+ */
162
+ void free_inactive_entries(int ioln_nr, struct io_hdr_stats *st_hdr_ioln)
163
+ {
164
+ int i;
165
+ struct io_hdr_stats *shi = st_hdr_ioln;
166
+
167
+ for (i = 0; i < ioln_nr; i++, shi++) {
168
+ if (!shi->active) {
169
+ shi->used = FALSE;
170
+ }
171
+ }
172
+ }
173
+
174
+ /*
175
+ ***************************************************************************
176
+ * Allocate and init I/O device structures.
177
+ *
178
+ * IN:
179
+ * @iodev_nr Number of devices and partitions.
180
+ ***************************************************************************
181
+ */
182
+ void salloc_device(int iodev_nr)
183
+ {
184
+ int i;
185
+
186
+ for (i = 0; i < 2; i++) {
187
+ if ((st_iodev[i] =
188
+ (struct io_stats *) malloc(IO_STATS_SIZE * iodev_nr)) == NULL) {
189
+ perror("malloc");
190
+ exit(4);
191
+ }
192
+ memset(st_iodev[i], 0, IO_STATS_SIZE * iodev_nr);
193
+ }
194
+
195
+ if ((st_hdr_iodev =
196
+ (struct io_hdr_stats *) malloc(IO_HDR_STATS_SIZE * iodev_nr)) == NULL) {
197
+ perror("malloc");
198
+ exit(4);
199
+ }
200
+ memset(st_hdr_iodev, 0, IO_HDR_STATS_SIZE * iodev_nr);
201
+ }
202
+
203
+ /*
204
+ ***************************************************************************
205
+ * Allocate structures for devices entered on the command line.
206
+ *
207
+ * IN:
208
+ * @list_len Number of arguments on the command line.
209
+ ***************************************************************************
210
+ */
211
+ void salloc_dev_list(int list_len)
212
+ {
213
+ if ((st_dev_list = (struct io_dlist *) malloc(IO_DLIST_SIZE * list_len)) == NULL) {
214
+ perror("malloc");
215
+ exit(4);
216
+ }
217
+ memset(st_dev_list, 0, IO_DLIST_SIZE * list_len);
218
+ }
219
+
220
+ /*
221
+ ***************************************************************************
222
+ * Free structures used for devices entered on the command line.
223
+ ***************************************************************************
224
+ */
225
+ void sfree_dev_list(void)
226
+ {
227
+ if (st_dev_list) {
228
+ free(st_dev_list);
229
+ }
230
+ }
231
+
232
+ /*
233
+ ***************************************************************************
234
+ * Look for the device in the device list and store it if necessary.
235
+ *
236
+ * IN:
237
+ * @dlist_idx Length of the device list.
238
+ * @device_name Name of the device.
239
+ *
240
+ * OUT:
241
+ * @dlist_idx Length of the device list.
242
+ *
243
+ * RETURNS:
244
+ * Position of the device in the list.
245
+ ***************************************************************************
246
+ */
247
+ int update_dev_list(int *dlist_idx, char *device_name)
248
+ {
249
+ int i;
250
+ struct io_dlist *sdli = st_dev_list;
251
+
252
+ for (i = 0; i < *dlist_idx; i++, sdli++) {
253
+ if (!strcmp(sdli->dev_name, device_name))
254
+ break;
255
+ }
256
+
257
+ if (i == *dlist_idx) {
258
+ /* Device not found: Store it */
259
+ (*dlist_idx)++;
260
+ strncpy(sdli->dev_name, device_name, MAX_NAME_LEN - 1);
261
+ }
262
+
263
+ return i;
264
+ }
265
+
266
+ /*
267
+ ***************************************************************************
268
+ * Allocate and init structures, according to system state.
269
+ ***************************************************************************
270
+ */
271
+ void io_sys_init(void)
272
+ {
273
+ /* Allocate and init stat common counters */
274
+ init_stats();
275
+
276
+ /* How many processors on this machine? */
277
+ cpu_nr = get_cpu_nr(~0);
278
+
279
+ /* Get number of block devices and partitions in /proc/diskstats */
280
+ if ((iodev_nr = get_diskstats_dev_nr(CNT_PART, CNT_ALL_DEV)) > 0) {
281
+ flags |= I_F_HAS_DISKSTATS;
282
+ iodev_nr += NR_DEV_PREALLOC;
283
+ }
284
+
285
+ if (!HAS_DISKSTATS(flags) ||
286
+ (DISPLAY_PARTITIONS(flags) && !DISPLAY_PART_ALL(flags))) {
287
+ /*
288
+ * If /proc/diskstats exists but we also want stats for the partitions
289
+ * of a particular device, stats will have to be found in /sys. So we
290
+ * need to know if /sys is mounted or not, and set flags accordingly.
291
+ */
292
+
293
+ /* Get number of block devices (and partitions) in sysfs */
294
+ if ((iodev_nr = get_sysfs_dev_nr(DISPLAY_PARTITIONS(flags))) > 0) {
295
+ flags |= I_F_HAS_SYSFS;
296
+ iodev_nr += NR_DEV_PREALLOC;
297
+ }
298
+ else {
299
+ fprintf(stderr, _("Cannot find disk data\n"));
300
+ exit(2);
301
+ }
302
+ }
303
+ /*
304
+ * Allocate structures for number of disks found.
305
+ * iodev_nr must be <> 0.
306
+ */
307
+ salloc_device(iodev_nr);
308
+ }
309
+
310
+ /*
311
+ ***************************************************************************
312
+ * Free various structures.
313
+ ***************************************************************************
314
+ */
315
+ void io_sys_free(void)
316
+ {
317
+ int i;
318
+
319
+ for (i = 0; i < 2; i++) {
320
+
321
+ /* Free CPU structures */
322
+ if (st_cpu[i]) {
323
+ free(st_cpu[i]);
324
+ }
325
+
326
+ /* Free I/O device structures */
327
+ if (st_iodev[i]) {
328
+ free(st_iodev[i]);
329
+ }
330
+ }
331
+
332
+ if (st_hdr_iodev) {
333
+ free(st_hdr_iodev);
334
+ }
335
+ }
336
+
337
+ /*
338
+ ***************************************************************************
339
+ * Save stats for current device or partition.
340
+ *
341
+ * IN:
342
+ * @name Name of the device/partition.
343
+ * @curr Index in array for current sample statistics.
344
+ * @st_io Structure with device or partition to save.
345
+ * @ioln_nr Number of devices and partitions.
346
+ * @st_hdr_ioln Pointer on structures describing a device/partition.
347
+ *
348
+ * OUT:
349
+ * @st_hdr_ioln Pointer on structures describing a device/partition.
350
+ ***************************************************************************
351
+ */
352
+ void save_stats(char *name, int curr, void *st_io, int ioln_nr,
353
+ struct io_hdr_stats *st_hdr_ioln)
354
+ {
355
+ int i;
356
+ struct io_hdr_stats *st_hdr_ioln_i;
357
+ struct io_stats *st_iodev_i;
358
+
359
+ /* Look for device in data table */
360
+ for (i = 0; i < ioln_nr; i++) {
361
+ st_hdr_ioln_i = st_hdr_ioln + i;
362
+ if (!strcmp(st_hdr_ioln_i->name, name)) {
363
+ break;
364
+ }
365
+ }
366
+
367
+ if (i == ioln_nr) {
368
+ /*
369
+ * This is a new device: look for an unused entry to store it.
370
+ * Thus we are able to handle dynamically registered devices.
371
+ */
372
+ for (i = 0; i < ioln_nr; i++) {
373
+ st_hdr_ioln_i = st_hdr_ioln + i;
374
+ if (!st_hdr_ioln_i->used) {
375
+ /* Unused entry found... */
376
+ st_hdr_ioln_i->used = TRUE; /* Indicate it is now used */
377
+ strcpy(st_hdr_ioln_i->name, name);
378
+ st_iodev_i = st_iodev[!curr] + i;
379
+ memset(st_iodev_i, 0, IO_STATS_SIZE);
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ if (i < ioln_nr) {
385
+ st_hdr_ioln_i = st_hdr_ioln + i;
386
+ st_hdr_ioln_i->active = TRUE;
387
+ st_iodev_i = st_iodev[curr] + i;
388
+ *st_iodev_i = *((struct io_stats *) st_io);
389
+ }
390
+ /*
391
+ * else it was a new device
392
+ * but there was no free structure to store it.
393
+ */
394
+ }
395
+
396
+ /*
397
+ ***************************************************************************
398
+ * Read sysfs stat for current block device or partition.
399
+ *
400
+ * IN:
401
+ * @curr Index in array for current sample statistics.
402
+ * @filename File name where stats will be read.
403
+ * @dev_name Device or partition name.
404
+ *
405
+ * RETURNS:
406
+ * 0 if file couldn't be opened, 1 otherwise.
407
+ ***************************************************************************
408
+ */
409
+ int read_sysfs_file_stat(int curr, char *filename, char *dev_name)
410
+ {
411
+ FILE *fp;
412
+ struct io_stats sdev;
413
+ int i;
414
+ unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
415
+ unsigned long ios_pgr, tot_ticks, rq_ticks, wr_merges, wr_ticks;
416
+ unsigned long long rd_sec_or_wr_ios, wr_sec;
417
+
418
+ /* Try to read given stat file */
419
+ if ((fp = fopen(filename, "r")) == NULL)
420
+ return 0;
421
+
422
+ i = fscanf(fp, "%lu %lu %llu %lu %lu %lu %llu %lu %lu %lu %lu",
423
+ &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
424
+ &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks);
425
+
426
+ if (i == 11) {
427
+ /* Device or partition */
428
+ sdev.rd_ios = rd_ios;
429
+ sdev.rd_merges = rd_merges_or_rd_sec;
430
+ sdev.rd_sectors = rd_sec_or_wr_ios;
431
+ sdev.rd_ticks = rd_ticks_or_wr_sec;
432
+ sdev.wr_ios = wr_ios;
433
+ sdev.wr_merges = wr_merges;
434
+ sdev.wr_sectors = wr_sec;
435
+ sdev.wr_ticks = wr_ticks;
436
+ sdev.ios_pgr = ios_pgr;
437
+ sdev.tot_ticks = tot_ticks;
438
+ sdev.rq_ticks = rq_ticks;
439
+ }
440
+ else if (i == 4) {
441
+ /* Partition without extended statistics */
442
+ sdev.rd_ios = rd_ios;
443
+ sdev.rd_sectors = rd_merges_or_rd_sec;
444
+ sdev.wr_ios = rd_sec_or_wr_ios;
445
+ sdev.wr_sectors = rd_ticks_or_wr_sec;
446
+ }
447
+
448
+ if ((i == 11) || !DISPLAY_EXTENDED(flags)) {
449
+ /*
450
+ * In fact, we _don't_ save stats if it's a partition without
451
+ * extended stats and yet we want to display ext stats.
452
+ */
453
+ save_stats(dev_name, curr, &sdev, iodev_nr, st_hdr_iodev);
454
+ }
455
+
456
+ fclose(fp);
457
+
458
+ return 1;
459
+ }
460
+
461
+ /*
462
+ ***************************************************************************
463
+ * Read sysfs stats for all the partitions of a device.
464
+ *
465
+ * IN:
466
+ * @curr Index in array for current sample statistics.
467
+ * @dev_name Device name.
468
+ ***************************************************************************
469
+ */
470
+ void read_sysfs_dlist_part_stat(int curr, char *dev_name)
471
+ {
472
+ DIR *dir;
473
+ struct dirent *drd;
474
+ char dfile[MAX_PF_NAME], filename[MAX_PF_NAME];
475
+
476
+ snprintf(dfile, MAX_PF_NAME, "%s/%s", SYSFS_BLOCK, dev_name);
477
+ dfile[MAX_PF_NAME - 1] = '\0';
478
+
479
+ /* Open current device directory in /sys/block */
480
+ if ((dir = opendir(dfile)) == NULL)
481
+ return;
482
+
483
+ /* Get current entry */
484
+ while ((drd = readdir(dir)) != NULL) {
485
+ if (!strcmp(drd->d_name, ".") || !strcmp(drd->d_name, ".."))
486
+ continue;
487
+ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", dfile, drd->d_name, S_STAT);
488
+ filename[MAX_PF_NAME - 1] = '\0';
489
+
490
+ /* Read current partition stats */
491
+ read_sysfs_file_stat(curr, filename, drd->d_name);
492
+ }
493
+
494
+ /* Close device directory */
495
+ closedir(dir);
496
+ }
497
+
498
+ /*
499
+ ***************************************************************************
500
+ * Read stats from the sysfs filesystem for the devices entered on the
501
+ * command line.
502
+ *
503
+ * IN:
504
+ * @curr Index in array for current sample statistics.
505
+ ***************************************************************************
506
+ */
507
+ void read_sysfs_dlist_stat(int curr)
508
+ {
509
+ int dev, ok;
510
+ char filename[MAX_PF_NAME];
511
+ char *slash;
512
+ struct io_dlist *st_dev_list_i;
513
+
514
+ /* Every I/O device (or partition) is potentially unregistered */
515
+ set_entries_inactive(iodev_nr, st_hdr_iodev);
516
+
517
+ for (dev = 0; dev < dlist_idx; dev++) {
518
+ st_dev_list_i = st_dev_list + dev;
519
+
520
+ /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
521
+ while ((slash = strchr(st_dev_list_i->dev_name, '/'))) {
522
+ *slash = '!';
523
+ }
524
+
525
+ snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
526
+ SYSFS_BLOCK, st_dev_list_i->dev_name, S_STAT);
527
+ filename[MAX_PF_NAME - 1] = '\0';
528
+
529
+ /* Read device stats */
530
+ ok = read_sysfs_file_stat(curr, filename, st_dev_list_i->dev_name);
531
+
532
+ if (ok && st_dev_list_i->disp_part) {
533
+ /* Also read stats for its partitions */
534
+ read_sysfs_dlist_part_stat(curr, st_dev_list_i->dev_name);
535
+ }
536
+ }
537
+
538
+ /* Free structures corresponding to unregistered devices */
539
+ free_inactive_entries(iodev_nr, st_hdr_iodev);
540
+ }
541
+
542
+ /*
543
+ ***************************************************************************
544
+ * Read stats from the sysfs filesystem for every block devices found.
545
+ *
546
+ * IN:
547
+ * @curr Index in array for current sample statistics.
548
+ ***************************************************************************
549
+ */
550
+ void read_sysfs_stat(int curr)
551
+ {
552
+ DIR *dir;
553
+ struct dirent *drd;
554
+ char filename[MAX_PF_NAME];
555
+ int ok;
556
+
557
+ /* Every I/O device entry is potentially unregistered */
558
+ set_entries_inactive(iodev_nr, st_hdr_iodev);
559
+
560
+ /* Open /sys/block directory */
561
+ if ((dir = opendir(SYSFS_BLOCK)) != NULL) {
562
+
563
+ /* Get current entry */
564
+ while ((drd = readdir(dir)) != NULL) {
565
+ if (!strcmp(drd->d_name, ".") || !strcmp(drd->d_name, ".."))
566
+ continue;
567
+ snprintf(filename, MAX_PF_NAME, "%s/%s/%s",
568
+ SYSFS_BLOCK, drd->d_name, S_STAT);
569
+ filename[MAX_PF_NAME - 1] = '\0';
570
+
571
+ /* If current entry is a directory, try to read its stat file */
572
+ ok = read_sysfs_file_stat(curr, filename, drd->d_name);
573
+
574
+ /*
575
+ * If '-p ALL' was entered on the command line,
576
+ * also try to read stats for its partitions
577
+ */
578
+ if (ok && DISPLAY_PART_ALL(flags)) {
579
+ read_sysfs_dlist_part_stat(curr, drd->d_name);
580
+ }
581
+ }
582
+
583
+ /* Close /sys/block directory */
584
+ closedir(dir);
585
+ }
586
+
587
+ /* Free structures corresponding to unregistered devices */
588
+ free_inactive_entries(iodev_nr, st_hdr_iodev);
589
+ }
590
+
591
+ /*
592
+ ***************************************************************************
593
+ * Read stats from /proc/diskstats.
594
+ *
595
+ * IN:
596
+ * @curr Index in array for current sample statistics.
597
+ ***************************************************************************
598
+ */
599
+ void read_diskstats_stat(int curr)
600
+ {
601
+ FILE *fp;
602
+ char line[256], dev_name[MAX_NAME_LEN];
603
+ char *dm_name;
604
+ struct io_stats sdev;
605
+ int i;
606
+ unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
607
+ unsigned long ios_pgr, tot_ticks, rq_ticks, wr_merges, wr_ticks;
608
+ unsigned long long rd_sec_or_wr_ios, wr_sec;
609
+ char *ioc_dname;
610
+ unsigned int major, minor;
611
+
612
+ /* Every I/O device entry is potentially unregistered */
613
+ set_entries_inactive(iodev_nr, st_hdr_iodev);
614
+
615
+ if ((fp = fopen(DISKSTATS, "r")) == NULL)
616
+ return;
617
+
618
+ while (fgets(line, 256, fp) != NULL) {
619
+
620
+ /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq */
621
+ i = sscanf(line, "%u %u %s %lu %lu %llu %lu %lu %lu %llu %lu %lu %lu %lu",
622
+ &major, &minor, dev_name,
623
+ &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
624
+ &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks);
625
+
626
+ if (i == 14) {
627
+ /* Device or partition */
628
+ if (!dlist_idx && !DISPLAY_PARTITIONS(flags) && !is_device(dev_name))
629
+ continue;
630
+ sdev.rd_ios = rd_ios;
631
+ sdev.rd_merges = rd_merges_or_rd_sec;
632
+ sdev.rd_sectors = rd_sec_or_wr_ios;
633
+ sdev.rd_ticks = rd_ticks_or_wr_sec;
634
+ sdev.wr_ios = wr_ios;
635
+ sdev.wr_merges = wr_merges;
636
+ sdev.wr_sectors = wr_sec;
637
+ sdev.wr_ticks = wr_ticks;
638
+ sdev.ios_pgr = ios_pgr;
639
+ sdev.tot_ticks = tot_ticks;
640
+ sdev.rq_ticks = rq_ticks;
641
+ }
642
+ else if (i == 7) {
643
+ /* Partition without extended statistics */
644
+ if (DISPLAY_EXTENDED(flags) ||
645
+ (!dlist_idx && !DISPLAY_PARTITIONS(flags)))
646
+ continue;
647
+
648
+ sdev.rd_ios = rd_ios;
649
+ sdev.rd_sectors = rd_merges_or_rd_sec;
650
+ sdev.wr_ios = rd_sec_or_wr_ios;
651
+ sdev.wr_sectors = rd_ticks_or_wr_sec;
652
+ }
653
+ else
654
+ /* Unknown entry: Ignore it */
655
+ continue;
656
+
657
+ if ((ioc_dname = ioc_name(major, minor)) != NULL) {
658
+ if (strcmp(dev_name, ioc_dname) && strcmp(ioc_dname, K_NODEV)) {
659
+ /*
660
+ * No match: Use name generated from sysstat.ioconf data
661
+ * (if different from "nodev") works around known issues
662
+ * with EMC PowerPath.
663
+ */
664
+ strncpy(dev_name, ioc_dname, MAX_NAME_LEN);
665
+ }
666
+ }
667
+
668
+ if ((DISPLAY_DEVMAP_NAME(flags)) && (major == dm_major)) {
669
+ /*
670
+ * If the device is a device mapper device, try to get its
671
+ * assigned name of its logical device.
672
+ */
673
+ dm_name = transform_devmapname(major, minor);
674
+ if (dm_name) {
675
+ strcpy(dev_name, dm_name);
676
+ }
677
+ }
678
+
679
+ save_stats(dev_name, curr, &sdev, iodev_nr, st_hdr_iodev);
680
+ }
681
+ fclose(fp);
682
+
683
+ /* Free structures corresponding to unregistered devices */
684
+ free_inactive_entries(iodev_nr, st_hdr_iodev);
685
+ }
686
+
687
+ /*
688
+ ***************************************************************************
689
+ * Display CPU utilization.
690
+ *
691
+ * IN:
692
+ * @curr Index in array for current sample statistics.
693
+ * @itv Interval of time.
694
+ ***************************************************************************
695
+ */
696
+ void write_cpu_stat(int curr, unsigned long long itv)
697
+ {
698
+ printf("avg-cpu: %%user %%nice %%system %%iowait %%steal %%idle\n");
699
+
700
+ printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n\n",
701
+ ll_sp_value(st_cpu[!curr]->cpu_user, st_cpu[curr]->cpu_user, itv),
702
+ ll_sp_value(st_cpu[!curr]->cpu_nice, st_cpu[curr]->cpu_nice, itv),
703
+ /*
704
+ * Time spent in system mode also includes time spent servicing
705
+ * hard and soft interrupts.
706
+ */
707
+ ll_sp_value(st_cpu[!curr]->cpu_sys + st_cpu[!curr]->cpu_softirq +
708
+ st_cpu[!curr]->cpu_hardirq,
709
+ st_cpu[curr]->cpu_sys + st_cpu[curr]->cpu_softirq +
710
+ st_cpu[curr]->cpu_hardirq, itv),
711
+ ll_sp_value(st_cpu[!curr]->cpu_iowait, st_cpu[curr]->cpu_iowait, itv),
712
+ ll_sp_value(st_cpu[!curr]->cpu_steal, st_cpu[curr]->cpu_steal, itv),
713
+ (st_cpu[curr]->cpu_idle < st_cpu[!curr]->cpu_idle) ?
714
+ 0.0 :
715
+ ll_sp_value(st_cpu[!curr]->cpu_idle, st_cpu[curr]->cpu_idle, itv));
716
+ }
717
+
718
+ /*
719
+ ***************************************************************************
720
+ * Display disk stats header.
721
+ *
722
+ * OUT:
723
+ * @fctr Conversion factor.
724
+ ***************************************************************************
725
+ */
726
+ void write_disk_stat_header(int *fctr)
727
+ {
728
+ if (DISPLAY_EXTENDED(flags)) {
729
+ /* Extended stats */
730
+ printf("Device: rrqm/s wrqm/s r/s w/s");
731
+ if (DISPLAY_MEGABYTES(flags)) {
732
+ printf(" rMB/s wMB/s");
733
+ *fctr = 2048;
734
+ }
735
+ else if (DISPLAY_KILOBYTES(flags)) {
736
+ printf(" rkB/s wkB/s");
737
+ *fctr = 2;
738
+ }
739
+ else {
740
+ printf(" rsec/s wsec/s");
741
+ }
742
+ printf(" avgrq-sz avgqu-sz await r_await w_await svctm %%util\n");
743
+ }
744
+ else {
745
+ /* Basic stats */
746
+ printf("Device: tps");
747
+ if (DISPLAY_KILOBYTES(flags)) {
748
+ printf(" kB_read/s kB_wrtn/s kB_read kB_wrtn\n");
749
+ *fctr = 2;
750
+ }
751
+ else if (DISPLAY_MEGABYTES(flags)) {
752
+ printf(" MB_read/s MB_wrtn/s MB_read MB_wrtn\n");
753
+ *fctr = 2048;
754
+ }
755
+ else {
756
+ printf(" Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn\n");
757
+ }
758
+ }
759
+ }
760
+
761
+ /*
762
+ ***************************************************************************
763
+ * Display extended stats, read from /proc/{diskstats,partitions} or /sys.
764
+ *
765
+ * IN:
766
+ * @curr Index in array for current sample statistics.
767
+ * @itv Interval of time.
768
+ * @fctr Conversion factor.
769
+ * @shi Structures describing the devices and partitions.
770
+ * @ioi Current sample statistics.
771
+ * @ioj Previous sample statistics.
772
+ ***************************************************************************
773
+ */
774
+ void write_ext_stat(int curr, unsigned long long itv, int fctr,
775
+ struct io_hdr_stats *shi, struct io_stats *ioi,
776
+ struct io_stats *ioj)
777
+ {
778
+ struct stats_disk sdc, sdp;
779
+ struct ext_disk_stats xds;
780
+ double r_await, w_await;
781
+
782
+ /*
783
+ * Counters overflows are possible, but don't need to be handled in
784
+ * a special way: the difference is still properly calculated if the
785
+ * result is of the same type as the two values.
786
+ * Exception is field rq_ticks which is incremented by the number of
787
+ * I/O in progress times the number of milliseconds spent doing I/O.
788
+ * But the number of I/O in progress (field ios_pgr) happens to be
789
+ * sometimes negative...
790
+ */
791
+ sdc.nr_ios = ioi->rd_ios + ioi->wr_ios;
792
+ sdp.nr_ios = ioj->rd_ios + ioj->wr_ios;
793
+
794
+ sdc.tot_ticks = ioi->tot_ticks;
795
+ sdp.tot_ticks = ioj->tot_ticks;
796
+
797
+ sdc.rd_ticks = ioi->rd_ticks;
798
+ sdp.rd_ticks = ioj->rd_ticks;
799
+ sdc.wr_ticks = ioi->wr_ticks;
800
+ sdp.wr_ticks = ioj->wr_ticks;
801
+
802
+ sdc.rd_sect = ioi->rd_sectors;
803
+ sdp.rd_sect = ioj->rd_sectors;
804
+ sdc.wr_sect = ioi->wr_sectors;
805
+ sdp.wr_sect = ioj->wr_sectors;
806
+
807
+ compute_ext_disk_stats(&sdc, &sdp, itv, &xds);
808
+
809
+ r_await = (ioi->rd_ios - ioj->rd_ios) ?
810
+ (ioi->rd_ticks - ioj->rd_ticks) /
811
+ ((double) (ioi->rd_ios - ioj->rd_ios)) : 0.0;
812
+ w_await = (ioi->wr_ios - ioj->wr_ios) ?
813
+ (ioi->wr_ticks - ioj->wr_ticks) /
814
+ ((double) (ioi->wr_ios - ioj->wr_ios)) : 0.0;
815
+
816
+ /* DEV rrq/s wrq/s r/s w/s rsec wsec rqsz qusz await r_await w_await svctm %util */
817
+ printf("%-13s %8.2f %8.2f %7.2f %7.2f %8.2f %8.2f %8.2f %8.2f %7.2f %7.2f %7.2f %6.2f %6.2f\n",
818
+ shi->name,
819
+ S_VALUE(ioj->rd_merges, ioi->rd_merges, itv),
820
+ S_VALUE(ioj->wr_merges, ioi->wr_merges, itv),
821
+ S_VALUE(ioj->rd_ios, ioi->rd_ios, itv),
822
+ S_VALUE(ioj->wr_ios, ioi->wr_ios, itv),
823
+ ll_s_value(ioj->rd_sectors, ioi->rd_sectors, itv) / fctr,
824
+ ll_s_value(ioj->wr_sectors, ioi->wr_sectors, itv) / fctr,
825
+ xds.arqsz,
826
+ S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0,
827
+ xds.await,
828
+ r_await,
829
+ w_await,
830
+ /* The ticks output is biased to output 1000 ticks per second */
831
+ xds.svctm,
832
+ /* Again: Ticks in milliseconds */
833
+ xds.util / 10.0);
834
+ }
835
+
836
+ /*
837
+ ***************************************************************************
838
+ * Write basic stats, read from /proc/diskstats or from sysfs.
839
+ *
840
+ * IN:
841
+ * @curr Index in array for current sample statistics.
842
+ * @itv Interval of time.
843
+ * @fctr Conversion factor.
844
+ * @shi Structures describing the devices and partitions.
845
+ * @ioi Current sample statistics.
846
+ * @ioj Previous sample statistics.
847
+ ***************************************************************************
848
+ */
849
+ void write_basic_stat(int curr, unsigned long long itv, int fctr,
850
+ struct io_hdr_stats *shi, struct io_stats *ioi,
851
+ struct io_stats *ioj)
852
+ {
853
+ unsigned long long rd_sec, wr_sec;
854
+
855
+ printf("%-13s", shi->name);
856
+
857
+ /* Print stats coming from /sys or /proc/diskstats */
858
+ rd_sec = ioi->rd_sectors - ioj->rd_sectors;
859
+ if ((ioi->rd_sectors < ioj->rd_sectors) && (ioj->rd_sectors <= 0xffffffff)) {
860
+ rd_sec &= 0xffffffff;
861
+ }
862
+ wr_sec = ioi->wr_sectors - ioj->wr_sectors;
863
+ if ((ioi->wr_sectors < ioj->wr_sectors) && (ioj->wr_sectors <= 0xffffffff)) {
864
+ wr_sec &= 0xffffffff;
865
+ }
866
+
867
+ printf(" %8.2f %12.2f %12.2f %10llu %10llu\n",
868
+ S_VALUE(ioj->rd_ios + ioj->wr_ios, ioi->rd_ios + ioi->wr_ios, itv),
869
+ ll_s_value(ioj->rd_sectors, ioi->rd_sectors, itv) / fctr,
870
+ ll_s_value(ioj->wr_sectors, ioi->wr_sectors, itv) / fctr,
871
+ (unsigned long long) rd_sec / fctr,
872
+ (unsigned long long) wr_sec / fctr);
873
+ }
874
+
875
+ /*
876
+ ***************************************************************************
877
+ * Print everything now (stats and uptime).
878
+ *
879
+ * IN:
880
+ * @curr Index in array for current sample statistics.
881
+ * @rectime Current date and time.
882
+ ***************************************************************************
883
+ */
884
+ void write_stats(int curr, struct tm *rectime)
885
+ {
886
+ int dev, i, fctr = 1;
887
+ unsigned long long itv;
888
+ struct io_hdr_stats *shi;
889
+ struct io_dlist *st_dev_list_i;
890
+
891
+ /* Test stdout */
892
+ TEST_STDOUT(STDOUT_FILENO);
893
+
894
+ /* Print time stamp */
895
+ if (DISPLAY_TIMESTAMP(flags)) {
896
+ if (DISPLAY_ISO(flags)) {
897
+ strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime);
898
+ }
899
+ else {
900
+ strftime(timestamp, sizeof(timestamp), "%x %X", rectime);
901
+ }
902
+ printf("%s\n", timestamp);
903
+ #ifdef DEBUG
904
+ if (DISPLAY_DEBUG(flags)) {
905
+ fprintf(stderr, "%s\n", timestamp);
906
+ }
907
+ #endif
908
+ }
909
+
910
+ /* Interval is multiplied by the number of processors */
911
+ itv = get_interval(uptime[!curr], uptime[curr]);
912
+
913
+ if (DISPLAY_CPU(flags)) {
914
+ #ifdef DEBUG
915
+ if (DISPLAY_DEBUG(flags)) {
916
+ /* Debug output */
917
+ fprintf(stderr, "itv=%llu st_cpu[curr]{ cpu_user=%llu cpu_nice=%llu "
918
+ "cpu_sys=%llu cpu_idle=%llu cpu_iowait=%llu cpu_steal=%llu "
919
+ "cpu_hardirq=%llu cpu_softirq=%llu cpu_guest=%llu }\n",
920
+ itv,
921
+ st_cpu[curr]->cpu_user,
922
+ st_cpu[curr]->cpu_nice,
923
+ st_cpu[curr]->cpu_sys,
924
+ st_cpu[curr]->cpu_idle,
925
+ st_cpu[curr]->cpu_iowait,
926
+ st_cpu[curr]->cpu_steal,
927
+ st_cpu[curr]->cpu_hardirq,
928
+ st_cpu[curr]->cpu_softirq,
929
+ st_cpu[curr]->cpu_guest
930
+ );
931
+ }
932
+ #endif
933
+
934
+ /* Display CPU utilization */
935
+ write_cpu_stat(curr, itv);
936
+ }
937
+
938
+ if (cpu_nr > 1) {
939
+ /* On SMP machines, reduce itv to one processor (see note above) */
940
+ itv = get_interval(uptime0[!curr], uptime0[curr]);
941
+ }
942
+
943
+ if (DISPLAY_DISK(flags)) {
944
+ struct io_stats *ioi, *ioj;
945
+
946
+ shi = st_hdr_iodev;
947
+
948
+ /* Display disk stats header */
949
+ write_disk_stat_header(&fctr);
950
+
951
+ for (i = 0; i < iodev_nr; i++, shi++) {
952
+ if (shi->used) {
953
+
954
+ if (dlist_idx && !HAS_SYSFS(flags)) {
955
+ /*
956
+ * With sysfs, only stats for the requested
957
+ * devices are read.
958
+ * With /proc/diskstats, stats for
959
+ * every device are read. Thus we need to check
960
+ * if stats for current device are to be displayed.
961
+ */
962
+ for (dev = 0; dev < dlist_idx; dev++) {
963
+ st_dev_list_i = st_dev_list + dev;
964
+ if (!strcmp(shi->name, st_dev_list_i->dev_name))
965
+ break;
966
+ }
967
+ if (dev == dlist_idx)
968
+ /* Device not found in list: Don't display it */
969
+ continue;
970
+ }
971
+
972
+ ioi = st_iodev[curr] + i;
973
+ ioj = st_iodev[!curr] + i;
974
+
975
+ if (!DISPLAY_UNFILTERED(flags)) {
976
+ if (!ioi->rd_ios && !ioi->wr_ios)
977
+ continue;
978
+ }
979
+
980
+ if (DISPLAY_ZERO_OMIT(flags)) {
981
+ if ((ioi->rd_ios == ioj->rd_ios) &&
982
+ (ioi->wr_ios == ioj->wr_ios))
983
+ /* No activity: Ignore it */
984
+ continue;
985
+ }
986
+ #ifdef DEBUG
987
+ if (DISPLAY_DEBUG(flags)) {
988
+ /* Debug output */
989
+ fprintf(stderr, "name=%s itv=%llu fctr=%d ioi{ rd_sectors=%llu "
990
+ "wr_sectors=%llu rd_ios=%lu rd_merges=%lu rd_ticks=%lu "
991
+ "wr_ios=%lu wr_merges=%lu wr_ticks=%lu ios_pgr=%lu tot_ticks=%lu "
992
+ "rq_ticks=%lu dk_drive=%lu dk_drive_rblk=%lu dk_drive_wblk=%lu }\n",
993
+ shi->name,
994
+ itv,
995
+ fctr,
996
+ ioi->rd_sectors,
997
+ ioi->wr_sectors,
998
+ ioi->rd_ios,
999
+ ioi->rd_merges,
1000
+ ioi->rd_ticks,
1001
+ ioi->wr_ios,
1002
+ ioi->wr_merges,
1003
+ ioi->wr_ticks,
1004
+ ioi->ios_pgr,
1005
+ ioi->tot_ticks,
1006
+ ioi->rq_ticks,
1007
+ ioi->dk_drive,
1008
+ ioi->dk_drive_rblk,
1009
+ ioi->dk_drive_wblk
1010
+ );
1011
+ }
1012
+ #endif
1013
+
1014
+ if (DISPLAY_EXTENDED(flags)) {
1015
+ write_ext_stat(curr, itv, fctr, shi, ioi, ioj);
1016
+ }
1017
+ else {
1018
+ write_basic_stat(curr, itv, fctr, shi, ioi, ioj);
1019
+ }
1020
+ }
1021
+ }
1022
+ printf("\n");
1023
+ }
1024
+ }
1025
+
1026
+ /*
1027
+ ***************************************************************************
1028
+ * Main loop: Read I/O stats from the relevant sources and display them.
1029
+ *
1030
+ * IN:
1031
+ * @count Number of lines of stats to print.
1032
+ * @rectime Current date and time.
1033
+ ***************************************************************************
1034
+ */
1035
+ void rw_io_stat_loop(long int count, struct tm *rectime)
1036
+ {
1037
+ int curr = 1;
1038
+
1039
+ /* Don't buffer data if redirected to a pipe */
1040
+ setbuf(stdout, NULL);
1041
+
1042
+ do {
1043
+ if (cpu_nr > 1) {
1044
+ /*
1045
+ * Read system uptime (only for SMP machines).
1046
+ * Init uptime0. So if /proc/uptime cannot fill it,
1047
+ * this will be done by /proc/stat.
1048
+ */
1049
+ uptime0[curr] = 0;
1050
+ read_uptime(&(uptime0[curr]));
1051
+ }
1052
+
1053
+ /*
1054
+ * Read stats for CPU "all" and 0.
1055
+ * Note that stats for CPU 0 are not used per se. It only makes
1056
+ * read_stat_cpu() fill uptime0.
1057
+ */
1058
+ read_stat_cpu(st_cpu[curr], 2, &(uptime[curr]), &(uptime0[curr]));
1059
+
1060
+ if (dlist_idx) {
1061
+ /*
1062
+ * A device or partition name was entered on the command line,
1063
+ * with or without -p option (but not -p ALL).
1064
+ */
1065
+ if (HAS_DISKSTATS(flags) && !DISPLAY_PARTITIONS(flags)) {
1066
+ read_diskstats_stat(curr);
1067
+ }
1068
+ else if (HAS_SYSFS(flags)) {
1069
+ read_sysfs_dlist_stat(curr);
1070
+ }
1071
+ }
1072
+ else {
1073
+ /*
1074
+ * No devices nor partitions entered on the command line
1075
+ * (for example if -p ALL was used).
1076
+ */
1077
+ if (HAS_DISKSTATS(flags)) {
1078
+ read_diskstats_stat(curr);
1079
+ }
1080
+ else if (HAS_SYSFS(flags)) {
1081
+ read_sysfs_stat(curr);
1082
+ }
1083
+ }
1084
+
1085
+ /* Get time */
1086
+ get_localtime(rectime);
1087
+
1088
+ /* Print results */
1089
+ write_stats(curr, rectime);
1090
+
1091
+ if (count > 0) {
1092
+ count--;
1093
+ }
1094
+ if (count) {
1095
+ curr ^= 1;
1096
+ pause();
1097
+ }
1098
+ }
1099
+ while (count);
1100
+ }