Add misc.normalizeCPU

Signed-off-by: Dominik <dl6er@dl6er.de>
This commit is contained in:
Dominik
2025-10-06 10:01:28 +02:00
parent fcee389467
commit 649b3f51ca
6 changed files with 44 additions and 23 deletions

View File

@@ -551,6 +551,8 @@ components:
type: boolean type: boolean
readOnly: readOnly:
type: boolean type: boolean
normalizeCPU:
type: boolean
check: check:
type: object type: object
properties: properties:
@@ -834,6 +836,7 @@ components:
dnsmasq_lines: [ ] dnsmasq_lines: [ ]
extraLogging: false extraLogging: false
readOnly: false readOnly: false
normalizeCPU: false
check: check:
load: true load: true
shmem: 90 shmem: 90

View File

@@ -1416,6 +1416,12 @@ void initConfig(struct config *conf)
conf->misc.readOnly.d.b = false; conf->misc.readOnly.d.b = false;
conf->misc.readOnly.c = validate_stub; // Only type-based checking conf->misc.readOnly.c = validate_stub; // Only type-based checking
conf->misc.normalizeCPU.k = "misc.normalizeCPU";
conf->misc.normalizeCPU.h = "Should FTL normalize reported CPU usage?\n\n On multi-core systems, a high workload can lead to CPU usage numbers exceeding 100% (e.g., 200% on a quad-core system if two out of four cores are fully utilized). This may look alarming at first glance even though the system is not actually overloaded. Enabling this setting will divide the CPU usage by the number of cores, leading to more intuitive numbers (e.g., 50% for the same load on a quad-core system).\n Note that this setting only affects how CPU usage is *reported*, it does not change the actual CPU usage.";
conf->misc.normalizeCPU.t = CONF_BOOL;
conf->misc.normalizeCPU.d.b = true;
conf->misc.normalizeCPU.c = validate_stub; // Only type-based checking
// sub-struct misc.check // sub-struct misc.check
conf->misc.check.load.k = "misc.check.load"; conf->misc.check.load.k = "misc.check.load";
conf->misc.check.load.h = "Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you should run Pi-hole on a server that is otherwise extremely busy as queuing on the system can lead to unnecessary delays in DNS operation as the system becomes less and less usable as the system load increases because all resources are permanently in use. To account for this, FTL regularly checks the system load. To bring this to your attention, FTL warns about excessive load when the 15 minute system load average exceeds the number of cores.\n\n This check can be disabled with this setting."; conf->misc.check.load.h = "Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you should run Pi-hole on a server that is otherwise extremely busy as queuing on the system can lead to unnecessary delays in DNS operation as the system becomes less and less usable as the system load increases because all resources are permanently in use. To account for this, FTL regularly checks the system load. To bring this to your attention, FTL warns about excessive load when the 15 minute system load average exceeds the number of cores.\n\n This check can be disabled with this setting.";

View File

@@ -316,6 +316,7 @@ struct config {
struct conf_item dnsmasq_lines; struct conf_item dnsmasq_lines;
struct conf_item extraLogging; struct conf_item extraLogging;
struct conf_item readOnly; struct conf_item readOnly;
struct conf_item normalizeCPU;
struct { struct {
struct conf_item load; struct conf_item load;
struct conf_item shmem; struct conf_item shmem;

View File

@@ -472,30 +472,27 @@ static float ftl_cpu_usage = 0.0f;
static float total_cpu_usage = 0.0f; static float total_cpu_usage = 0.0f;
void calc_cpu_usage(const unsigned int interval) void calc_cpu_usage(const unsigned int interval)
{ {
// Get number of cores if we are normalizing CPU usage below
const unsigned int norm_factor = config.misc.normalizeCPU.v.b ? get_nprocs_conf() : 1;
// Get the current FTL CPU time // Get the current FTL CPU time
const double ftl_cpu_time = parse_proc_self_stat(); const double ftl_cpu_time = parse_proc_self_stat();
// Calculate the CPU usage in this interval // Calculate the CPU usage in this interval
static double last_ftl_cpu_time = 0.0f; static double last_ftl_cpu_time = 0.0f;
ftl_cpu_usage = 100.0 * (ftl_cpu_time - last_ftl_cpu_time) / interval; ftl_cpu_usage = 100.0 * (ftl_cpu_time - last_ftl_cpu_time) / interval / norm_factor;
log_info("FTL CPU time since last call: %.2f seconds", ftl_cpu_time - last_ftl_cpu_time);
// Store the current time for the next call to this function
last_ftl_cpu_time = ftl_cpu_time; last_ftl_cpu_time = ftl_cpu_time;
// Calculate the total CPU usage // Calculate the total CPU usage
static double last_cpu_time = 0.0f;
const double cpu_time = parse_proc_stat(); const double cpu_time = parse_proc_stat();
// Calculate the CPU usage since the last call to this function // Calculate the CPU usage since the last call to this function
total_cpu_usage = 100.0 * (cpu_time - last_cpu_time) / interval; static double last_cpu_time = 0.0f;
total_cpu_usage = 100.0 * (cpu_time - last_cpu_time) / interval / norm_factor;
log_info("Total CPU time since last call: %.2f seconds", cpu_time - last_cpu_time);
last_cpu_time = cpu_time; last_cpu_time = cpu_time;
log_debug(DEBUG_EXTRA, "CPU usage in the last %u seconds: FTL %.4f%%, total %.4f%%", log_debug(DEBUG_EXTRA, "CPU usage in the last %u seconds: FTL %.4f%%, total %.4f%% (normalized by factor %ux)",
interval, ftl_cpu_usage, total_cpu_usage); interval, ftl_cpu_usage, total_cpu_usage, norm_factor);
} }
float __attribute__((pure)) get_ftl_cpu_percentage(void) float __attribute__((pure)) get_ftl_cpu_percentage(void)

View File

@@ -320,6 +320,7 @@ double parse_proc_stat(void)
// Read the file until we find the first line starting with "cpu " // Read the file until we find the first line starting with "cpu "
char line[256]; char line[256];
bool found = false;
while(fgets(line, sizeof(line), statfile)) while(fgets(line, sizeof(line), statfile))
{ {
if(strncmp(line, "cpu ", 4) == 0) if(strncmp(line, "cpu ", 4) == 0)
@@ -331,12 +332,12 @@ double parse_proc_stat(void)
fclose(statfile); fclose(statfile);
return -1.0; return -1.0;
} }
found = true;
break; break;
} }
} }
if (feof(statfile)) { if(!found) {
log_warn("No CPU line found in /proc/stat"); log_warn("No CPU line found in /proc/stat");
fclose(statfile); fclose(statfile);
return -1.0; return -1.0;
@@ -370,14 +371,13 @@ double parse_proc_self_stat(void)
// Read utime and stime // Read utime and stime
unsigned long utime = 0, stime = 0; unsigned long utime = 0, stime = 0;
if(fscanf(file, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", const bool parsed = fscanf(file, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", &utime, &stime) == 2;
&utime, &stime) != 2)
{
fclose(file);
return -1.0;
}
fclose(file); fclose(file);
// If we could not parse the file, return -1.0
if(!parsed)
return -1.0;
// Convert clock ticks to seconds // Convert clock ticks to seconds
const long ticks = sysconf(_SC_CLK_TCK); const long ticks = sysconf(_SC_CLK_TCK);
if(ticks <= 0) if(ticks <= 0)

View File

@@ -1,7 +1,7 @@
# Pi-hole configuration file (v6.2.3-274-g68959ed0) on branch new/dns_domainLocal # Pi-hole configuration file (v6.2.3-301-gfcee3894-dirty) on branch tweak/api_info_system_ftl
# Encoding: UTF-8 # Encoding: UTF-8
# This file is managed by pihole-FTL # This file is managed by pihole-FTL
# Last updated on 2025-09-28 20:09:54 UTC # Last updated on 2025-10-06 08:00:17 UTC
[dns] [dns]
# Upstream DNS Servers to be used by Pi-hole. If this is not set, Pi-hole will not # Upstream DNS Servers to be used by Pi-hole. If this is not set, Pi-hole will not
@@ -1399,6 +1399,20 @@
# true or false # true or false
readOnly = false readOnly = false
# Should FTL normalize reported CPU usage?
#
# On multi-core systems, a high workload can lead to CPU usage numbers exceeding 100%
# (e.g., 200% on a quad-core system if two out of four cores are fully utilized). This
# may look alarming at first glance even though the system is not actually overloaded.
# Enabling this setting will divide the CPU usage by the number of cores, leading to
# more intuitive numbers (e.g., 50% for the same load on a quad-core system).
# Note that this setting only affects how CPU usage is *reported*, it does not change
# the actual CPU usage.
#
# Allowed values are:
# true or false
normalizeCPU = true
[misc.check] [misc.check]
# Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you # Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you
# should run Pi-hole on a server that is otherwise extremely busy as queuing on the # should run Pi-hole on a server that is otherwise extremely busy as queuing on the
@@ -1648,7 +1662,7 @@
all = true ### CHANGED, default = false all = true ### CHANGED, default = false
# Configuration statistics: # Configuration statistics:
# 159 total entries out of which 106 entries are default # 160 total entries out of which 107 entries are default
# --> 53 entries are modified # --> 53 entries are modified
# 3 entries are forced through environment: # 3 entries are forced through environment:
# - misc.nice # - misc.nice