diff --git a/include/cpu.h b/include/cpu.h index e1fb60e..ee3db10 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -5,6 +5,7 @@ extern "C" { #endif /* __cplusplus */ #include +#include #include typedef struct { @@ -15,6 +16,7 @@ typedef struct { float max_MHz; float min_MHz; float curr_temp; + float curr_usage; uint16_t cpu_family; uint16_t model; uint16_t stepping; @@ -23,8 +25,21 @@ typedef struct { uint16_t online_cores; } CPU; +typedef struct { + uint64_t user; + uint64_t nice; + uint64_t system; + uint64_t idle; + uint64_t iowait; + uint64_t irq; + uint64_t softirq; + uint64_t steal; +} CPUTimes; + cJSON *cpu_to_json_obj(const CPU *cpu); + CPU *cpu_get_info(void); + void free_cpu(CPU *cpu); #ifdef __cplusplus diff --git a/src/display.c b/src/display.c index 44b7e85..962e0e6 100644 --- a/src/display.c +++ b/src/display.c @@ -44,6 +44,14 @@ display_cpu(const CPU *cpu) print_field("Vendor", "%s", STR_OR_UNK(cpu->vendor_id)); print_field("Model", "%s", STR_OR_UNK(cpu->model_name)); print_field("Arch", "%s", STR_OR_UNK(cpu->arch)); + + + if (cpu->curr_usage >= 0.0f) { + print_field("Usage", "%.1f %%", cpu->curr_usage); + } else { + print_field("Usage", "--"); + } + print_field("Cores", "%u Physical / %u Logical", cpu->total_cores, cpu->total_threads); print_field("Frequency", "%.2f MHz - %.2f MHz", cpu->min_MHz, cpu->max_MHz); diff --git a/src/linux/hardware/cpu.c b/src/linux/hardware/cpu.c index 2aed6f3..6f4abd1 100644 --- a/src/linux/hardware/cpu.c +++ b/src/linux/hardware/cpu.c @@ -2,8 +2,76 @@ #include "file.h" #include "io.h" #include +#include +#include #include "cpu.h" +static bool +cpu_read_times(CPUTimes *out) +{ + char *stat = file_read_stripped("/proc/stat", NULL, false); + if (stat == NULL) { + return false; + } + + int n = sscanf(stat, + "cpu %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 + " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, + &out->user, &out->nice, &out->system, &out->idle, + &out->iowait, &out->irq, &out->softirq, &out->steal + ); + + + free(stat); + + return n == 8; +} + +static float +cpu_usage_percent(const CPUTimes *a, const CPUTimes *b) +{ + uint64_t idle_a = a->idle + a->iowait; + uint64_t idle_b = b->idle + b->iowait; + uint64_t busy_a = a->user + a->nice + a->system + a->irq + a->softirq + a->steal; + uint64_t busy_b = b->user + b->nice + b->system + b->irq + b->softirq + b->steal; + + uint64_t total_d = (busy_b + idle_b) - (busy_a + idle_a); + uint64_t busy_d = busy_b - busy_a; + + if (total_d == 0) { + return -1.0f; + } + + return 100.0f * (float)busy_d / (float)total_d; +} + +static float +cpu_get_usage(void) +{ + static CPUTimes prev; + static bool have_prev = false; + CPUTimes curr; + + if (!cpu_read_times(&curr)) { + return -1.0f; + } + + if (!have_prev) { + prev = curr; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 200000000 }; /* 200ms */ + nanosleep(&ts, NULL); + if (!cpu_read_times(&curr)) { + return -1.0f; + } + } + + float pct = cpu_usage_percent(&prev, &curr); + prev = curr; + have_prev = true; + + return pct; +} + static int16_t cpu_get_total_cores(void) { @@ -87,6 +155,7 @@ cpu_get_info(void) cpu->model_name = str_find_value(cpu_info, "model name", "\n"); cpu->flags = str_find_value(cpu_info, "flags", "\n"); cpu->arch = cpu_get_arch(cpu->flags); + cpu->curr_usage = cpu_get_usage(); cpu->online_cores = cpu_get_total_cores(); if (cpu->online_cores > 0) { @@ -104,6 +173,7 @@ cpu_get_info(void) cpu->total_threads = str_parse_value(cpu_info, "siblings", "\n"); free(cpu_info); + return cpu; } @@ -115,7 +185,7 @@ free_cpu(CPU *cpu) } free(cpu->vendor_id); free(cpu->model_name); - free(cpu->flags); +free(cpu->flags); free(cpu); }