summaryrefslogtreecommitdiffstats
path: root/src/collectors/windows.plugin/perflib-storage.c
blob: d3b80052f97bb499193e1a675e8a97cab8b03496 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// 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;
}