Просмотр исходного кода

Windows storage metrics (#18810)

* added disk.ops

* claiming should wait for node id and status ONLINE only

* fix compilation on linux

* fix ops on windows

* added disk.util

* disk.busy

* disk.iotime

* disk.qops

* added cleanup to windows disk metrics

* updated cmake

* remove duplicate cleanup

* undo identation

* do not repeateadly try to find non-existing metrics

* log once the metrics perflib gives up
Costa Tsaousis 4 месяцев назад
Родитель
Сommit
7d8da98bc7

+ 5 - 0
CMakeLists.txt

@@ -1170,7 +1170,12 @@ endif()
 
 set(INTERNAL_COLLECTORS_FILES
         src/collectors/common-contexts/common-contexts.h
+        src/collectors/common-contexts/disk-busy.h
         src/collectors/common-contexts/disk-io.h
+        src/collectors/common-contexts/disk-iotime.h
+        src/collectors/common-contexts/disk-ops.h
+        src/collectors/common-contexts/disk-qops.h
+        src/collectors/common-contexts/disk-util.h
         src/collectors/common-contexts/system-io.h
         src/collectors/common-contexts/system-interrupts.h
         src/collectors/common-contexts/system-processes.h

+ 5 - 0
src/collectors/common-contexts/common-contexts.h

@@ -27,5 +27,10 @@ typedef void (*instance_labels_cb_t)(RRDSET *st, void *data);
 #include "mem-pgfaults.h"
 #include "mem-available.h"
 #include "disk-io.h"
+#include "disk-ops.h"
+#include "disk-qops.h"
+#include "disk-util.h"
+#include "disk-busy.h"
+#include "disk-iotime.h"
 
 #endif //NETDATA_COMMON_CONTEXTS_H

+ 43 - 0
src/collectors/common-contexts/disk-busy.h

@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_DISK_BUSY_H
+#define NETDATA_DISK_BUSY_H
+
+#include "common-contexts.h"
+
+typedef struct {
+    RRDSET *st_busy;
+    RRDDIM *rd_busy;
+} ND_DISK_BUSY;
+
+static inline void common_disk_busy(ND_DISK_BUSY *d, const char *id, const char *name, uint64_t busy_ms, int update_every, instance_labels_cb_t cb, void *data) {
+    if(unlikely(!d->st_busy)) {
+        d->st_busy = rrdset_create_localhost(
+                "disk_busy"
+                , id
+                , name
+                , "utilization"
+                , "disk.busy"
+                , "Disk Busy Time"
+                , "milliseconds"
+                , _COMMON_PLUGIN_NAME
+                , _COMMON_PLUGIN_MODULE_NAME
+                , NETDATA_CHART_PRIO_DISK_BUSY
+                , update_every
+                , RRDSET_TYPE_AREA
+        );
+
+        rrdset_flag_set(d->st_busy, RRDSET_FLAG_DETAIL);
+
+        d->rd_busy  = rrddim_add(d->st_busy, "busy",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+        if(cb)
+            cb(d->st_busy, data);
+    }
+
+    // this always have to be in base units, so that exporting sends base units to other time-series db
+    rrddim_set_by_pointer(d->st_busy, d->rd_busy, (collected_number)busy_ms);
+    rrdset_done(d->st_busy);
+}
+
+#endif //NETDATA_DISK_BUSY_H

+ 46 - 0
src/collectors/common-contexts/disk-iotime.h

@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_DISK_IOTIME_H
+#define NETDATA_DISK_IOTIME_H
+
+#include "common-contexts.h"
+
+typedef struct {
+    RRDSET *st_iotime;
+    RRDDIM *rd_reads_ms;
+    RRDDIM *rd_writes_ms;
+} ND_DISK_IOTIME;
+
+static inline void common_disk_iotime(ND_DISK_IOTIME *d, const char *id, const char *name, uint64_t reads_ms, uint64_t writes_ms, int update_every, instance_labels_cb_t cb, void *data) {
+    if(unlikely(!d->st_iotime)) {
+        d->st_iotime = rrdset_create_localhost(
+                "disk_iotime"
+                , id
+                , name
+                , "utilization"
+                , "disk.iotime"
+                , "Disk Total I/O Time"
+                , "milliseconds/s"
+                , _COMMON_PLUGIN_NAME
+                , _COMMON_PLUGIN_MODULE_NAME
+                , NETDATA_CHART_PRIO_DISK_IOTIME
+                , update_every
+                , RRDSET_TYPE_AREA
+        );
+
+        rrdset_flag_set(d->st_iotime, RRDSET_FLAG_DETAIL);
+
+        d->rd_reads_ms  = rrddim_add(d->st_iotime, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+        d->rd_writes_ms = rrddim_add(d->st_iotime, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+        if(cb)
+            cb(d->st_iotime, data);
+    }
+
+    // this always have to be in base units, so that exporting sends base units to other time-series db
+    rrddim_set_by_pointer(d->st_iotime, d->rd_reads_ms, (collected_number)reads_ms);
+    rrddim_set_by_pointer(d->st_iotime, d->rd_writes_ms, (collected_number)writes_ms);
+    rrdset_done(d->st_iotime);
+}
+
+#endif //NETDATA_DISK_IOTIME_H

+ 44 - 0
src/collectors/common-contexts/disk-ops.h

@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_DISK_OPS_H
+#define NETDATA_DISK_OPS_H
+
+#include "common-contexts.h"
+
+typedef struct {
+    RRDSET *st_ops;
+    RRDDIM *rd_ops_reads;
+    RRDDIM *rd_ops_writes;
+} ND_DISK_OPS;
+
+static inline void common_disk_ops(ND_DISK_OPS *d, const char *id, const char *name, uint64_t ops_read, uint64_t ops_write, int update_every, instance_labels_cb_t cb, void *data) {
+    if(unlikely(!d->st_ops)) {
+        d->st_ops = rrdset_create_localhost(
+                "disk_ops"
+                , id
+                , name
+                , "ops"
+                , "disk.ops"
+                , "Disk Completed I/O Operations"
+                , "operations/s"
+                , _COMMON_PLUGIN_NAME
+                , _COMMON_PLUGIN_MODULE_NAME
+                , NETDATA_CHART_PRIO_DISK_OPS
+                , update_every
+                , RRDSET_TYPE_LINE
+        );
+
+        d->rd_ops_reads  = rrddim_add(d->st_ops, "reads",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+        d->rd_ops_writes = rrddim_add(d->st_ops, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+        if(cb)
+            cb(d->st_ops, data);
+    }
+
+    // this always have to be in base units, so that exporting sends base units to other time-series db
+    rrddim_set_by_pointer(d->st_ops, d->rd_ops_reads, (collected_number)ops_read);
+    rrddim_set_by_pointer(d->st_ops, d->rd_ops_writes, (collected_number)ops_write);
+    rrdset_done(d->st_ops);
+}
+
+#endif //NETDATA_DISK_OPS_H

+ 41 - 0
src/collectors/common-contexts/disk-qops.h

@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_DISK_QOPS_H
+#define NETDATA_DISK_QOPS_H
+
+#include "common-contexts.h"
+
+typedef struct {
+    RRDSET *st_qops;
+    RRDDIM *rd_qops;
+} ND_DISK_QOPS;
+
+static inline void common_disk_qops(ND_DISK_QOPS *d, const char *id, const char *name, uint64_t queued_ops, int update_every, instance_labels_cb_t cb, void *data) {
+    if(unlikely(!d->st_qops)) {
+        d->st_qops = rrdset_create_localhost(
+                "disk_qops"
+                , id
+                , name
+                , "ops"
+                , "disk.qops"
+                , "Disk Current I/O Operations"
+                , "operations"
+                , _COMMON_PLUGIN_NAME
+                , _COMMON_PLUGIN_MODULE_NAME
+                , NETDATA_CHART_PRIO_DISK_QOPS
+                , update_every
+                , RRDSET_TYPE_LINE
+        );
+
+        d->rd_qops  = rrddim_add(d->st_qops, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+
+        if(cb)
+            cb(d->st_qops, data);
+    }
+
+    // this always have to be in base units, so that exporting sends base units to other time-series db
+    rrddim_set_by_pointer(d->st_qops, d->rd_qops, (collected_number)queued_ops);
+    rrdset_done(d->st_qops);
+}
+
+#endif //NETDATA_DISK_QOPS_H

+ 43 - 0
src/collectors/common-contexts/disk-util.h

@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_DISK_UTIL_H
+#define NETDATA_DISK_UTIL_H
+
+#include "common-contexts.h"
+
+typedef struct {
+    RRDSET *st_util;
+    RRDDIM *rd_util;
+} ND_DISK_UTIL;
+
+static inline void common_disk_util(ND_DISK_UTIL *d, const char *id, const char *name, uint64_t percent, int update_every, instance_labels_cb_t cb, void *data) {
+    if(unlikely(!d->st_util)) {
+        d->st_util = rrdset_create_localhost(
+                "disk_util"
+                , id
+                , name
+                , "utilization"
+                , "disk.util"
+                , "Disk Utilization Time"
+                , "% of time working"
+                , _COMMON_PLUGIN_NAME
+                , _COMMON_PLUGIN_MODULE_NAME
+                , NETDATA_CHART_PRIO_DISK_UTIL
+                , update_every
+                , RRDSET_TYPE_AREA
+        );
+
+        rrdset_flag_set(d->st_util, RRDSET_FLAG_DETAIL);
+
+        d->rd_util  = rrddim_add(d->st_util, "utilization",  NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+
+        if(cb)
+            cb(d->st_util, data);
+    }
+
+    // this always have to be in base units, so that exporting sends base units to other time-series db
+    rrddim_set_by_pointer(d->st_util, d->rd_util, (collected_number)percent);
+    rrdset_done(d->st_util);
+}
+
+#endif //NETDATA_DISK_UTIL_H

+ 76 - 170
src/collectors/proc.plugin/proc_diskstats.c

@@ -81,30 +81,22 @@ static struct disk {
     usec_t bcache_priority_stats_elapsed_usec;
 
     ND_DISK_IO disk_io;
+    ND_DISK_OPS disk_ops;
+    ND_DISK_QOPS disk_qops;
+    ND_DISK_UTIL disk_util;
+    ND_DISK_BUSY disk_busy;
+    ND_DISK_IOTIME disk_iotime;
 
     RRDSET *st_ext_io;
     RRDDIM *rd_io_discards;
 
-    RRDSET *st_ops;
-    RRDDIM *rd_ops_reads;
-    RRDDIM *rd_ops_writes;
-
     RRDSET *st_ext_ops;
     RRDDIM *rd_ops_discards;
     RRDDIM *rd_ops_flushes;
 
-    RRDSET *st_qops;
-    RRDDIM *rd_qops_operations;
-
     RRDSET *st_backlog;
     RRDDIM *rd_backlog_backlog;
 
-    RRDSET *st_busy;
-    RRDDIM *rd_busy_busy;
-
-    RRDSET *st_util;
-    RRDDIM *rd_util_utilization;
-
     RRDSET *st_mops;
     RRDDIM *rd_mops_reads;
     RRDDIM *rd_mops_writes;
@@ -112,10 +104,6 @@ static struct disk {
     RRDSET *st_ext_mops;
     RRDDIM *rd_mops_discards;
 
-    RRDSET *st_iotime;
-    RRDDIM *rd_iotime_reads;
-    RRDDIM *rd_iotime_writes;
-
     RRDSET *st_ext_iotime;
     RRDDIM *rd_iotime_discards;
     RRDDIM *rd_iotime_flushes;
@@ -1049,15 +1037,15 @@ static int diskstats_function_block_devices(BUFFER *wb, const char *function __m
             max_io = MAX(max_io, io_total);
         }
         // Backlog and Busy Time
-        double busy_perc = rrddim_get_last_stored_value(d->rd_util_utilization, &max_busy_perc, 1);
-        double busy_time = rrddim_get_last_stored_value(d->rd_busy_busy, &max_busy_time, 1);
+        double busy_perc = rrddim_get_last_stored_value(d->disk_util.rd_util, &max_busy_perc, 1);
+        double busy_time = rrddim_get_last_stored_value(d->disk_busy.rd_busy, &max_busy_time, 1);
         double backlog_time = rrddim_get_last_stored_value(d->rd_backlog_backlog, &max_backlog_time, 1);
         // IOPS
-        double iops_reads = rrddim_get_last_stored_value(d->rd_ops_reads, &max_iops_reads, 1);
-        double iops_writes = rrddim_get_last_stored_value(d->rd_ops_writes, &max_iops_writes, 1);
+        double iops_reads = rrddim_get_last_stored_value(d->disk_ops.rd_ops_reads, &max_iops_reads, 1);
+        double iops_writes = rrddim_get_last_stored_value(d->disk_ops.rd_ops_writes, &max_iops_writes, 1);
         // IO Time
-        double iops_time_reads = rrddim_get_last_stored_value(d->rd_iotime_reads, &max_iops_time_reads, 1);
-        double iops_time_writes = rrddim_get_last_stored_value(d->rd_iotime_writes, &max_iops_time_writes, 1);
+        double iops_time_reads = rrddim_get_last_stored_value(d->disk_iotime.rd_reads_ms, &max_iops_time_reads, 1);
+        double iops_time_writes = rrddim_get_last_stored_value(d->disk_iotime.rd_writes_ms, &max_iops_time_writes, 1);
         // Avg IO Time
         double iops_avg_time_read = rrddim_get_last_stored_value(d->rd_await_reads, &max_iops_avg_time_read, 1);
         double iops_avg_time_write = rrddim_get_last_stored_value(d->rd_await_writes, &max_iops_avg_time_write, 1);
@@ -1287,23 +1275,25 @@ static void diskstats_cleanup_disks() {
         if (unlikely(global_cleanup_removed_disks && !d->updated)) {
             struct disk *t = d;
 
+            rrdset_obsolete_and_pointer_null(d->disk_io.st_io);
+            rrdset_obsolete_and_pointer_null(d->disk_ops.st_ops);
+            rrdset_obsolete_and_pointer_null(d->disk_qops.st_qops);
+            rrdset_obsolete_and_pointer_null(d->disk_util.st_util);
+            rrdset_obsolete_and_pointer_null(d->disk_busy.st_busy);
+            rrdset_obsolete_and_pointer_null(d->disk_iotime.st_iotime);
+
             rrdset_obsolete_and_pointer_null(d->st_avgsz);
             rrdset_obsolete_and_pointer_null(d->st_ext_avgsz);
             rrdset_obsolete_and_pointer_null(d->st_await);
             rrdset_obsolete_and_pointer_null(d->st_ext_await);
             rrdset_obsolete_and_pointer_null(d->st_backlog);
-            rrdset_obsolete_and_pointer_null(d->st_busy);
             rrdset_obsolete_and_pointer_null(d->disk_io.st_io);
             rrdset_obsolete_and_pointer_null(d->st_ext_io);
-            rrdset_obsolete_and_pointer_null(d->st_iotime);
             rrdset_obsolete_and_pointer_null(d->st_ext_iotime);
             rrdset_obsolete_and_pointer_null(d->st_mops);
             rrdset_obsolete_and_pointer_null(d->st_ext_mops);
-            rrdset_obsolete_and_pointer_null(d->st_ops);
             rrdset_obsolete_and_pointer_null(d->st_ext_ops);
-            rrdset_obsolete_and_pointer_null(d->st_qops);
             rrdset_obsolete_and_pointer_null(d->st_svctm);
-            rrdset_obsolete_and_pointer_null(d->st_util);
             rrdset_obsolete_and_pointer_null(d->st_bcache);
             rrdset_obsolete_and_pointer_null(d->st_bcache_bypass);
             rrdset_obsolete_and_pointer_null(d->st_bcache_rates);
@@ -1453,18 +1443,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         char *disk;
         unsigned long       major = 0, minor = 0;
 
-        collected_number    reads = 0,  mreads = 0,  readsectors = 0,  readms = 0,
-                            writes = 0, mwrites = 0, writesectors = 0, writems = 0,
+        collected_number rd_ios = 0,  mreads = 0,  readsectors = 0,  readms = 0, wr_ios = 0, mwrites = 0, writesectors = 0, writems = 0,
                             queued_ios = 0, busy_ms = 0, backlog_ms = 0,
                             discards = 0, mdiscards = 0, discardsectors = 0, discardms = 0,
                             flushes = 0, flushms = 0;
 
 
-        collected_number    last_reads = 0,  last_readsectors = 0,  last_readms = 0,
-                            last_writes = 0, last_writesectors = 0, last_writems = 0,
-                            last_busy_ms = 0,
-                            last_discards = 0, last_discardsectors = 0, last_discardms = 0,
-                            last_flushes = 0, last_flushms = 0;
+        collected_number last_rd_ios = 0,  last_readsectors = 0,  last_readms = 0,
+                         last_wr_ios = 0, last_writesectors = 0, last_writems = 0,
+                         last_busy_ms = 0,
+                         last_discards = 0, last_discardsectors = 0, last_discardms = 0,
+                         last_flushes = 0, last_flushms = 0;
 
         size_t words = procfile_linewords(ff, l);
         if(unlikely(words < 14)) continue;
@@ -1475,8 +1464,8 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // # of reads completed # of writes completed
         // This is the total number of reads or writes completed successfully.
-        reads           = str2ull(procfile_lineword(ff, l, 3), NULL);  // rd_ios
-        writes          = str2ull(procfile_lineword(ff, l, 7), NULL);  // wr_ios
+        rd_ios = str2ull(procfile_lineword(ff, l, 3), NULL);  // rd_ios
+        wr_ios = str2ull(procfile_lineword(ff, l, 7), NULL);  // wr_ios
 
         // # of reads merged # of writes merged
         // Reads and writes which are adjacent to each other may be merged for
@@ -1615,33 +1604,15 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         if (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO) {
             d->do_ops = CONFIG_BOOLEAN_YES;
 
-            if(unlikely(!d->st_ops)) {
-                d->st_ops = rrdset_create_localhost(
-                        "disk_ops"
-                        , d->chart_id
-                        , d->disk
-                        , family
-                        , "disk.ops"
-                        , "Disk Completed I/O Operations"
-                        , "operations/s"
-                        , PLUGIN_PROC_NAME
-                        , PLUGIN_PROC_MODULE_DISKSTATS_NAME
-                        , NETDATA_CHART_PRIO_DISK_OPS
-                        , update_every
-                        , RRDSET_TYPE_LINE
-                );
-
-                rrdset_flag_set(d->st_ops, RRDSET_FLAG_DETAIL);
-
-                d->rd_ops_reads  = rrddim_add(d->st_ops, "reads",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
-                d->rd_ops_writes = rrddim_add(d->st_ops, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
-
-                add_labels_to_disk(d, d->st_ops);
-            }
+            last_rd_ios = d->disk_ops.rd_ops_reads ? d->disk_ops.rd_ops_reads->collector.last_collected_value : 0;
+            last_wr_ios = d->disk_ops.rd_ops_writes ? d->disk_ops.rd_ops_writes->collector.last_collected_value : 0;
 
-            last_reads  = rrddim_set_by_pointer(d->st_ops, d->rd_ops_reads, reads);
-            last_writes = rrddim_set_by_pointer(d->st_ops, d->rd_ops_writes, writes);
-            rrdset_done(d->st_ops);
+            common_disk_ops(&d->disk_ops,
+                           d->chart_id,
+                           d->disk, rd_ios, wr_ios,
+                           update_every,
+                           disk_labels_cb,
+                           d);
         }
 
         if (do_dc_stats && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
@@ -1679,31 +1650,14 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         if (d->do_qops == CONFIG_BOOLEAN_YES || d->do_qops == CONFIG_BOOLEAN_AUTO) {
             d->do_qops = CONFIG_BOOLEAN_YES;
 
-            if(unlikely(!d->st_qops)) {
-                d->st_qops = rrdset_create_localhost(
-                        "disk_qops"
-                        , d->chart_id
-                        , d->disk
-                        , family
-                        , "disk.qops"
-                        , "Disk Current I/O Operations"
-                        , "operations"
-                        , PLUGIN_PROC_NAME
-                        , PLUGIN_PROC_MODULE_DISKSTATS_NAME
-                        , NETDATA_CHART_PRIO_DISK_QOPS
-                        , update_every
-                        , RRDSET_TYPE_LINE
-                );
-
-                rrdset_flag_set(d->st_qops, RRDSET_FLAG_DETAIL);
-
-                d->rd_qops_operations = rrddim_add(d->st_qops, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
-
-                add_labels_to_disk(d, d->st_qops);
-            }
-
-            rrddim_set_by_pointer(d->st_qops, d->rd_qops_operations, queued_ios);
-            rrdset_done(d->st_qops);
+            common_disk_qops(
+                    &d->disk_qops,
+                    d->chart_id,
+                    d->disk,
+                    queued_ios,
+                    update_every,
+                    disk_labels_cb,
+                    d);
         }
 
         if (d->do_backlog == CONFIG_BOOLEAN_YES || d->do_backlog == CONFIG_BOOLEAN_AUTO) {
@@ -1739,61 +1693,28 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         if (d->do_util == CONFIG_BOOLEAN_YES || d->do_util == CONFIG_BOOLEAN_AUTO) {
             d->do_util = CONFIG_BOOLEAN_YES;
 
-            if(unlikely(!d->st_busy)) {
-                d->st_busy = rrdset_create_localhost(
-                        "disk_busy"
-                        , d->chart_id
-                        , d->disk
-                        , family
-                        , "disk.busy"
-                        , "Disk Busy Time"
-                        , "milliseconds"
-                        , PLUGIN_PROC_NAME
-                        , PLUGIN_PROC_MODULE_DISKSTATS_NAME
-                        , NETDATA_CHART_PRIO_DISK_BUSY
-                        , update_every
-                        , RRDSET_TYPE_AREA
-                );
-
-                rrdset_flag_set(d->st_busy, RRDSET_FLAG_DETAIL);
-
-                d->rd_busy_busy = rrddim_add(d->st_busy, "busy", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
-
-                add_labels_to_disk(d, d->st_busy);
-            }
-
-            last_busy_ms = rrddim_set_by_pointer(d->st_busy, d->rd_busy_busy, busy_ms);
-            rrdset_done(d->st_busy);
+            last_busy_ms = d->disk_busy.rd_busy ? d->disk_busy.rd_busy->collector.last_collected_value : 0;
 
-            if(unlikely(!d->st_util)) {
-                d->st_util = rrdset_create_localhost(
-                        "disk_util"
-                        , d->chart_id
-                        , d->disk
-                        , family
-                        , "disk.util"
-                        , "Disk Utilization Time"
-                        , "% of time working"
-                        , PLUGIN_PROC_NAME
-                        , PLUGIN_PROC_MODULE_DISKSTATS_NAME
-                        , NETDATA_CHART_PRIO_DISK_UTIL
-                        , update_every
-                        , RRDSET_TYPE_AREA
-                );
-
-                rrdset_flag_set(d->st_util, RRDSET_FLAG_DETAIL);
-
-                d->rd_util_utilization = rrddim_add(d->st_util, "utilization", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
-
-                add_labels_to_disk(d, d->st_util);
-            }
+            common_disk_busy(&d->disk_busy,
+                             d->chart_id,
+                             d->disk,
+                             busy_ms,
+                             update_every,
+                             disk_labels_cb,
+                             d);
 
             collected_number disk_utilization = (busy_ms - last_busy_ms) / (10 * update_every);
             if (disk_utilization > 100)
                 disk_utilization = 100;
 
-            rrddim_set_by_pointer(d->st_util, d->rd_util_utilization, disk_utilization);
-            rrdset_done(d->st_util);
+            common_disk_util(&d->disk_util,
+                             d->chart_id,
+                             d->disk,
+                             disk_utilization,
+                             update_every,
+                             disk_labels_cb,
+                             d);
+
         }
 
         if (d->do_mops == CONFIG_BOOLEAN_YES || d->do_mops == CONFIG_BOOLEAN_AUTO) {
@@ -1861,33 +1782,18 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         if (d->do_iotime == CONFIG_BOOLEAN_YES || d->do_iotime == CONFIG_BOOLEAN_AUTO) {
             d->do_iotime = CONFIG_BOOLEAN_YES;
 
-            if(unlikely(!d->st_iotime)) {
-                d->st_iotime = rrdset_create_localhost(
-                        "disk_iotime"
-                        , d->chart_id
-                        , d->disk
-                        , family
-                        , "disk.iotime"
-                        , "Disk Total I/O Time"
-                        , "milliseconds/s"
-                        , PLUGIN_PROC_NAME
-                        , PLUGIN_PROC_MODULE_DISKSTATS_NAME
-                        , NETDATA_CHART_PRIO_DISK_IOTIME
-                        , update_every
-                        , RRDSET_TYPE_LINE
-                );
-
-                rrdset_flag_set(d->st_iotime, RRDSET_FLAG_DETAIL);
-
-                d->rd_iotime_reads  = rrddim_add(d->st_iotime, "reads",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
-                d->rd_iotime_writes = rrddim_add(d->st_iotime, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
-
-                add_labels_to_disk(d, d->st_iotime);
-            }
-
-            last_readms  = rrddim_set_by_pointer(d->st_iotime, d->rd_iotime_reads, readms);
-            last_writems = rrddim_set_by_pointer(d->st_iotime, d->rd_iotime_writes, writems);
-            rrdset_done(d->st_iotime);
+            last_readms  = d->disk_iotime.rd_reads_ms ? d->disk_iotime.rd_reads_ms->collector.last_collected_value : 0;
+            last_writems = d->disk_iotime.rd_writes_ms ? d->disk_iotime.rd_writes_ms->collector.last_collected_value : 0;
+
+            common_disk_iotime(
+                    &d->disk_iotime,
+                    d->chart_id,
+                    d->disk,
+                    readms,
+                    writems,
+                    update_every,
+                    disk_labels_cb,
+                    d);
         }
 
         if(do_dc_stats && d->do_iotime == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
@@ -1952,8 +1858,8 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                     add_labels_to_disk(d, d->st_await);
                 }
 
-                double read_avg = (reads - last_reads) ? (double)(readms - last_readms) / (reads - last_reads) : 0;
-                double write_avg = (writes - last_writes) ? (double)(writems - last_writems) / (writes - last_writes) : 0;
+                double read_avg = (rd_ios - last_rd_ios) ? (double)(readms - last_readms) / (rd_ios - last_rd_ios) : 0;
+                double write_avg = (wr_ios - last_wr_ios) ? (double)(writems - last_writems) / (wr_ios - last_wr_ios) : 0;
 
                 rrddim_set_by_pointer(d->st_await, d->rd_await_reads, (collected_number)(read_avg * 1000));
                 rrddim_set_by_pointer(d->st_await, d->rd_await_writes, (collected_number)(write_avg * 1000));
@@ -2025,8 +1931,8 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                     add_labels_to_disk(d, d->st_avgsz);
                 }
 
-                rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_reads,  (reads  - last_reads)  ? (readsectors  - last_readsectors)  / (reads  - last_reads)  : 0);
-                rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_writes, (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+                rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_reads,  (rd_ios - last_rd_ios)  ? (readsectors  - last_readsectors)  / (rd_ios - last_rd_ios)  : 0);
+                rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_writes, (wr_ios - last_wr_ios) ? (writesectors - last_writesectors) / (wr_ios - last_wr_ios) : 0);
                 rrdset_done(d->st_avgsz);
             }
 
@@ -2087,8 +1993,8 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                 }
 
                 double svctm_avg =
-                    ((reads - last_reads) + (writes - last_writes)) ?
-                        (double)(busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) :
+                    ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) ?
+                        (double)(busy_ms - last_busy_ms) / ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) :
                         0;
 
                 rrddim_set_by_pointer(d->st_svctm, d->rd_svctm_svctm, (collected_number)(svctm_avg * 1000));

+ 190 - 50
src/collectors/windows.plugin/perflib-storage.c

@@ -8,6 +8,7 @@
 #include "../common-contexts/common-contexts.h"
 
 struct logical_disk {
+    usec_t last_collected;
     bool collected_metadata;
 
     STRING *filesystem;
@@ -21,6 +22,7 @@ struct logical_disk {
 };
 
 struct physical_disk {
+    usec_t last_collected;
     bool collected_metadata;
 
     STRING *device;
@@ -30,11 +32,23 @@ struct physical_disk {
     COUNTER_DATA diskReadBytesPerSec;
     COUNTER_DATA diskWriteBytesPerSec;
 
+    ND_DISK_OPS disk_ops;
+    COUNTER_DATA diskReadsPerSec;
+    COUNTER_DATA diskWritesPerSec;
+
+    ND_DISK_UTIL disk_util;
     COUNTER_DATA percentIdleTime;
+
+    ND_DISK_BUSY disk_busy;
     COUNTER_DATA percentDiskTime;
+
+    ND_DISK_IOTIME disk_iotime;
     COUNTER_DATA percentDiskReadTime;
     COUNTER_DATA percentDiskWriteTime;
+
+    ND_DISK_QOPS disk_qops;
     COUNTER_DATA currentDiskQueueLength;
+
     COUNTER_DATA averageDiskQueueLength;
     COUNTER_DATA averageDiskReadQueueLength;
     COUNTER_DATA averageDiskWriteQueueLength;
@@ -42,8 +56,6 @@ struct physical_disk {
     COUNTER_DATA averageDiskSecondsPerRead;
     COUNTER_DATA averageDiskSecondsPerWrite;
     COUNTER_DATA diskTransfersPerSec;
-    COUNTER_DATA diskReadsPerSec;
-    COUNTER_DATA diskWritesPerSec;
     COUNTER_DATA diskBytesPerSec;
     COUNTER_DATA averageDiskBytesPerTransfer;
     COUNTER_DATA averageDiskBytesPerRead;
@@ -55,45 +67,61 @@ struct physical_disk system_physical_total = {
     .collected_metadata = true,
 };
 
-void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
-    struct logical_disk *ld = value;
+static void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
+    struct logical_disk *d = value;
+
+    d->percentDiskFree.key = "% Free Space";
+    // d->freeMegabytes.key = "Free Megabytes";
+}
+
+static void logical_disk_cleanup(struct logical_disk *d) {
+    rrdset_is_obsolete___safe_from_collector_thread(d->st_disk_space);
+}
 
-    ld->percentDiskFree.key = "% Free Space";
-    // ld->freeMegabytes.key = "Free Megabytes";
+static void physical_disk_initialize(struct physical_disk *d) {
+    d->percentIdleTime.key = "% Idle Time";
+    d->percentDiskTime.key = "% Disk Time";
+    d->percentDiskReadTime.key = "% Disk Read Time";
+    d->percentDiskWriteTime.key = "% Disk Write Time";
+    d->currentDiskQueueLength.key = "Current Disk Queue Length";
+    d->averageDiskQueueLength.key = "Avg. Disk Queue Length";
+    d->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
+    d->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
+    d->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
+    d->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
+    d->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
+    d->diskTransfersPerSec.key = "Disk Transfers/sec";
+    d->diskReadsPerSec.key = "Disk Reads/sec";
+    d->diskWritesPerSec.key = "Disk Writes/sec";
+    d->diskBytesPerSec.key = "Disk Bytes/sec";
+    d->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
+    d->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
+    d->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
+    d->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
+    d->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
+    d->splitIoPerSec.key = "Split IO/Sec";
 }
 
-void initialize_physical_disk(struct physical_disk *pd) {
-    pd->percentIdleTime.key = "% Idle Time";
-    pd->percentDiskTime.key = "% Disk Time";
-    pd->percentDiskReadTime.key = "% Disk Read Time";
-    pd->percentDiskWriteTime.key = "% Disk Write Time";
-    pd->currentDiskQueueLength.key = "Current Disk Queue Length";
-    pd->averageDiskQueueLength.key = "Avg. Disk Queue Length";
-    pd->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
-    pd->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
-    pd->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
-    pd->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
-    pd->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
-    pd->diskTransfersPerSec.key = "Disk Transfers/sec";
-    pd->diskReadsPerSec.key = "Disk Reads/sec";
-    pd->diskWritesPerSec.key = "Disk Writes/sec";
-    pd->diskBytesPerSec.key = "Disk Bytes/sec";
-    pd->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
-    pd->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
-    pd->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
-    pd->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
-    pd->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
-    pd->splitIoPerSec.key = "Split IO/Sec";
+static void physical_disk_cleanup(struct physical_disk *d) {
+    string_freez(d->device);
+    string_freez(d->mount_point);
+
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_io.st_io);
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_ops.st_ops);
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_util.st_util);
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_busy.st_busy);
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_iotime.st_iotime);
+    rrdset_is_obsolete___safe_from_collector_thread(d->disk_qops.st_qops);
 }
 
 void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
     struct physical_disk *pd = value;
-    initialize_physical_disk(pd);
+    physical_disk_initialize(pd);
 }
 
 static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL;
 static void initialize(void) {
-    initialize_physical_disk(&system_physical_total);
+    physical_disk_initialize(&system_physical_total);
 
     logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
                                                   DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk));
@@ -144,7 +172,7 @@ static STRING *getFileSystemType(const char* diskName) {
         return NULL;
 }
 
-static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
+static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) {
     DICTIONARY *dict = logicalDisks;
 
     PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk");
@@ -162,6 +190,7 @@ static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
             continue;
 
         struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
+        d->last_collected = now_ut;
 
         if(!d->collected_metadata) {
             d->filesystem = getFileSystemType(windows_shared_buffer);
@@ -174,8 +203,10 @@ static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
         if(!d->st_disk_space) {
             d->st_disk_space = rrdset_create_localhost(
                 "disk_space"
-                , windows_shared_buffer, NULL
-                , windows_shared_buffer, "disk.space"
+                , windows_shared_buffer
+                , NULL
+                , windows_shared_buffer
+                , "disk.space"
                 , "Disk Space Usage"
                 , "GiB"
                 , PLUGIN_WINDOWS_NAME
@@ -201,6 +232,19 @@ static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
         rrdset_done(d->st_disk_space);
     }
 
+    // cleanup
+    {
+        struct logical_disk *d;
+        dfe_start_write(dict, d) {
+            if(d->last_collected < now_ut) {
+                logical_disk_cleanup(d);
+                dictionary_del(dict, d_dfe.name);
+            }
+        }
+        dfe_done(d);
+        dictionary_garbage_collect(dict);
+    }
+
     return true;
 }
 
@@ -214,7 +258,12 @@ static void physical_disk_labels(RRDSET *st, void *data) {
         rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO);
 }
 
-static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
+static bool str_is_numeric(const char *s) {
+    while(*s) if(!isdigit((uint8_t)*s++)) return false;
+    return true;
+}
+
+static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) {
     DICTIONARY *dict = physicalDisks;
 
     PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
@@ -230,12 +279,7 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
             strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
 
         char *device = windows_shared_buffer;
-        char *mount_point = NULL;
-
-        if((mount_point = strchr(device, ' '))) {
-            *mount_point = '\0';
-            mount_point++;
-        }
+        char mount_point[128]; mount_point[0] = '\0';
 
         struct physical_disk *d;
         bool is_system;
@@ -244,9 +288,22 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
             is_system = true;
         }
         else {
+            char *space;
+            if((space = strchr(windows_shared_buffer, ' '))) {
+                *space++ = '\0';
+                strncpyz(mount_point, space, sizeof(mount_point) - 1);
+            }
+
+            if(str_is_numeric(windows_shared_buffer)) {
+                uint64_t n = str2ull(device, NULL);
+                snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %" PRIu64, n);
+                device = windows_shared_buffer;
+            }
+
             d = dictionary_set(dict, device, NULL, sizeof(*d));
             is_system = false;
         }
+        d->last_collected = now_ut;
 
         if (!d->collected_metadata) {
             // TODO collect metadata - device_type, serial, id
@@ -258,7 +315,10 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
         if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) &&
             perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) {
             if(is_system)
-                common_system_io(d->diskReadBytesPerSec.current.Data, d->diskWriteBytesPerSec.current.Data, update_every);
+                common_system_io(
+                        d->diskReadBytesPerSec.current.Data,
+                        d->diskWriteBytesPerSec.current.Data,
+                        update_every);
             else
                 common_disk_io(
                     &d->disk_io,
@@ -271,11 +331,79 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
                     d);
         }
 
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength);
+        if(is_system) continue;
+
+        if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec) &&
+            perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec)) {
+
+            common_disk_ops(
+                    &d->disk_ops,
+                    device,
+                    NULL,
+                    d->diskReadBytesPerSec.current.Data,
+                    d->diskWriteBytesPerSec.current.Data,
+                    update_every,
+                    physical_disk_labels,
+                    d);
+        }
+
+        if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime)) {
+            if (d->percentIdleTime.previous.Data && d->percentIdleTime.previous.Time &&
+                    d->percentIdleTime.current.Time > d->percentIdleTime.previous.Time) {
+                collected_number idle_percentage =
+                        100 * (d->percentIdleTime.current.Data - d->percentIdleTime.previous.Data)
+                        / (d->percentIdleTime.current.Time - d->percentIdleTime.previous.Time);
+
+                if (idle_percentage > 100)
+                    idle_percentage = 100;
+
+                common_disk_util(
+                        &d->disk_util,
+                        device,
+                        NULL,
+                        100 - idle_percentage,
+                        update_every,
+                        physical_disk_labels,
+                        d);
+            }
+        }
+
+        if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime)) {
+            common_disk_busy(
+                    &d->disk_busy,
+                    device,
+                    NULL,
+                    d->percentDiskTime.current.Data / NS100_PER_MS,
+                    update_every,
+                    physical_disk_labels,
+                    d);
+        }
+
+        if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime) &&
+            perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime)) {
+
+            common_disk_iotime(
+                    &d->disk_iotime,
+                    device,
+                    NULL,
+                    d->percentDiskReadTime.current.Data / NS100_PER_MS,
+                    d->percentDiskWriteTime.current.Data / NS100_PER_MS,
+                    update_every,
+                    physical_disk_labels,
+                    d);
+        }
+
+        if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength)) {
+            common_disk_qops(
+                    &d->disk_qops,
+                    device,
+                    NULL,
+                    d->currentDiskQueueLength.current.Data,
+                    update_every,
+                    physical_disk_labels,
+                    d);
+        }
+
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength);
@@ -283,8 +411,6 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskTransfersPerSec);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec);
-        perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskBytesPerSec);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerTransfer);
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead);
@@ -292,6 +418,19 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
         perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec);
     }
 
+    // cleanup
+    {
+        struct physical_disk *d;
+        dfe_start_write(dict, d) {
+            if(d->last_collected < now_ut) {
+                physical_disk_cleanup(d);
+                dictionary_del(dict, d_dfe.name);
+            }
+        }
+        dfe_done(d);
+        dictionary_garbage_collect(dict);
+    }
+
     return true;
 }
 
@@ -310,8 +449,9 @@ int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) {
     PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
     if(!pDataBlock) return -1;
 
-    do_logical_disk(pDataBlock, update_every);
-    do_physical_disk(pDataBlock, update_every);
+    usec_t now_ut = now_monotonic_usec();
+    do_logical_disk(pDataBlock, update_every, now_ut);
+    do_physical_disk(pDataBlock, update_every, now_ut);
 
     return 0;
 }

+ 2 - 0
src/libnetdata/clocks/clocks.h

@@ -77,6 +77,8 @@ typedef struct heartbeat {
 #define MSEC_PER_SEC    1000ULL
 #endif
 
+#define NS100_PER_MS    10000ULL
+
 #define USEC_PER_MS     1000ULL
 
 #ifndef HAVE_CLOCK_GETTIME

Некоторые файлы не были показаны из-за большого количества измененных файлов