forked from external/yambar
module/cpu: make ‘content’ particle a template
Before this patch, the cpu module instantiated a single particle (the ‘content’ particle), with one tag ("cpu") representing the total CPU usage, and then one tag (cpuN) for each core. This makes it cumbersome to configure, since you need to explicitly reference each cpuN tag to get per-core usage. This patch rewrites this, so that ‘content’ is now a template. It’s instantiated once to represent the total CPU usage, and then once for each core. Each instance has a "cpu" tag, representing the CPU usage of that core (or total usage). It also has an "id" tag. The ID is 0..n for actual cores, and -1 for total usage. This means you can do something like this in your config: - cpu: content: map: conditions: id < 0: {string: {text: "Total: {cpu}%"}} id >= 0: {string: {text: "Core #{id}: {cpu}%"}} Closes #207
This commit is contained in:
parent
2e0e1a402f
commit
b195bc4dcb
4 changed files with 135 additions and 55 deletions
|
@ -62,6 +62,9 @@
|
||||||
non-empty ([#191][191])
|
non-empty ([#191][191])
|
||||||
* alsa: use dB instead of raw volume values, if possible, when
|
* alsa: use dB instead of raw volume values, if possible, when
|
||||||
calculating the `percent` tag ([#202][202])
|
calculating the `percent` tag ([#202][202])
|
||||||
|
* cpu: `content` particle is now a template instantiated once for each
|
||||||
|
core, and once for the total CPU usage. See
|
||||||
|
**yambar-modules-cpu**(5) for more information ([#207][207]).
|
||||||
* **BREAKING CHANGE**: overhaul of the `map` particle. Instead of
|
* **BREAKING CHANGE**: overhaul of the `map` particle. Instead of
|
||||||
specifying a `tag` and then an array of `values`, you must now
|
specifying a `tag` and then an array of `values`, you must now
|
||||||
simply use an array of `conditions`, that consist of:
|
simply use an array of `conditions`, that consist of:
|
||||||
|
@ -119,6 +122,7 @@
|
||||||
[182]: https://codeberg.org/dnkl/yambar/issues/182
|
[182]: https://codeberg.org/dnkl/yambar/issues/182
|
||||||
[191]: https://codeberg.org/dnkl/yambar/issues/191
|
[191]: https://codeberg.org/dnkl/yambar/issues/191
|
||||||
[202]: https://codeberg.org/dnkl/yambar/issues/202
|
[202]: https://codeberg.org/dnkl/yambar/issues/202
|
||||||
|
[207]: https://codeberg.org/dnkl/yambar/issues/207
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
|
@ -3,17 +3,24 @@ yambar-modules-cpu(5)
|
||||||
# NAME
|
# NAME
|
||||||
cpu - This module provides the CPU usage
|
cpu - This module provides the CPU usage
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module reports CPU usage, in percent. The _content_ particle is a
|
||||||
|
template that is instantiated once for each core, and once for the
|
||||||
|
total CPU usage.
|
||||||
|
|
||||||
# TAGS
|
# TAGS
|
||||||
|
|
||||||
[[ *Name*
|
[[ *Name*
|
||||||
:[ *Type*
|
:[ *Type*
|
||||||
:[ *Description*
|
:[ *Description*
|
||||||
|
| id
|
||||||
|
: int
|
||||||
|
: Core ID. 0..n represents individual cores, and -1 represents the
|
||||||
|
total usage
|
||||||
| cpu
|
| cpu
|
||||||
: range
|
: range
|
||||||
: Current usage of the whole CPU in percent
|
: Current usage of CPU core {id}, in percent
|
||||||
| cpu<0..X>
|
|
||||||
: range
|
|
||||||
: Current usage of CPU core X in percent
|
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
|
@ -24,19 +31,49 @@ cpu - This module provides the CPU usage
|
||||||
| interval
|
| interval
|
||||||
: int
|
: int
|
||||||
: no
|
: no
|
||||||
: Refresh interval of the CPU usage stats in ms (default=500). Cannot be less then 500 ms
|
: Refresh interval of the CPU usage stats in ms (default=500). Cannot
|
||||||
|
be less then 500 ms
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Display total CPU usage as a number
|
||||||
```
|
```
|
||||||
bar:
|
bar:
|
||||||
left:
|
left:
|
||||||
- cpu:
|
- cpu:
|
||||||
interval: 2500
|
interval: 2500
|
||||||
content:
|
content:
|
||||||
string: {text: "{cpu1}%"}
|
map:
|
||||||
|
conditions:
|
||||||
|
id < 0:
|
||||||
|
- string: {text: , font: Font Awesome 6 Free:style=solid}
|
||||||
|
- string: {text: "{cpu}%"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Display a vertical bar for each core
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- cpu:
|
||||||
|
interval: 2500
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
conditions:
|
||||||
|
id >= 0:
|
||||||
|
- ramp:
|
||||||
|
tag: cpu
|
||||||
|
items:
|
||||||
|
- string: {text: ▁}
|
||||||
|
- string: {text: ▂}
|
||||||
|
- string: {text: ▃}
|
||||||
|
- string: {text: ▄}
|
||||||
|
- string: {text: ▅}
|
||||||
|
- string: {text: ▆}
|
||||||
|
- string: {text: ▇}
|
||||||
|
- string: {text: █}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
135
modules/cpu.c
135
modules/cpu.c
|
@ -14,11 +14,14 @@
|
||||||
#define LOG_MODULE "cpu"
|
#define LOG_MODULE "cpu"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#define SMALLEST_INTERVAL 500
|
#define SMALLEST_INTERVAL 500
|
||||||
|
#include "../log.h"
|
||||||
|
|
||||||
#include "../bar/bar.h"
|
#include "../bar/bar.h"
|
||||||
#include "../config-verify.h"
|
#include "../config-verify.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include "../log.h"
|
#include "../particles/dynlist.h"
|
||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
|
|
||||||
struct cpu_stats {
|
struct cpu_stats {
|
||||||
uint32_t *prev_cores_idle;
|
uint32_t *prev_cores_idle;
|
||||||
uint32_t *prev_cores_nidle;
|
uint32_t *prev_cores_nidle;
|
||||||
|
@ -27,10 +30,10 @@ struct cpu_stats {
|
||||||
uint32_t *cur_cores_nidle;
|
uint32_t *cur_cores_nidle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct private
|
struct private {
|
||||||
{
|
struct particle *template;
|
||||||
struct particle *label;
|
|
||||||
uint16_t interval;
|
uint16_t interval;
|
||||||
|
size_t core_count;
|
||||||
struct cpu_stats cpu_stats;
|
struct cpu_stats cpu_stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,12 +41,14 @@ static void
|
||||||
destroy(struct module *mod)
|
destroy(struct module *mod)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
m->label->destroy(m->label);
|
|
||||||
|
m->template->destroy(m->template);
|
||||||
free(m->cpu_stats.prev_cores_idle);
|
free(m->cpu_stats.prev_cores_idle);
|
||||||
free(m->cpu_stats.prev_cores_nidle);
|
free(m->cpu_stats.prev_cores_nidle);
|
||||||
free(m->cpu_stats.cur_cores_idle);
|
free(m->cpu_stats.cur_cores_idle);
|
||||||
free(m->cpu_stats.cur_cores_nidle);
|
free(m->cpu_stats.cur_cores_nidle);
|
||||||
free(m);
|
free(m);
|
||||||
|
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +68,10 @@ get_cpu_nb_cores()
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle,
|
parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice,
|
||||||
uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest,
|
uint32_t *system, uint32_t *idle, uint32_t *iowait,
|
||||||
uint32_t *guestnice)
|
uint32_t *irq, uint32_t *softirq, uint32_t *steal,
|
||||||
|
uint32_t *guest, uint32_t *guestnice)
|
||||||
{
|
{
|
||||||
int32_t core_id;
|
int32_t core_id;
|
||||||
if (line[sizeof("cpu") - 1] == ' ') {
|
if (line[sizeof("cpu") - 1] == ' ') {
|
||||||
|
@ -91,21 +97,26 @@ parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t
|
||||||
static uint8_t
|
static uint8_t
|
||||||
get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx)
|
get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx)
|
||||||
{
|
{
|
||||||
uint32_t prev_total = cpu_stats->prev_cores_idle[core_idx + 1] + cpu_stats->prev_cores_nidle[core_idx + 1];
|
uint32_t prev_total =
|
||||||
uint32_t cur_total = cpu_stats->cur_cores_idle[core_idx + 1] + cpu_stats->cur_cores_nidle[core_idx + 1];
|
cpu_stats->prev_cores_idle[core_idx + 1] +
|
||||||
|
cpu_stats->prev_cores_nidle[core_idx + 1];
|
||||||
|
|
||||||
|
uint32_t cur_total =
|
||||||
|
cpu_stats->cur_cores_idle[core_idx + 1] +
|
||||||
|
cpu_stats->cur_cores_nidle[core_idx + 1];
|
||||||
|
|
||||||
double totald = cur_total - prev_total;
|
double totald = cur_total - prev_total;
|
||||||
double nidled = cpu_stats->cur_cores_nidle[core_idx + 1] - cpu_stats->prev_cores_nidle[core_idx + 1];
|
double nidled =
|
||||||
|
cpu_stats->cur_cores_nidle[core_idx + 1] -
|
||||||
|
cpu_stats->prev_cores_nidle[core_idx + 1];
|
||||||
|
|
||||||
double percent = (nidled * 100) / (totald + 1);
|
double percent = (nidled * 100) / (totald + 1);
|
||||||
|
|
||||||
return round(percent);
|
return round(percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
refresh_cpu_stats(struct cpu_stats *cpu_stats)
|
refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count)
|
||||||
{
|
{
|
||||||
uint32_t nb_cores = get_cpu_nb_cores();
|
|
||||||
int32_t core = 0;
|
int32_t core = 0;
|
||||||
uint32_t user = 0;
|
uint32_t user = 0;
|
||||||
uint32_t nice = 0;
|
uint32_t nice = 0;
|
||||||
|
@ -129,11 +140,11 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((read = getline(&line, &len, fp)) != -1 && core <= nb_cores) {
|
while ((read = getline(&line, &len, fp)) != -1 && core <= core_count) {
|
||||||
if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) {
|
if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) {
|
||||||
if (!parse_proc_stat_line(
|
if (!parse_proc_stat_line(
|
||||||
line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal,
|
line, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
|
||||||
&guest, &guestnice))
|
&steal, &guest, &guestnice))
|
||||||
{
|
{
|
||||||
LOG_ERR("unable to parse /proc/stat line");
|
LOG_ERR("unable to parse /proc/stat line");
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -148,6 +159,7 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats)
|
||||||
core++;
|
core++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
free(line);
|
free(line);
|
||||||
|
@ -156,29 +168,45 @@ exit:
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
const struct private *p = mod->private;
|
const struct private *m = mod->private;
|
||||||
uint32_t nb_cores = get_cpu_nb_cores();
|
|
||||||
|
|
||||||
char cpu_name[32];
|
|
||||||
struct tag_set tags;
|
|
||||||
tags.count = nb_cores + 1;
|
|
||||||
tags.tags = calloc(tags.count, sizeof(*tags.tags));
|
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, -1);
|
|
||||||
tags.tags[0] = tag_new_int_range(mod, "cpu", cpu_usage, 0, 100);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < nb_cores; ++i) {
|
const size_t list_count = m->core_count + 1;
|
||||||
uint8_t cpu_usage = get_cpu_usage_percent(&p->cpu_stats, i);
|
struct exposable *parts[list_count];
|
||||||
snprintf(cpu_name, sizeof(cpu_name), "cpu%u", i);
|
|
||||||
tags.tags[i + 1] = tag_new_int_range(mod, cpu_name, cpu_usage, 0, 100);
|
{
|
||||||
|
uint8_t total_usage = get_cpu_usage_percent(&m->cpu_stats, -1);
|
||||||
|
|
||||||
|
struct tag_set tags = {
|
||||||
|
.tags = (struct tag *[]){
|
||||||
|
tag_new_int(mod, "id", -1),
|
||||||
|
tag_new_int_range(mod, "cpu", total_usage, 0, 100),
|
||||||
|
},
|
||||||
|
.count = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
parts[0] = m->template->instantiate(m->template, &tags);
|
||||||
|
tag_set_destroy(&tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m->core_count; i++) {
|
||||||
|
uint8_t core_usage = get_cpu_usage_percent(&m->cpu_stats, i);
|
||||||
|
|
||||||
|
struct tag_set tags = {
|
||||||
|
.tags = (struct tag *[]){
|
||||||
|
tag_new_int(mod, "id", i),
|
||||||
|
tag_new_int_range(mod, "cpu", core_usage, 0, 100),
|
||||||
|
},
|
||||||
|
.count = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
parts[i + 1] = m->template->instantiate(m->template, &tags);
|
||||||
|
tag_set_destroy(&tags);
|
||||||
|
}
|
||||||
|
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
|
return dynlist_exposable_new(parts, list_count, 0, 0);
|
||||||
struct exposable *exposable = p->label->instantiate(p->label, &tags);
|
|
||||||
|
|
||||||
tag_set_destroy(&tags);
|
|
||||||
free(tags.tags);
|
|
||||||
return exposable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -186,6 +214,7 @@ run(struct module *mod)
|
||||||
{
|
{
|
||||||
const struct bar *bar = mod->bar;
|
const struct bar *bar = mod->bar;
|
||||||
bar->refresh(bar);
|
bar->refresh(bar);
|
||||||
|
|
||||||
struct private *p = mod->private;
|
struct private *p = mod->private;
|
||||||
while (true) {
|
while (true) {
|
||||||
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
||||||
|
@ -203,7 +232,7 @@ run(struct module *mod)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
refresh_cpu_stats(&p->cpu_stats);
|
refresh_cpu_stats(&p->cpu_stats, p->core_count);
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
bar->refresh(bar);
|
bar->refresh(bar);
|
||||||
}
|
}
|
||||||
|
@ -212,17 +241,24 @@ run(struct module *mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct module *
|
static struct module *
|
||||||
cpu_new(uint16_t interval, struct particle *label)
|
cpu_new(uint16_t interval, struct particle *template)
|
||||||
{
|
{
|
||||||
struct private *p = calloc(1, sizeof(*p));
|
|
||||||
p->label = label;
|
|
||||||
uint32_t nb_cores = get_cpu_nb_cores();
|
uint32_t nb_cores = get_cpu_nb_cores();
|
||||||
p->interval = interval;
|
|
||||||
p->cpu_stats.prev_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle));
|
|
||||||
p->cpu_stats.prev_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle));
|
|
||||||
|
|
||||||
p->cpu_stats.cur_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle));
|
struct private *p = calloc(1, sizeof(*p));
|
||||||
p->cpu_stats.cur_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle));
|
p->template = template;
|
||||||
|
p->interval = interval;
|
||||||
|
p->core_count = nb_cores;
|
||||||
|
|
||||||
|
p->cpu_stats.prev_cores_nidle = calloc(
|
||||||
|
nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle));
|
||||||
|
p->cpu_stats.prev_cores_idle = calloc(
|
||||||
|
nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle));
|
||||||
|
|
||||||
|
p->cpu_stats.cur_cores_nidle = calloc(
|
||||||
|
nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle));
|
||||||
|
p->cpu_stats.cur_cores_idle = calloc(
|
||||||
|
nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle));
|
||||||
|
|
||||||
struct module *mod = module_common_new();
|
struct module *mod = module_common_new();
|
||||||
mod->private = p;
|
mod->private = p;
|
||||||
|
@ -236,10 +272,12 @@ cpu_new(uint16_t interval, struct particle *label)
|
||||||
static struct module *
|
static struct module *
|
||||||
from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
{
|
{
|
||||||
const struct yml_node *interval = yml_get_value(node, "interval");
|
const struct yml_node *interval = yml_get_value(node, "poll-interval");
|
||||||
const struct yml_node *c = yml_get_value(node, "content");
|
const struct yml_node *c = yml_get_value(node, "content");
|
||||||
|
|
||||||
return cpu_new(interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval), conf_to_particle(c, inherited));
|
return cpu_new(
|
||||||
|
interval == NULL ? SMALLEST_INTERVAL : yml_value_as_int(interval),
|
||||||
|
conf_to_particle(c, inherited));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -249,7 +287,8 @@ conf_verify_interval(keychain_t *chain, const struct yml_node *node)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (yml_value_as_int(node) < SMALLEST_INTERVAL) {
|
if (yml_value_as_int(node) < SMALLEST_INTERVAL) {
|
||||||
LOG_ERR("%s: interval value cannot be less than %d ms", conf_err_prefix(chain, node), SMALLEST_INTERVAL);
|
LOG_ERR("%s: interval value cannot be less than %d ms",
|
||||||
|
conf_err_prefix(chain, node), SMALLEST_INTERVAL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +299,7 @@ static bool
|
||||||
verify_conf(keychain_t *chain, const struct yml_node *node)
|
verify_conf(keychain_t *chain, const struct yml_node *node)
|
||||||
{
|
{
|
||||||
static const struct attr_info attrs[] = {
|
static const struct attr_info attrs[] = {
|
||||||
{"interval", false, &conf_verify_interval},
|
{"poll-interval", false, &conf_verify_interval},
|
||||||
MODULE_COMMON_ATTRS,
|
MODULE_COMMON_ATTRS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ if plugin_clock_enabled
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if plugin_cpu_enabled
|
if plugin_cpu_enabled
|
||||||
mod_data += {'cpu': [[], []]}
|
mod_data += {'cpu': [[], [dynlist]]}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if plugin_disk_io_enabled
|
if plugin_disk_io_enabled
|
||||||
|
|
Loading…
Add table
Reference in a new issue