diff options
author | Eldar Yusupov <eyusupov@gmail.com> | 2024-04-22 16:35:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-22 13:35:04 +0000 |
commit | 632f7a367ecb05eaa232c4b571d65f7f9d6ed94b (patch) | |
tree | a688bb7b636625de35c49be852deaefe2c5990ee | |
parent | d1494e0f5f6dd1a4a58b0fd681fc539e44279033 (diff) |
Add power consumption metric to power supply monitoring module (#17329)
-rw-r--r-- | src/collectors/all.h | 9 | ||||
-rw-r--r-- | src/collectors/proc.plugin/README.md | 1 | ||||
-rw-r--r-- | src/collectors/proc.plugin/integrations/power_supply.md | 1 | ||||
-rw-r--r-- | src/collectors/proc.plugin/metadata.yaml | 6 | ||||
-rw-r--r-- | src/collectors/proc.plugin/sys_class_power_supply.c | 191 | ||||
-rw-r--r-- | src/web/gui/dashboard_info.js | 4 |
6 files changed, 137 insertions, 75 deletions
diff --git a/src/collectors/all.h b/src/collectors/all.h index 24236e9bf8..ffbd075b97 100644 --- a/src/collectors/all.h +++ b/src/collectors/all.h @@ -377,10 +377,11 @@ // Linux Power Supply -#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 4 charts per power supply -#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9501 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9502 -#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9503 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 5 charts per power supply +#define NETDATA_CHART_PRIO_POWER_SUPPLY_POWER 9501 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9502 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9503 +#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9504 // Wireless diff --git a/src/collectors/proc.plugin/README.md b/src/collectors/proc.plugin/README.md index 28c204f5cd..99913da350 100644 --- a/src/collectors/proc.plugin/README.md +++ b/src/collectors/proc.plugin/README.md @@ -518,6 +518,7 @@ and metrics: # battery capacity = yes # battery charge = no # battery energy = no + # battery power = yes # power supply voltage = no # keep files open = auto # directory to monitor = /sys/class/power_supply diff --git a/src/collectors/proc.plugin/integrations/power_supply.md b/src/collectors/proc.plugin/integrations/power_supply.md index 53191dfff5..1df26e1bed 100644 --- a/src/collectors/proc.plugin/integrations/power_supply.md +++ b/src/collectors/proc.plugin/integrations/power_supply.md @@ -68,6 +68,7 @@ Metrics: | Metric | Dimensions | Unit | |:------|:----------|:----| | powersupply.capacity | capacity | percentage | +| powersupply.power | now | percentage | | powersupply.charge | empty_design, empty, now, full, full_design | Ah | | powersupply.energy | empty_design, empty, now, full, full_design | Wh | | powersupply.voltage | min_design, min, now, max, max_design | V | diff --git a/src/collectors/proc.plugin/metadata.yaml b/src/collectors/proc.plugin/metadata.yaml index 68d67f3de0..fd834dd38d 100644 --- a/src/collectors/proc.plugin/metadata.yaml +++ b/src/collectors/proc.plugin/metadata.yaml @@ -5123,6 +5123,12 @@ modules: chart_type: line dimensions: - name: capacity + - name: powersupply.power + description: Battery discharge rate + unit: "W" + chart_type: line + dimensions: + - name: power - name: powersupply.charge description: Battery charge unit: "Ah" diff --git a/src/collectors/proc.plugin/sys_class_power_supply.c b/src/collectors/proc.plugin/sys_class_power_supply.c index f59374f209..c6be72679f 100644 --- a/src/collectors/proc.plugin/sys_class_power_supply.c +++ b/src/collectors/proc.plugin/sys_class_power_supply.c @@ -3,11 +3,19 @@ #include "plugin_proc.h" #define PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME "/sys/class/power_supply" +#define PROP_VALUE_LENGTH_MAX 30 const char *ps_property_names[] = { "charge", "energy", "voltage"}; const char *ps_property_titles[] = {"Battery charge", "Battery energy", "Power supply voltage"}; const char *ps_property_units[] = { "Ah", "Wh", "V"}; +const long ps_property_priorities[] = { + NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE, + NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY, + NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE +}; + + const char *ps_property_dim_names[] = {"empty_design", "empty", "now", "full", "full_design", "empty_design", "empty", "now", "full", "full_design", "min_design", "min", "now", "max", "max_design"}; @@ -29,6 +37,8 @@ struct ps_property { char *title; char *units; + long priority; + RRDSET *st; struct ps_property_dim *property_dim_root; @@ -36,12 +46,13 @@ struct ps_property { struct ps_property *next; }; -struct capacity { +struct simple_property { char *filename; int fd; RRDSET *st; RRDDIM *rd; + bool ok; unsigned long long value; }; @@ -50,7 +61,7 @@ struct power_supply { uint32_t hash; int found; - struct capacity *capacity; + struct simple_property *capacity, *power; struct ps_property *property_root; @@ -60,17 +71,22 @@ struct power_supply { static struct power_supply *power_supply_root = NULL; static int files_num = 0; +static void free_simple_prop(struct simple_property *prop) { + if(likely(prop)) { + if(likely(prop->st)) rrdset_is_obsolete___safe_from_collector_thread(prop->st); + freez(prop->filename); + if(likely(prop->fd != -1)) close(prop->fd); + files_num--; + freez(prop); + } +} + void power_supply_free(struct power_supply *ps) { if(likely(ps)) { // free capacity structure - if(likely(ps->capacity)) { - if(likely(ps->capacity->st)) rrdset_is_obsolete___safe_from_collector_thread(ps->capacity->st); - freez(ps->capacity->filename); - if(likely(ps->capacity->fd != -1)) close(ps->capacity->fd); - files_num--; - freez(ps->capacity); - } + free_simple_prop(ps->capacity); + free_simple_prop(ps->power); freez(ps->name); struct ps_property *pr = ps->property_root; @@ -116,14 +132,78 @@ static void add_labels_to_power_supply(struct power_supply *ps, RRDSET *st) { rrdlabels_add(st->rrdlabels, "device", ps->name, RRDLABEL_SRC_AUTO); } +static void read_simple_property(struct simple_property *prop, bool keep_fds_open) { + char buffer[PROP_VALUE_LENGTH_MAX + 1]; + + prop->ok = false; + if(unlikely(prop->fd == -1)) { + prop->fd = open(prop->filename, O_RDONLY | O_CLOEXEC, 0666); + if(unlikely(prop->fd == -1)) { + collector_error("Cannot open file '%s'", prop->filename); + return; + } + } + ssize_t r = read(prop->fd, buffer, PROP_VALUE_LENGTH_MAX); + if(unlikely(r < 1)) { + collector_error("Cannot read file '%s'", prop->filename); + } + else { + buffer[r] = '\0'; + prop->value = str2ull(buffer, NULL); + prop->ok = true; + } + + if(unlikely(!keep_fds_open)) { + close(prop->fd); + prop->fd = -1; + } + else if(unlikely(prop->ok && lseek(prop->fd, 0, SEEK_SET) == -1)) { + collector_error("Cannot seek in file '%s'", prop->filename); + close(prop->fd); + prop->fd = -1; + } + return; +} + +static void rrdset_create_simple_prop(struct power_supply *ps, struct simple_property *prop, char *title, char *dim, collected_number divisor, char *units, long priority, int update_every) { + if(unlikely(!prop->st)) { + char id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "powersupply_%s", dim); + snprintfz(context, RRD_ID_LENGTH_MAX, "powersupply.%s", dim); + + prop->st = rrdset_create_localhost( + id + , ps->name + , NULL + , dim + , context + , title + , units + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME + , priority + , update_every + , RRDSET_TYPE_LINE + ); + + add_labels_to_power_supply(ps, prop->st); + } + + if(unlikely(!prop->rd)) prop->rd = rrddim_add(prop->st, dim, NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(prop->st, prop->rd, prop->value); + + rrdset_done(prop->st); +} + int do_sys_class_power_supply(int update_every, usec_t dt) { (void)dt; - static int do_capacity = -1, do_property[3] = {-1}; + static int do_capacity = -1, do_power = -1, do_property[3] = {-1}; static int keep_fds_open = CONFIG_BOOLEAN_NO, keep_fds_open_config = -1; static char *dirname = NULL; if(unlikely(do_capacity == -1)) { do_capacity = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery capacity", CONFIG_BOOLEAN_YES); + do_power = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery power", CONFIG_BOOLEAN_YES); do_property[0] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery charge", CONFIG_BOOLEAN_NO); do_property[1] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery energy", CONFIG_BOOLEAN_NO); do_property[2] = config_get_boolean("plugin:proc:/sys/class/power_supply", "power supply voltage", CONFIG_BOOLEAN_NO); @@ -175,13 +255,24 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "capacity"); if (stat(filename, &stbuf) == 0) { - ps->capacity = callocz(sizeof(struct capacity), 1); + ps->capacity = callocz(sizeof(struct simple_property), 1); ps->capacity->filename = strdupz(filename); ps->capacity->fd = -1; files_num++; } } + if(likely(do_power != CONFIG_BOOLEAN_NO)) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "power_now"); + if (stat(filename, &stbuf) == 0) { + ps->power = callocz(sizeof(struct simple_property), 1); + ps->power->filename = strdupz(filename); + ps->power->fd = -1; + files_num++; + } + } + // allocate memory and initialize structures for every property and file found size_t pr_idx, pd_idx; size_t prev_idx = 3; // there is no property with this index @@ -210,6 +301,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { pr->name = strdupz(ps_property_names[pr_idx]); pr->title = strdupz(ps_property_titles[pr_idx]); pr->units = strdupz(ps_property_units[pr_idx]); + pr->priority = ps_property_priorities[pr_idx]; prev_idx = pr_idx; pr->next = ps->property_root; ps->property_root = pr; @@ -242,52 +334,29 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { // read capacity file if(likely(ps->capacity)) { - char buffer[30 + 1]; - - if(unlikely(ps->capacity->fd == -1)) { - ps->capacity->fd = open(ps->capacity->filename, O_RDONLY | O_CLOEXEC, 0666); - if(unlikely(ps->capacity->fd == -1)) { - collector_error("Cannot open file '%s'", ps->capacity->filename); - power_supply_free(ps); - ps = NULL; - } - } + read_simple_property(ps->capacity, keep_fds_open); + } - if (ps) - { - ssize_t r = read(ps->capacity->fd, buffer, 30); - if(unlikely(r < 1)) { - collector_error("Cannot read file '%s'", ps->capacity->filename); - power_supply_free(ps); - ps = NULL; - } - else { - buffer[r] = '\0'; - ps->capacity->value = str2ull(buffer, NULL); + // read power file + if(likely(ps->power)) { + read_simple_property(ps->power, keep_fds_open); + } - if(unlikely(!keep_fds_open)) { - close(ps->capacity->fd); - ps->capacity->fd = -1; - } - else if(unlikely(lseek(ps->capacity->fd, 0, SEEK_SET) == -1)) { - collector_error("Cannot seek in file '%s'", ps->capacity->filename); - close(ps->capacity->fd); - ps->capacity->fd = -1; - } - } - } + if(unlikely((!ps->power || !ps->power->ok) && (!ps->capacity || !ps->capacity->ok))) { + power_supply_free(ps); + ps = NULL; } // read property files int read_error = 0; struct ps_property *pr; - if (ps) + if (likely(ps)) { for(pr = ps->property_root; pr && !read_error; pr = pr->next) { struct ps_property_dim *pd; for(pd = pr->property_dim_root; pd; pd = pd->next) { if(likely(!pd->always_zero)) { - char buffer[30 + 1]; + char buffer[PROP_VALUE_LENGTH_MAX + 1]; if(unlikely(pd->fd == -1)) { pd->fd = open(pd->filename, O_RDONLY | O_CLOEXEC, 0666); @@ -299,7 +368,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { } } - ssize_t r = read(pd->fd, buffer, 30); + ssize_t r = read(pd->fd, buffer, PROP_VALUE_LENGTH_MAX); if(unlikely(r < 1)) { collector_error("Cannot read file '%s'", pd->filename); read_error = 1; @@ -346,30 +415,12 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { continue; } - if(likely(ps->capacity)) { - if(unlikely(!ps->capacity->st)) { - ps->capacity->st = rrdset_create_localhost( - "powersupply_capacity" - , ps->name - , NULL - , "capacity" - , "powersupply.capacity" - , "Battery capacity" - , "percentage" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME - , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY - , update_every - , RRDSET_TYPE_LINE - ); - - add_labels_to_power_supply(ps, ps->capacity->st); - } - - if(unlikely(!ps->capacity->rd)) ps->capacity->rd = rrddim_add(ps->capacity->st, "capacity", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - rrddim_set_by_pointer(ps->capacity->st, ps->capacity->rd, ps->capacity->value); + if(likely(ps->capacity && ps->capacity->ok)) { + rrdset_create_simple_prop(ps, ps->capacity, "Battery capacity", "capacity", 1, "percentage", NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY, update_every); + } - rrdset_done(ps->capacity->st); + if(likely(ps->power && ps->power->ok)) { + rrdset_create_simple_prop(ps, ps->power, "Battery power", "power", 1000000, "W", NETDATA_CHART_PRIO_POWER_SUPPLY_POWER, update_every); } struct ps_property *pr; @@ -389,7 +440,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { , pr->units , PLUGIN_PROC_NAME , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME - , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY + , pr->priority , update_every , RRDSET_TYPE_LINE ); diff --git a/src/web/gui/dashboard_info.js b/src/web/gui/dashboard_info.js index 404f54e7f5..065324828a 100644 --- a/src/web/gui/dashboard_info.js +++ b/src/web/gui/dashboard_info.js @@ -6813,6 +6813,9 @@ netdataDashboard.context = { info: 'The current battery charge.' }, + 'powersupply.power': { + info: 'The current battery discharge rate in Watts.' + }, 'powersupply.charge': { info: '<p>The battery charge in Amp-hours.</p>'+ '<p><b>now</b> - actual charge value. '+ @@ -6839,7 +6842,6 @@ netdataDashboard.context = { '<b>max_design</b>, <b>min_design</b> - design values for maximal and minimal power supply voltages. '+ 'Maximal/minimal means values of voltages when battery considered "full"/"empty" at normal conditions.</p>' }, - // ------------------------------------------------------------------------ // VMware vSphere |