Skip to content
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
5 changes: 5 additions & 0 deletions lib/OperatingSystems/Dummy.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public function getCPU(): CPU {
return new CPU('Unknown Processor', -1);
}

#[\Override]
public function getAverageLoad(): array|false {
return false;
}

#[\Override]
public function sampleUptime(): int {
return -1;
Expand Down
5 changes: 5 additions & 0 deletions lib/OperatingSystems/FreeBSD.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public function getCPU(): CPU {
return new CPU($name, $cores);
}

#[\Override]
public function getAverageLoad(): array|false {
return false;
}

#[\Override]
public function sampleUptime(): int {
return -1;
Expand Down
7 changes: 7 additions & 0 deletions lib/OperatingSystems/IOperatingSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public function supported(): bool;

public function getCPU(): CPU;

/**
* Get the 1, 5, and 15 minute load averages, or false when unavailable.
*
* @return array{0: float, 1: float, 2: float}|false
*/
public function getAverageLoad(): array|false;

/**
* Uptime obtained by a means that may cost a process fork, used only when
* getUptime() cannot answer.
Expand Down
23 changes: 23 additions & 0 deletions lib/OperatingSystems/Linux.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,29 @@ public function getCPU(): CPU {
return new CPU($matches[1][0], $threads);
}

#[\Override]
public function getAverageLoad(): array|false {
try {
$loadavg = $this->readContent('/proc/loadavg');
} catch (RuntimeException) {
return false;
}

$fields = preg_split('/\s+/', $loadavg);
if ($fields === false || count($fields) < 3) {
return false;
}

$averages = array_slice($fields, 0, 3);
foreach ($averages as $average) {
if (!is_numeric($average) || (float)$average < 0) {
return false;
}
}

return [(float)$averages[0], (float)$averages[1], (float)$averages[2]];
}

/**
* Read memory from free(1) when /proc/meminfo is out of reach.
*
Expand Down
18 changes: 14 additions & 4 deletions lib/Os.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,24 @@ public function __construct(
}

/**
* The load average comes from sys_getloadavg() and needs nothing that
* getCPU() collects, which is why it does not go through it: getCPU() parses
* /proc/cpuinfo, or forks nproc, and this runs on a two-second poll.
* Prefer the operating-system backend because container-aware virtual files
* can differ from sys_getloadavg(), which may expose the host's load. Fall
* back to PHP's portable implementation when no native value is available.
*
* @return array{0: float, 1: float, 2: float}|false
*/
#[\Override]
public function getAverageLoad(): array|false {
return sys_getloadavg();
$load = $this->backend->getAverageLoad();
if ($load !== false) {
return $load;
}

if (function_exists('sys_getloadavg')) {
return sys_getloadavg();
}

return false;
}

#[\Override]
Expand Down
12 changes: 0 additions & 12 deletions lib/Resources/CPU.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,6 @@ public function getThreads(): int {
return $this->threads;
}

/**
* Retrieves the system load averages.
*
* @return array|false Returns an array containing the system load averages for the last 1, 5, and 15 minutes.
*/
public function getAverageLoad(): array|false {
if (function_exists('sys_getloadavg')) {
return sys_getloadavg();
}
return false;
}

#[\Override]
public function jsonSerialize(): array {
return [
Expand Down
2 changes: 1 addition & 1 deletion lib/SystemStatistics.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function getSystemStatistics(bool $skipApps = false, bool $skipUpdate = t
'memcache.locking' => $this->config->getSystemValue('memcache.locking', 'none'),
'debug' => $this->config->getSystemValue('debug', false) ? 'yes' : 'no',
'freespace' => $this->getFreeSpace(),
'cpuload' => $cpu->getAverageLoad(),
'cpuload' => $this->os->getAverageLoad(),
'cpunum' => $cpu->getThreads(),
'mem_total' => $this->toKibibytes($memory->getMemTotal()),
'mem_free' => $this->toKibibytes($memory->getMemAvailable()),
Expand Down
24 changes: 24 additions & 0 deletions tests/lib/LinuxTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ public function testGetCpu(): void {
$this->assertEquals(4, $cpu->getThreads());
}

public function testGetAverageLoad(): void {
$this->os->method('readContent')
->with('/proc/loadavg')
->willReturn('0.22 0.41 0.53 1/123 456');

$this->assertSame([0.22, 0.41, 0.53], $this->os->getAverageLoad());
}

public function testGetAverageLoadNoData(): void {
$this->os->method('readContent')
->with('/proc/loadavg')
->willThrowException(new RuntimeException('Unable to read: "/proc/loadavg"'));

$this->assertFalse($this->os->getAverageLoad());
}

public function testGetAverageLoadInvalidData(): void {
$this->os->method('readContent')
->with('/proc/loadavg')
->willReturn('invalid data');

$this->assertFalse($this->os->getAverageLoad());
}

public function testGetCpuOneCore(): void {
$this->os->method('readContent')
->with('/proc/cpuinfo')
Expand Down
Loading