Merge pull request #270 from Stunkymonkey/mem-support

initial support of memory-usage for linux
This commit is contained in:
Ingo Bürk 2018-05-19 22:08:42 +02:00 committed by GitHub
commit b850f5852d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 300 additions and 0 deletions

View File

@ -421,6 +421,19 @@ int main(int argc, char *argv[]) {
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
CFG_END()};
cfg_opt_t memory_opts[] = {
CFG_STR("format", "%used %free %available", CFGF_NONE),
CFG_STR("format_degraded", NULL, CFGF_NONE),
CFG_STR("threshold_degraded", NULL, CFGF_NONE),
CFG_STR("threshold_critical", NULL, CFGF_NONE),
CFG_STR("memory_used_method", "classical", CFGF_NONE),
CFG_CUSTOM_ALIGN_OPT,
CFG_CUSTOM_COLOR_OPTS,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
CFG_END()};
cfg_opt_t usage_opts[] = {
CFG_STR("format", "%usage", CFGF_NONE),
CFG_STR("format_above_threshold", NULL, CFGF_NONE),
@ -490,6 +503,7 @@ int main(int argc, char *argv[]) {
CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI),
CFG_SEC("ddate", ddate_opts, CFGF_NONE),
CFG_SEC("load", load_opts, CFGF_NONE),
CFG_SEC("memory", memory_opts, CFGF_NONE),
CFG_SEC("cpu_usage", usage_opts, CFGF_NONE),
CFG_END()};
@ -724,6 +738,12 @@ int main(int argc, char *argv[]) {
SEC_CLOSE_MAP;
}
CASE_SEC("memory") {
SEC_OPEN_MAP("memory");
print_memory(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_degraded"), cfg_getstr(sec, "threshold_degraded"), cfg_getstr(sec, "threshold_critical"), cfg_getstr(sec, "memory_used_method"));
SEC_CLOSE_MAP;
}
CASE_SEC("time") {
SEC_OPEN_MAP("time");
print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, NULL, NULL, tv.tv_sec);

View File

@ -17,6 +17,7 @@ order += "wireless _first_"
order += "ethernet _first_"
order += "battery all"
order += "load"
order += "memory"
order += "tztime local"
wireless _first_ {
@ -42,6 +43,12 @@ load {
format = "%1min"
}
memory {
format = "%used | %available"
threshold_degraded = "1G"
format_degraded = "MEMORY < %available"
}
disk "/" {
format = "%avail"
}

View File

@ -223,6 +223,7 @@ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const
void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold);
void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down);
void print_load(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const float max_threshold);
void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *format_degraded, const char *threshold_degraded, const char *threshold_critical, const char *memory_used_method);
void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx);
bool process_runs(const char *path);
int volume_pulseaudio(uint32_t sink_idx, const char *sink_name);

View File

@ -55,6 +55,7 @@ order += "wireless wlan0"
order += "ethernet eth0"
order += "battery 0"
order += "cpu_temperature 0"
order += "memory"
order += "load"
order += "tztime local"
order += "tztime berlin"
@ -113,6 +114,12 @@ cpu_temperature 0 {
path = "/sys/devices/platform/coretemp.0/temp1_input"
}
memory {
format = "%used"
threshold_degraded = "10%"
format_degraded = "MEMORY: %free"
}
disk "/" {
format = "%free"
}
@ -192,6 +199,7 @@ double-quote (""") characters need to be replaced with "`&amp;`", "`&lt;`",
for generated content (e.g. wireless ESSID, time).
*Example configuration*:
-------------------------------------------------------------
general {
output_format = "xmobar"
@ -428,6 +436,45 @@ starting from %cpu0. This feature is currently not supported in FreeBSD.
*Example format_above_degraded_threshold*: +Warning above degraded threshold: %usage+
=== Memory
Gets the memory usage from system on a Linux system from +/proc/meminfo+. Other
systems are currently not supported.
As format placeholders, +total+, +used+, +free+, +available+ and +shared+ are
available. These will print human readable values. It's also possible to prefix
the placeholders with +percentage_+ to get a value in percent.
It's possible to define a +threshold_degraded+ and a +threshold_critical+ to
color the status bar output in yellow or red, if the available memory falls
below the given threshold. Possible values of the threshold can be any integer,
suffixed with an iec symbol (+T+, +G+, +M+, +K+). Alternatively, the integer
can be suffixed by a percent sign, which then rets evaluated relatively to
total memory.
If the +format_degraded+ parameter is given and either the critical or the
degraded threshold applies, +format_degraded+ will get used as format string.
It acts equivalently to +format+.
As Linux' meminfo doesn't expose the overall memory in use, there are multiple
methods to distinguish the actually used memory.
*Example used_memory_method*: +memavailable+ ("total memory" - "MemAvailable", matches gnome system monitor)
*Example used_memory_method*: +classical+ ("total memory" - "free" - "buffers" - "cache", matches gnome system monitor)
*Example order*: +memory+
*Example format*: +%free %available (%used) / %total+
*Example format*: +%percentage_used used, %percentage_free free, %percentage_shared shared+
*Example threshold_degraded*: +10%+
*Example threshold_critical*: +5%+
*Example format_degraded*: +Memory LOW: %free+
=== Load
Gets the system load (number of processes waiting for CPU time in the last

225
src/print_mem.c Normal file
View File

@ -0,0 +1,225 @@
// vim:ts=4:sw=4:expandtab
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <yajl/yajl_gen.h>
#include <yajl/yajl_version.h>
#include "i3status.h"
#define BINARY_BASE UINT64_C(1024)
#define MAX_EXPONENT 4
static const char *const iec_symbols[MAX_EXPONENT + 1] = {"", "Ki", "Mi", "Gi", "Ti"};
static const char memoryfile_linux[] = "/proc/meminfo";
/*
* Prints the given amount of bytes in a human readable manner.
*
*/
static int print_bytes_human(char *outwalk, uint64_t bytes) {
double size = bytes;
int exponent = 0;
int bin_base = BINARY_BASE;
while (size >= bin_base && exponent < MAX_EXPONENT) {
size /= bin_base;
exponent += 1;
}
return sprintf(outwalk, "%.1f %sB", size, iec_symbols[exponent]);
}
/*
* Convert a string to its absolute representation based on the total
* memory of `mem_total`.
*
* The string can contain any percentage values, which then return a
* the value of `size` in relation to `mem_total`.
* Alternatively an absolute value can be given, suffixed with an iec
* symbol.
*
*/
static long memory_absolute(const long mem_total, const char *size) {
long mem_absolute = -1;
char *endptr = NULL;
mem_absolute = strtol(size, &endptr, 10);
if (endptr) {
while (endptr[0] != '\0' && isspace(endptr[0]))
endptr++;
switch (endptr[0]) {
case 'T':
case 't':
mem_absolute *= BINARY_BASE;
case 'G':
case 'g':
mem_absolute *= BINARY_BASE;
case 'M':
case 'm':
mem_absolute *= BINARY_BASE;
case 'K':
case 'k':
mem_absolute *= BINARY_BASE;
break;
case '%':
mem_absolute = mem_total * mem_absolute / 100;
break;
default:
break;
}
}
return mem_absolute;
}
void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *format_degraded, const char *threshold_degraded, const char *threshold_critical, const char *memory_used_method) {
char *outwalk = buffer;
#if defined(linux)
const char *selected_format = format;
const char *walk;
const char *output_color = NULL;
long ram_total = -1;
long ram_free = -1;
long ram_available = -1;
long ram_used = -1;
long ram_shared = -1;
long ram_cached = -1;
long ram_buffers = -1;
FILE *file = fopen(memoryfile_linux, "r");
if (!file) {
goto error;
}
char line[128];
while (fgets(line, sizeof line, file)) {
if (BEGINS_WITH(line, "MemTotal:")) {
ram_total = strtol(line + strlen("MemTotal:"), NULL, 10);
}
if (BEGINS_WITH(line, "MemFree:")) {
ram_free = strtol(line + strlen("MemFree:"), NULL, 10);
}
if (BEGINS_WITH(line, "MemAvailable:")) {
ram_available = strtol(line + strlen("MemAvailable:"), NULL, 10);
}
if (BEGINS_WITH(line, "Buffers:")) {
ram_buffers = strtol(line + strlen("Buffers:"), NULL, 10);
}
if (BEGINS_WITH(line, "Cached:")) {
ram_cached = strtol(line + strlen("Cached:"), NULL, 10);
}
if (BEGINS_WITH(line, "Shmem:")) {
ram_shared = strtol(line + strlen("Shmem:"), NULL, 10);
}
if (ram_total != -1 && ram_free != -1 && ram_available != -1 && ram_buffers != -1 && ram_cached != -1 && ram_shared != -1) {
break;
}
}
fclose(file);
if (ram_total == -1 || ram_free == -1 || ram_available == -1 || ram_buffers == -1 || ram_cached == -1 || ram_shared == -1) {
goto error;
}
ram_total = ram_total * BINARY_BASE;
ram_free = ram_free * BINARY_BASE;
ram_available = ram_available * BINARY_BASE;
ram_buffers = ram_buffers * BINARY_BASE;
ram_cached = ram_cached * BINARY_BASE;
ram_shared = ram_shared * BINARY_BASE;
if (BEGINS_WITH(memory_used_method, "memavailable")) {
ram_used = ram_total - ram_available;
} else if (BEGINS_WITH(memory_used_method, "classical")) {
ram_used = ram_total - ram_free - ram_buffers - ram_cached;
}
if (threshold_degraded) {
long abs = memory_absolute(ram_total, threshold_degraded);
if (ram_available < abs) {
output_color = "color_degraded";
}
}
if (threshold_critical) {
long abs = memory_absolute(ram_total, threshold_critical);
if (ram_available < abs) {
output_color = "color_bad";
}
}
if (output_color) {
START_COLOR(output_color);
if (format_degraded)
selected_format = format_degraded;
}
for (walk = selected_format; *walk != '\0'; walk++) {
if (*walk != '%') {
*(outwalk++) = *walk;
continue;
}
if (BEGINS_WITH(walk + 1, "total")) {
outwalk += print_bytes_human(outwalk, ram_total);
walk += strlen("total");
}
if (BEGINS_WITH(walk + 1, "used")) {
outwalk += print_bytes_human(outwalk, ram_used);
walk += strlen("used");
}
if (BEGINS_WITH(walk + 1, "free")) {
outwalk += print_bytes_human(outwalk, ram_free);
walk += strlen("free");
}
if (BEGINS_WITH(walk + 1, "available")) {
outwalk += print_bytes_human(outwalk, ram_available);
walk += strlen("available");
}
if (BEGINS_WITH(walk + 1, "shared")) {
outwalk += print_bytes_human(outwalk, ram_shared);
walk += strlen("shared");
}
if (BEGINS_WITH(walk + 1, "percentage_free")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_free / ram_total, pct_mark);
walk += strlen("percentage_free");
}
if (BEGINS_WITH(walk + 1, "percentage_available")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_available / ram_total, pct_mark);
walk += strlen("percentage_available");
}
if (BEGINS_WITH(walk + 1, "percentage_used")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_used / ram_total, pct_mark);
walk += strlen("percentage_used");
}
if (BEGINS_WITH(walk + 1, "percentage_shared")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_shared / ram_total, pct_mark);
walk += strlen("percentage_shared");
}
}
if (output_color)
END_COLOR;
*outwalk = '\0';
OUTPUT_FULL_TEXT(buffer);
return;
error:
OUTPUT_FULL_TEXT("can't read memory");
fputs("i3status: Cannot read system memory using /proc/meminfo\n", stderr);
#else
OUTPUT_FULL_TEXT("");
fputs("i3status: Memory status information is not supported on this system\n", stderr);
#endif
}