aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorverdant <im@verdant.ee>2026-07-22 14:44:38 +0800
committerverdant <im@verdant.ee>2026-07-22 14:44:38 +0800
commita6d136f0c34f6bb3de6b86ce7fe3ab608bd61283 (patch)
treec011fd99ad64aaec04bc18401c51f6ca590a2ddb
parent80eeedf25b8f6f0c5797c0eca5c3b6f5fcffe96e (diff)
downloadsf-a6d136f0c34f6bb3de6b86ce7fe3ab608bd61283.tar.gz
sf-a6d136f0c34f6bb3de6b86ce7fe3ab608bd61283.zip
Implement Exponential Moving Average algorithm
For solving that CPU status updating is so fast, use EMA to calc a smooth value
-rw-r--r--stat.c340
-rw-r--r--stat.h70
2 files changed, 229 insertions, 181 deletions
diff --git a/stat.c b/stat.c
index c0d94a3..17f9af3 100644
--- a/stat.c
+++ b/stat.c
@@ -12,190 +12,238 @@
#include "log.h"
#include "mem.h"
-stat* stat_init() {
- stat* s = xmalloc(sizeof(stat));
- *s = (stat){
- .sysver = "",
- .meminfo = {.mem_available = 0, .mem_total = 0},
- .diskinfo = {.disk_available = 0, .disk_total = 0},
- };
-
- return s;
+stat *stat_init()
+{
+ stat *s = xmalloc(sizeof(stat));
+ *s = (stat){
+ .sysver = "",
+ .meminfo = {.mem_available = 0, .mem_total = 0},
+ .diskinfo = {.disk_available = 0, .disk_total = 0},
+ };
+ s->cpu_occ = xmalloc(sizeof(cpu_occupy));
+
+ if (s->cpu_occ)
+ *s->cpu_occ = (cpu_occupy){0};
+ return s;
}
-void stat_get_sysver(stat* s) {
- FILE* fp = fopen(PROC_PATH_VERSION, "r");
- if (!fp) die("Cannot open %s", PROC_PATH_VERSION);
+void stat_get_sysver(stat *s)
+{
+ FILE *fp = fopen(PROC_PATH_VERSION, "r");
+ if (!fp)
+ die("Cannot open %s", PROC_PATH_VERSION);
- if (fgets(s->sysver, sizeof(s->sysver), fp) != NULL) {
- s->sysver[strcspn(s->sysver, "\n")] = '\0';
- }
- fclose(fp);
+ if (fgets(s->sysver, sizeof(s->sysver), fp) != NULL) {
+ s->sysver[strcspn(s->sysver, "\n")] = '\0';
+ }
+ fclose(fp);
}
-void stat_print_cpu_info(WINDOW* w, int y, int maxx, stat* s) {
- mvwprintw(w, y, maxx - STAT_INFO_WIDTH, " %.2f %%", s->cpu_occupy.cpu);
+void stat_print_cpu_info(WINDOW *w, int y, int maxx, stat *s)
+{
+ mvwprintw(w, y, maxx - STAT_INFO_WIDTH, " %.2f %%",
+ s->cpu_occ->smooth_cpu);
}
-void stat_print_info(WINDOW* w, int y, int maxx, const char* label,
- unsigned long total, unsigned long ava) {
- unsigned long long t_bytes = total;
- unsigned long long a_bytes = ava;
+void stat_print_info(WINDOW *w, int y, int maxx, const char *label,
+ unsigned long total, unsigned long ava)
+{
+ unsigned long long t_bytes = total;
+ unsigned long long a_bytes = ava;
- if (strcmp(label, "Mem") == 0 || strcmp(label, "Mem ") == 0) {
- t_bytes *= 1024;
- a_bytes *= 1024;
- }
+ if (strcmp(label, "Mem") == 0 || strcmp(label, "Mem ") == 0) {
+ t_bytes *= 1024;
+ a_bytes *= 1024;
+ }
- const char* units[] = {"B", "K", "M", "G", "T"};
+ const char *units[] = {"B", "K", "M", "G", "T"};
- int t_idx = 0;
- int a_idx = 0;
+ int t_idx = 0;
+ int a_idx = 0;
- double d_total = (double)t_bytes;
- double d_ava = (double)a_bytes;
+ double d_total = (double)t_bytes;
+ double d_ava = (double)a_bytes;
- while (d_total >= 1024.0 && t_idx < 4) {
- d_total /= 1024.0;
- t_idx++;
- }
+ while (d_total >= 1024.0 && t_idx < 4) {
+ d_total /= 1024.0;
+ t_idx++;
+ }
- while (d_ava >= 1024.0 && a_idx < 4) {
- d_ava /= 1024.0;
- a_idx++;
- }
+ while (d_ava >= 1024.0 && a_idx < 4) {
+ d_ava /= 1024.0;
+ a_idx++;
+ }
- int startx = maxx - STAT_INFO_WIDTH;
+ int startx = maxx - STAT_INFO_WIDTH;
- mvwprintw(w, y, startx, " %.2f%s/%.2f%s", d_ava, units[t_idx], d_total,
- units[t_idx]);
+ mvwprintw(w, y, startx, " %.2f%s/%.2f%s", d_ava, units[t_idx], d_total,
+ units[t_idx]);
- /* wrefresh(w); */
+ /* wrefresh(w); */
}
int stat_get_bar_ticks(int maxx, unsigned long long total,
- unsigned long long ava) {
- if (total == 0) return 0;
- if (ava >= total) return 0;
+ unsigned long long ava)
+{
+ if (total == 0)
+ return 0;
+ if (ava >= total)
+ return 0;
- unsigned long long used = total - ava;
+ unsigned long long used = total - ava;
- unsigned long long slots = (unsigned long long)(maxx);
+ unsigned long long slots = (unsigned long long)(maxx);
- return (int)((slots * used) / total);
+ return (int)((slots * used) / total);
}
-void stat_draw_cpu_bar(WINDOW* w, int maxx, int y, int x, stat* s) {
- int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH;
- int total_hashes = ava_to_draw_x * s->cpu_occupy.cpu;
- log_write(LOG_INFO, "stat_draw_cpu_bar: ava_to_draw = %d", ava_to_draw_x);
- log_write(LOG_INFO, "s->cpu_occupy.cpu = %f", s->cpu_occupy.cpu);
- log_write(LOG_INFO, "CPU Hashes = %d", total_hashes);
- char hashes[2048] = {0};
+void stat_draw_cpu_bar(WINDOW *w, int maxx, int y, int x, stat *s)
+{
+ int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH;
+ int total_hashes = ava_to_draw_x * (s->cpu_occ->smooth_cpu * 0.1);
- if (total_hashes > ava_to_draw_x) total_hashes = ava_to_draw_x;
- memset(hashes, '#', total_hashes);
- hashes[total_hashes] = '\0';
+ char hashes[2048] = {0};
- mvwprintw(w, y, x, "%-*s [", 4, "CPU");
- wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes);
+ if (total_hashes > ava_to_draw_x)
+ total_hashes = ava_to_draw_x;
+ memset(hashes, '#', total_hashes);
+ hashes[total_hashes] = '\0';
- int remaining_spaces = ava_to_draw_x - total_hashes;
- if (remaining_spaces > 0) {
- wprintw(w, "%*s", remaining_spaces, "");
- }
+ mvwprintw(w, y, x, "%-*s [", 4, "CPU");
+ wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes);
- waddch(w, ']');
-}
+ int remaining_spaces = ava_to_draw_x - total_hashes;
+ if (remaining_spaces > 0) {
+ wprintw(w, "%*s", remaining_spaces, "");
+ }
-void stat_draw_bar(WINDOW* w, const char* label, int maxx, int y, int x,
- unsigned long total, unsigned long ava) {
- int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH;
- int total_hashes = stat_get_bar_ticks(ava_to_draw_x, total, ava);
- char hashes[2048] = {0};
+ waddch(w, ']');
+}
- init_pair(COLOR_PAIR_PROGRESS, COLOR_GREEN, COLOR_BLACK);
+void stat_draw_bar(WINDOW *w, const char *label, int maxx, int y, int x,
+ unsigned long total, unsigned long ava)
+{
+ int ava_to_draw_x = maxx - STAT_INFO_WIDTH - BAR_LABEL_WIDTH;
+ int total_hashes = stat_get_bar_ticks(ava_to_draw_x, total, ava);
+ char hashes[2048] = {0};
- if (total_hashes > ava_to_draw_x) total_hashes = ava_to_draw_x;
- memset(hashes, '#', total_hashes);
- hashes[total_hashes] = '\0';
+ init_pair(COLOR_PAIR_PROGRESS, COLOR_GREEN, COLOR_BLACK);
- mvwprintw(w, y, x, "%-*s [", 4, label);
- wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes);
+ if (total_hashes > ava_to_draw_x)
+ total_hashes = ava_to_draw_x;
+ memset(hashes, '#', total_hashes);
+ hashes[total_hashes] = '\0';
- int remaining_spaces = ava_to_draw_x - total_hashes;
- if (remaining_spaces > 0) {
- wprintw(w, "%*s", remaining_spaces, "");
- }
+ mvwprintw(w, y, x, "%-*s [", 4, label);
+ wprintw_colorful(w, COLOR_PAIR_PROGRESS, "%.*s", total_hashes, hashes);
- waddch(w, ']');
+ int remaining_spaces = ava_to_draw_x - total_hashes;
+ if (remaining_spaces > 0) {
+ wprintw(w, "%*s", remaining_spaces, "");
+ }
- /* wrefresh(w); */
-}
+ waddch(w, ']');
-void stat_get_meminfo(stat* s) {
- static char buf[128];
- char key[32] = "";
- unsigned long long val = 0;
-
- FILE* fp = fopen(PROC_PATH_MEMINFO, "r");
- if (!fp) die("Cannot open %s", PROC_PATH_MEMINFO);
-
- while (fgets(buf, sizeof(buf), fp)) {
- if (sscanf(buf, "%31[^:]: %llu", key, &val) == 2) {
- if (strcmp(key, "MemTotal") == 0) {
- s->meminfo.mem_total = val;
- } else if (strcmp(key, "MemAvailable") == 0) {
- s->meminfo.mem_available = val;
- break;
- }
- }
- }
-
- fclose(fp);
+ /* wrefresh(w); */
}
-void stat_get_diskinfo(stat* s) {
- struct statvfs vfs;
- if (statvfs("/", &vfs) != 0) {
- s->diskinfo.disk_total = 0;
- s->diskinfo.disk_available = 0;
- log_write(LOG_WARN, "Cannot get disk info");
- }
-
- s->diskinfo.disk_total = (unsigned long long)vfs.f_blocks * vfs.f_frsize;
- s->diskinfo.disk_available =
- (unsigned long long)vfs.f_bavail * vfs.f_frsize;
+void stat_get_meminfo(stat *s)
+{
+ static char buf[128];
+ char key[32] = "";
+ unsigned long long val = 0;
+
+ FILE *fp = fopen(PROC_PATH_MEMINFO, "r");
+ if (!fp)
+ die("Cannot open %s", PROC_PATH_MEMINFO);
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (sscanf(buf, "%31[^:]: %llu", key, &val) == 2) {
+ if (strcmp(key, "MemTotal") == 0) {
+ s->meminfo.mem_total = val;
+ } else if (strcmp(key, "MemAvailable") == 0) {
+ s->meminfo.mem_available = val;
+ break;
+ }
+ }
+ }
+
+ fclose(fp);
}
-void stat_get_cpuinfo(stat* s) {
- char buf[128];
- FILE* fp = fopen(PROC_PATH_STAT, "r");
- if (!fp) {
- log_write(LOG_WARN, "Cannot open %s", PROC_PATH_STAT);
- return;
- }
-
- fgets(buf, sizeof(buf), fp);
- sscanf(buf, "%s %llu %llu %llu %llu", s->cpu_occupy.name,
- &s->cpu_occupy.user, &s->cpu_occupy.nice, &s->cpu_occupy.system,
- &s->cpu_occupy.idle);
- fclose(fp);
+void stat_get_diskinfo(stat *s)
+{
+ struct statvfs vfs;
+ if (statvfs("/", &vfs) != 0) {
+ s->diskinfo.disk_total = 0;
+ s->diskinfo.disk_available = 0;
+ log_write(LOG_WARN, "Cannot get disk info");
+ }
+
+ s->diskinfo.disk_total =
+ (unsigned long long)vfs.f_blocks * vfs.f_frsize;
+ s->diskinfo.disk_available =
+ (unsigned long long)vfs.f_bavail * vfs.f_frsize;
}
-void stat_calc_cpuocc(stat* s) {
- cpu_occupy* co = xmalloc(sizeof(cpu_occupy));
- stat_get_cpuinfo(s);
- co->idle = s->cpu_occupy.idle;
- co->nice = s->cpu_occupy.nice;
- co->system = s->cpu_occupy.system;
- co->user = s->cpu_occupy.user;
- usleep(200000);
- stat_get_cpuinfo(s);
- unsigned long long t1 = co->user + co->nice + co->system + co->idle;
- unsigned long long t2 = s->cpu_occupy.user + s->cpu_occupy.nice +
- s->cpu_occupy.system + s->cpu_occupy.idle;
- unsigned long long user = s->cpu_occupy.user - co->user;
- unsigned long long system = s->cpu_occupy.system - co->system;
-
- s->cpu_occupy.cpu = (user + system) * 1.0 / (t2 - t1);
- free(co);
+void stat_get_cpuinfo(stat *s)
+{
+ char buf[256];
+ FILE *fp = fopen(PROC_PATH_STAT, "r");
+ if (!fp) {
+ log_write(LOG_WARN, "Cannot open %s", PROC_PATH_STAT);
+ return;
+ }
+
+ if (!fgets(buf, sizeof(buf), fp)) {
+ fclose(fp);
+ return;
+ }
+ fclose(fp);
+
+ unsigned long long user = 0, nice = 0, system = 0, idle = 0;
+ unsigned long long iowait = 0, irq = 0, softirq = 0;
+
+ sscanf(buf, "%*s %llu %llu %llu %llu %llu %llu %llu", &user, &nice,
+ &system, &idle, &iowait, &irq, &softirq);
+
+ s->cpu_occ->user = user;
+ s->cpu_occ->nice = nice;
+ s->cpu_occ->system = system;
+ s->cpu_occ->idle = idle;
+ s->cpu_occ->iowait = iowait;
+ s->cpu_occ->irq = irq;
+ s->cpu_occ->softirq = softirq;
+
+ unsigned long long curr_idle = idle + iowait;
+ unsigned long long curr_total =
+ user + nice + system + idle + iowait + irq + softirq;
+
+ unsigned long long total_diff = curr_total - s->cpu_occ->prev_total;
+ unsigned long long idle_diff = curr_idle - s->cpu_occ->prev_idle;
+
+ if (total_diff > 0) {
+ s->cpu_occ->cpu = (double)(total_diff - idle_diff) /
+ (double)total_diff * 100.0;
+ } else {
+ s->cpu_occ->cpu = 0.0; // 防零除
+ }
+
+ s->cpu_occ->prev_total = curr_total;
+ s->cpu_occ->prev_idle = curr_idle;
+
+ /* EMA - Exponential Moving Average */
+ /* DisplayCPU = alpha * raw_cpu + (1 - alpha) * pre_cpu */
+ double alpha = 0.1;
+ if (total_diff > 0) {
+ double raw_cpu = (double)(total_diff - idle_diff) /
+ (double)total_diff * 100.0;
+ if (s->cpu_occ->smooth_cpu == 0.0) {
+ s->cpu_occ->smooth_cpu = raw_cpu;
+ } else {
+ s->cpu_occ->smooth_cpu =
+ alpha * raw_cpu +
+ (1 - alpha) * s->cpu_occ->smooth_cpu;
+ }
+
+ s->cpu_occ->cpu = raw_cpu;
+ }
}
diff --git a/stat.h b/stat.h
index 81d84cc..5b14226 100644
--- a/stat.h
+++ b/stat.h
@@ -12,46 +12,46 @@
#define STAT_BAR_HEIGHT 1
typedef struct {
- char name[16];
- unsigned int user; /* User mode */
- unsigned int nice; /* Low-priority user mode */
- unsigned int system; /* Kernel mode */
- unsigned int idle; /* Free time of CPU */
+ char name[16];
+ unsigned int user; /* User mode */
+ unsigned int nice; /* Low-priority user mode */
+ unsigned int system; /* Kernel mode */
+ unsigned int idle; /* Free time of CPU */
+ unsigned long long iowait;
+ unsigned long long irq;
+ unsigned long long softirq;
+
+ unsigned long long prev_total;
+ unsigned long long prev_idle;
+ double cpu;
+ double smooth_cpu;
} cpu_occupy;
typedef struct {
- char sysver[30];
- struct {
- unsigned long mem_total;
- unsigned long mem_available;
- } meminfo;
- struct {
- unsigned long long disk_total;
- unsigned long long disk_available;
- } diskinfo;
- struct {
- char name[16];
- unsigned long long user; /* User mode */
- unsigned long long nice; /* Low-priority user mode */
- unsigned long long system; /* Kernel mode */
- unsigned long long idle; /* Free time of CPU */
- double cpu;
- } cpu_occupy;
+ char sysver[30];
+ struct {
+ unsigned long mem_total;
+ unsigned long mem_available;
+ } meminfo;
+ struct {
+ unsigned long long disk_total;
+ unsigned long long disk_available;
+ } diskinfo;
+ cpu_occupy *cpu_occ;
} stat;
-void stat_get_sysver(stat* s);
-stat* stat_init();
-void stat_print_info(WINDOW* w, int y, int maxx, const char* label,
- unsigned long total, unsigned long ava);
-void stat_print_cpu_info(WINDOW* w, int y, int maxx, stat* s);
-void stat_draw_bar(WINDOW* w, const char* label, int maxx, int y, int x,
- unsigned long total, unsigned long ava);
-void stat_draw_cpu_bar(WINDOW* w, int maxx, int y, int x, stat* s);
-void stat_get_meminfo(stat* s);
-void stat_get_diskinfo(stat* s);
-void stat_get_cpuinfo(stat* s);
-void stat_calc_cpuocc(stat* s);
+void stat_get_sysver(stat *s);
+stat *stat_init();
+void stat_print_info(WINDOW *w, int y, int maxx, const char *label,
+ unsigned long total, unsigned long ava);
+void stat_print_cpu_info(WINDOW *w, int y, int maxx, stat *s);
+void stat_draw_bar(WINDOW *w, const char *label, int maxx, int y, int x,
+ unsigned long total, unsigned long ava);
+void stat_draw_cpu_bar(WINDOW *w, int maxx, int y, int x, stat *s);
+void stat_get_meminfo(stat *s);
+void stat_get_diskinfo(stat *s);
+void stat_get_cpuinfo(stat *s);
int stat_get_bar_ticks(int maxx, unsigned long long total,
- unsigned long long ava);
+ unsigned long long ava);
#endif /* SF_STAT_H */