perfmonger 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }