diff options
Diffstat (limited to 'multiload/src/linux-proc.c')
-rw-r--r-- | multiload/src/linux-proc.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/multiload/src/linux-proc.c b/multiload/src/linux-proc.c new file mode 100644 index 00000000..6059e9e3 --- /dev/null +++ b/multiload/src/linux-proc.c @@ -0,0 +1,442 @@ +/* From wmload.c, v0.9.2, licensed under the GPL. */ +#include <config.h> +#include <sys/types.h> +#include <sys/statvfs.h> +#include <math.h> +#include <fcntl.h> +#include <unistd.h> + +#include <glibtop.h> +#include <glibtop/cpu.h> +#include <glibtop/mem.h> +#include <glibtop/swap.h> +#include <glibtop/loadavg.h> +#include <glibtop/netload.h> +#include <glibtop/netlist.h> +#include <glibtop/mountlist.h> +#include <glibtop/fsusage.h> + +#include "linux-proc.h" +#include "autoscaler.h" + +static const unsigned needed_cpu_flags = +(1 << GLIBTOP_CPU_USER) + +(1 << GLIBTOP_CPU_IDLE) + +(1 << GLIBTOP_CPU_SYS) + +(1 << GLIBTOP_CPU_NICE); + +#if 0 +static const unsigned needed_page_flags = +(1 << GLIBTOP_SWAP_PAGEIN) + +(1 << GLIBTOP_SWAP_PAGEOUT); +#endif + +static const unsigned needed_mem_flags = +(1 << GLIBTOP_MEM_USED) + +(1 << GLIBTOP_MEM_FREE); + +static const unsigned needed_swap_flags = +(1 << GLIBTOP_SWAP_USED) + +(1 << GLIBTOP_SWAP_FREE); + +static const unsigned needed_loadavg_flags = +(1 << GLIBTOP_LOADAVG_LOADAVG); + +static const unsigned needed_netload_flags = +(1 << GLIBTOP_NETLOAD_IF_FLAGS) + +(1 << GLIBTOP_NETLOAD_BYTES_TOTAL); + +void +GetLoad (int Maximum, + int data [cpuload_n], + LoadGraph *g) +{ + MultiloadApplet *multiload; + glibtop_cpu cpu; + long cpu_aux [cpuload_n], used = 0, total = 0; + int current_scaled, used_scaled = 0; + int i; + + glibtop_get_cpu (&cpu); + + g_return_if_fail ((cpu.flags & needed_cpu_flags) == needed_cpu_flags); + + multiload = g->multiload; + + multiload->cpu_time [cpuload_usr] = cpu.user; + multiload->cpu_time [cpuload_nice] = cpu.nice; + multiload->cpu_time [cpuload_sys] = cpu.sys; + multiload->cpu_time [cpuload_iowait] = cpu.iowait + cpu.irq + cpu.softirq; + multiload->cpu_time [cpuload_free] = cpu.idle; + + if (!multiload->cpu_initialized) { + memcpy (multiload->cpu_last, multiload->cpu_time, sizeof (multiload->cpu_last)); + multiload->cpu_initialized = 1; + } + + for (i = 0; i < cpuload_n; i++) { + cpu_aux [i] = multiload->cpu_time [i] - multiload->cpu_last [i]; + total += cpu_aux [i]; + } + + for (i = 0; i < cpuload_free; i++) { + used += cpu_aux [i]; + current_scaled = rint ((float)(cpu_aux [i] * Maximum) / (float)total); + used_scaled += current_scaled; + data [i] = current_scaled; + } + data [cpuload_free] = Maximum - used_scaled; + + multiload->cpu_used_ratio = (float)(used) / (float)total; + + memcpy (multiload->cpu_last, multiload->cpu_time, sizeof multiload->cpu_last); +} + +void +GetDiskLoad (int Maximum, + int data [diskload_n], + LoadGraph *g) +{ + static gboolean first_call = TRUE; + static guint64 lastread = 0, lastwrite = 0; + static AutoScaler scaler; + + guint i; + int max; + + guint64 read, write; + guint64 readdiff, writediff; + + MultiloadApplet *multiload; + + multiload = g->multiload; + + + if(first_call) + { + autoscaler_init(&scaler, 60, 500); + } + + read = write = 0; + + if (multiload->nvme_diskstats) + { + FILE *fdr; + char line[255]; + guint64 s_read, s_write; + + fdr = fopen("/proc/diskstats", "r"); + if (!fdr) + { + multiload->nvme_diskstats = FALSE; + g_settings_set_boolean (multiload->settings, "diskload-nvme-diskstats", FALSE); + return; + } + + while (fgets(line, 255, fdr)) + { + /* Match main device, rather than individual partitions (e.g. nvme0n1) */ + if (!g_regex_match_simple("\\snvme\\d+\\w+\\d+\\s", line, 0, 0)) + { + continue; + } + + /* + 6 - sectors read + 10 - sectors written + */ + if (sscanf(line, "%*d %*d %*s %*d %*d %ld %*d %*d %*d %ld", &s_read, &s_write) == 2) + { + read += 512 * s_read; + write += 512 * s_write; + } + } + fclose(fdr); + } + else + { + glibtop_mountlist mountlist; + glibtop_mountentry *mountentries; + + mountentries = glibtop_get_mountlist (&mountlist, FALSE); + + for (i = 0; i < mountlist.number; i++) + { + struct statvfs statresult; + glibtop_fsusage fsusage; + + if (strstr (mountentries[i].devname, "/dev/") == NULL) + continue; + + if (strstr (mountentries[i].mountdir, "/media/") != NULL) + continue; + + if (statvfs (mountentries[i].mountdir, &statresult) < 0) + { + g_debug ("Failed to get statistics for mount entry: %s. Reason: %s. Skipping entry.", + mountentries[i].mountdir, strerror(errno)); + continue; + } + + glibtop_get_fsusage(&fsusage, mountentries[i].mountdir); + read += fsusage.read; write += fsusage.write; + } + + g_free(mountentries); + } + + readdiff = read - lastread; + writediff = write - lastwrite; + + lastread = read; + lastwrite = write; + + if(first_call) + { + first_call = FALSE; + memset(data, 0, 3 * sizeof data[0]); + return; + } + + max = autoscaler_get_max(&scaler, readdiff + writediff); + + multiload->diskload_used_ratio = (float)(readdiff + writediff) / (float)max; + + data [diskload_read] = rint ((float)Maximum * (float)readdiff / (float)max); + data [diskload_write] = rint ((float)Maximum * (float)writediff / (float)max); + data [diskload_free] = Maximum - (data [0] + data[1]); +} + +/* GNU/Linux: + * aux [memload_user] = (mem.total - mem.free) - (mem.cached + mem.buffer) + * aux [memload_shared] = mem.shared; + * aux [memload_cached] = mem.cached - mem.shared; + * aux [memload_buffer] = mem.buffer; + * + * Other operating systems: + * aux [memload_user] = mem.user; + * aux [memload_shared] = mem.shared; + * aux [memload_cached] = mem.cached; + * aux [memload_buffer] = mem.buffer; + */ +void +GetMemory (int Maximum, + int data [memload_n], + LoadGraph *g) +{ + MultiloadApplet *multiload; + glibtop_mem mem; + guint64 aux [memload_n], cache = 0; + int current_scaled, used_scaled = 0; + int i; + + glibtop_get_mem (&mem); + + g_return_if_fail ((mem.flags & needed_mem_flags) == needed_mem_flags); + +#ifndef __linux__ + aux [memload_user] = mem.user; + aux [memload_cached] = mem.cached; +#else + aux [memload_user] = mem.total - mem.free - mem.buffer - mem.cached;; + aux [memload_cached] = mem.cached - mem.shared; +#endif /* __linux__ */ + aux [memload_shared] = mem.shared; + aux [memload_buffer] = mem.buffer; + + for (i = 0; i < memload_free; i++) { + current_scaled = rint ((float)(aux [i] * Maximum) / (float)mem.total); + if (i != memload_user) { + cache += aux [i]; + } + used_scaled += current_scaled; + data [i] = current_scaled; + } + data [memload_free] = MAX (Maximum - used_scaled, 0); + + multiload = g->multiload; + multiload->memload_user = aux [memload_user]; + multiload->memload_cache = cache; + multiload->memload_total = mem.total; +} + +void +GetSwap (int Maximum, + int data [swapload_n], + LoadGraph *g) +{ + int used; + MultiloadApplet *multiload; + glibtop_swap swap; + + glibtop_get_swap (&swap); + g_return_if_fail ((swap.flags & needed_swap_flags) == needed_swap_flags); + + multiload = g->multiload; + + if (swap.total == 0) { + used = 0; + multiload->swapload_used_ratio = 0.0f; + } + else { + float ratio; + + ratio = (float)swap.used / (float)swap.total; + used = rint ((float) Maximum * ratio); + multiload->swapload_used_ratio = ratio; + } + + data [0] = used; + data [1] = Maximum - used; +} + +void +GetLoadAvg (int Maximum, + int data [2], + LoadGraph *g) +{ + glibtop_loadavg loadavg; + MultiloadApplet *multiload; + + glibtop_get_loadavg (&loadavg); + + g_return_if_fail ((loadavg.flags & needed_loadavg_flags) == needed_loadavg_flags); + + multiload = g->multiload; + multiload->loadavg1 = loadavg.loadavg[0]; + + data [0] = rint ((float) Maximum * loadavg.loadavg[0]); + data [1] = Maximum - data[0]; +} + +/* + * Return true if a network device (identified by its name) is virtual + * (ie: not corresponding to a physical device). In case it is a physical + * device or unknown, returns false. + */ +static gboolean +is_net_device_virtual(char *device) +{ + /* + * There is not definitive way to find out. On some systems (Linux + * kernels ≳ 2.19 without option SYSFS_DEPRECATED), there exist a + * directory /sys/devices/virtual/net which only contains virtual + * devices. It's also possible to detect by the fact that virtual + * devices do not have a symlink "device" in + * /sys/class/net/name-of-dev/ . This second method is more complex + * but more reliable. + */ + gboolean ret = FALSE; + char *path = malloc (strlen (device) + strlen ("/sys/class/net//device") + 1); + + if (path == NULL) + return FALSE; + + /* Check if /sys/class/net/name-of-dev/ exists (may be old linux kernel + * or not linux at all). */ + do { + if (sprintf(path, "/sys/class/net/%s", device) < 0) + break; + if (access(path, F_OK) != 0) + break; /* unknown */ + + if (sprintf(path, "/sys/class/net/%s/device", device) < 0) + break; + if (access(path, F_OK) != 0) + ret = TRUE; + } while (0); + + free (path); + return ret; +} + +void +GetNet (int Maximum, + int data [4], + LoadGraph *g) +{ + enum Types { + IN_COUNT = 0, + OUT_COUNT = 1, + LOCAL_COUNT = 2, + COUNT_TYPES = 3 + }; + + static int ticks = 0; + static gulong past[COUNT_TYPES] = {0}; + static AutoScaler scaler; + + gulong present[COUNT_TYPES] = {0}; + + guint i; + gchar **devices; + glibtop_netlist netlist; + + MultiloadApplet *multiload; + + multiload = g->multiload; + + if(ticks == 0) + { + autoscaler_init(&scaler, 60, 501); + } + + devices = glibtop_get_netlist(&netlist); + + for(i = 0; i < netlist.number; ++i) + { + glibtop_netload netload; + + glibtop_get_netload(&netload, devices[i]); + + g_return_if_fail((netload.flags & needed_netload_flags) == needed_netload_flags); + + if (!(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP))) + continue; + + if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) { + /* for loopback in and out are identical, so only count in */ + present[LOCAL_COUNT] += netload.bytes_in; + continue; + } + + /* + * Do not include virtual devices (VPN, PPPOE...) to avoid + * counting the same throughput several times. + */ + if (is_net_device_virtual(devices[i])) + continue; + + present[IN_COUNT] += netload.bytes_in; + present[OUT_COUNT] += netload.bytes_out; + } + + g_strfreev(devices); + netspeed_add (multiload->netspeed_in, present[IN_COUNT]); + netspeed_add (multiload->netspeed_out, present[OUT_COUNT]); + + if(ticks < 2) /* avoid initial spike */ + { + ticks++; + memset(data, 0, COUNT_TYPES * sizeof data[0]); + } + else + { + int delta[COUNT_TYPES]; + + for (i = 0; i < COUNT_TYPES; i++) + { + /* protect against weirdness */ + if (present[i] >= past[i]) + delta[i] = (present[i] - past[i]); + else + delta[i] = 0; + } + + for (i = 0; i < COUNT_TYPES; i++) + data[i] = delta[i]; + + } + + memcpy(past, present, sizeof past); +} |