mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-03 12:02:29 +02:00
feat(admin): show each stream's producer, CPU and memory on the streams page
A new "Resources" column between Stream Info and Actions: which process is producing the channel (the fanout daemon's native remuxer, ffmpeg, or PHP for the LLOD segmenter / loopback relay), the CPU it is burning and the memory it holds. With the native remuxer now an option per stream, "what does this channel actually cost" and "which backend is it on" are the two questions the list could not answer. ProcessManager reads both from /proc/PID/stat (fields 14/15 and 24, with the page size derived rather than assumed — 64K pages are normal on arm64). CPU there is cumulative, so a percentage needs two readings: cron:streams samples each producer once a pass and folds cpu/mem/producer into the stream's progress_info, carrying the previous reading in the same JSON to subtract from. cpuPercent() returns null rather than a wild figure when the pair says nothing — no previous sample, same instant, or a counter that went backwards because the producer restarted. Only the node running a stream can read its own /proc, so the sampling happens there and reaches the panel in the row the cron already writes; MAIN just renders it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZP7fc2Hz36wbPmuLd9F9o
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use XcVm\Core\Process\ProcessManager;
|
||||
|
||||
/**
|
||||
* Per-process CPU and memory readings — what the admin streams list shows for
|
||||
* each stream's producer. The readings come from /proc, so they are taken
|
||||
* against this test's own process.
|
||||
*/
|
||||
final class ProcessResourceUsageTest extends TestCase {
|
||||
|
||||
public function testSamplesThisProcess(): void {
|
||||
$rSample = ProcessManager::resourceSample(getmypid());
|
||||
$this->assertIsArray($rSample);
|
||||
$this->assertArrayHasKey('ticks', $rSample);
|
||||
$this->assertGreaterThan(0, $rSample['rss'], 'a running process holds memory');
|
||||
$this->assertGreaterThan(0, $rSample['at']);
|
||||
// PHP itself is a few MB at least, and nothing here is a gigabyte: a
|
||||
// wrong page size or a misread field shows up as an absurd figure.
|
||||
$this->assertGreaterThan(1 << 20, $rSample['rss']);
|
||||
$this->assertLessThan(4 << 30, $rSample['rss']);
|
||||
}
|
||||
|
||||
public function testAProcessThatDoesNotExistReadsNothing(): void {
|
||||
$rMax = (int) @file_get_contents('/proc/sys/kernel/pid_max');
|
||||
$this->assertNull(ProcessManager::resourceSample($rMax > 0 ? $rMax + 1 : 4194305));
|
||||
$this->assertNull(ProcessManager::resourceSample(0));
|
||||
$this->assertNull(ProcessManager::resourceSample(-1));
|
||||
}
|
||||
|
||||
public function testCpuIsPercentOfOneCore(): void {
|
||||
// /proc counts CPU time in USER_HZ = 100, so 150 ticks is 1.5 s of CPU;
|
||||
// spent over 3 s of wall clock that is half a core.
|
||||
$this->assertSame(50.0, ProcessManager::cpuPercent(
|
||||
['ticks' => 150, 'at' => 103.0],
|
||||
['ticks' => 0, 'at' => 100.0]
|
||||
));
|
||||
// A transcode on several cores legitimately passes 100%.
|
||||
$this->assertSame(250.0, ProcessManager::cpuPercent(
|
||||
['ticks' => 500, 'at' => 102.0],
|
||||
['ticks' => 0, 'at' => 100.0]
|
||||
));
|
||||
}
|
||||
|
||||
public function testAPairThatSaysNothingReportsNothing(): void {
|
||||
// Restarted producer: the pid is new, its counter starts over. Reporting
|
||||
// the difference would show a wild negative percentage.
|
||||
$this->assertNull(ProcessManager::cpuPercent(['ticks' => 5, 'at' => 200.0], ['ticks' => 900, 'at' => 100.0]));
|
||||
// Two readings from the same instant divide by zero.
|
||||
$this->assertNull(ProcessManager::cpuPercent(['ticks' => 10, 'at' => 100.0], ['ticks' => 5, 'at' => 100.0]));
|
||||
// No previous reading at all (the first pass after a start).
|
||||
$this->assertNull(ProcessManager::cpuPercent(['ticks' => 10, 'at' => 100.0], []));
|
||||
}
|
||||
|
||||
public function testProducerKind(): void {
|
||||
$this->assertSame('php', ProcessManager::producerKind(getmypid()));
|
||||
$rMax = (int) @file_get_contents('/proc/sys/kernel/pid_max');
|
||||
$this->assertNull(ProcessManager::producerKind($rMax > 0 ? $rMax + 1 : 4194305));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user