/* 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 [5], LoadGraph *g) { int usr, nice, sys, iowait, free; int total; glibtop_cpu cpu; glibtop_get_cpu (&cpu); g_return_if_fail ((cpu.flags & needed_cpu_flags) == needed_cpu_flags); g->cpu_time [0] = cpu.user; g->cpu_time [1] = cpu.nice; g->cpu_time [2] = cpu.sys; g->cpu_time [3] = cpu.iowait + cpu.irq + cpu.softirq; g->cpu_time [4] = cpu.idle; if (!g->cpu_initialized) { memcpy (g->cpu_last, g->cpu_time, sizeof (g->cpu_last)); g->cpu_initialized = 1; } usr = g->cpu_time [0] - g->cpu_last [0]; nice = g->cpu_time [1] - g->cpu_last [1]; sys = g->cpu_time [2] - g->cpu_last [2]; iowait = g->cpu_time [3] - g->cpu_last [3]; free = g->cpu_time [4] - g->cpu_last [4]; total = usr + nice + sys + free + iowait; memcpy(g->cpu_last, g->cpu_time, sizeof g->cpu_last); usr = rint (Maximum * (float)(usr) / total); nice = rint (Maximum * (float)(nice) / total); sys = rint (Maximum * (float)(sys) / total); iowait = rint (Maximum * (float)(iowait) / total); free = Maximum - usr - nice - sys - iowait; data [0] = usr; data [1] = sys; data [2] = nice; data [3] = iowait; data [4] = free; } void GetDiskLoad (int Maximum, int data [3], LoadGraph *g) { static gboolean first_call = TRUE; static guint64 lastread = 0, lastwrite = 0; static AutoScaler scaler; guint i; int max; gboolean nvme_diskstats; guint64 read, write; guint64 readdiff, writediff; nvme_diskstats = g_settings_get_boolean (g->multiload->settings, "diskload-nvme-diskstats"); if(first_call) { autoscaler_init(&scaler, 60, 500); } read = write = 0; if (nvme_diskstats) { FILE *fdr; char line[255]; guint64 s_read, s_write; fdr = fopen("/proc/diskstats", "r"); if (!fdr) { g_settings_set_boolean (g->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); data[0] = (float)Maximum * readdiff / (float)max; data[1] = (float)Maximum * writediff / (float)max; data[2] = (float)Maximum - (data [0] + data[1]); } #if 0 void GetPage (int Maximum, int data [3], LoadGraph *g) { static int max = 100; /* guess at maximum page rate (= in + out) */ static u_int64_t lastin = 0; static u_int64_t lastout = 0; int in, out, idle; glibtop_swap swap; glibtop_get_swap (&swap); assert ((swap.flags & needed_page_flags) == needed_page_flags); if ((lastin > 0) && (lastin < swap.pagein)) { in = swap.pagein - lastin; } else { in = 0; } lastin = swap.pagein; if ((lastout > 0) && (lastout < swap.pageout)) { out = swap.pageout - lastout; } else { out = 0; } lastout = swap.pageout; if ((in + out) > max) { /* Maximum page rate has increased. Change the scale without any indication whatsoever to the user (not a nice thing to do). */ max = in + out; } in = rint (Maximum * ((float)in / max)); out = rint (Maximum * ((float)out / max)); idle = Maximum - in - out; data [0] = in; data [1] = out; data [2] = idle; } #endif /* 0 */ void GetMemory (int Maximum, int data [5], LoadGraph *g) { int user, shared, buffer, cached; glibtop_mem mem; glibtop_get_mem (&mem); g_return_if_fail ((mem.flags & needed_mem_flags) == needed_mem_flags); user = rint (Maximum * (float)mem.user / (float)mem.total); shared = rint (Maximum * (float)mem.shared / (float)mem.total); buffer = rint (Maximum * (float)mem.buffer / (float)mem.total); cached = rint (Maximum * (float)mem.cached / (float)mem.total); data [0] = user; data [1] = shared; data [2] = buffer; data [3] = cached; data [4] = Maximum-user-shared-buffer-cached; } void GetSwap (int Maximum, int data [2], LoadGraph *g) { int used; glibtop_swap swap; glibtop_get_swap (&swap); g_return_if_fail ((swap.flags & needed_swap_flags) == needed_swap_flags); if (swap.total == 0) { used = 0; } else { used = rint (Maximum * (float)swap.used / swap.total); } data [0] = used; data [1] = Maximum - used; } void GetLoadAvg (int Maximum, int data [2], LoadGraph *g) { float used; glibtop_loadavg loadavg; glibtop_get_loadavg (&loadavg); g_return_if_fail ((loadavg.flags & needed_loadavg_flags) == needed_loadavg_flags); /* g->loadavg1 represents %used */ g->loadavg1 = loadavg.loadavg[0]; data [0] = rint ((float) Maximum * g->loadavg1); 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; if(ticks == 0) { autoscaler_init(&scaler, 60, 501); } devices = glibtop_get_netlist(&netlist); for(i = 0; i < netlist.number; ++i) { int index; 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(g->netspeed_in, present[IN_COUNT]); netspeed_add(g->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]; int max; int total = 0; 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; total += delta[i]; } //max = autoscaler_get_max(&scaler, total); for (i = 0; i < COUNT_TYPES; i++) data[i] = delta[i]; } memcpy(past, present, sizeof past); }