diff options
Diffstat (limited to 'src/collectors/windows.plugin/perflib-storage.c')
-rw-r--r-- | src/collectors/windows.plugin/perflib-storage.c | 949 |
1 files changed, 632 insertions, 317 deletions
diff --git a/src/collectors/windows.plugin/perflib-storage.c b/src/collectors/windows.plugin/perflib-storage.c index d3b80052f..823ba2c04 100644 --- a/src/collectors/windows.plugin/perflib-storage.c +++ b/src/collectors/windows.plugin/perflib-storage.c @@ -1,317 +1,632 @@ -// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "windows_plugin.h"
-#include "windows-internals.h"
-
-#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME
-#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage"
-#include "../common-contexts/common-contexts.h"
-
-struct logical_disk {
- bool collected_metadata;
-
- STRING *filesystem;
-
- RRDSET *st_disk_space;
- RRDDIM *rd_disk_space_used;
- RRDDIM *rd_disk_space_free;
-
- COUNTER_DATA percentDiskFree;
- // COUNTER_DATA freeMegabytes;
-};
-
-struct physical_disk {
- bool collected_metadata;
-
- STRING *device;
- STRING *mount_point;
-
- ND_DISK_IO disk_io;
- COUNTER_DATA diskReadBytesPerSec;
- COUNTER_DATA diskWriteBytesPerSec;
-
- COUNTER_DATA percentIdleTime;
- COUNTER_DATA percentDiskTime;
- COUNTER_DATA percentDiskReadTime;
- COUNTER_DATA percentDiskWriteTime;
- COUNTER_DATA currentDiskQueueLength;
- COUNTER_DATA averageDiskQueueLength;
- COUNTER_DATA averageDiskReadQueueLength;
- COUNTER_DATA averageDiskWriteQueueLength;
- COUNTER_DATA averageDiskSecondsPerTransfer;
- COUNTER_DATA averageDiskSecondsPerRead;
- COUNTER_DATA averageDiskSecondsPerWrite;
- COUNTER_DATA diskTransfersPerSec;
- COUNTER_DATA diskReadsPerSec;
- COUNTER_DATA diskWritesPerSec;
- COUNTER_DATA diskBytesPerSec;
- COUNTER_DATA averageDiskBytesPerTransfer;
- COUNTER_DATA averageDiskBytesPerRead;
- COUNTER_DATA averageDiskBytesPerWrite;
- COUNTER_DATA splitIoPerSec;
-};
-
-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;
-
- ld->percentDiskFree.key = "% Free Space";
- // ld->freeMegabytes.key = "Free Megabytes";
-}
-
-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";
-}
-
-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);
-}
-
-static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL;
-static void initialize(void) {
- initialize_physical_disk(&system_physical_total);
-
- logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
- DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk));
-
- dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL);
-
- physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
- DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk));
-
- dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL);
-}
-
-static STRING *getFileSystemType(const char* diskName) {
- if (!diskName || !*diskName) return NULL;
-
- char fileSystemNameBuffer[128] = {0}; // Buffer for file system name
- char pathBuffer[256] = {0}; // Path buffer to accommodate different formats
- DWORD serialNumber = 0;
- DWORD maxComponentLength = 0;
- DWORD fileSystemFlags = 0;
- BOOL success;
-
- // Check if the input is likely a drive letter (e.g., "C:")
- if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0')
- snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\\"
- else
- // Assume it's a Volume GUID path or a device path
- snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s", diskName); // Format as "\\.\HarddiskVolume1"
-
- // Attempt to get the volume information
- success = GetVolumeInformation(
- pathBuffer, // Path to the disk
- NULL, // We don't need the volume name
- 0, // Size of volume name buffer is 0
- &serialNumber, // Volume serial number
- &maxComponentLength, // Maximum component length
- &fileSystemFlags, // File system flags
- fileSystemNameBuffer, // File system name buffer
- sizeof(fileSystemNameBuffer) // Size of file system name buffer
- );
-
- if (success && fileSystemNameBuffer[0]) {
- char *s = fileSystemNameBuffer;
- while(*s) { *s = tolower((uint8_t)*s); s++; }
- return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
- }
- else
- return NULL;
-}
-
-static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
- DICTIONARY *dict = logicalDisks;
-
- PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk");
- if(!pObjectType) return false;
-
- PERF_INSTANCE_DEFINITION *pi = NULL;
- for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
- pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
- if(!pi) break;
-
- if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
- strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
-
- if(strcasecmp(windows_shared_buffer, "_Total") == 0)
- continue;
-
- struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
-
- if(!d->collected_metadata) {
- d->filesystem = getFileSystemType(windows_shared_buffer);
- d->collected_metadata = true;
- }
-
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree);
- // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes);
-
- if(!d->st_disk_space) {
- d->st_disk_space = rrdset_create_localhost(
- "disk_space"
- , windows_shared_buffer, NULL
- , windows_shared_buffer, "disk.space"
- , "Disk Space Usage"
- , "GiB"
- , PLUGIN_WINDOWS_NAME
- , "PerflibStorage"
- , NETDATA_CHART_PRIO_DISKSPACE_SPACE
- , update_every
- , RRDSET_TYPE_STACKED
- );
-
- rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO);
- // rrdlabels_add(d->st->rrdlabels, "mount_root", name, RRDLABEL_SRC_AUTO);
-
- if(d->filesystem)
- rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", string2str(d->filesystem), RRDLABEL_SRC_AUTO);
-
- d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
- d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
- }
-
- // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB.
- rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data);
- rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data));
- rrdset_done(d->st_disk_space);
- }
-
- return true;
-}
-
-static void physical_disk_labels(RRDSET *st, void *data) {
- struct physical_disk *d = data;
-
- if(d->device)
- rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO);
-
- if (d->mount_point)
- 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) {
- DICTIONARY *dict = physicalDisks;
-
- PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
- if(!pObjectType) return false;
-
- PERF_INSTANCE_DEFINITION *pi = NULL;
- for (LONG i = 0; i < pObjectType->NumInstances; i++) {
- pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
- if (!pi)
- break;
-
- if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
- 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++;
- }
-
- struct physical_disk *d;
- bool is_system;
- if (strcasecmp(windows_shared_buffer, "_Total") == 0) {
- d = &system_physical_total;
- is_system = true;
- }
- else {
- d = dictionary_set(dict, device, NULL, sizeof(*d));
- is_system = false;
- }
-
- if (!d->collected_metadata) {
- // TODO collect metadata - device_type, serial, id
- d->device = string_strdupz(device);
- d->mount_point = string_strdupz(mount_point);
- d->collected_metadata = true;
- }
-
- 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);
- else
- common_disk_io(
- &d->disk_io,
- device,
- NULL,
- d->diskReadBytesPerSec.current.Data,
- d->diskWriteBytesPerSec.current.Data,
- update_every,
- physical_disk_labels,
- 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);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer);
- 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);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite);
- perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec);
- }
-
- return true;
-}
-
-int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) {
- static bool initialized = false;
-
- if(unlikely(!initialized)) {
- initialize();
- initialized = true;
- }
-
- DWORD id = RegistryFindIDByName("LogicalDisk");
- if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
- return -1;
-
- PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
- if(!pDataBlock) return -1;
-
- do_logical_disk(pDataBlock, update_every);
- do_physical_disk(pDataBlock, update_every);
-
- return 0;
-}
+// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME +#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage" +#include "../common-contexts/common-contexts.h" +#include "libnetdata/os/windows-wmi/windows-wmi.h" + +struct logical_disk { + usec_t last_collected; + bool collected_metadata; + + UINT DriveType; + DWORD SerialNumber; + bool readonly; + + STRING *filesystem; + + RRDSET *st_disk_space; + RRDDIM *rd_disk_space_used; + RRDDIM *rd_disk_space_free; + + COUNTER_DATA percentDiskFree; + // COUNTER_DATA freeMegabytes; +}; + +struct physical_disk { + usec_t last_collected; + bool collected_metadata; + + STRING *device; + STRING *mount_point; + STRING *manufacturer; + STRING *model; + STRING *media_type; + STRING *name; + STRING *device_id; + + ND_DISK_IO disk_io; + // COUNTER_DATA diskBytesPerSec; + COUNTER_DATA diskReadBytesPerSec; + COUNTER_DATA diskWriteBytesPerSec; + + ND_DISK_OPS disk_ops; + // COUNTER_DATA diskTransfersPerSec; + 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; + + ND_DISK_AWAIT disk_await; + COUNTER_DATA averageDiskSecondsPerRead; + COUNTER_DATA averageDiskSecondsPerWrite; + + ND_DISK_SVCTM disk_svctm; + COUNTER_DATA averageDiskSecondsPerTransfer; + + ND_DISK_AVGSZ disk_avgsz; + //COUNTER_DATA averageDiskBytesPerTransfer; + COUNTER_DATA averageDiskBytesPerRead; + COUNTER_DATA averageDiskBytesPerWrite; + + COUNTER_DATA splitIoPerSec; + RRDSET *st_split; + RRDDIM *rd_split; +}; + +struct physical_disk system_physical_total = { + .collected_metadata = true, +}; + +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); +} + +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"; +} + +static void physical_disk_cleanup(struct physical_disk *d) { + string_freez(d->device); + string_freez(d->mount_point); + string_freez(d->manufacturer); + string_freez(d->model); + string_freez(d->media_type); + string_freez(d->name); + string_freez(d->device_id); + + 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); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_await.st_await); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_svctm.st_svctm); + rrdset_is_obsolete___safe_from_collector_thread(d->disk_avgsz.st_avgsz); + rrdset_is_obsolete___safe_from_collector_thread(d->st_split); +} + +void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct physical_disk *pd = value; + physical_disk_initialize(pd); +} + +static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL; +static void initialize(void) { + physical_disk_initialize(&system_physical_total); + + logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk)); + + dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL); + + physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk)); + + dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL); +} + +static STRING *getFileSystemType(struct logical_disk *d, const char* diskName) { + if (!diskName || !*diskName) return NULL; + + char fileSystemNameBuffer[128] = {0}; // Buffer for file system name + char pathBuffer[256] = {0}; // Path buffer to accommodate different formats + DWORD serialNumber = 0; + DWORD maxComponentLength = 0; + DWORD fileSystemFlags = 0; + BOOL success; + + // Check if the input is likely a drive letter (e.g., "C:") + if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0') + snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\" + else + // Assume it's a Volume GUID path or a device path + snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s\\", diskName); // Format as "\\.\HarddiskVolume1\" + + d->DriveType = GetDriveTypeA(pathBuffer); + + // Attempt to get the volume information + success = GetVolumeInformationA( + pathBuffer, // Path to the disk + NULL, // We don't need the volume name + 0, // Size of volume name buffer is 0 + &serialNumber, // Volume serial number + &maxComponentLength, // Maximum component length + &fileSystemFlags, // File system flags + fileSystemNameBuffer, // File system name buffer + sizeof(fileSystemNameBuffer) // Size of file system name buffer + ); + + if(success) { + d->readonly = fileSystemFlags & FILE_READ_ONLY_VOLUME; + d->SerialNumber = serialNumber; + + if (fileSystemNameBuffer[0]) { + char *s = fileSystemNameBuffer; + while (*s) { + *s = tolower((uint8_t) *s); + s++; + } + return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name + } + } + return NULL; +} + +static const char *drive_type_to_str(UINT type) { + switch(type) { + default: + case 0: return "unknown"; + case 1: return "norootdir"; + case 2: return "removable"; + case 3: return "fixed"; + case 4: return "remote"; + case 5: return "cdrom"; + case 6: return "ramdisk"; + } +} + +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"); + if(!pObjectType) return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for(LONG i = 0; i < pObjectType->NumInstances ; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if(!pi) break; + + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if(strcasecmp(windows_shared_buffer, "_Total") == 0) + 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(d, windows_shared_buffer); + d->collected_metadata = true; + } + + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree); + // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes); + + if(!d->st_disk_space) { + d->st_disk_space = rrdset_create_localhost( + "disk_space" + , windows_shared_buffer + , NULL + , windows_shared_buffer + , "disk.space" + , "Disk Space Usage" + , "GiB" + , PLUGIN_WINDOWS_NAME + , "PerflibStorage" + , NETDATA_CHART_PRIO_DISKSPACE_SPACE + , update_every + , RRDSET_TYPE_STACKED + ); + + rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "drive_type", drive_type_to_str(d->DriveType), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", d->filesystem ? string2str(d->filesystem) : "unknown", RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_disk_space->rrdlabels, "rw_mode", d->readonly ? "ro" : "rw", RRDLABEL_SRC_AUTO); + + { + char buf[UINT64_HEX_MAX_LENGTH]; + print_uint64_hex(buf, d->SerialNumber); + rrdlabels_add(d->st_disk_space->rrdlabels, "serial_number", buf, RRDLABEL_SRC_AUTO); + } + + d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + } + + // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB. + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data); + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data)); + 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; +} + +static void physical_disk_labels(RRDSET *st, void *data) { + struct physical_disk *d = data; + + if(d->device) + rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO); + + if (d->mount_point) + rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO); + +// if (d->manufacturer) +// rrdlabels_add(st->rrdlabels, "manufacturer", string2str(d->manufacturer), RRDLABEL_SRC_AUTO); + + if (d->model) + rrdlabels_add(st->rrdlabels, "model", string2str(d->model), RRDLABEL_SRC_AUTO); + +// if (d->media_type) +// rrdlabels_add(st->rrdlabels, "media_type", string2str(d->media_type), RRDLABEL_SRC_AUTO); + +// if (d->name) +// rrdlabels_add(st->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if (d->device_id) + rrdlabels_add(st->rrdlabels, "device_id", string2str(d->device_id), RRDLABEL_SRC_AUTO); +} + +static bool str_is_numeric(const char *s) { + while(*s) if(!isdigit((uint8_t)*s++)) return false; + return true; +} + +static inline double perflib_average_timer_ms(COUNTER_DATA *d) { + if(!d->updated) return 0.0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + LONGLONG freq1 = d->current.Frequency; + + if(data1 >= data0 && time1 > time0 && time0 && freq1) + return ((double)(data1 - data0) / (double)(freq1 / MSEC_PER_SEC)) / (double)(time1 - time0); + + return 0; +} + +static inline uint64_t perflib_average_bulk(COUNTER_DATA *d) { + if(!d->updated) return 0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + + if(data1 >= data0 && time1 > time0 && time0) + return (data1 - data0) / (time1 - time0); + + return 0; +} + +static inline uint64_t perflib_idle_time_percent(COUNTER_DATA *d) { + if(!d->updated) return 0.0; + + ULONGLONG data1 = d->current.Data; + ULONGLONG data0 = d->previous.Data; + LONGLONG time1 = d->current.Time; + LONGLONG time0 = d->previous.Time; + + if(data1 >= data0 && time1 > time0 && time0) { + uint64_t pcent = 100 * (data1 - data0) / (time1 - time0); + return pcent > 100 ? 100 : pcent; + } + + return 0; +} + +#define MAX_WMI_DRIVES 100 +static DiskDriveInfoWMI infos[MAX_WMI_DRIVES]; + +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"); + if(!pObjectType) return false; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + int device_index = -1; + char *device = windows_shared_buffer; + char mount_point[128]; mount_point[0] = '\0'; + + struct physical_disk *d; + bool is_system; + if (strcasecmp(windows_shared_buffer, "_Total") == 0) { + d = &system_physical_total; + 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)) { + device_index = str2ull(device, NULL); + snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %d", device_index); + device = windows_shared_buffer; + } + + d = dictionary_set(dict, device, NULL, sizeof(*d)); + is_system = false; + } + d->last_collected = now_ut; + + if (!d->collected_metadata) { + if(!is_system && device_index != -1) { + size_t infoCount = GetDiskDriveInfo(infos, _countof(infos)); + for(size_t k = 0; k < infoCount ; k++) { + if(infos[k].Index != device_index) + continue; + + d->manufacturer = string_strdupz(infos[k].Manufacturer); + d->model = string_strdupz(infos[k].Model); + d->media_type = string_strdupz(infos[k].MediaType); + d->name = string_strdupz(infos[k].Name); + d->device_id = string_strdupz(infos[k].DeviceID); + + break; + } + } + + d->device = string_strdupz(device); + d->mount_point = string_strdupz(mount_point); + d->collected_metadata = true; + } + + 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); + else + common_disk_io( + &d->disk_io, + device, + NULL, + d->diskReadBytesPerSec.current.Data, + d->diskWriteBytesPerSec.current.Data, + update_every, + physical_disk_labels, + d); + } + + 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->diskReadsPerSec.current.Data, + d->diskWritesPerSec.current.Data, + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime)) { + common_disk_util( + &d->disk_util, + device, + NULL, + 100 - perflib_idle_time_percent(&d->percentIdleTime), + 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); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite)) { + + common_disk_await( + &d->disk_await, + device, + NULL, + perflib_average_timer_ms(&d->averageDiskSecondsPerRead), + perflib_average_timer_ms(&d->averageDiskSecondsPerWrite), + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer)) { + common_disk_svctm( + &d->disk_svctm, + device, + NULL, + perflib_average_timer_ms(&d->averageDiskSecondsPerTransfer), + update_every, + physical_disk_labels, + d); + } + + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead) && + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite)) { + + common_disk_avgsz( + &d->disk_avgsz, + device, + NULL, + perflib_average_bulk(&d->averageDiskBytesPerRead), + perflib_average_bulk(&d->averageDiskBytesPerWrite), + update_every, + physical_disk_labels, + d); + } + + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec)) { + if (!d->st_split) { + d->st_split = rrdset_create_localhost( + "disk_split", + device, + NULL, + "iops", + "disk.split", + "Split I/O Operations", + "operations/s", + _COMMON_PLUGIN_NAME, + _COMMON_PLUGIN_MODULE_NAME, + NETDATA_CHART_PRIO_DISK_SPLIT, + update_every, + RRDSET_TYPE_LINE + ); + + d->rd_split = rrddim_add(d->st_split, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + physical_disk_labels(d->st_split, d); + } + + rrddim_set_by_pointer(d->st_split, d->rd_split, d->splitIoPerSec.current.Data); + rrdset_done(d->st_split); + } + } + + // 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; +} + +int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if(unlikely(!initialized)) { + initialize(); + initialized = true; + } + + DWORD id = RegistryFindIDByName("LogicalDisk"); + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if(!pDataBlock) return -1; + + 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; +} |