Implement support for memory-usage on Linux

This commit is contained in:
Felix Buehler 2018-03-20 15:35:28 +01:00
parent 2cb0ffa01a
commit c55754542e
4 changed files with 280 additions and 0 deletions

View File

@ -421,6 +421,22 @@ int main(int argc, char *argv[]) {
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT, CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
CFG_END()}; CFG_END()};
cfg_opt_t memory_opts[] = {
CFG_STR("format", "%used %free %available", CFGF_NONE),
CFG_STR("degraded_format_below_threshold", NULL, CFGF_NONE),
CFG_STR("degraded_threshold_type", "percentage_avail", CFGF_NONE),
CFG_FLOAT("degraded_low_threshold", 0, CFGF_NONE),
CFG_STR("critical_format_below_threshold", NULL, CFGF_NONE),
CFG_STR("critical_threshold_type", "percentage_avail", CFGF_NONE),
CFG_FLOAT("critical_low_threshold", 0, CFGF_NONE),
CFG_BOOL("use_available_memory", true, 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_opt_t usage_opts[] = {
CFG_STR("format", "%usage", CFGF_NONE), CFG_STR("format", "%usage", CFGF_NONE),
CFG_STR("format_above_threshold", NULL, CFGF_NONE), CFG_STR("format_above_threshold", NULL, CFGF_NONE),
@ -490,6 +506,7 @@ int main(int argc, char *argv[]) {
CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI), CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI),
CFG_SEC("ddate", ddate_opts, CFGF_NONE), CFG_SEC("ddate", ddate_opts, CFGF_NONE),
CFG_SEC("load", load_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_SEC("cpu_usage", usage_opts, CFGF_NONE),
CFG_END()}; CFG_END()};
@ -724,6 +741,12 @@ int main(int argc, char *argv[]) {
SEC_CLOSE_MAP; SEC_CLOSE_MAP;
} }
CASE_SEC("memory") {
SEC_OPEN_MAP("memory");
print_memory(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "degraded_format_below_threshold"), cfg_getstr(sec, "degraded_threshold_type"), cfg_getfloat(sec, "degraded_low_threshold"), cfg_getstr(sec, "critical_format_below_threshold"), cfg_getstr(sec, "critical_threshold_type"), cfg_getfloat(sec, "critical_low_threshold"), cfg_getbool(sec, "use_available_memory"));
SEC_CLOSE_MAP;
}
CASE_SEC("time") { CASE_SEC("time") {
SEC_OPEN_MAP("time"); SEC_OPEN_MAP("time");
print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, NULL, NULL, tv.tv_sec); print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, NULL, NULL, tv.tv_sec);

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_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_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_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 *degraded_format_below_threshold, const char *degraded_threshold_type, const float degraded_low_threshold, const char *critical_format_below_threshold, const char *critical_threshold_type, const float critical_low_threshold, const bool use_available_memory);
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); 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); bool process_runs(const char *path);
int volume_pulseaudio(uint32_t sink_idx, const char *sink_name); int volume_pulseaudio(uint32_t sink_idx, const char *sink_name);

View File

@ -55,6 +55,7 @@ order += "wireless wlan0"
order += "ethernet eth0" order += "ethernet eth0"
order += "battery 0" order += "battery 0"
order += "cpu_temperature 0" order += "cpu_temperature 0"
order += "memory"
order += "load" order += "load"
order += "tztime local" order += "tztime local"
order += "tztime berlin" order += "tztime berlin"
@ -113,6 +114,10 @@ cpu_temperature 0 {
path = "/sys/devices/platform/coretemp.0/temp1_input" path = "/sys/devices/platform/coretemp.0/temp1_input"
} }
memory {
format = "%used"
}
disk "/" { disk "/" {
format = "%free" format = "%free"
} }
@ -424,6 +429,49 @@ starting from %cpu0. This feature is currently not supported in FreeBSD.
*Example format_above_degraded_threshold*: +Warning above degraded threshold: %usage+ *Example format_above_degraded_threshold*: +Warning above degraded threshold: %usage+
=== Memory
Gets the memory usage from system.
On Linux, the information is taken from +/proc/meminfo+.
These values can also be expressed in percentages with the +percentage_used+,
+percentage_free+ and +percentage_shared+ formats.
If a +critical_low_threshold+ is defined, the output will be colored using
+color_bad+. The unit of this threshold is defined by the unit given in
+critical_threshold_type+, which can be one of "bytes_free", "bytes_avail",
"percentage_free" and "percentage_avail". Additionally, the former two can be
prefixed with one of "k", "m", "g" or "t" to change the exact unit.
For example, setting critical_low_threshold to 2 and threshold_type to "gbytes_avail"
causes available memory below 2 GiB to be colored with +color_bad+. The
defaults are "percentage_avail" with a threshold of 0.
Furthermore, the format used when the threshold is reached can be customized
using the option +critical_format_below_threshold+.
Same applies to +degraded_low_threshold+, +degraded_threshold_type+ and
+degraded_format_below_threshold+ using +color_degraded+.
The most common one is:
"used memory" = "total memory" - "free memory" - "buffers" - "cache"
This is the default in i3status. Some other programs use
"used memory" = "total memory" - "available memory"
You can disable this behavior using +use_available_memory+.
*Example order*: +memory+
*Example format*: +%free %available (%used)/ %total+
*Example format*: +used %percentage_used , free %percentage_free, shared %percentage_shared+
*Example degraded_low_threshold*: +10+
*Example degraded_threshold_type*: +percentage_free+
*Example critical_low_threshold*: +5+
*Example critical_format_below_threshold*: +Warning: %percentage_free+
*Example use_available_memory: +false+
=== Load === Load
Gets the system load (number of processes waiting for CPU time in the last Gets the system load (number of processes waiting for CPU time in the last

208
src/print_mem.c Normal file
View File

@ -0,0 +1,208 @@
// 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 filename[] = "/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]);
}
/*
* Determines whether remaining bytes are below given threshold.
*
*/
static bool below_threshold(const long totalRam, const long usedRam, const char *threshold_type, const long low_threshold) {
// empty is available or free, based on "use_available_memory"
long empty = totalRam - usedRam;
if (BEGINS_WITH(threshold_type, "percentage_")) {
return 100.0 * empty / totalRam < low_threshold;
} else if (strcasecmp(threshold_type, "bytes_free") == 0) {
return empty < low_threshold;
} else if (threshold_type[0] != '\0' && strncasecmp(threshold_type + 1, "bytes_", strlen("bytes_")) == 0) {
uint64_t base = BINARY_BASE;
long factor = 1;
switch (threshold_type[0]) {
case 'T':
case 't':
factor *= base;
case 'G':
case 'g':
factor *= base;
case 'M':
case 'm':
factor *= base;
case 'K':
case 'k':
factor *= base;
break;
default:
return false;
}
return empty < low_threshold * factor;
}
return false;
}
void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *degraded_format_below_threshold, const char *degraded_threshold_type, const float degraded_low_threshold, const char *critical_format_below_threshold, const char *critical_threshold_type, const float critical_low_threshold, const bool use_available_memory) {
char *outwalk = buffer;
#if defined(linux)
const char *selected_format = format;
const char *walk;
bool colorful_output = false;
long totalRam = -1;
long freeRam = -1;
long availableRam = -1;
long usedRam = -1;
long sharedRam = -1;
long cached = -1;
long buffers = -1;
FILE *file = fopen(filename, "r");
if (!file) {
goto error;
}
char line[128];
while (fgets(line, sizeof line, file)) {
if (BEGINS_WITH(line, "MemTotal:")) {
totalRam = strtol(line + strlen("MemTotal:"), NULL, 10);
}
if (BEGINS_WITH(line, "MemFree:")) {
freeRam = strtol(line + strlen("MemFree:"), NULL, 10);
}
if (BEGINS_WITH(line, "MemAvailable:")) {
availableRam = strtol(line + strlen("MemAvailable:"), NULL, 10);
}
if (BEGINS_WITH(line, "Buffers:")) {
buffers = strtol(line + strlen("Buffers:"), NULL, 10);
}
if (BEGINS_WITH(line, "Cached:")) {
cached = strtol(line + strlen("Cached:"), NULL, 10);
}
if (BEGINS_WITH(line, "Shmem:")) {
sharedRam = strtol(line + strlen("Shmem:"), NULL, 10);
}
if (totalRam != -1 && freeRam != -1 && availableRam != -1 && buffers != -1 && cached != -1 && sharedRam != -1) {
break;
}
}
fclose(file);
if (totalRam == -1 || freeRam == -1 || availableRam == -1 || buffers == -1 || cached == -1 || sharedRam == -1) {
goto error;
}
totalRam = totalRam * BINARY_BASE;
freeRam = freeRam * BINARY_BASE;
availableRam = availableRam * BINARY_BASE;
buffers = buffers * BINARY_BASE;
cached = cached * BINARY_BASE;
sharedRam = sharedRam * BINARY_BASE;
if (use_available_memory) {
usedRam = totalRam - availableRam;
} else {
usedRam = totalRam - freeRam - buffers - cached;
}
if (degraded_low_threshold > 0 && below_threshold(totalRam, usedRam, degraded_threshold_type, degraded_low_threshold)) {
if (critical_low_threshold > 0 && below_threshold(totalRam, usedRam, critical_threshold_type, critical_low_threshold)) {
START_COLOR("color_bad");
colorful_output = true;
if (critical_format_below_threshold != NULL)
selected_format = critical_format_below_threshold;
} else {
START_COLOR("color_degraded");
colorful_output = true;
if (degraded_format_below_threshold != NULL)
selected_format = degraded_format_below_threshold;
}
}
for (walk = selected_format; *walk != '\0'; walk++) {
if (*walk != '%') {
*(outwalk++) = *walk;
continue;
}
if (BEGINS_WITH(walk + 1, "total")) {
outwalk += print_bytes_human(outwalk, totalRam);
walk += strlen("total");
}
if (BEGINS_WITH(walk + 1, "used")) {
outwalk += print_bytes_human(outwalk, usedRam);
walk += strlen("used");
}
if (BEGINS_WITH(walk + 1, "free")) {
outwalk += print_bytes_human(outwalk, freeRam);
walk += strlen("free");
}
if (BEGINS_WITH(walk + 1, "available")) {
outwalk += print_bytes_human(outwalk, availableRam);
walk += strlen("available");
}
if (BEGINS_WITH(walk + 1, "shared")) {
outwalk += print_bytes_human(outwalk, sharedRam);
walk += strlen("shared");
}
if (BEGINS_WITH(walk + 1, "percentage_free")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * freeRam / totalRam, pct_mark);
walk += strlen("percentage_free");
}
if (BEGINS_WITH(walk + 1, "percentage_available")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * availableRam / totalRam, pct_mark);
walk += strlen("percentage_available");
}
if (BEGINS_WITH(walk + 1, "percentage_used")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * usedRam / totalRam, pct_mark);
walk += strlen("percentage_used");
}
if (BEGINS_WITH(walk + 1, "percentage_shared")) {
outwalk += sprintf(outwalk, "%.01f%s", 100.0 * sharedRam / totalRam, pct_mark);
walk += strlen("percentage_shared");
}
}
if (colorful_output)
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
fputs("i3status: Memory status information is not supported on this system\n", stderr);
#endif
}