@cryptotaxi247 / netdata-1 / commits / 7766ec91a

Add linux powercap metrics collector (#15364)

Frank Riley committed Jul 20, 2023 at 05:17 UTC 7766ec91a54f3ee7328d6187ae28255099b06ac8
9 files changed +306
CMakeLists.txt
+1
@@ -512,6 +512,7 @@ set(DEBUGFS_PLUGIN_FILES
512 collectors/debugfs.plugin/debugfs_plugin.h
513 collectors/debugfs.plugin/debugfs_extfrag.c
514 collectors/debugfs.plugin/debugfs_zswap.c
515 + collectors/debugfs.plugin/sys_devices_virtual_powercap.c
516 )
517
518 set(FREEBSD_PLUGIN_FILES
Makefile.am
+1
@@ -219,6 +219,7 @@ DEBUGFS_PLUGIN_FILES = \
219 collectors/debugfs.plugin/debugfs_plugin.h \
220 collectors/debugfs.plugin/debugfs_extfrag.c \
221 collectors/debugfs.plugin/debugfs_zswap.c \
222 + collectors/debugfs.plugin/sys_devices_virtual_powercap.c \
223 $(LIBNETDATA_FILES) \
224 $(NULL)
225
build_external/scenarios/aclk-testing/agent_netdata.conf
+45
@@ -7108,3 +7108,48 @@
7108 # dim netdata algorithm = absolute
7109 # dim netdata multiplier = 1
7110 # dim netdata divisor = 1
7111 +
7112 +[system.power_consumption]
7113 + history = 5
7114 + # enabled = yes
7115 + # cache directory = /var/cache/netdata/system.power_consumption
7116 + # chart type = stacked
7117 + # type = system
7118 + # family = power_consumption
7119 + # units = Watts
7120 + # context = system.power_consumption
7121 + # priority = 950
7122 + # name = system.power_consumption
7123 + # title = Power Consumption
7124 + # dim HI name = HI
7125 + # dim HI algorithm = incremental
7126 + # dim HI multiplier = 1
7127 + # dim HI divisor = 1
7128 + # dim TIMER name = TIMER
7129 + # dim TIMER algorithm = incremental
7130 + # dim TIMER multiplier = 1
7131 + # dim TIMER divisor = 1
7132 + # dim NET_TX name = NET_TX
7133 + # dim NET_TX algorithm = incremental
7134 + # dim NET_TX multiplier = 1
7135 + # dim NET_TX divisor = 1
7136 + # dim NET_RX name = NET_RX
7137 + # dim NET_RX algorithm = incremental
7138 + # dim NET_RX multiplier = 1
7139 + # dim NET_RX divisor = 1
7140 + # dim TASKLET name = TASKLET
7141 + # dim TASKLET algorithm = incremental
7142 + # dim TASKLET multiplier = 1
7143 + # dim TASKLET divisor = 1
7144 + # dim SCHED name = SCHED
7145 + # dim SCHED algorithm = incremental
7146 + # dim SCHED multiplier = 1
7147 + # dim SCHED divisor = 1
7148 + # dim HRTIMER name = HRTIMER
7149 + # dim HRTIMER algorithm = incremental
7150 + # dim HRTIMER multiplier = 1
7151 + # dim HRTIMER divisor = 1
7152 + # dim RCU name = RCU
7153 + # dim RCU algorithm = incremental
7154 + # dim RCU multiplier = 1
7155 + # dim RCU divisor = 1
build_external/scenarios/gaps_lo/mostly_off.conf
+3
@@ -961,3 +961,6 @@ enabled = no
961
962 [netfilter.netlink_expect]
963 enabled = no
964 +
965 +[system.power_consumption]
966 +enabled = no
collectors/all.h
+3
@@ -367,6 +367,9 @@
367 #define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9502
368 #define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9503
369
370 +// Linux powercap
371 +
372 +#define NETDATA_CHART_PRIO_POWERCAP 9600
373
374 // Wireless
375
collectors/debugfs.plugin/debugfs_plugin.c
+3
@@ -20,6 +20,9 @@ static struct debugfs_module {
20 .func = do_debugfs_extfrag},
21 { .name = "/sys/kernel/debug/zswap", .enabled = CONFIG_BOOLEAN_YES,
22 .func = do_debugfs_zswap},
23 + // Linux powercap metrics is here because it needs privilege to read each RAPL zone
24 + { .name = "/sys/devices/virtual/powercap", .enabled = CONFIG_BOOLEAN_YES,
25 + .func = do_sys_devices_virtual_powercap},
26
27 // The terminator
28 { .name = NULL, .enabled = CONFIG_BOOLEAN_NO, .func = NULL}
collectors/debugfs.plugin/debugfs_plugin.h
+1
@@ -9,6 +9,7 @@
9
10 int do_debugfs_extfrag(int update_every, const char *name);
11 int do_debugfs_zswap(int update_every, const char *name);
12 +int do_sys_devices_virtual_powercap(int update_every, const char *name);
13 void debugfs2lower(char *name);
14 const char *debugfs_rrdset_type_name(RRDSET_TYPE chart_type);
15 const char *debugfs_rrd_algorithm_name(RRD_ALGORITHM algorithm);
collectors/debugfs.plugin/sys_devices_virtual_powercap.c new
+241
@@ -0,0 +1,241 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "debugfs_plugin.h"
4 +
5 +#define METRIC_ID "system.power_consumption"
6 +
7 +struct measurement_t {
8 + unsigned long long energy_uj;
9 + usec_t time_us;
10 +};
11 +
12 +struct zone_t {
13 + char name[FILENAME_MAX + 1];
14 + char path[FILENAME_MAX + 1];
15 + unsigned long long max_energy_range_uj;
16 + struct measurement_t measurement;
17 +};
18 +
19 +static int g_zones_count = 0;
20 +static struct zone_t *g_zones = NULL;
21 +
22 +static int get_measurement(const char *path, struct measurement_t *measurement)
23 +{
24 + int result;
25 + if (likely((result = read_single_number_file(path, &measurement->energy_uj)) == 0)) {
26 + measurement->time_us = now_monotonic_high_precision_usec();
27 + }
28 + return result;
29 +}
30 +
31 +static double
32 +calculate_watts(unsigned long long max_energy_range_uj, struct measurement_t *before, struct measurement_t *after)
33 +{
34 + unsigned long long energy_uj = 0;
35 + usec_t delta_us = after->time_us - before->time_us;
36 + if (unlikely(delta_us == 0)) {
37 + return 0;
38 + }
39 +
40 + if (likely(after->energy_uj >= before->energy_uj)) {
41 + energy_uj = after->energy_uj - before->energy_uj;
42 + } else {
43 + energy_uj = after->energy_uj + (max_energy_range_uj - before->energy_uj);
44 + }
45 + return (double)energy_uj / delta_us;
46 +}
47 +
48 +static struct zone_t *
49 +get_zone(const char *control_type, struct zone_t *parent, const char *dirname, struct zone_t **zones, size_t *count)
50 +{
51 + char temp[FILENAME_MAX + 1];
52 + snprintfz(temp, FILENAME_MAX, "%s/%s", dirname, "name");
53 +
54 + char name[FILENAME_MAX + 1] = "";
55 + if (read_file(temp, name, sizeof(name) - 1) != 0) {
56 + return NULL;
57 + }
58 +
59 + char *trimmed = trim(name);
60 + if (unlikely(trimmed == NULL || trimmed[0] == 0)) {
61 + return NULL;
62 + }
63 +
64 + snprintfz(temp, FILENAME_MAX, "%s/%s", dirname, "max_energy_range_uj");
65 + unsigned long long max_energy_range_uj = 0;
66 + if (unlikely(read_single_number_file(temp, &max_energy_range_uj) != 0)) {
67 + collector_error("Cannot read %s", temp);
68 + return NULL;
69 + }
70 +
71 + snprintfz(temp, FILENAME_MAX, "%s/%s", dirname, "energy_uj");
72 + struct measurement_t measurement;
73 + if (unlikely(get_measurement(temp, &measurement) != 0)) {
74 + collector_error("%s: Cannot read %s", trimmed, temp);
75 + return NULL;
76 + }
77 +
78 + int parent_idx = -1;
79 + if (parent != NULL) {
80 + parent_idx = parent - *zones;
81 + }
82 +
83 + *zones = (struct zone_t *)reallocz(*zones, sizeof(struct zone_t) * (*count + 1));
84 +
85 + struct zone_t *zone = &(*zones)[*count];
86 + strcpy(zone->path, temp);
87 + if (parent_idx >= 0) {
88 + snprintfz(zone->name, FILENAME_MAX, "%s/%s", (*zones)[parent_idx].name, trimmed);
89 + } else {
90 + snprintfz(zone->name, FILENAME_MAX, "%s/%s", control_type, trimmed);
91 + }
92 + zone->max_energy_range_uj = max_energy_range_uj;
93 + zone->measurement = measurement;
94 +
95 + collector_info("Found zone: \"%s\"", zone->name);
96 +
97 + ++(*count);
98 +
99 + return zone;
100 +}
101 +
102 +static void
103 +look_for_zones(const char *control_type, struct zone_t *parent, const char *path, struct zone_t **zones, size_t *count)
104 +{
105 + DIR *dir = opendir(path);
106 + if (unlikely(dir == NULL)) {
107 + return;
108 + }
109 +
110 + struct dirent *de = NULL;
111 + while ((de = readdir(dir))) {
112 + if (de->d_type != DT_DIR || de->d_name[0] == '.') {
113 + continue;
114 + }
115 +
116 + char zone_path[FILENAME_MAX + 1];
117 + snprintfz(zone_path, FILENAME_MAX, "%s/%s", path, de->d_name);
118 +
119 + collector_info("Looking for zone in \"%s\"", zone_path);
120 +
121 + struct zone_t *zone = get_zone(control_type, parent, zone_path, zones, count);
122 + if (zone != NULL) {
123 + look_for_zones(control_type, zone, zone_path, zones, count);
124 + }
125 + }
126 +
127 + closedir(dir);
128 +}
129 +
130 +static int get_rapl_zones(struct zone_t **zones)
131 +{
132 + *zones = NULL;
133 +
134 + char dirname[FILENAME_MAX + 1];
135 + snprintfz(dirname, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/virtual/powercap");
136 +
137 + DIR *dir = opendir(dirname);
138 + if (unlikely(dir == NULL)) {
139 + return 0;
140 + }
141 +
142 + size_t count = 0;
143 +
144 + struct dirent *de = NULL;
145 + while ((de = readdir(dir))) {
146 + if (de->d_type != DT_DIR || de->d_name[0] == '.') {
147 + continue;
148 + }
149 +
150 + char control_type_path[FILENAME_MAX + 1];
151 + snprintfz(control_type_path, FILENAME_MAX, "%s/%s", dirname, de->d_name);
152 +
153 + collector_info("Looking at control type \"%s\"", de->d_name);
154 + look_for_zones(de->d_name, NULL, control_type_path, zones, &count);
155 + }
156 + closedir(dir);
157 +
158 + return count;
159 +}
160 +
161 +static void send_chart(int update_every, const char *name)
162 +{
163 + fprintf(
164 + stdout,
165 + "CHART %s '' 'Power Consumption' 'Watts' 'power consumption' '' '%s' %d %d '%s' 'debugfs.plugin' ''\n",
166 + METRIC_ID,
167 + debugfs_rrdset_type_name(RRDSET_TYPE_LINE),
168 + NETDATA_CHART_PRIO_POWERCAP,
169 + update_every,
170 + name);
171 +}
172 +
173 +static void send_dimension(const char *zone_name)
174 +{
175 + fprintf(
176 + stdout,
177 + "DIMENSION '%s' '%s' %s 1 %d ''\n",
178 + zone_name,
179 + zone_name,
180 + debugfs_rrd_algorithm_name(RRD_ALGORITHM_ABSOLUTE),
181 + 1000);
182 +}
183 +
184 +static void send_begin()
185 +{
186 + fprintf(stdout, "BEGIN %s\n", METRIC_ID);
187 +}
188 +
189 +static void send_set(const char *zone_name, collected_number value)
190 +{
191 + fprintf(stdout, "SET %s = %lld\n", zone_name, value);
192 +}
193 +
194 +static void send_end_and_flush()
195 +{
196 + fprintf(stdout, "END\n");
197 + fflush(stdout);
198 +}
199 +
200 +int do_sys_devices_virtual_powercap(int update_every, const char *name)
201 +{
202 + if (unlikely(g_zones_count <= 0)) {
203 + g_zones_count = get_rapl_zones(&g_zones);
204 + if (unlikely(g_zones_count < 0)) {
205 + collector_error("Failed to find powercap zones.");
206 + return 1;
207 + } else if (unlikely(g_zones_count == 0)) {
208 + collector_info("No powercap zones found.");
209 + return 1;
210 + }
211 +
212 + send_chart(update_every, name);
213 +
214 + for (int ii = 0; ii < g_zones_count; ++ii) {
215 + send_dimension(g_zones[ii].name);
216 + }
217 +
218 + // This measurement is a derivative so start reporting on the next call
219 + return 0;
220 + }
221 +
222 + send_begin();
223 +
224 + for (int ii = 0; ii < g_zones_count; ++ii) {
225 + struct measurement_t measurement;
226 + struct zone_t *zone = &g_zones[ii];
227 +
228 + if (unlikely(get_measurement(zone->path, &measurement) != 0)) {
229 + collector_error("%s: Cannot read %s", zone->name, zone->path);
230 + continue;
231 + }
232 + double watts = calculate_watts(zone->max_energy_range_uj, &zone->measurement, &measurement);
233 + zone->measurement = measurement;
234 +
235 + send_set(zone->name, (collected_number)(watts * 1000));
236 + }
237 +
238 + send_end_and_flush();
239 +
240 + return 0;
241 +}
web/gui/dashboard_info.js
+8
@@ -1177,6 +1177,10 @@ netdataDashboard.submenu = {
1177 info: 'A connection is an established line of communication between a client and the PostgreSQL server. Each connection adds to the load on the PostgreSQL server. To guard against running out of memory or overloading the database the <i>max_connections</i> parameter (default = 100) defines the maximum number of concurrent connections to the database server. A separate parameter, <i>superuser_reserved_connections</i> (default = 3), defines the quota for superuser connections (so that superusers can connect even if all other connection slots are blocked).'
1178 },
1179
1180 + 'system.power_consumption': {
1181 + info: 'The current power consumption of the zones defined by the <a href="https://www.kernel.org/doc/html/next/power/powercap/powercap.html" target="_blank">power capping framework</a>.'
1182 + }
1183 +
1184 };
1185
1186 // ----------------------------------------------------------------------------
@@ -1730,6 +1734,10 @@ netdataDashboard.context = {
1734 info: 'Difference between the number of calls to <a href="https://learn.netdata.cloud/docs/agent/collectors/ebpf.plugin#process-exit" target="_blank">functions</a> that close a task and release a task.'+ ebpfChartProvides
1735 },
1736
1737 + 'system.power_consumption': {
1738 + info: 'The current power consumption of the zones defined by the <a href="https://www.kernel.org/doc/html/next/power/powercap/powercap.html" target="_blank">power capping framework</a>.'
1739 + },
1740 +
1741 // ------------------------------------------------------------------------
1742 // CPU charts
1743