| 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_free_zero; |
| 10 | |
| 11 | COUNTER_DATA standby; |
| 12 | COUNTER_DATA free_zero; |
| 13 | }; |
| 14 | |
| 15 | static DICTIONARY *numa_dict = NULL; |
| 16 | |
| 17 | static void numa_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) |
| 18 | { |
| 19 | struct netdata_numa *d = value; |
| 20 | |
| 21 | d->standby.key = "Standby List MBytes"; |
| 22 | d->free_zero.key = "Free & Zero Page List MBytes"; |
| 23 | } |
| 24 | |
| 25 | static void initialize(void) |
| 26 | { |
| 27 | numa_dict = dictionary_create_advanced( |
| 28 | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct netdata_numa)); |
| 29 | |
| 30 | dictionary_register_insert_callback(numa_dict, numa_insert_cb, NULL); |
| 31 | } |
| 32 | |
| 33 | static void netdata_numa_chart(struct netdata_numa *nn, int update_every) |
| 34 | { |
| 35 | if (unlikely(!nn->st_numa)) { |
| 36 | char id[RRD_ID_LENGTH_MAX + 1]; |
| 37 | snprintfz(id, RRD_ID_LENGTH_MAX, "numa_node_%s_mem_usage", windows_shared_buffer); |
| 38 | |
| 39 | nn->st_numa = rrdset_create_localhost( |
| 40 | "numa_node_mem_usage", |
| 41 | id, |
| 42 | NULL, |
| 43 | "numa", |
| 44 | "mem.numa_node_mem_usage", |
| 45 | "NUMA Node Memory Usage", |
| 46 | "bytes", |
| 47 | PLUGIN_WINDOWS_NAME, |
| 48 | "PerflibNUMA", |
| 49 | NETDATA_CHART_PRIO_MEM_NUMA_NODES_MEMINFO, |
| 50 | update_every, |
| 51 | RRDSET_TYPE_LINE); |
| 52 | |
| 53 | rrdlabels_add(nn->st_numa->rrdlabels, "node", windows_shared_buffer, RRDLABEL_SRC_AUTO); |
| 54 | |
| 55 | nn->rd_free_zero = rrddim_add(nn->st_numa, "free", NULL, MEGA_FACTOR, 1, RRD_ALGORITHM_ABSOLUTE); |
| 56 | |
| 57 | nn->rd_standby = rrddim_add(nn->st_numa, "standby", NULL, MEGA_FACTOR, 1, RRD_ALGORITHM_ABSOLUTE); |
| 58 | } |
| 59 | |
| 60 | rrddim_set_by_pointer(nn->st_numa, nn->rd_free_zero, (collected_number)nn->free_zero.current.Data); |
| 61 | |
| 62 | rrddim_set_by_pointer(nn->st_numa, nn->rd_standby, (collected_number)nn->standby.current.Data); |
| 63 | |
| 64 | rrdset_done(nn->st_numa); |
| 65 | } |
| 66 | |
| 67 | static bool do_numa(PERF_DATA_BLOCK *pDataBlock, int update_every) |
| 68 | { |
| 69 | PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "NUMA Node Memory"); |
| 70 | if (!pObjectType) |
| 71 | return false; |
| 72 | |
| 73 | PERF_INSTANCE_DEFINITION *pi = NULL; |
| 74 | for (LONG i = 0; i < pObjectType->NumInstances; i++) { |
| 75 | pi = perflibForEachInstance(pDataBlock, pObjectType, pi); |
| 76 | if (!pi) |
| 77 | break; |
| 78 | |
| 79 | if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) |
| 80 | strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); |
| 81 | |
| 82 | if (strcasecmp(windows_shared_buffer, "_Total") == 0) |
| 83 | continue; |
| 84 | |
| 85 | struct netdata_numa *nn = dictionary_set(numa_dict, windows_shared_buffer, NULL, sizeof(*nn)); |
| 86 | |
| 87 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &nn->standby); |
| 88 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &nn->free_zero); |
| 89 | netdata_numa_chart(nn, update_every); |
| 90 | } |
| 91 | |
| 92 | return true; |
| 93 | } |
| 94 | |
| 95 | int do_PerflibNUMA(int update_every, usec_t dt __maybe_unused) |
| 96 | { |
| 97 | static bool initialized = false; |
| 98 | |
| 99 | if (unlikely(!initialized)) { |
| 100 | initialize(); |
| 101 | initialized = true; |
| 102 | } |
| 103 | |
| 104 | DWORD id = RegistryFindIDByName("NUMA Node Memory"); |
| 105 | if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) |
| 106 | return -1; |
| 107 | |
| 108 | PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); |
| 109 | if (!pDataBlock) |
| 110 | return -1; |
| 111 | |
| 112 | do_numa(pDataBlock, update_every); |
| 113 | |
| 114 | return 0; |
| 115 | } |