Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add total CPU % to /info/system endpoint #2297

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/api/docs/content/specs/info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ components:
type: integer
description: Number of available processors
example: 8
"%cpu":
type: number
description: Total CPU usage in percent (may be higher than 100% on multi-core systems and negative if the value cannot be computed)
example: 0.0
load:
type: object
description: 1, 5, and 15 minute load averages
Expand Down
6 changes: 4 additions & 2 deletions src/api/info.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include "datastructure.h"
// uname()
#include <sys/utsname.h>
// get_cpu_percentage()
// get_ftl_cpu_percentage()
#include "daemon.h"
// getProcessMemory()
#include "procps.h"
Expand Down Expand Up @@ -220,6 +220,8 @@ int get_system_obj(struct ftl_conn *api, cJSON *system)
cJSON *cpu = JSON_NEW_OBJECT();
// Number of available processors
JSON_ADD_NUMBER_TO_OBJECT(cpu, "nprocs", nprocs);
// Averaged total CPU usage in percent
JSON_ADD_NUMBER_TO_OBJECT(cpu, "%cpu", get_total_cpu_percentage());

// 1, 5, and 15 minute load averages (we need to convert them)
cJSON *raw = JSON_NEW_ARRAY();
Expand Down Expand Up @@ -599,7 +601,7 @@ static int get_ftl_obj(struct ftl_conn *api, cJSON *ftl)
parse_proc_meminfo(&mem);
getProcessMemory(&pmem, mem.total);
JSON_ADD_NUMBER_TO_OBJECT(ftl, "%mem", pmem.VmRSS_percent);
JSON_ADD_NUMBER_TO_OBJECT(ftl, "%cpu", get_cpu_percentage());
JSON_ADD_NUMBER_TO_OBJECT(ftl, "%cpu", get_ftl_cpu_percentage());

JSON_ADD_BOOL_TO_OBJECT(ftl, "allow_destructive", config.webserver.api.allow_destructive.v.b);

Expand Down
2 changes: 1 addition & 1 deletion src/api/padd.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ int api_padd(struct ftl_conn *api)
parse_proc_meminfo(&mem);
getProcessMemory(&pmem, mem.total);
JSON_ADD_NUMBER_TO_OBJECT(json, "%mem", pmem.VmRSS_percent);
JSON_ADD_NUMBER_TO_OBJECT(json, "%cpu", get_cpu_percentage());
JSON_ADD_NUMBER_TO_OBJECT(json, "%cpu", get_total_cpu_percentage());
JSON_ADD_NUMBER_TO_OBJECT(json, "pid", getpid());

// info/sensors -> CPU temp sensor
Expand Down
44 changes: 37 additions & 7 deletions src/daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <locale.h>
// freeEnvVars()
#include "config/env.h"
// parse_proc_stat()
#include "procps.h"

pthread_t threads[THREADS_MAX] = { 0 };
bool resolver_ready = false;
Expand Down Expand Up @@ -436,8 +438,8 @@ void cleanup(const int ret)
log_info("########## FTL terminated after%s (code %i)! ##########", buffer, ret);
}

static float last_clock = 0.0f;
static float cpu_usage = 0.0f;
static float ftl_cpu_usage = 0.0f;
static float total_cpu_usage = 0.0f;
void calc_cpu_usage(const unsigned int interval)
{
// Get the current resource usage
Expand All @@ -454,18 +456,46 @@ void calc_cpu_usage(const unsigned int interval)
// kernel mode by this process since the total time since the last call
// to this function. 100% means one core is fully used, 200% means two
// cores are fully used, etc.
const float this_clock = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec + 1e-6 * (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
const float ftl_cpu_time = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec + 1e-6 * (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);

// Calculate the CPU usage in this interval
cpu_usage = 100.0 * (this_clock - last_clock) / interval;
static float last_ftl_cpu_time = 0.0f;
ftl_cpu_usage = 100.0 * (ftl_cpu_time - last_ftl_cpu_time) / interval;

// Store the current time for the next call to this function
last_clock = this_clock;
last_ftl_cpu_time = ftl_cpu_time;

// The number of clock ticks per second
static long user_hz = 0;
if(user_hz == 0)
user_hz = sysconf(_SC_CLK_TCK);

// Calculate the total CPU usage
unsigned long total_total, total_idle;
if(!parse_proc_stat(&total_total, &total_idle))
{
total_cpu_usage = -1.0f;
return;
}

// Calculate the CPU usage since the last call to this function
static unsigned long last_total_total = 0, last_total_idle = 0;
if(total_total - last_total_total > 0)
total_cpu_usage = 100.0 * (total_total - last_total_total - (total_idle - last_total_idle)) / (total_total - last_total_total);

// Store the current time for the next call to this function
last_total_idle = total_idle;
last_total_total = total_total;
}

float __attribute__((pure)) get_ftl_cpu_percentage(void)
{
return ftl_cpu_usage;
}

float __attribute__((pure)) get_cpu_percentage(void)
float __attribute__((pure)) get_total_cpu_percentage(void)
{
return cpu_usage;
return total_cpu_usage;
}

ssize_t getrandom_fallback(void *buf, size_t buflen, unsigned int flags)
Expand Down
3 changes: 2 additions & 1 deletion src/daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ bool is_fork(const pid_t mpid, const pid_t pid) __attribute__ ((const));
void cleanup(const int ret);
void set_nice(void);
void calc_cpu_usage(const unsigned int interval);
float get_cpu_percentage(void) __attribute__((pure));
float get_ftl_cpu_percentage(void) __attribute__((pure));
float get_total_cpu_percentage(void) __attribute__((pure));
bool ipv6_enabled(void);
void init_locale(void);

Expand Down
95 changes: 95 additions & 0 deletions src/procps.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,98 @@ bool parse_proc_meminfo(struct proc_meminfo *mem)
// Return success
return true;
}


/**
* @brief Parses the /proc/stat file to extract CPU statistics.
*
* This function reads the /proc/stat file to gather CPU usage statistics,
* including user, system, idle, iowait, irq, softirq, steal, guest, and guest_nice times.
* It calculates the total CPU time and idle time, and stores these values in the provided
* pointers.
*
* @param total_sum Pointer to store the total CPU time.
* @param idle_sum Pointer to store the idle CPU time.
* @return true if the parsing is successful, false otherwise.
*/
bool parse_proc_stat(unsigned long *total_sum, unsigned long *idle_sum)
{
FILE *statfile = fopen("/proc/stat", "r");
if(statfile == NULL)
return false;

unsigned long user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;
/*
user (1) Time spent in user mode. (includes guest and guest_nice time)

nice (2) Time spent in user mode with low priority (nice).

system (3) Time spent in system mode.

idle (4) Time spent in the idle task. This value should be USER_HZ times
the second entry in the /proc/uptime pseudo-file.

iowait (since Linux 2.5.41)
(5) Time waiting for I/O to complete.

irq (since Linux 2.6.0-test4)
(6) Time servicing interrupts.

softirq (since Linux 2.6.0-test4)
(7) Time servicing softirqs.

steal (since Linux 2.6.11)
(8) Stolen time, which is the time spent in other operating systems
when running in a virtualized environment

guest (since Linux 2.6.24)
(9) Time spent running a virtual CPU for guest operating systems
under the control of the Linux kernel.

guest_nice (since Linux 2.6.33)
(10) Time spent running a niced guest (virtual CPU for guest operat-
ing systems under the control of the Linux kernel).
*/

// Read the file until we find the first line starting with "cpu "
char line[256];
while(fgets(line, sizeof(line), statfile))
{
if(strncmp(line, "cpu ", 4) == 0)
{
if(sscanf(line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
&user, &nice, &system, &idle,
&iowait, &irq, &softirq, &steal,
&guest, &guest_nice) != 10)
{
log_debug(DEBUG_ANY, "Failed to parse CPU line in /proc/stat");
fclose(statfile);
return false;
}

break;
}
}

if (feof(statfile)) {
log_warn("No CPU line found in /proc/stat");
fclose(statfile);
return false;
}

fclose(statfile);

// Guest time is already accounted in usertime
user -= guest;
nice -= guest_nice;

// Fields existing on kernels >= 2.6
// (and RHEL's patched kernel 2.4...)
const unsigned long long int sys_all = system + irq + softirq;
const unsigned long long int virtual = guest + guest_nice;
const unsigned long long int busy_sum = user + nice + sys_all + steal + virtual;
*idle_sum = idle + iowait;
*total_sum = busy_sum + *idle_sum;

return true;
}
1 change: 1 addition & 0 deletions src/procps.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ struct proc_meminfo {

bool getProcessMemory(struct proc_mem *mem, const unsigned long total_memory);
bool parse_proc_meminfo(struct proc_meminfo *mem);
bool parse_proc_stat(unsigned long *total_sum, unsigned long *idle_sum);

#endif // PROCPS_H
Loading