NUMA Windows (#20573)
thiagoftsm committed
Jun 25, 2025 at 11:15 UTC
35b62d244436700e2ff06362603967135c29cec3
5 files changed
+227
-4
CMakeLists.txt
+1
@@ -2064,6 +2064,7 @@ set(WINDOWS_PLUGIN_FILES
2064
src/collectors/windows.plugin/perflib-services.c
2065
src/collectors/windows.plugin/perflib-hyperv.c
2066
src/collectors/windows.plugin/perflib-exchange.c
2067
+ src/collectors/windows.plugin/perflib-numa.c
2068
)
2069
2070
set(PROC_PLUGIN_FILES
src/collectors/windows.plugin/metadata.yaml
+87
@@ -3452,3 +3452,90 @@ modules:
3452
chart_type: line
3453
dimensions:
3454
- name: serviced
3455
+ - meta:
3456
+ plugin_name: windows.plugin
3457
+ module_name: PerflibNUMA
3458
+ monitored_instance:
3459
+ name: NUMA Architecture
3460
+ link: "https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support"
3461
+ categories:
3462
+ - data-collection.windows-systems
3463
+ icon_filename: "windows.svg"
3464
+ related_resources:
3465
+ integrations:
3466
+ list: []
3467
+ info_provided_to_referring_integrations:
3468
+ description: ""
3469
+ keywords:
3470
+ - windows
3471
+ - NUMA
3472
+ - processor
3473
+ most_popular: false
3474
+ overview:
3475
+ data_collection:
3476
+ metrics_description: |
3477
+ This collector monitors NUMA Architecture on Windows.
3478
+ method_description: |
3479
+ It queries NUMA Node Memory from Perflib in order to gather the metrics.
3480
+ supported_platforms:
3481
+ include: ["windows"]
3482
+ exclude: []
3483
+ multi_instance: false
3484
+ additional_permissions:
3485
+ description: ""
3486
+ default_behavior:
3487
+ auto_detection:
3488
+ description: |
3489
+ The collector automatically detects all of the metrics, no further configuration is required.
3490
+ limits:
3491
+ description: ""
3492
+ performance_impact:
3493
+ description: ""
3494
+ setup:
3495
+ prerequisites:
3496
+ list: []
3497
+ configuration:
3498
+ file:
3499
+ name: "netdata.conf"
3500
+ section_name: "[plugin:windows]"
3501
+ description: "The Netdata main configuration file"
3502
+ options:
3503
+ description: ""
3504
+ folding:
3505
+ title: "Config option"
3506
+ enabled: false
3507
+ list:
3508
+ - name: PerflibNUMA
3509
+ description: An option to enable or disable the data collection.
3510
+ default_value: yes
3511
+ required: false
3512
+ examples:
3513
+ folding:
3514
+ enabled: true
3515
+ title: ""
3516
+ list: []
3517
+ troubleshooting:
3518
+ problems:
3519
+ list: []
3520
+ alerts: []
3521
+ metrics:
3522
+ folding:
3523
+ title: Metrics
3524
+ enabled: false
3525
+ description: ""
3526
+ availability: []
3527
+ scopes:
3528
+ - name: NUMA
3529
+ description: "These metrics refer to memory utilization on NUMA systems."
3530
+ labels:
3531
+ - name: node
3532
+ description: The identifier of the CPU node.
3533
+ metrics:
3534
+ - name: mem.numa_node_mem_usage
3535
+ description: NUMA Node Memory Usage
3536
+ unit: bytes
3537
+ chart_type: line
3538
+ dimensions:
3539
+ - name: free
3540
+ - name: standby
3541
+ - name: available
src/collectors/windows.plugin/perflib-numa.c
new
+123
@@ -0,0 +1,123 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "windows_plugin.h"
4
+#include "windows-internals.h"
5
+
6
+struct netdata_numa {
7
+ RRDSET *st_numa;
8
+ RRDDIM *rd_standby;
9
+ RRDDIM *rd_available;
10
+ RRDDIM *rd_free_zero;
11
+
12
+ COUNTER_DATA standby;
13
+ COUNTER_DATA available;
14
+ COUNTER_DATA free_zero;
15
+};
16
+
17
+static DICTIONARY *numa_dict = NULL;
18
+
19
+static void numa_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
20
+{
21
+ struct netdata_numa *d = value;
22
+
23
+ d->standby.key = "Standby List MBytes";
24
+ d->available.key = "Available MBytes";
25
+ d->free_zero.key = "Free & Zero Page List MBytes";
26
+}
27
+
28
+static void initialize(void)
29
+{
30
+ numa_dict = dictionary_create_advanced(
31
+ DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct netdata_numa));
32
+
33
+ dictionary_register_insert_callback(numa_dict, numa_insert_cb, NULL);
34
+}
35
+
36
+static void netdata_numa_chart(struct netdata_numa *nn, int update_every)
37
+{
38
+ if (unlikely(!nn->st_numa)) {
39
+ char id[RRD_ID_LENGTH_MAX + 1];
40
+ snprintfz(id, RRD_ID_LENGTH_MAX, "numae_node_%s_mem_usage", windows_shared_buffer);
41
+
42
+ nn->st_numa = rrdset_create_localhost(
43
+ "numa_node_mem_usage",
44
+ id,
45
+ NULL,
46
+ "numa",
47
+ "mem.numa_node_mem_usage",
48
+ "NUMA Node Memory Usage",
49
+ "bytes",
50
+ PLUGIN_WINDOWS_NAME,
51
+ "PerflibNUMA",
52
+ NETDATA_CHART_PRIO_MEM_NUMA_NODES_MEMINFO,
53
+ update_every,
54
+ RRDSET_TYPE_LINE);
55
+
56
+ rrdlabels_add(nn->st_numa->rrdlabels, "node", windows_shared_buffer, RRDLABEL_SRC_AUTO);
57
+
58
+ nn->rd_free_zero = rrddim_add(nn->st_numa, "free", NULL, MEGA_FACTOR, 1, RRD_ALGORITHM_ABSOLUTE);
59
+
60
+ nn->rd_standby = rrddim_add(nn->st_numa, "standby", NULL, MEGA_FACTOR, 1, RRD_ALGORITHM_ABSOLUTE);
61
+
62
+ nn->rd_available = rrddim_add(nn->st_numa, "available", NULL, MEGA_FACTOR, 1, RRD_ALGORITHM_ABSOLUTE);
63
+ }
64
+
65
+ rrddim_set_by_pointer(nn->st_numa, nn->rd_free_zero, (collected_number)nn->free_zero.current.Data);
66
+
67
+ rrddim_set_by_pointer(nn->st_numa, nn->rd_available, (collected_number)nn->available.current.Data);
68
+
69
+ rrddim_set_by_pointer(nn->st_numa, nn->rd_standby, (collected_number)nn->standby.current.Data);
70
+
71
+ rrdset_done(nn->st_numa);
72
+}
73
+
74
+static bool do_numa(PERF_DATA_BLOCK *pDataBlock, int update_every)
75
+{
76
+ PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "NUMA Node Memory");
77
+ if (!pObjectType)
78
+ return false;
79
+
80
+ PERF_INSTANCE_DEFINITION *pi = NULL;
81
+ for (LONG i = 0; i < pObjectType->NumInstances; i++) {
82
+ pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
83
+ if (!pi)
84
+ break;
85
+
86
+ if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
87
+ strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
88
+
89
+ if (strcasecmp(windows_shared_buffer, "_Total") == 0)
90
+ continue;
91
+
92
+ struct netdata_numa *nn = dictionary_set(numa_dict, windows_shared_buffer, NULL, sizeof(*nn));
93
+
94
+ if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &nn->standby) ||
95
+ perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &nn->available) ||
96
+ perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &nn->free_zero))
97
+ netdata_numa_chart(nn, update_every);
98
+ }
99
+
100
+ return true;
101
+}
102
+
103
+int do_PerflibNUMA(int update_every, usec_t dt __maybe_unused)
104
+{
105
+ static bool initialized = false;
106
+
107
+ if (unlikely(!initialized)) {
108
+ initialize();
109
+ initialized = true;
110
+ }
111
+
112
+ DWORD id = RegistryFindIDByName("NUMA Node Memory");
113
+ if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
114
+ return -1;
115
+
116
+ PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
117
+ if (!pDataBlock)
118
+ return -1;
119
+
120
+ do_numa(pDataBlock, update_every);
121
+
122
+ return 0;
123
+}
src/collectors/windows.plugin/windows_plugin.c
+11
-4
@@ -113,10 +113,17 @@ static struct proc_module {
113
.func = do_PerflibServices},
114
115
{.name = "PerflibExchange",
116
- .dim = "PerflibADFS",
117
- .enabled = CONFIG_BOOLEAN_YES,
118
- .update_every = UPDATE_EVERY_MIN,
119
- .func = do_PerflibExchange},
116
+ .dim = "PerflibADFS",
117
+ .enabled = CONFIG_BOOLEAN_YES,
118
+ .update_every = UPDATE_EVERY_MIN,
119
+ .func = do_PerflibExchange},
120
+
121
+ {.name = "PerflibNUMA",
122
+ .dim = "PerflibNUMA",
123
+ .enabled = CONFIG_BOOLEAN_YES,
124
+ .update_every = UPDATE_EVERY_MIN,
125
+ .func = do_PerflibNUMA},
126
+
127
// the terminator of this array
128
{.name = NULL, .dim = NULL, .func = NULL}};
129
src/collectors/windows.plugin/windows_plugin.h
+5
@@ -11,6 +11,10 @@
11
// 2^24
12
#define WINDOWS_MAX_KERNEL_OBJECT 16777216
13
14
+#ifndef MEGA_FACTOR
15
+#define MEGA_FACTOR (1048576)
16
+#endif
17
+
18
void *win_plugin_main(void *ptr);
19
20
extern char windows_shared_buffer[8192];
@@ -38,6 +42,7 @@ int do_PerflibADFS(int update_every, usec_t dt);
42
int do_PerflibHyperV(int update_every, usec_t dt);
43
int do_PerflibServices(int update_every, usec_t dt);
44
int do_PerflibExchange(int update_every, usec_t dt __maybe_unused);
45
+int do_PerflibNUMA(int update_every, usec_t dt __maybe_unused);
46
47
enum PERFLIB_PRIO {
48
PRIO_WEBSITE_IIS_REQUESTS_RATE = 21000, // PRIO selected, because APPS is using 20YYY