]> arthur.barton.de Git - netdata.git/commitdiff
extended disk statistics
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 25 Oct 2015 23:23:50 +0000 (01:23 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 25 Oct 2015 23:23:50 +0000 (01:23 +0200)
src/common.c
src/common.h
src/main.c
src/plugin_tc.c
src/proc_diskstats.c
src/rrd.c
src/rrd.h
src/rrd2json.c

index 9240e3c9768d7ca6a50a77a4180042f8cf06a1e1..825c102235ac16829e4f8cc3e1a354dba60d8da8 100755 (executable)
@@ -170,3 +170,20 @@ int fd_is_valid(int fd) {
     return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
 }
 
+/*
+ ***************************************************************************
+ * Get number of clock ticks per second.
+ ***************************************************************************
+ */
+unsigned int hz;
+
+void get_HZ(void)
+{
+       long ticks;
+
+       if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
+               perror("sysconf");
+       }
+
+       hz = (unsigned int) ticks;
+}
index 6a4ac03b5a384e1261e25eec4c055f8116e27925..6be1b530ab62d45302c3024025bf952af04ad259 100755 (executable)
@@ -28,4 +28,10 @@ extern char *global_host_prefix;
 #define unlikely(x)     (x)
 #endif
 
+
+/* Number of ticks per second */
+#define HZ             hz
+extern unsigned int hz;
+extern void get_HZ(void);
+
 #endif /* NETDATA_COMMON_H */
index 0fb0f0d9b7507a8a29475bb6f9609f5fee5458dc..3446b2a16cfa0d23ede970ded42d118ce9d107da 100755 (executable)
@@ -168,6 +168,9 @@ int main(int argc, char **argv)
        int config_loaded = 0;
        int dont_fork = 0;
 
+       // global initialization
+       get_HZ();
+
        // set the name for logging
        program_name = "netdata";
 
index 3f6665a945a901af9a216b94ba880c62deda0876..c4fc08ae7040e36382406f6ce116b8c508b5d047 100755 (executable)
@@ -237,23 +237,23 @@ static void tc_device_commit(struct tc_device *d)
                        if(!c->updated) continue;
 
                        if(c->isleaf && c->hasparent) {
-                               if(rrddim_set(st, c->id, c->bytes) != 0) {
+                               RRDDIM *rd = rrddim_find(st, c->id);
+
+                               if(!rd) {
                                        debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s'", st->id, c->id, c->name);
                                        
                                        // new class, we have to add it
-                                       rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
-                                       rrddim_set(st, c->id, c->bytes);
+                                       rd = rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
                                }
                                else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", st->id, c->id);
 
+                               rrddim_set_by_pointer(st, rd, c->bytes);
+
                                // if it has a name, different to the id
                                if(c->name) {
                                        // update the rrd dimension with the new name
-                                       RRDDIM *rd = rrddim_find(st, c->id);
-                                       if(rd) {
-                                               debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name);
-                                               rrddim_set_name(st, rd, c->name);
-                                       }
+                                       debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name);
+                                       rrddim_set_name(st, rd, c->name);
 
                                        free(c->name);
                                        c->name = NULL;
index 5bbd3d58c8b675f8f991bb8f52d46af0739fac8f..5d8cc273c25d001c9788c722cd7da219ae9e952a 100755 (executable)
@@ -19,7 +19,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
        static procfile *ff = NULL;
        static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
        static int enable_new_disks = -1;
-       static int do_io = -1, do_ops = -1, do_merged_ops = -1, do_iotime = -1, do_cur_ops = -1;
+       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;
 
        if(enable_new_disks == -1)      enable_new_disks = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", 1);
 
@@ -28,8 +28,8 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
        if(do_merged_ops == -1) do_merged_ops   = config_get_boolean("plugin:proc:/proc/diskstats", "merged operations for all disks", 1);
        if(do_iotime == -1)             do_iotime               = config_get_boolean("plugin:proc:/proc/diskstats", "i/o time for all disks", 1);
        if(do_cur_ops == -1)    do_cur_ops              = config_get_boolean("plugin:proc:/proc/diskstats", "current operations for all disks", 1);
-
-       if(dt) {};
+       if(do_util == -1)               do_util                 = config_get_boolean("plugin:proc:/proc/diskstats", "utilization percentage for all disks", 1);
+       if(do_qsize == -1)              do_qsize                = config_get_boolean("plugin:proc:/proc/diskstats", "queue size for all disks", 1);
 
        if(!ff) {
                char filename[FILENAME_MAX + 1];
@@ -57,23 +57,56 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                                                        writes = 0, writes_merged = 0, writesectors = 0, writems = 0,
                                                        currentios = 0, iosms = 0, wiosms = 0;
 
+               unsigned long long      last_reads = 0,  last_readsectors = 0,  last_readms = 0,
+                                                       last_writes = 0, last_writesectors = 0, last_writems = 0,
+                                                       last_iosms = 0;
+
                words = procfile_linewords(ff, l);
                if(words < 14) continue;
 
                major                   = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
                minor                   = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
                disk                    = procfile_lineword(ff, l, 2);
-               reads                   = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-               reads_merged    = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-               readsectors     = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-               readms                  = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-               writes                  = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-               writes_merged   = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-               writesectors    = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-               writems                 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-               currentios              = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-               iosms                   = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-               wiosms                  = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+
+               // # of reads completed # of writes completed
+               // This is the total number of reads or writes completed successfully.
+               reads                   = strtoull(procfile_lineword(ff, l, 3), NULL, 10);      // rd_ios
+               writes                  = strtoull(procfile_lineword(ff, l, 7), NULL, 10);      // wr_ios
+
+               // # of reads merged # of writes merged
+               // Reads and writes which are adjacent to each other may be merged for
+           // efficiency.  Thus two 4K reads may become one 8K read before it is
+           // ultimately handed to the disk, and so it will be counted (and queued)
+               reads_merged    = strtoull(procfile_lineword(ff, l, 4), NULL, 10);      // rd_merges_or_rd_sec
+               writes_merged   = strtoull(procfile_lineword(ff, l, 8), NULL, 10);      // wr_merges
+
+               // # of sectors read # of sectors written
+               // This is the total number of sectors read or written successfully.
+               readsectors     = strtoull(procfile_lineword(ff, l, 5), NULL, 10);      // rd_sec_or_wr_ios
+               writesectors    = strtoull(procfile_lineword(ff, l, 9), NULL, 10);      // wr_sec
+
+               // # of milliseconds spent reading # of milliseconds spent writing
+               // This is the total number of milliseconds spent by all reads or writes (as
+               // measured from __make_request() to end_that_request_last()).
+               readms                  = strtoull(procfile_lineword(ff, l, 6), NULL, 10);      // rd_ticks_or_wr_sec
+               writems                 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);     // wr_ticks
+
+               // # of I/Os currently in progress
+               // The only field that should go to zero. Incremented as requests are
+               // given to appropriate struct request_queue and decremented as they finish.
+               currentios              = strtoull(procfile_lineword(ff, l, 11), NULL, 10);     // ios_pgr
+
+               // # of milliseconds spent doing I/Os
+               // This field increases so long as field currentios is nonzero.
+               iosms                   = strtoull(procfile_lineword(ff, l, 12), NULL, 10);     // tot_ticks
+
+               // weighted # of milliseconds spent doing I/Os
+               // This field is incremented at each I/O start, I/O completion, I/O
+               // merge, or read of these stats by the number of I/Os in progress
+               // (field currentios) times the number of milliseconds spent doing I/O since the
+               // last update of this field.  This can provide an easy measure of both
+               // I/O completion time and the backlog that may be accumulating.
+               wiosms                  = strtoull(procfile_lineword(ff, l, 13), NULL, 10);     // rq_ticks
 
                int def_enabled = 0;
 
@@ -205,12 +238,12 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
 
                // --------------------------------------------------------------------
 
+               int sector_size = 512;
                if(do_io) {
                        st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
                        if(!st) {
                                char tf[FILENAME_MAX + 1], *t;
                                char ssfilename[FILENAME_MAX + 1];
-                               int sector_size = 512;
 
                                strncpy(tf, disk, FILENAME_MAX);
                                tf[FILENAME_MAX] = '\0';
@@ -242,10 +275,10 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                                rrddim_add(st, "reads", NULL, sector_size, 1024 * update_every, RRDDIM_INCREMENTAL);
                                rrddim_add(st, "writes", NULL, sector_size * -1, 1024 * update_every, RRDDIM_INCREMENTAL);
                        }
-                       else rrdset_next(st);
+                       else rrdset_next_usec(st, dt);
 
-                       rrddim_set(st, "reads", readsectors);
-                       rrddim_set(st, "writes", writesectors);
+                       last_readsectors  = rrddim_set(st, "reads", readsectors);
+                       last_writesectors = rrddim_set(st, "writes", writesectors);
                        rrdset_done(st);
                }
 
@@ -260,25 +293,73 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                                rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
                                rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
                        }
-                       else rrdset_next(st);
+                       else rrdset_next_usec(st, dt);
+
+                       last_reads  = rrddim_set(st, "reads", reads);
+                       last_writes = rrddim_set(st, "writes", writes);
+                       rrdset_done(st);
+               }
+
+               // --------------------------------------------------------------------
+
+               if(do_util) {
+                       st = rrdset_find_bytype("disk_util", disk);
+                       if(!st) {
+                               st = rrdset_create("disk_util", disk, NULL, disk, "Disk Utilization", "%", 2002, update_every, RRDSET_TYPE_AREA);
+                               st->isdetail = 1;
+
+                               rrddim_add(st, "utilization", NULL, 1, 10 * update_every, RRDDIM_INCREMENTAL);
+                       }
+                       else rrdset_next_usec(st, dt);
+
+                       last_iosms = rrddim_set(st, "utilization", iosms);
+                       rrdset_done(st);
+               }
+
+               // --------------------------------------------------------------------
+
+               if(do_qsize) {
+                       st = rrdset_find_bytype("disk_qsize", disk);
+                       if(!st) {
+                               st = rrdset_create("disk_qsize", disk, NULL, disk, "Disk Average Queue Size", "queue size", 2003, update_every, RRDSET_TYPE_AREA);
+                               st->isdetail = 1;
+
+                               rrddim_add(st, "queue_size", NULL, 1, 1000 * update_every, RRDDIM_INCREMENTAL);
+                       }
+                       else rrdset_next_usec(st, dt);
 
-                       rrddim_set(st, "reads", reads);
-                       rrddim_set(st, "writes", writes);
+                       rrddim_set(st, "queue_size", wiosms);
                        rrdset_done(st);
                }
-               
+
+               // --------------------------------------------------------------------
+
+               if(do_cur_ops) {
+                       st = rrdset_find_bytype("disk_cur_ops", disk);
+                       if(!st) {
+                               st = rrdset_create("disk_cur_ops", disk, NULL, disk, "Current Disk I/O operations", "operations", 2004, update_every, RRDSET_TYPE_LINE);
+                               st->isdetail = 1;
+
+                               rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                       }
+                       else rrdset_next_usec(st, dt);
+
+                       rrddim_set(st, "operations", currentios);
+                       rrdset_done(st);
+               }
+
                // --------------------------------------------------------------------
 
                if(do_merged_ops) {
                        st = rrdset_find_bytype("disk_merged_ops", disk);
                        if(!st) {
-                               st = rrdset_create("disk_merged_ops", disk, NULL, disk, "Merged Disk Operations", "operations/s", 2010, update_every, RRDSET_TYPE_LINE);
+                               st = rrdset_create("disk_merged_ops", disk, NULL, disk, "Disk Merged Operations", "operations/s", 2021, update_every, RRDSET_TYPE_LINE);
                                st->isdetail = 1;
 
                                rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
                                rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
                        }
-                       else rrdset_next(st);
+                       else rrdset_next_usec(st, dt);
 
                        rrddim_set(st, "reads", reads_merged);
                        rrddim_set(st, "writes", writes_merged);
@@ -290,38 +371,79 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                if(do_iotime) {
                        st = rrdset_find_bytype("disk_iotime", disk);
                        if(!st) {
-                               st = rrdset_create("disk_iotime", disk, NULL, disk, "Disk I/O Time", "milliseconds/s", 2005, update_every, RRDSET_TYPE_LINE);
+                               st = rrdset_create("disk_iotime", disk, NULL, disk, "Disk I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
                                st->isdetail = 1;
 
                                rrddim_add(st, "reads", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
                                rrddim_add(st, "writes", NULL, -1, 1 * update_every, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "latency", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "weighted", NULL, 1, 1 * update_every, RRDDIM_INCREMENTAL);
                        }
-                       else rrdset_next(st);
+                       else rrdset_next_usec(st, dt);
 
-                       rrddim_set(st, "reads", readms);
-                       rrddim_set(st, "writes", writems);
-                       rrddim_set(st, "latency", iosms);
-                       rrddim_set(st, "weighted", wiosms);
+                       last_readms  = rrddim_set(st, "reads", readms);
+                       last_writems = rrddim_set(st, "writes", writems);
                        rrdset_done(st);
                }
 
                // --------------------------------------------------------------------
+               // calculate differential charts
+               // only if this is not the first time we run
+
+               if(dt) {
+                       if(do_iotime && do_ops) {
+                               st = rrdset_find_bytype("disk_await", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_await", disk, NULL, disk, "Average Wait Time", "ms per operation", 2005, update_every, RRDSET_TYPE_AREA);
+                                       st->isdetail = 1;
+
+                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                               }
+                               else rrdset_next_usec(st, dt);
 
-               if(do_cur_ops) {
-                       st = rrdset_find_bytype("disk_cur_ops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_cur_ops", disk, NULL, disk, "Current Disk I/O operations", "operations", 2004, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
+                               rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
+                               rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
+                               rrdset_done(st);
+                       }
 
-                               rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                       if(do_io && do_ops) {
+                               st = rrdset_find_bytype("disk_avgsz", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_avgsz", disk, NULL, disk, "Average Operation Size", "kilobytes", 2006, update_every, RRDSET_TYPE_AREA);
+                                       st->isdetail = 1;
+
+                                       rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_ABSOLUTE);
+                                       rrddim_add(st, "writes", NULL, -sector_size, 1024, RRDDIM_ABSOLUTE);
+                               }
+                               else rrdset_next_usec(st, dt);
+
+                               rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
+                               rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+                               rrdset_done(st);
                        }
-                       else rrdset_next(st);
 
-                       rrddim_set(st, "operations", currentios);
-                       rrdset_done(st);
+                       if(do_util && do_ops) {
+                               st = rrdset_find_bytype("disk_svctm", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_svctm", disk, NULL, disk, "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_AREA);
+                                       st->isdetail = 1;
+
+                                       rrddim_add(st, "svctm", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                               }
+                               else rrdset_next_usec(st, dt);
+
+                               rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (iosms - last_iosms) / ((reads - last_reads) + (writes - last_writes)) : 0);
+                               rrdset_done(st);
+                       }
                }
+
+               // TODO
+               // the differential (readms + writems) / (reads + writes) = average wait time
+               // the differential (readms ) / (reads ) = the average wait time for reads
+               // the differential (writems) / (writes) = the average wait time for writes
+
+               // similarly for bytes to find the average read and write size
+
+               // svctm = % of utilization per request (reads + writes)
        }
        
        return 0;
index 92fd245238f985d40e06cc96d91b08ba06b8e44a..aabcd5c0959d19c078039ea04fc8ebada3769040 100755 (executable)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -745,33 +745,37 @@ int rrddim_unhide(RRDSET *st, const char *id)
        return 0;
 }
 
-void rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
+collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
 {
        debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
        
        gettimeofday(&rd->last_collected_time, NULL);
        rd->collected_value = value;
        rd->updated = 1;
+
+       return rd->last_collected_value;
 }
 
-int rrddim_set(RRDSET *st, const char *id, collected_number value)
+collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
 {
        RRDDIM *rd = rrddim_find(st, id);
        if(unlikely(!rd)) {
                error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-               return 1;
+               return 0;
        }
 
-       rrddim_set_by_pointer(st, rd, value);
-       return 0;
+       return rrddim_set_by_pointer(st, rd, value);
 }
 
 void rrdset_next_usec(RRDSET *st, unsigned long long microseconds)
 {
-       debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
+       if(!microseconds) rrdset_next(st);
+       else {
+               debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
 
-       if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
-       st->usec_since_last_update = microseconds;
+               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
+               st->usec_since_last_update = microseconds;
+       }
 }
 
 void rrdset_next(RRDSET *st)
index 3443e25b4879eb55db1910f205b4bad9c2f3c16b..17882366aeef6d144494ab016148a692494214d1 100755 (executable)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -315,10 +315,7 @@ extern RRDDIM *rrddim_find(RRDSET *st, const char *id);
 extern int rrddim_hide(RRDSET *st, const char *id);
 extern int rrddim_unhide(RRDSET *st, const char *id);
 
-extern void rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value);
-extern int rrddim_set(RRDSET *st, const char *id, collected_number value);
-
-
-
+extern collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value);
+extern collected_number rrddim_set(RRDSET *st, const char *id, collected_number value);
 
 #endif /* NETDATA_RRD_H */
index af951c544f28a7c68cf7877b7060ff88657bb55e..e2a88d9ef451e4cd9734482b14f60121e8ae69fb 100755 (executable)
@@ -383,11 +383,6 @@ RRDR *rrd2rrdr(RRDSET *st, long points, time_t after, time_t before, int group_m
        if(group <= 0) group = 1;
        if(duration / group > points) group++;
 
-       // align timestamps to group
-       before -= before % group;
-       after -= after % group;
-       duration = before - after;
-
        // error("NEW: points=%d after=%d before=%d group=%d, duration=%d", points, after, before, group, duration);
 
        // Now we have:
@@ -459,6 +454,9 @@ RRDR *rrd2rrdr(RRDSET *st, long points, time_t after, time_t before, int group_m
                        add_this = 0,
                        stop_now = 0;
 
+       // align to group for proper panning of data
+       t -= t % group;
+
        time_t  now = rrdset_slot2time(st, t),
                        dt = st->update_every,
                        group_start_t = 0;