| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "windows_plugin.h" |
| 4 | #include "windows-internals.h" |
| 5 | |
| 6 | #define _COMMON_PLUGIN_NAME "windows.plugin" |
| 7 | #define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses" |
| 8 | #include "../common-contexts/common-contexts.h" |
| 9 | |
| 10 | struct processor { |
| 11 | bool collected_metadata; |
| 12 | |
| 13 | RRDSET *st; |
| 14 | RRDDIM *rd_user; |
| 15 | RRDDIM *rd_system; |
| 16 | RRDDIM *rd_irq; |
| 17 | RRDDIM *rd_dpc; |
| 18 | RRDDIM *rd_idle; |
| 19 | |
| 20 | // RRDSET *st2; |
| 21 | // RRDDIM *rd2_busy; |
| 22 | |
| 23 | COUNTER_DATA percentProcessorTime; |
| 24 | COUNTER_DATA percentUserTime; |
| 25 | COUNTER_DATA percentPrivilegedTime; |
| 26 | COUNTER_DATA percentDPCTime; |
| 27 | COUNTER_DATA percentInterruptTime; |
| 28 | COUNTER_DATA percentIdleTime; |
| 29 | |
| 30 | COUNTER_DATA interruptsPerSec; |
| 31 | }; |
| 32 | |
| 33 | struct processor total = {0}; |
| 34 | |
| 35 | void initialize_processor_keys(struct processor *p) |
| 36 | { |
| 37 | p->percentProcessorTime.key = "% Processor Time"; |
| 38 | p->percentUserTime.key = "% User Time"; |
| 39 | p->percentPrivilegedTime.key = "% Privileged Time"; |
| 40 | p->percentDPCTime.key = "% DPC Time"; |
| 41 | p->percentInterruptTime.key = "% Interrupt Time"; |
| 42 | p->percentIdleTime.key = "% Idle Time"; |
| 43 | p->interruptsPerSec.key = "Interrupts/sec"; |
| 44 | } |
| 45 | |
| 46 | void dict_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) |
| 47 | { |
| 48 | struct processor *p = value; |
| 49 | initialize_processor_keys(p); |
| 50 | } |
| 51 | |
| 52 | static DICTIONARY *processors = NULL; |
| 53 | |
| 54 | static void initialize(void) |
| 55 | { |
| 56 | initialize_processor_keys(&total); |
| 57 | |
| 58 | processors = dictionary_create_advanced( |
| 59 | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct processor)); |
| 60 | |
| 61 | dictionary_register_insert_callback(processors, dict_processor_insert_cb, NULL); |
| 62 | } |
| 63 | |
| 64 | static bool do_processors(PERF_DATA_BLOCK *pDataBlock, int update_every) |
| 65 | { |
| 66 | PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Processor"); |
| 67 | if (!pObjectType) |
| 68 | return false; |
| 69 | |
| 70 | static const RRDVAR_ACQUIRED *cpus_var = NULL; |
| 71 | int cores_found = 0; |
| 72 | uint64_t totalIPC = 0; |
| 73 | |
| 74 | PERF_INSTANCE_DEFINITION *pi = NULL; |
| 75 | for (LONG i = 0; i < pObjectType->NumInstances; i++) { |
| 76 | pi = perflibForEachInstance(pDataBlock, pObjectType, pi); |
| 77 | if (!pi) |
| 78 | break; |
| 79 | |
| 80 | if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) |
| 81 | strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); |
| 82 | |
| 83 | bool is_total = false; |
| 84 | struct processor *p; |
| 85 | int cpu = -1; |
| 86 | if (strcasecmp(windows_shared_buffer, "_Total") == 0) { |
| 87 | p = &total; |
| 88 | is_total = true; |
| 89 | cpu = -1; |
| 90 | } else { |
| 91 | p = dictionary_set(processors, windows_shared_buffer, NULL, sizeof(*p)); |
| 92 | is_total = false; |
| 93 | cpu = str2i(windows_shared_buffer); |
| 94 | snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "cpu%d", cpu); |
| 95 | |
| 96 | if (cpu + 1 > cores_found) |
| 97 | cores_found = cpu + 1; |
| 98 | } |
| 99 | |
| 100 | if (!is_total && !p->collected_metadata) { |
| 101 | // TODO collect processor metadata |
| 102 | p->collected_metadata = true; |
| 103 | } |
| 104 | |
| 105 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentProcessorTime); |
| 106 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentUserTime); |
| 107 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentPrivilegedTime); |
| 108 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentDPCTime); |
| 109 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentInterruptTime); |
| 110 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentIdleTime); |
| 111 | |
| 112 | perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->interruptsPerSec); |
| 113 | |
| 114 | if (!p->st) { |
| 115 | p->st = rrdset_create_localhost( |
| 116 | is_total ? "system" : "cpu", |
| 117 | is_total ? "cpu" : windows_shared_buffer, |
| 118 | NULL, |
| 119 | is_total ? "cpu" : "utilization", |
| 120 | is_total ? "system.cpu" : "cpu.cpu", |
| 121 | is_total ? "Total CPU Utilization" : "Core Utilization", |
| 122 | "percentage", |
| 123 | PLUGIN_WINDOWS_NAME, |
| 124 | "PerflibProcessor", |
| 125 | is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE, |
| 126 | update_every, |
| 127 | RRDSET_TYPE_STACKED); |
| 128 | |
| 129 | p->rd_irq = rrddim_add(p->st, "interrupts", "irq", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); |
| 130 | p->rd_user = rrddim_add(p->st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); |
| 131 | p->rd_system = rrddim_add(p->st, "privileged", "system", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); |
| 132 | p->rd_dpc = rrddim_add(p->st, "dpc", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); |
| 133 | p->rd_idle = rrddim_add(p->st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); |
| 134 | rrddim_hide(p->st, "idle"); |
| 135 | |
| 136 | if (!is_total) |
| 137 | rrdlabels_add(p->st->rrdlabels, "cpu", windows_shared_buffer, RRDLABEL_SRC_AUTO); |
| 138 | else |
| 139 | cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors"); |
| 140 | } |
| 141 | |
| 142 | uint64_t user = p->percentUserTime.current.Data; |
| 143 | uint64_t system = p->percentPrivilegedTime.current.Data; |
| 144 | uint64_t dpc = p->percentDPCTime.current.Data; |
| 145 | uint64_t irq = p->percentInterruptTime.current.Data; |
| 146 | uint64_t idle = p->percentIdleTime.current.Data; |
| 147 | |
| 148 | totalIPC += p->interruptsPerSec.current.Data; |
| 149 | |
| 150 | rrddim_set_by_pointer(p->st, p->rd_user, (collected_number)user); |
| 151 | rrddim_set_by_pointer(p->st, p->rd_system, (collected_number)system); |
| 152 | rrddim_set_by_pointer(p->st, p->rd_irq, (collected_number)irq); |
| 153 | rrddim_set_by_pointer(p->st, p->rd_dpc, (collected_number)dpc); |
| 154 | rrddim_set_by_pointer(p->st, p->rd_idle, (collected_number)idle); |
| 155 | rrdset_done(p->st); |
| 156 | |
| 157 | // if(!p->st2) { |
| 158 | // p->st2 = rrdset_create_localhost( |
| 159 | // is_total ? "system" : "cpu2" |
| 160 | // , is_total ? "cpu3" : buffer |
| 161 | // , NULL |
| 162 | // , is_total ? "utilization" : buffer |
| 163 | // , is_total ? "system.cpu3" : "cpu2.cpu" |
| 164 | // , is_total ? "Total CPU Utilization" : "Core Utilization" |
| 165 | // , "percentage" |
| 166 | // , PLUGIN_WINDOWS_NAME |
| 167 | // , "PerflibProcessor" |
| 168 | // , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE |
| 169 | // , update_every |
| 170 | // , RRDSET_TYPE_STACKED |
| 171 | // ); |
| 172 | // |
| 173 | // p->rd2_busy = perflib_rrddim_add(p->st2, "busy", NULL, 1, 1, &p->percentProcessorTime); |
| 174 | // rrddim_hide(p->st2, "idle"); |
| 175 | // |
| 176 | // if(!is_total) |
| 177 | // rrdlabels_add(p->st->rrdlabels, "cpu", buffer, RRDLABEL_SRC_AUTO); |
| 178 | // } |
| 179 | // |
| 180 | // perflib_rrddim_set_by_pointer(p->st2, p->rd2_busy, &p->percentProcessorTime); |
| 181 | // rrdset_done(p->st2); |
| 182 | } |
| 183 | |
| 184 | if (cpus_var) |
| 185 | rrdvar_host_variable_set(localhost, cpus_var, cores_found); |
| 186 | |
| 187 | common_interrupts(totalIPC, update_every, NULL); |
| 188 | |
| 189 | return true; |
| 190 | } |
| 191 | |
| 192 | int do_PerflibProcessor(int update_every, usec_t dt __maybe_unused) |
| 193 | { |
| 194 | static bool initialized = false; |
| 195 | |
| 196 | if (unlikely(!initialized)) { |
| 197 | initialize(); |
| 198 | initialized = true; |
| 199 | } |
| 200 | |
| 201 | DWORD id = RegistryFindIDByName("Processor"); |
| 202 | if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) |
| 203 | return -1; |
| 204 | |
| 205 | PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); |
| 206 | if (!pDataBlock) |
| 207 | return -1; |
| 208 | |
| 209 | do_processors(pDataBlock, update_every); |
| 210 | |
| 211 | return 0; |
| 212 | } |