/* From wmload.c, v0.9.2, licensed under the GPL. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, g->speed, 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 guint64 past[COUNT_TYPES] = {0}; guint64 present[COUNT_TYPES] = {0}; guint i; gchar **devices; glibtop_netlist netlist; MultiloadApplet *multiload; multiload = g->multiload; 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 + 1) * sizeof data[0]); } else { data[COUNT_TYPES] = 0; float seconds = (float) g->speed / 1000.0f; for (i = 0; i < COUNT_TYPES; i++) { /* protect against weirdness */ if (present[i] >= past[i]) data[i] = (int) ((float) (present[i] - past[i]) / seconds); else data[i] = 0; data[COUNT_TYPES] += data[i]; } } memcpy(past, present, sizeof past); }