73 lines
3.5 KiB
Python
73 lines
3.5 KiB
Python
"""Run the isolated synthetic C# harness and measure its own source FFmpeg only.
|
|
|
|
No Jellyfin configuration changes, restarts, API keys or receiver connections.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import signal
|
|
import subprocess
|
|
import time
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--harness', required=True)
|
|
parser.add_argument('--ffmpeg', default='/usr/lib/jellyfin-ffmpeg/ffmpeg')
|
|
parser.add_argument('--output', required=True)
|
|
parser.add_argument('--long', action='store_true')
|
|
parser.add_argument('--native', action='store_true')
|
|
args = parser.parse_args()
|
|
root = Path(args.output).resolve()
|
|
if root.exists():
|
|
raise ValueError('Choose a new output directory')
|
|
samples, previous = [], {}
|
|
with Path(str(root) + '.log').open('x') as log:
|
|
process = subprocess.Popen([args.harness, '--native-continuity-e2e' if args.native else '--long-continuity-e2e' if args.long else '--continuity-e2e', args.ffmpeg, str(root)], stdout=log,
|
|
stderr=subprocess.STDOUT, start_new_session=True)
|
|
deadline = time.monotonic() + 240
|
|
try:
|
|
while process.poll() is None:
|
|
if time.monotonic() > deadline:
|
|
raise TimeoutError('Synthetic harness deadline exceeded')
|
|
now = time.monotonic()
|
|
for proc in Path('/proc').glob('[0-9]*'):
|
|
try:
|
|
if not str((proc / 'cwd').resolve()).startswith(str(root / 'cache' / 'source-')):
|
|
continue
|
|
value = (proc / 'stat').read_text()
|
|
fields = value[value.rfind(')') + 2:].split()
|
|
cpu = (int(fields[11]) + int(fields[12])) / os.sysconf('SC_CLK_TCK')
|
|
rss = int(fields[21]) * os.sysconf('SC_PAGE_SIZE') / 1048576
|
|
if proc.name in previous:
|
|
before, at = previous[proc.name]
|
|
samples.append({'one_core_percent': round((cpu-before)/(now-at)*100, 1),
|
|
'rss_mib': round(rss, 1)})
|
|
previous[proc.name] = (cpu, now)
|
|
except OSError:
|
|
pass
|
|
time.sleep(1)
|
|
finally:
|
|
if process.poll() is None:
|
|
os.killpg(process.pid, signal.SIGTERM)
|
|
try:
|
|
process.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
os.killpg(process.pid, signal.SIGKILL)
|
|
process.wait()
|
|
report = {'harness_exit_code': process.returncode, 'samples': samples,
|
|
'scope': 'Only synthetic plugin source encoding; excludes fixture and player decoders'}
|
|
(root / 'cpu-sample.json').write_text(json.dumps(report, indent=2))
|
|
if process.returncode == 0 and (args.long or args.native):
|
|
subprocess.run(['python3', str(Path(__file__).with_name('verify-continuity-recording.py')), '--output', str(root), '--ffmpeg', args.ffmpeg], check=True)
|
|
print(Path(str(root) + '.log').read_text()[-3500:])
|
|
print(json.dumps({'exit_code': process.returncode, 'cpu_samples': len(samples),
|
|
'maximum_one_core_percent': max((s['one_core_percent'] for s in samples), default=None),
|
|
'maximum_rss_mib': max((s['rss_mib'] for s in samples), default=None)}))
|
|
raise SystemExit(0 if process.returncode == 0 else 1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|