Implement support for memory-usage on Linux
This commit is contained in:
parent
2cb0ffa01a
commit
c55754542e
23
i3status.c
23
i3status.c
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
208
src/print_mem.c
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user