Windows.plugin (improvements and fixes) (#22039)
thiagoftsm committed
Mar 25, 2026 at 22:30 UTC
5a80d3eb803acdd64091775238a5eef0d4eaed41
9 files changed
+303
-70
src/collectors/windows.plugin/GetHardwareInfo.c
+18
-5
@@ -35,6 +35,22 @@ static const int IOCTL_RETRY_DELAY_MS = 10;
35
static const int THREAD_JOIN_FALLBACK_WAIT_MS = 2000;
36
#define INVALID_TEMP ((collected_number)(-1))
37
38
+static bool netdata_expand_driver_path(char *expanded_path, size_t expanded_path_size)
39
+{
40
+ DWORD ret = ExpandEnvironmentStringsA(drv_path, expanded_path, (DWORD)expanded_path_size);
41
+ if (ret == 0) {
42
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot expand environment strings. Error= %lu \n", GetLastError());
43
+ return false;
44
+ }
45
+
46
+ if (ret > expanded_path_size) {
47
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "Expanded driver path exceeds buffer size (%lu bytes needed)\n", ret);
48
+ return false;
49
+ }
50
+
51
+ return true;
52
+}
53
+
54
static void netdata_stop_driver()
55
{
56
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
@@ -79,8 +95,7 @@ int netdata_install_driver()
95
}
96
97
char expanded_path[MAX_PATH];
82
- if (ExpandEnvironmentStringsA(drv_path, expanded_path, sizeof(expanded_path)) == 0) {
83
- nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot expand environment strings. Error= %lu \n", GetLastError());
98
+ if (!netdata_expand_driver_path(expanded_path, sizeof(expanded_path))) {
99
CloseServiceHandle(scm);
100
return -1;
101
}
@@ -390,9 +405,7 @@ static void netdata_detect_cpu()
405
static int initialize()
406
{
407
char expanded_path[MAX_PATH];
393
- if (ExpandEnvironmentStringsA(drv_path, expanded_path, sizeof(expanded_path)) == 0) {
394
- nd_log(
395
- NDLS_COLLECTORS, NDLP_ERR, "Cannot expand driver path environment strings. Error= %lu \n", GetLastError());
408
+ if (!netdata_expand_driver_path(expanded_path, sizeof(expanded_path))) {
409
return -1;
410
}
411
src/collectors/windows.plugin/GetPowerSupply.c
+73
-36
@@ -15,17 +15,34 @@
15
#include <setupapi.h> // for SetupDi*
16
#include <batclass.h> // for BATTERY_*
17
18
-static struct power_supply *power_supply_root = NULL;
18
+struct win_battery {
19
+ struct power_supply ps;
20
+ struct simple_property voltage;
21
+ bool seen; // true if discovered in the current collection pass
22
+ struct win_battery *next;
23
+};
24
20
-static inline void netdata_allocate_power_supply(char *path)
25
+static struct win_battery *batteries_root = NULL;
26
+
27
+static struct win_battery *netdata_get_or_create_battery(const char *name)
28
{
22
- power_supply_root = callocz(1, sizeof(struct power_supply));
23
- power_supply_root->capacity = callocz(1, sizeof(struct simple_property));
29
+ for (struct win_battery *battery = batteries_root; battery; battery = battery->next) {
30
+ if (battery->ps.name && !strcmp(battery->ps.name, name))
31
+ return battery;
32
+ }
33
+
34
+ struct win_battery *battery = callocz(1, sizeof(*battery));
35
+ battery->ps.name = strdupz(name);
36
+ battery->ps.capacity = callocz(1, sizeof(struct simple_property));
37
+ battery->next = batteries_root;
38
+ batteries_root = battery;
39
+
40
+ return battery;
41
}
42
43
static inline void netdata_update_power_supply_values(
44
+ struct win_battery *battery,
45
HANDLE hBattery,
28
- struct simple_property *voltage,
46
BATTERY_INFORMATION *bi,
47
BATTERY_QUERY_INFORMATION *bqi)
48
{
@@ -42,19 +59,18 @@ static inline void netdata_update_power_supply_values(
59
NETDATA_DOUBLE den = bi->FullChargedCapacity;
60
num = (den) ? num / den : 0;
61
45
- power_supply_root->capacity->value = (unsigned long long)(num * 100.0);
62
+ battery->ps.capacity->value = (unsigned long long)(num * 100.0);
63
}
64
48
- if (bs.Voltage != BATTERY_UNKNOWN_VOLTAGE) {
49
- voltage->value = bs.Voltage;
50
- }
65
+ if (bs.Voltage != BATTERY_UNKNOWN_VOLTAGE)
66
+ battery->voltage.value = bs.Voltage;
67
}
68
53
-static void netdata_power_supply_plot(struct simple_property *voltage, int update_every)
69
+static void netdata_power_supply_plot(struct win_battery *battery, int update_every)
70
{
71
rrdset_create_simple_prop(
56
- power_supply_root,
57
- power_supply_root->capacity,
72
+ &battery->ps,
73
+ battery->ps.capacity,
74
"Battery capacity",
75
"capacity",
76
1,
@@ -63,8 +79,8 @@ static void netdata_power_supply_plot(struct simple_property *voltage, int updat
79
update_every);
80
81
rrdset_create_simple_prop(
66
- power_supply_root,
67
- voltage,
82
+ &battery->ps,
83
+ &battery->voltage,
84
"Power supply voltage",
85
"now",
86
1000,
@@ -73,9 +89,27 @@ static void netdata_power_supply_plot(struct simple_property *voltage, int updat
89
update_every);
90
}
91
92
+void do_GetPowerSupply_cleanup(void)
93
+{
94
+ while (batteries_root) {
95
+ struct win_battery *battery = batteries_root;
96
+ batteries_root = battery->next;
97
+
98
+ if (battery->ps.capacity && battery->ps.capacity->st)
99
+ rrdset_is_obsolete___safe_from_collector_thread(battery->ps.capacity->st);
100
+ if (battery->voltage.st)
101
+ rrdset_is_obsolete___safe_from_collector_thread(battery->voltage.st);
102
+
103
+ freez(battery->ps.name);
104
+ freez(battery->ps.capacity);
105
+ freez(battery);
106
+ }
107
+}
108
+
109
int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
110
{
78
- static struct simple_property voltage = {0};
111
+ for (struct win_battery *b = batteries_root; b; b = b->next)
112
+ b->seen = false;
113
114
HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
115
if (hdev == INVALID_HANDLE_VALUE)
@@ -88,6 +122,7 @@ int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
122
DWORD cbRequired = 0;
123
PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = NULL;
124
HANDLE hBattery = NULL;
125
+
126
SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0);
127
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
128
goto endPowerSupply;
@@ -112,7 +147,6 @@ int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
147
goto endPowerSupply;
148
149
BATTERY_QUERY_INFORMATION bqi = {0};
115
-
150
DWORD dwWait = 0;
151
DWORD dwOut;
152
@@ -130,34 +164,19 @@ int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
164
165
BATTERY_INFORMATION bi = {0};
166
bqi.InformationLevel = BatteryInformation;
133
-
167
if (!DeviceIoControl(
168
hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL))
169
goto endPowerSupply;
170
138
- if (!power_supply_root)
139
- netdata_allocate_power_supply(pdidd->DevicePath);
140
-
171
char name[RRD_ID_LENGTH_MAX + 1];
172
snprintfz(name, sizeof(name), "BAT%d", i + 1);
173
+ struct win_battery *battery = netdata_get_or_create_battery(name);
174
+ battery->seen = true;
175
+ netdata_update_power_supply_values(battery, hBattery, &bi, &bqi);
176
+ netdata_power_supply_plot(battery, update_every);
177
144
- if (likely(power_supply_root->name))
145
- freez(power_supply_root->name);
146
- if (likely(power_supply_root->capacity->filename))
147
- freez(power_supply_root->capacity->filename);
148
- if (likely(voltage.filename))
149
- freez(voltage.filename);
150
-
151
- power_supply_root->name = power_supply_root->capacity->filename = voltage.filename = NULL;
152
- power_supply_root->name = strdupz(name);
153
- power_supply_root->capacity->filename = strdupz(power_supply_root->name);
154
- voltage.filename = strdupz(power_supply_root->name);
155
-
156
- netdata_update_power_supply_values(hBattery, &voltage, &bi, &bqi);
157
-
158
- netdata_power_supply_plot(&voltage, update_every);
178
endPowerSupply:
160
- if (hBattery)
179
+ if (hBattery != NULL && hBattery != INVALID_HANDLE_VALUE)
180
CloseHandle(hBattery);
181
182
if (pdidd)
@@ -166,5 +185,23 @@ int do_GetPowerSupply(int update_every, usec_t dt __maybe_unused)
185
186
SetupDiDestroyDeviceInfoList(hdev);
187
188
+ // Retire batteries that were not seen in this discovery pass (e.g. physically removed).
189
+ struct win_battery **pp = &batteries_root;
190
+ while (*pp) {
191
+ struct win_battery *b = *pp;
192
+ if (!b->seen) {
193
+ if (b->ps.capacity && b->ps.capacity->st)
194
+ rrdset_is_obsolete___safe_from_collector_thread(b->ps.capacity->st);
195
+ if (b->voltage.st)
196
+ rrdset_is_obsolete___safe_from_collector_thread(b->voltage.st);
197
+ *pp = b->next;
198
+ freez(b->ps.name);
199
+ freez(b->ps.capacity);
200
+ freez(b);
201
+ } else {
202
+ pp = &b->next;
203
+ }
204
+ }
205
+
206
return 0;
207
}
src/collectors/windows.plugin/GetSensors.c
+42
@@ -346,6 +346,39 @@ struct sensor_data {
346
347
DICTIONARY *sensors;
348
349
+static void netdata_sensors_free_extra_values(struct netdata_sensors_extra_values *values)
350
+{
351
+ while (values) {
352
+ struct netdata_sensors_extra_values *next = values->next;
353
+ freez(values);
354
+ values = next;
355
+ }
356
+}
357
+
358
+static void netdata_sensor_cleanup(struct sensor_data *sd)
359
+{
360
+ if (sd->st_sensor_state)
361
+ rrdset_is_obsolete___safe_from_collector_thread(sd->st_sensor_state);
362
+ if (sd->st_sensor_data)
363
+ rrdset_is_obsolete___safe_from_collector_thread(sd->st_sensor_data);
364
+
365
+ freez((void *)sd->type);
366
+ freez((void *)sd->category);
367
+ freez((void *)sd->name);
368
+ freez((void *)sd->manufacturer);
369
+ freez((void *)sd->model);
370
+ freez(sd->external_config);
371
+ netdata_sensors_free_extra_values(sd->values);
372
+
373
+ sd->type = NULL;
374
+ sd->category = NULL;
375
+ sd->name = NULL;
376
+ sd->manufacturer = NULL;
377
+ sd->model = NULL;
378
+ sd->external_config = NULL;
379
+ sd->values = NULL;
380
+}
381
+
382
// Microsoft appends additional data
383
#define ADDTIONAL_UUID_STR_LEN (UUID_STR_LEN + 17)
384
@@ -712,6 +745,12 @@ void dict_sensor_insert(const DICTIONARY_ITEM *item __maybe_unused, void *value,
745
sd->add_factor = 0.0;
746
}
747
748
+void dict_sensor_delete(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
749
+{
750
+ struct sensor_data *sd = value;
751
+ netdata_sensor_cleanup(sd);
752
+}
753
+
754
static int initialize(int update_every)
755
{
756
// Note: COM and Sensor API initialization is now done in the sensor thread
@@ -724,6 +763,7 @@ static int initialize(int update_every)
763
sensors = dictionary_create_advanced(
764
DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct sensor_data));
765
dictionary_register_insert_callback(sensors, dict_sensor_insert, NULL);
766
+ dictionary_register_delete_callback(sensors, dict_sensor_delete, NULL);
767
768
sensors_thread_update =
769
nd_thread_create("sensors_upd", NETDATA_THREAD_OPTION_DEFAULT, netdata_sensors_monitor, &update_every);
@@ -906,9 +946,11 @@ void do_Sensors_cleanup()
946
// The thread handles its own COM cleanup (CoUninitialize) and pSensorManager release
947
if (nd_thread_join(sensors_thread_update))
948
nd_log_daemon(NDLP_ERR, "Failed to join sensors thread update");
949
+ sensors_thread_update = NULL;
950
951
__netdata_mutex_destroy(&sensors_mutex);
952
dictionary_destroy(sensors);
953
+ sensors = NULL;
954
// Note: pSensorManager is owned and cleaned up by the sensor thread itself
955
// No additional cleanup needed here since the thread has already released resources
956
}
src/collectors/windows.plugin/GetServicesStatus.c
+27
@@ -7,6 +7,7 @@
7
struct win_service {
8
char *service_name;
9
DWORD pid;
10
+ bool present;
11
12
RRDSET *st_service_state;
13
RRDDIM *rd_service_state_running;
@@ -25,6 +26,8 @@ static DICTIONARY *win_services = NULL;
26
27
static void win_service_cleanup(struct win_service *s)
28
{
29
+ if (s->st_service_state)
30
+ rrdset_is_obsolete___safe_from_collector_thread(s->st_service_state);
31
freez(s->service_name);
32
s->service_name = NULL;
33
}
@@ -60,6 +63,16 @@ static void initialize(void)
63
dictionary_register_delete_callback(win_services, dict_win_service_delete_cb, NULL);
64
}
65
66
+static int dict_win_services_mark_missing_cb(
67
+ const DICTIONARY_ITEM *item __maybe_unused,
68
+ void *value,
69
+ void *data __maybe_unused)
70
+{
71
+ struct win_service *p = value;
72
+ p->present = false;
73
+ return 1;
74
+}
75
+
76
static BOOL fill_dictionary_with_content()
77
{
78
PVOID buffer = NULL;
@@ -86,6 +99,7 @@ static BOOL fill_dictionary_with_content()
99
NULL);
100
101
if (ret) {
102
+ dictionary_walkthrough_write(win_services, dict_win_services_mark_missing_cb, NULL);
103
// This only happens if there are truly 0 services in the system (a valid edge case).
104
goto endServiceCollection;
105
}
@@ -117,6 +131,7 @@ static BOOL fill_dictionary_with_content()
131
goto endServiceCollection;
132
}
133
134
+ dictionary_walkthrough_write(win_services, dict_win_services_mark_missing_cb, NULL);
135
services = (LPENUM_SERVICE_STATUS_PROCESS)buffer;
136
137
for (ULONG i = 0; i < total_services; i++) {
@@ -130,6 +145,7 @@ static BOOL fill_dictionary_with_content()
145
146
p->ServiceState.current.Data = service->ServiceStatusProcess.dwCurrentState;
147
p->pid = service->ServiceStatusProcess.dwProcessId;
148
+ p->present = true;
149
}
150
151
ret = TRUE;
@@ -231,6 +247,16 @@ static int dict_win_services_charts_cb(const DICTIONARY_ITEM *item __maybe_unuse
247
return 1;
248
}
249
250
+static int dict_win_services_delete_missing_cb(const DICTIONARY_ITEM *item, void *value, void *data __maybe_unused)
251
+{
252
+ struct win_service *p = value;
253
+
254
+ if (!p->present)
255
+ dictionary_del(win_services, dictionary_acquired_item_name(item));
256
+
257
+ return 1;
258
+}
259
+
260
int do_GetServicesStatus(int update_every, usec_t dt __maybe_unused)
261
{
262
#define NETDATA_SERVICE_MAX_TRY (5)
@@ -255,6 +281,7 @@ int do_GetServicesStatus(int update_every, usec_t dt __maybe_unused)
281
}
282
283
limit = 0;
284
+ dictionary_walkthrough_write(win_services, dict_win_services_delete_missing_cb, NULL);
285
dictionary_sorted_walkthrough_read(win_services, dict_win_services_charts_cb, &update_every);
286
287
return 0;
src/collectors/windows.plugin/perflib-netframework.c
+100
-11
@@ -16,6 +16,8 @@ enum netdata_netframework_metrics {
16
};
17
18
struct net_framework_instances {
19
+ usec_t last_collected;
20
+
21
RRDSET *st_clrexception_thrown;
22
RRDDIM *rd_clrexception_thrown;
23
@@ -154,6 +156,9 @@ struct net_framework_instances {
156
COUNTER_DATA NETFrameworkCLRLocksAndThreadsContentions;
157
};
158
159
+static usec_t netframework_now_ut = 0;
160
+static DICTIONARY *processes = NULL;
161
+
162
static inline void initialize_net_framework_processes_keys(struct net_framework_instances *p)
163
{
164
p->NETFrameworkCLRExceptionFilters.key = "# of Filters / sec";
@@ -207,7 +212,65 @@ void dict_net_framework_processes_insert_cb(
212
initialize_net_framework_processes_keys(p);
213
}
214
210
-static DICTIONARY *processes = NULL;
215
+static void netframework_mark_chart_obsolete(RRDSET **st)
216
+{
217
+ if (*st)
218
+ rrdset_is_obsolete___safe_from_collector_thread(*st);
219
+
220
+ *st = NULL;
221
+}
222
+
223
+static void netframework_process_cleanup(struct net_framework_instances *p)
224
+{
225
+ netframework_mark_chart_obsolete(&p->st_clrexception_thrown);
226
+ netframework_mark_chart_obsolete(&p->st_clrexception_filters);
227
+ netframework_mark_chart_obsolete(&p->st_clrexception_finallys);
228
+ netframework_mark_chart_obsolete(&p->st_clrexception_total_catch_depth);
229
+ netframework_mark_chart_obsolete(&p->st_clrinterop_com_callable_wrappers);
230
+ netframework_mark_chart_obsolete(&p->st_clrinterop_marshalling);
231
+ netframework_mark_chart_obsolete(&p->st_clrinterop_interop_stubs_created);
232
+ netframework_mark_chart_obsolete(&p->st_clrjit_methods);
233
+ netframework_mark_chart_obsolete(&p->st_clrjit_time);
234
+ netframework_mark_chart_obsolete(&p->st_clrjit_standard_failures);
235
+ netframework_mark_chart_obsolete(&p->st_clrjit_il_bytes);
236
+ netframework_mark_chart_obsolete(&p->st_clrloading_heap_size);
237
+ netframework_mark_chart_obsolete(&p->st_clrloading_app_domains_loaded);
238
+ netframework_mark_chart_obsolete(&p->st_clrloading_app_domains_unloaded);
239
+ netframework_mark_chart_obsolete(&p->st_clrloading_assemblies_loaded);
240
+ netframework_mark_chart_obsolete(&p->st_clrloading_classes_loaded);
241
+ netframework_mark_chart_obsolete(&p->st_clrloading_class_load_failure);
242
+ netframework_mark_chart_obsolete(&p->st_clrremoting_channels);
243
+ netframework_mark_chart_obsolete(&p->st_clrremoting_context_bound_classes_loaded);
244
+ netframework_mark_chart_obsolete(&p->st_clrremoting_context_bound_objects);
245
+ netframework_mark_chart_obsolete(&p->st_clrremoting_context_proxies);
246
+ netframework_mark_chart_obsolete(&p->st_clrremoting_contexts);
247
+ netframework_mark_chart_obsolete(&p->st_clrremoting_remote_calls);
248
+ netframework_mark_chart_obsolete(&p->st_clrsecurity_link_time_checks);
249
+ netframework_mark_chart_obsolete(&p->st_clrsecurity_rt_checks_time);
250
+ netframework_mark_chart_obsolete(&p->st_clrsecurity_stack_walk_depth);
251
+ netframework_mark_chart_obsolete(&p->st_clrsecurity_run_time_checks);
252
+ netframework_mark_chart_obsolete(&p->st_clrlocksandthreads_queue_length);
253
+ netframework_mark_chart_obsolete(&p->st_clrlocksandthreads_current_logical_threads);
254
+ netframework_mark_chart_obsolete(&p->st_clrlocksandthreads_current_physical_threads);
255
+ netframework_mark_chart_obsolete(&p->st_clrlocksandthreads_recognized_threads);
256
+ netframework_mark_chart_obsolete(&p->st_clrlocksandthreads_contentions);
257
+}
258
+
259
+static void dict_net_framework_processes_delete_cb(
260
+ const DICTIONARY_ITEM *item __maybe_unused,
261
+ void *value,
262
+ void *data __maybe_unused)
263
+{
264
+ struct net_framework_instances *p = value;
265
+ netframework_process_cleanup(p);
266
+}
267
+
268
+static inline struct net_framework_instances *netframework_process_get(const char *name)
269
+{
270
+ struct net_framework_instances *p = dictionary_set(processes, name, NULL, sizeof(*p));
271
+ p->last_collected = netframework_now_ut;
272
+ return p;
273
+}
274
275
static void initialize(void)
276
{
@@ -215,6 +278,7 @@ static void initialize(void)
278
DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct net_framework_instances));
279
280
dictionary_register_insert_callback(processes, dict_net_framework_processes_insert_cb, NULL);
281
+ dictionary_register_delete_callback(processes, dict_net_framework_processes_delete_cb, NULL);
282
}
283
284
static void
@@ -233,7 +297,7 @@ netdata_framework_clr_exceptions(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *
297
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
298
continue;
299
236
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
300
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
301
302
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRExceptionThrown)) {
303
if (!p->st_clrexception_thrown) {
@@ -383,7 +447,7 @@ static void netdata_framework_clr_interop(PERF_DATA_BLOCK *pDataBlock, PERF_OBJE
447
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
448
continue;
449
386
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
450
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
451
452
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRInteropCOMCallableWrappers)) {
453
if (!p->st_clrinterop_com_callable_wrappers) {
@@ -509,7 +573,7 @@ static void netdata_framework_clr_jit(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_T
573
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
574
continue;
575
512
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
576
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
577
578
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRJITMethods)) {
579
if (!p->st_clrjit_methods) {
@@ -653,7 +717,7 @@ static void netdata_framework_clr_loading(PERF_DATA_BLOCK *pDataBlock, PERF_OBJE
717
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
718
continue;
719
656
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
720
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
721
722
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLoadingHeapSize)) {
723
if (!p->st_clrloading_heap_size) {
@@ -867,12 +931,12 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
931
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
932
continue;
933
870
- netdata_fix_chart_name(windows_shared_buffer);
871
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
934
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
935
936
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingChannels)) {
937
if (!p->st_clrremoting_channels) {
938
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_channels", windows_shared_buffer);
939
+ netdata_fix_chart_name(id);
940
p->st_clrremoting_channels = rrdset_create_localhost(
941
"netframework",
942
id,
@@ -904,6 +968,7 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
968
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingContextBoundClassesLoaded)) {
969
if (!p->st_clrremoting_context_bound_classes_loaded) {
970
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_context_bound_classes_loaded", windows_shared_buffer);
971
+ netdata_fix_chart_name(id);
972
p->st_clrremoting_context_bound_classes_loaded = rrdset_create_localhost(
973
"netframework",
974
id,
@@ -938,6 +1003,7 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1003
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingContextBoundObjects)) {
1004
if (!p->st_clrremoting_context_bound_objects) {
1005
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_context_bound_objects", windows_shared_buffer);
1006
+ netdata_fix_chart_name(id);
1007
p->st_clrremoting_context_bound_objects = rrdset_create_localhost(
1008
"netframework",
1009
id,
@@ -971,6 +1037,7 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1037
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingContextProxies)) {
1038
if (!p->st_clrremoting_context_proxies) {
1039
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_context_proxies", windows_shared_buffer);
1040
+ netdata_fix_chart_name(id);
1041
p->st_clrremoting_context_proxies = rrdset_create_localhost(
1042
"netframework",
1043
id,
@@ -1002,6 +1069,7 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1069
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingContexts)) {
1070
if (!p->st_clrremoting_contexts) {
1071
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_contexts", windows_shared_buffer);
1072
+ netdata_fix_chart_name(id);
1073
p->st_clrremoting_contexts = rrdset_create_localhost(
1074
"netframework",
1075
id,
@@ -1033,6 +1101,7 @@ static void netdata_framework_clr_remoting(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1101
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRRemotingRemoteCalls)) {
1102
if (!p->st_clrremoting_remote_calls) {
1103
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrremoting_calls", windows_shared_buffer);
1104
+ netdata_fix_chart_name(id);
1105
p->st_clrremoting_remote_calls = rrdset_create_localhost(
1106
"netframework",
1107
id,
@@ -1078,12 +1147,12 @@ static void netdata_framework_clr_security(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1147
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
1148
continue;
1149
1081
- netdata_fix_chart_name(windows_shared_buffer);
1082
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
1150
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
1151
1152
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRSecurityLinkTimeChecks)) {
1153
if (!p->st_clrsecurity_link_time_checks) {
1154
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrsecurity_link_time_checks", windows_shared_buffer);
1155
+ netdata_fix_chart_name(id);
1156
p->st_clrsecurity_link_time_checks = rrdset_create_localhost(
1157
"netframework",
1158
id,
@@ -1116,6 +1185,7 @@ static void netdata_framework_clr_security(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1185
perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRSecurityFrequency_PerfTime)) {
1186
if (!p->st_clrsecurity_rt_checks_time) {
1187
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrsecurity_checks_time", windows_shared_buffer);
1188
+ netdata_fix_chart_name(id);
1189
p->st_clrsecurity_rt_checks_time = rrdset_create_localhost(
1190
"netframework",
1191
id,
@@ -1150,6 +1220,7 @@ static void netdata_framework_clr_security(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1220
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRSecurityStackWalkDepth)) {
1221
if (!p->st_clrsecurity_stack_walk_depth) {
1222
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrsecurity_stack_walk_depth", windows_shared_buffer);
1223
+ netdata_fix_chart_name(id);
1224
p->st_clrsecurity_stack_walk_depth = rrdset_create_localhost(
1225
"netframework",
1226
id,
@@ -1181,6 +1252,7 @@ static void netdata_framework_clr_security(PERF_DATA_BLOCK *pDataBlock, PERF_OBJ
1252
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRSecurityRunTimeChecks)) {
1253
if (!p->st_clrsecurity_run_time_checks) {
1254
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrsecurity_runtime_checks", windows_shared_buffer);
1255
+ netdata_fix_chart_name(id);
1256
p->st_clrsecurity_run_time_checks = rrdset_create_localhost(
1257
"netframework",
1258
id,
@@ -1227,12 +1299,12 @@ netdata_framework_clr_locks_and_threads(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT
1299
if (strcasecmp(windows_shared_buffer, "_Global_") == 0)
1300
continue;
1301
1230
- netdata_fix_chart_name(windows_shared_buffer);
1231
- struct net_framework_instances *p = dictionary_set(processes, windows_shared_buffer, NULL, sizeof(*p));
1302
+ struct net_framework_instances *p = netframework_process_get(windows_shared_buffer);
1303
1304
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLocksAndThreadsQueueLength)) {
1305
if (!p->st_clrlocksandthreads_queue_length) {
1306
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrlocksandthreads_queue_length", windows_shared_buffer);
1307
+ netdata_fix_chart_name(id);
1308
p->st_clrlocksandthreads_queue_length = rrdset_create_localhost(
1309
"netframework",
1310
id,
@@ -1268,6 +1340,7 @@ netdata_framework_clr_locks_and_threads(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT
1340
if (!p->st_clrlocksandthreads_current_logical_threads) {
1341
snprintfz(
1342
id, RRD_ID_LENGTH_MAX, "%s_clrlocksandthreads_current_logical_threads", windows_shared_buffer);
1343
+ netdata_fix_chart_name(id);
1344
p->st_clrlocksandthreads_current_logical_threads = rrdset_create_localhost(
1345
"netframework",
1346
id,
@@ -1303,6 +1376,7 @@ netdata_framework_clr_locks_and_threads(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT
1376
if (!p->st_clrlocksandthreads_current_physical_threads) {
1377
snprintfz(
1378
id, RRD_ID_LENGTH_MAX, "%s_clrlocksandthreads_current_physical_threads", windows_shared_buffer);
1379
+ netdata_fix_chart_name(id);
1380
p->st_clrlocksandthreads_current_physical_threads = rrdset_create_localhost(
1381
"netframework",
1382
id,
@@ -1337,6 +1411,7 @@ netdata_framework_clr_locks_and_threads(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT
1411
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLocksAndThreadsRecognizedThreads)) {
1412
if (!p->st_clrlocksandthreads_recognized_threads) {
1413
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrlocksandthreads_recognized_threads", windows_shared_buffer);
1414
+ netdata_fix_chart_name(id);
1415
p->st_clrlocksandthreads_recognized_threads = rrdset_create_localhost(
1416
"netframework",
1417
id,
@@ -1371,6 +1446,7 @@ netdata_framework_clr_locks_and_threads(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT
1446
if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->NETFrameworkCLRLocksAndThreadsContentions)) {
1447
if (!p->st_clrlocksandthreads_contentions) {
1448
snprintfz(id, RRD_ID_LENGTH_MAX, "%s_clrlocksandthreads_contentions", windows_shared_buffer);
1449
+ netdata_fix_chart_name(id);
1450
p->st_clrlocksandthreads_contentions = rrdset_create_localhost(
1451
"netframework",
1452
id,
@@ -1427,6 +1503,8 @@ int do_PerflibNetFramework(int update_every, usec_t dt __maybe_unused)
1503
initialized = true;
1504
}
1505
1506
+ netframework_now_ut = now_monotonic_usec();
1507
+
1508
int i;
1509
for (i = 0; i < NETDATA_NETFRAMEWORK_END; i++) {
1510
DWORD id = RegistryFindIDByName(netframewrk_obj[i].object);
@@ -1444,5 +1522,16 @@ int do_PerflibNetFramework(int update_every, usec_t dt __maybe_unused)
1522
netframewrk_obj[i].fnct(pDataBlock, pObjectType, update_every);
1523
}
1524
1525
+ {
1526
+ struct net_framework_instances *p;
1527
+ dfe_start_write(processes, p)
1528
+ {
1529
+ if (p->last_collected < netframework_now_ut)
1530
+ dictionary_del(processes, p_dfe.name);
1531
+ }
1532
+ dfe_done(p);
1533
+ dictionary_garbage_collect(processes);
1534
+ }
1535
+
1536
return 0;
1537
}
src/collectors/windows.plugin/perflib-rrd.c
+5
-5
@@ -174,19 +174,19 @@ collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_D
174
175
case PERF_COUNTER_MULTI_TIMER:
176
// 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
177
- if (!VALID_DELTA(cd))
177
+ if (!VALID_DELTA(cd) || !cd->current.Frequency || !cd->current.MultiCounterData)
178
return 0;
179
numerator = cd->current.Data - cd->previous.Data;
180
denominator = cd->current.Time - cd->previous.Time;
181
- denominator /= cd->current.Frequency;
182
- doubleValue = 100.0 * ((double)numerator / (double)denominator) / cd->current.MultiCounterData;
181
+ doubleValue = 100.0 * ((double)numerator / ((double)denominator / (double)cd->current.Frequency)) /
182
+ (double)cd->current.MultiCounterData;
183
// printf("Display value is (multi-timer): %f%%\n", doubleValue);
184
value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
185
break;
186
187
case PERF_100NSEC_MULTI_TIMER:
188
// 100 * ((N1 - N0) / (D1 - D0)) / B1
189
- if (!VALID_DELTA(cd))
189
+ if (!VALID_DELTA(cd) || !cd->current.MultiCounterData)
190
return 0;
191
numerator = cd->current.Data - cd->previous.Data;
192
denominator = cd->current.Time - cd->previous.Time;
@@ -198,7 +198,7 @@ collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_D
198
case PERF_COUNTER_MULTI_TIMER_INV:
199
case PERF_100NSEC_MULTI_TIMER_INV:
200
// 100 * (B1 - ((N1 - N0) / (D1 - D0)))
201
- if (!VALID_DELTA(cd))
201
+ if (!VALID_DELTA(cd) || !cd->current.MultiCounterData)
202
return 0;
203
numerator = cd->current.Data - cd->previous.Data;
204
denominator = cd->current.Time - cd->previous.Time;
src/collectors/windows.plugin/perflib-storage.c
+32
-10
@@ -437,7 +437,7 @@ static inline double perflib_average_timer_ms(COUNTER_DATA *d)
437
LONGLONG freq1 = d->current.Frequency;
438
439
if (data1 >= data0 && time1 > time0 && time0 && freq1)
440
- return ((double)(data1 - data0) / (double)(freq1 / MSEC_PER_SEC)) / (double)(time1 - time0);
440
+ return ((double)(data1 - data0) * (double)MSEC_PER_SEC) / ((double)freq1 * (double)(time1 - time0));
441
442
return 0;
443
}
@@ -482,6 +482,8 @@ static DiskDriveInfoWMI infos[MAX_WMI_DRIVES];
482
static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut)
483
{
484
DICTIONARY *dict = physicalDisks;
485
+ size_t infoCount = 0;
486
+ bool infos_loaded = false;
487
488
PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
489
if (!pObjectType)
@@ -526,7 +528,11 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
528
529
if (!d->collected_metadata) {
530
if (!is_system && device_index != -1) {
529
- size_t infoCount = GetDiskDriveInfo(infos, _countof(infos));
531
+ if (!infos_loaded) {
532
+ infoCount = GetDiskDriveInfo(infos, _countof(infos));
533
+ infos_loaded = true;
534
+ }
535
+
536
for (size_t k = 0; k < infoCount; k++) {
537
if (infos[k].Index != device_index)
538
continue;
@@ -706,23 +712,39 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
712
int do_PerflibStorage(int update_every, usec_t dt __maybe_unused)
713
{
714
static bool initialized = false;
715
+ DWORD logical_id, physical_id;
716
717
if (unlikely(!initialized)) {
718
initialize();
719
initialized = true;
720
}
721
715
- DWORD id = RegistryFindIDByName("LogicalDisk");
716
- if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
717
- return -1;
722
+ logical_id = RegistryFindIDByName("LogicalDisk");
723
+ physical_id = RegistryFindIDByName("PhysicalDisk");
724
719
- PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
720
- if (!pDataBlock)
725
+ if (logical_id == PERFLIB_REGISTRY_NAME_NOT_FOUND && physical_id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
726
return -1;
727
728
+ // Each perflibGetPerformanceData() call reuses the same internal buffer, so we must
729
+ // query and consume each block before issuing the next call to avoid pointer aliasing.
730
usec_t now_ut = now_monotonic_usec();
724
- do_logical_disk(pDataBlock, update_every, now_ut);
725
- do_physical_disk(pDataBlock, update_every, now_ut);
731
+ bool processed_any = false;
732
727
- return 0;
733
+ if (logical_id != PERFLIB_REGISTRY_NAME_NOT_FOUND) {
734
+ PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(logical_id);
735
+ if (pDataBlock) {
736
+ do_logical_disk(pDataBlock, update_every, now_ut);
737
+ processed_any = true;
738
+ }
739
+ }
740
+
741
+ if (physical_id != PERFLIB_REGISTRY_NAME_NOT_FOUND) {
742
+ PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(physical_id);
743
+ if (pDataBlock) {
744
+ do_physical_disk(pDataBlock, update_every, now_ut);
745
+ processed_any = true;
746
+ }
747
+ }
748
+
749
+ return processed_any ? 0 : -1;
750
}
src/collectors/windows.plugin/windows_plugin.c
+5
-3
@@ -39,7 +39,7 @@ static struct proc_module {
39
.func = do_GetPowerSupply,
40
.rd = NULL,
41
.thread = NULL,
42
- .cleanup = NULL},
42
+ .cleanup = do_GetPowerSupply_cleanup},
43
{.name = "GetSensors",
44
.dim = "GetSensors",
45
.enabled = CONFIG_BOOLEAN_YES,
@@ -326,11 +326,13 @@ void win_plugin_main(void *ptr)
326
// Join threads
327
for (i = 0; win_modules[i].name; i++) {
328
struct proc_module *pm = &win_modules[i];
329
- if (pm->cleanup)
330
- pm->cleanup();
329
330
if (pm->thread) {
331
nd_thread_join(pm->thread);
332
+ pm->thread = NULL;
333
}
334
+
335
+ if (pm->cleanup)
336
+ pm->cleanup();
337
}
338
}
src/collectors/windows.plugin/windows_plugin.h
+1
@@ -55,6 +55,7 @@ void do_GetHardwareInfo_cleanup();
55
void do_Sensors_cleanup();
56
void do_GetServicesStatus_cleanup();
57
void do_GetSystemCPU_cleanup();
58
+void do_GetPowerSupply_cleanup();
59
60
enum PERFLIB_PRIO {
61
PRIO_WEBSITE_IIS_REQUESTS_RATE = 21000, // PRIO selected, because APPS is using 20YYY