extended disk statistics
Costa Tsaousis (ktsaou) committed
Oct 26, 2015 at 01:23 UTC
b9d27c7c18d312616077e1ac601a459985e60e10
8 files changed
+214
-67
src/common.c
+17
@@ -170,3 +170,20 @@ int fd_is_valid(int fd) {
170
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
171
}
172
173
+/*
174
+ ***************************************************************************
175
+ * Get number of clock ticks per second.
176
+ ***************************************************************************
177
+ */
178
+unsigned int hz;
179
+
180
+void get_HZ(void)
181
+{
182
+ long ticks;
183
+
184
+ if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
185
+ perror("sysconf");
186
+ }
187
+
188
+ hz = (unsigned int) ticks;
189
+}
src/common.h
+6
@@ -28,4 +28,10 @@ extern char *global_host_prefix;
28
#define unlikely(x) (x)
29
#endif
30
31
+
32
+/* Number of ticks per second */
33
+#define HZ hz
34
+extern unsigned int hz;
35
+extern void get_HZ(void);
36
+
37
#endif /* NETDATA_COMMON_H */
src/main.c
+3
@@ -168,6 +168,9 @@ int main(int argc, char **argv)
168
int config_loaded = 0;
169
int dont_fork = 0;
170
171
+ // global initialization
172
+ get_HZ();
173
+
174
// set the name for logging
175
program_name = "netdata";
176
src/plugin_tc.c
+8
-8
@@ -237,23 +237,23 @@ static void tc_device_commit(struct tc_device *d)
237
if(!c->updated) continue;
238
239
if(c->isleaf && c->hasparent) {
240
- if(rrddim_set(st, c->id, c->bytes) != 0) {
240
+ RRDDIM *rd = rrddim_find(st, c->id);
241
+
242
+ if(!rd) {
243
debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s'", st->id, c->id, c->name);
244
245
// new class, we have to add it
244
- rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
245
- rrddim_set(st, c->id, c->bytes);
246
+ rd = rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
247
}
248
else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", st->id, c->id);
249
250
+ rrddim_set_by_pointer(st, rd, c->bytes);
251
+
252
// if it has a name, different to the id
253
if(c->name) {
254
// update the rrd dimension with the new name
252
- RRDDIM *rd = rrddim_find(st, c->id);
253
- if(rd) {
254
- debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name);
255
- rrddim_set_name(st, rd, c->name);
256
- }
255
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name);
256
+ rrddim_set_name(st, rd, c->name);
257
258
free(c->name);
259
c->name = NULL;
src/proc_diskstats.c
+163
-41
@@ -19,7 +19,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
19
static procfile *ff = NULL;
20
static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
21
static int enable_new_disks = -1;
22
- static int do_io = -1, do_ops = -1, do_merged_ops = -1, do_iotime = -1, do_cur_ops = -1;
22
+ static int do_io = -1, do_ops = -1, do_merged_ops = -1, do_iotime = -1, do_cur_ops = -1, do_util = -1, do_qsize = -1;
23
24
if(enable_new_disks == -1) enable_new_disks = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", 1);
25
@@ -28,8 +28,8 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
28
if(do_merged_ops == -1) do_merged_ops = config_get_boolean("plugin:proc:/proc/diskstats", "merged operations for all disks", 1);
29
if(do_iotime == -1) do_iotime = config_get_boolean("plugin:proc:/proc/diskstats", "i/o time for all disks", 1);
30
if(do_cur_ops == -1) do_cur_ops = config_get_boolean("plugin:proc:/proc/diskstats", "current operations for all disks", 1);
31
-
32
- if(dt) {};
31
+ if(do_util == -1) do_util = config_get_boolean("plugin:proc:/proc/diskstats", "utilization percentage for all disks", 1);
32
+ if(do_qsize == -1) do_qsize = config_get_boolean("plugin:proc:/proc/diskstats", "queue size for all disks", 1);
33
34
if(!ff) {
35
char filename[FILENAME_MAX + 1];
@@ -57,23 +57,56 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
57
writes = 0, writes_merged = 0, writesectors = 0, writems = 0,
58
currentios = 0, iosms = 0, wiosms = 0;
59
60
+ unsigned long long last_reads = 0, last_readsectors = 0, last_readms = 0,
61
+ last_writes = 0, last_writesectors = 0, last_writems = 0,
62
+ last_iosms = 0;
63
+
64
words = procfile_linewords(ff, l);
65
if(words < 14) continue;
66
67
major = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
68
minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
69
disk = procfile_lineword(ff, l, 2);
66
- reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
67
- reads_merged = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
68
- readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
69
- readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
70
- writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
71
- writes_merged = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
72
- writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
73
- writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
74
- currentios = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
75
- iosms = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
76
- wiosms = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
70
+
71
+ // # of reads completed # of writes completed
72
+ // This is the total number of reads or writes completed successfully.
73
+ reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10); // rd_ios
74
+ writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10); // wr_ios
75
+
76
+ // # of reads merged # of writes merged
77
+ // Reads and writes which are adjacent to each other may be merged for
78
+ // efficiency. Thus two 4K reads may become one 8K read before it is
79
+ // ultimately handed to the disk, and so it will be counted (and queued)
80
+ reads_merged = strtoull(procfile_lineword(ff, l, 4), NULL, 10); // rd_merges_or_rd_sec
81
+ writes_merged = strtoull(procfile_lineword(ff, l, 8), NULL, 10); // wr_merges
82
+
83
+ // # of sectors read # of sectors written
84
+ // This is the total number of sectors read or written successfully.
85
+ readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10); // rd_sec_or_wr_ios
86
+ writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10); // wr_sec
87
+
88
+ // # of milliseconds spent reading # of milliseconds spent writing
89
+ // This is the total number of milliseconds spent by all reads or writes (as
90
+ // measured from __make_request() to end_that_request_last()).
91
+ readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10); // rd_ticks_or_wr_sec
92
+ writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10); // wr_ticks
93
+
94
+ // # of I/Os currently in progress
95
+ // The only field that should go to zero. Incremented as requests are
96
+ // given to appropriate struct request_queue and decremented as they finish.
97
+ currentios = strtoull(procfile_lineword(ff, l, 11), NULL, 10); // ios_pgr
98
+
99
+ // # of milliseconds spent doing I/Os
100
+ // This field increases so long as field currentios is nonzero.
101
+ iosms = strtoull(procfile_lineword(ff, l, 12), NULL, 10); // tot_ticks
102
+
103
+ // weighted # of milliseconds spent doing I/Os
104
+ // This field is incremented at each I/O start, I/O completion, I/O
105
+ // merge, or read of these stats by the number of I/Os in progress
106
+ // (field currentios) times the number of milliseconds spent doing I/O since the
107
+ // last update of this field. This can provide an easy measure of both
108
+ // I/O completion time and the backlog that may be accumulating.
109
+ wiosms = strtoull(procfile_lineword(ff, l, 13), NULL, 10); // rq_ticks
110
111
int def_enabled = 0;
112
@@ -205,12 +238,12 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
238
239
// --------------------------------------------------------------------
240
241
+ int sector_size = 512;
242
if(do_io) {
243
st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
244
if(!st) {
245
char tf[FILENAME_MAX + 1], *t;
246
char ssfilename[FILENAME_MAX + 1];
213
- int sector_size = 512;
247
248
strncpy(tf, disk, FILENAME_MAX);
249
tf[FILENAME_MAX] = '\0';
@@ -242,10 +275,10 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
275
rrddim_add(st, "reads", NULL, sector_size, 1024 * update_every, RRDDIM_INCREMENTAL);
276
rrddim_add(st, "writes", NULL, sector_size * -1, 1024 * update_every, RRDDIM_INCREMENTAL);
277
}
245
- else rrdset_next(st);
278
+ else rrdset_next_usec(st, dt);
279
247
- rrddim_set(st, "reads", readsectors);
248
- rrddim_set(st, "writes", writesectors);
280
+ last_readsectors = rrddim_set(st, "reads", readsectors);
281
+ last_writesectors = rrddim_set(st, "writes", writesectors);
282
rrdset_done(st);
283
}
284
@@ -260,25 +293,73 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
293
rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
294
rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
295
}
263
- else rrdset_next(st);
296
+ else rrdset_next_usec(st, dt);
297
+
298
+ last_reads = rrddim_set(st, "reads", reads);
299
+ last_writes = rrddim_set(st, "writes", writes);
300
+ rrdset_done(st);
301
+ }
302
+
303
+ // --------------------------------------------------------------------
304
+
305
+ if(do_util) {
306
+ st = rrdset_find_bytype("disk_util", disk);
307
+ if(!st) {
308
+ st = rrdset_create("disk_util", disk, NULL, disk, "Disk Utilization", "%", 2002, update_every, RRDSET_TYPE_AREA);
309
+ st->isdetail = 1;
310
+
311
+ rrddim_add(st, "utilization", NULL, 1, 10 * update_every, RRDDIM_INCREMENTAL);
312
+ }
313
+ else rrdset_next_usec(st, dt);
314
+
315
+ last_iosms = rrddim_set(st, "utilization", iosms);
316
+ rrdset_done(st);
317
+ }
318
+
319
+ // --------------------------------------------------------------------
320
+
321
+ if(do_qsize) {
322
+ st = rrdset_find_bytype("disk_qsize", disk);
323
+ if(!st) {
324
+ st = rrdset_create("disk_qsize", disk, NULL, disk, "Disk Average Queue Size", "queue size", 2003, update_every, RRDSET_TYPE_AREA);
325
+ st->isdetail = 1;
326
+
327
+ rrddim_add(st, "queue_size", NULL, 1, 1000 * update_every, RRDDIM_INCREMENTAL);
328
+ }
329
+ else rrdset_next_usec(st, dt);
330
265
- rrddim_set(st, "reads", reads);
266
- rrddim_set(st, "writes", writes);
331
+ rrddim_set(st, "queue_size", wiosms);
332
rrdset_done(st);
333
}
269
-
334
+
335
+ // --------------------------------------------------------------------
336
+
337
+ if(do_cur_ops) {
338
+ st = rrdset_find_bytype("disk_cur_ops", disk);
339
+ if(!st) {
340
+ st = rrdset_create("disk_cur_ops", disk, NULL, disk, "Current Disk I/O operations", "operations", 2004, update_every, RRDSET_TYPE_LINE);
341
+ st->isdetail = 1;
342
+
343
+ rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
344
+ }
345
+ else rrdset_next_usec(st, dt);
346
+
347
+ rrddim_set(st, "operations", currentios);
348
+ rrdset_done(st);
349
+ }
350
+
351
// --------------------------------------------------------------------
352
353
if(do_merged_ops) {
354
st = rrdset_find_bytype("disk_merged_ops", disk);
355
if(!st) {
275
- st = rrdset_create("disk_merged_ops", disk, NULL, disk, "Merged Disk Operations", "operations/s", 2010, update_every, RRDSET_TYPE_LINE);
356
+ st = rrdset_create("disk_merged_ops", disk, NULL, disk, "Disk Merged Operations", "operations/s", 2021, update_every, RRDSET_TYPE_LINE);
357
st->isdetail = 1;
358
359
rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
360
rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
361
}
281
- else rrdset_next(st);
362
+ else rrdset_next_usec(st, dt);
363
364
rrddim_set(st, "reads", reads_merged);
365
rrddim_set(st, "writes", writes_merged);
@@ -290,38 +371,79 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
371
if(do_iotime) {
372
st = rrdset_find_bytype("disk_iotime", disk);
373
if(!st) {
293
- st = rrdset_create("disk_iotime", disk, NULL, disk, "Disk I/O Time", "milliseconds/s", 2005, update_every, RRDSET_TYPE_LINE);
374
+ st = rrdset_create("disk_iotime", disk, NULL, disk, "Disk I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
375
st->isdetail = 1;
376
377
rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
378
rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
298
- rrddim_add(st, "latency", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
299
- rrddim_add(st, "weighted", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
379
}
301
- else rrdset_next(st);
380
+ else rrdset_next_usec(st, dt);
381
303
- rrddim_set(st, "reads", readms);
304
- rrddim_set(st, "writes", writems);
305
- rrddim_set(st, "latency", iosms);
306
- rrddim_set(st, "weighted", wiosms);
382
+ last_readms = rrddim_set(st, "reads", readms);
383
+ last_writems = rrddim_set(st, "writes", writems);
384
rrdset_done(st);
385
}
386
387
// --------------------------------------------------------------------
388
+ // calculate differential charts
389
+ // only if this is not the first time we run
390
+
391
+ if(dt) {
392
+ if(do_iotime && do_ops) {
393
+ st = rrdset_find_bytype("disk_await", disk);
394
+ if(!st) {
395
+ st = rrdset_create("disk_await", disk, NULL, disk, "Average Wait Time", "ms per operation", 2005, update_every, RRDSET_TYPE_AREA);
396
+ st->isdetail = 1;
397
+
398
+ rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
399
+ rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
400
+ }
401
+ else rrdset_next_usec(st, dt);
402
312
- if(do_cur_ops) {
313
- st = rrdset_find_bytype("disk_cur_ops", disk);
314
- if(!st) {
315
- st = rrdset_create("disk_cur_ops", disk, NULL, disk, "Current Disk I/O operations", "operations", 2004, update_every, RRDSET_TYPE_LINE);
316
- st->isdetail = 1;
403
+ rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
404
+ rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
405
+ rrdset_done(st);
406
+ }
407
318
- rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
408
+ if(do_io && do_ops) {
409
+ st = rrdset_find_bytype("disk_avgsz", disk);
410
+ if(!st) {
411
+ st = rrdset_create("disk_avgsz", disk, NULL, disk, "Average Operation Size", "kilobytes", 2006, update_every, RRDSET_TYPE_AREA);
412
+ st->isdetail = 1;
413
+
414
+ rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_ABSOLUTE);
415
+ rrddim_add(st, "writes", NULL, -sector_size, 1024, RRDDIM_ABSOLUTE);
416
+ }
417
+ else rrdset_next_usec(st, dt);
418
+
419
+ rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
420
+ rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
421
+ rrdset_done(st);
422
}
320
- else rrdset_next(st);
423
322
- rrddim_set(st, "operations", currentios);
323
- rrdset_done(st);
424
+ if(do_util && do_ops) {
425
+ st = rrdset_find_bytype("disk_svctm", disk);
426
+ if(!st) {
427
+ st = rrdset_create("disk_svctm", disk, NULL, disk, "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_AREA);
428
+ st->isdetail = 1;
429
+
430
+ rrddim_add(st, "svctm", NULL, -1, 1, RRDDIM_ABSOLUTE);
431
+ }
432
+ else rrdset_next_usec(st, dt);
433
+
434
+ rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (iosms - last_iosms) / ((reads - last_reads) + (writes - last_writes)) : 0);
435
+ rrdset_done(st);
436
+ }
437
}
438
+
439
+ // TODO
440
+ // the differential (readms + writems) / (reads + writes) = average wait time
441
+ // the differential (readms ) / (reads ) = the average wait time for reads
442
+ // the differential (writems) / (writes) = the average wait time for writes
443
+
444
+ // similarly for bytes to find the average read and write size
445
+
446
+ // svctm = % of utilization per request (reads + writes)
447
}
448
449
return 0;
src/rrd.c
+12
-8
@@ -745,33 +745,37 @@ int rrddim_unhide(RRDSET *st, const char *id)
745
return 0;
746
}
747
748
-void rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
748
+collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
749
{
750
debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
751
752
gettimeofday(&rd->last_collected_time, NULL);
753
rd->collected_value = value;
754
rd->updated = 1;
755
+
756
+ return rd->last_collected_value;
757
}
758
757
-int rrddim_set(RRDSET *st, const char *id, collected_number value)
759
+collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
760
{
761
RRDDIM *rd = rrddim_find(st, id);
762
if(unlikely(!rd)) {
763
error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
762
- return 1;
764
+ return 0;
765
}
766
765
- rrddim_set_by_pointer(st, rd, value);
766
- return 0;
767
+ return rrddim_set_by_pointer(st, rd, value);
768
}
769
770
void rrdset_next_usec(RRDSET *st, unsigned long long microseconds)
771
{
771
- debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
772
+ if(!microseconds) rrdset_next(st);
773
+ else {
774
+ debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
775
773
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
774
- st->usec_since_last_update = microseconds;
776
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
777
+ st->usec_since_last_update = microseconds;
778
+ }
779
}
780
781
void rrdset_next(RRDSET *st)
src/rrd.h
+2
-5
@@ -315,10 +315,7 @@ extern RRDDIM *rrddim_find(RRDSET *st, const char *id);
315
extern int rrddim_hide(RRDSET *st, const char *id);
316
extern int rrddim_unhide(RRDSET *st, const char *id);
317
318
-extern void rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value);
319
-extern int rrddim_set(RRDSET *st, const char *id, collected_number value);
320
-
321
-
322
-
318
+extern collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value);
319
+extern collected_number rrddim_set(RRDSET *st, const char *id, collected_number value);
320
321
#endif /* NETDATA_RRD_H */
src/rrd2json.c
+3
-5
@@ -383,11 +383,6 @@ RRDR *rrd2rrdr(RRDSET *st, long points, time_t after, time_t before, int group_m
383
if(group <= 0) group = 1;
384
if(duration / group > points) group++;
385
386
- // align timestamps to group
387
- before -= before % group;
388
- after -= after % group;
389
- duration = before - after;
390
-
386
// error("NEW: points=%d after=%d before=%d group=%d, duration=%d", points, after, before, group, duration);
387
388
// Now we have:
@@ -459,6 +454,9 @@ RRDR *rrd2rrdr(RRDSET *st, long points, time_t after, time_t before, int group_m
454
add_this = 0,
455
stop_now = 0;
456
457
+ // align to group for proper panning of data
458
+ t -= t % group;
459
+
460
time_t now = rrdset_slot2time(st, t),
461
dt = st->update_every,
462
group_start_t = 0;