httpd: fix dashboard accuracy and add memory tracking

- Add from __future__ import division
- Change "Tests This Session" to "Tests (Cumulative)"
- Fix Tor status to show IDLE when available but 0% success
- Add memory leak detection: RSS growth, peak, GC stats
This commit is contained in:
Username
2025-12-24 00:19:05 +01:00
parent de750a1312
commit f2e6b8216b

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import division
"""HTTP API server with advanced web dashboard for PPF.""" """HTTP API server with advanced web dashboard for PPF."""
import BaseHTTPServer import BaseHTTPServer
@@ -7,9 +8,16 @@ import json
import threading import threading
import time import time
import os import os
import gc
import mysqlite import mysqlite
from misc import _log from misc import _log
# Memory tracking for leak detection
_memory_samples = []
_memory_sample_max = 60 # Keep last 60 samples (5 min at 5s intervals)
_peak_rss = 0
_start_rss = 0
def get_system_stats(): def get_system_stats():
"""Collect system resource statistics.""" """Collect system resource statistics."""
@@ -74,6 +82,33 @@ def get_system_stats():
stats['proc_rss'] = 0 stats['proc_rss'] = 0
stats['proc_threads'] = 0 stats['proc_threads'] = 0
# Memory leak detection
global _memory_samples, _peak_rss, _start_rss
rss = stats.get('proc_rss', 0)
if rss > 0:
if _start_rss == 0:
_start_rss = rss
if rss > _peak_rss:
_peak_rss = rss
_memory_samples.append((time.time(), rss))
if len(_memory_samples) > _memory_sample_max:
_memory_samples.pop(0)
stats['proc_rss_peak'] = _peak_rss
stats['proc_rss_start'] = _start_rss
stats['proc_rss_growth'] = rss - _start_rss if _start_rss > 0 else 0
# GC stats for leak detection
try:
gc_counts = gc.get_count()
stats['gc_count_gen0'] = gc_counts[0]
stats['gc_count_gen1'] = gc_counts[1]
stats['gc_count_gen2'] = gc_counts[2]
stats['gc_objects'] = len(gc.get_objects())
except Exception:
stats['gc_count_gen0'] = stats['gc_count_gen1'] = stats['gc_count_gen2'] = 0
stats['gc_objects'] = 0
return stats return stats
@@ -197,6 +232,7 @@ body {
@keyframes pulse { 50% { opacity: 0.5; } } @keyframes pulse { 50% { opacity: 0.5; } }
.mode-badge { padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; } .mode-badge { padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.mode-ssl { background: rgba(63,185,80,0.2); color: var(--green); border: 1px solid var(--green); } .mode-ssl { background: rgba(63,185,80,0.2); color: var(--green); border: 1px solid var(--green); }
.mode-profile { background: rgba(255,165,0,0.2); color: #ffa500; border: 1px solid #ffa500; margin-left: 6px; }
.mode-judges { background: rgba(88,166,255,0.2); color: var(--blue); border: 1px solid var(--blue); } .mode-judges { background: rgba(88,166,255,0.2); color: var(--blue); border: 1px solid var(--blue); }
.mode-http { background: rgba(210,153,34,0.2); color: var(--yellow); border: 1px solid var(--yellow); } .mode-http { background: rgba(210,153,34,0.2); color: var(--yellow); border: 1px solid var(--yellow); }
.mode-irc { background: rgba(163,113,247,0.2); color: var(--purple); border: 1px solid var(--purple); } .mode-irc { background: rgba(163,113,247,0.2); color: var(--purple); border: 1px solid var(--purple); }
@@ -426,6 +462,11 @@ function update(d) {
ctBadge.textContent = ct.toUpperCase(); ctBadge.textContent = ct.toUpperCase();
ctBadge.className = 'mode-badge mode-' + ct; ctBadge.className = 'mode-badge mode-' + ct;
} }
// Profiling badge
var profBadge = $('profileBadge');
if (profBadge) {
profBadge.style.display = d.profiling ? 'inline-block' : 'none';
}
// System monitor bar // System monitor bar
var sys = d.system || {}; var sys = d.system || {};
@@ -438,7 +479,9 @@ function update(d) {
$('sysDiskPct').textContent = (sys.disk_pct || 0) + '%'; $('sysDiskPct').textContent = (sys.disk_pct || 0) + '%';
var diskFill = $('sysDiskFill'); var diskFill = $('sysDiskFill');
if (diskFill) { diskFill.style.width = (sys.disk_pct || 0) + '%'; diskFill.style.cssText = 'width:' + (sys.disk_pct || 0) + '%;' + setBarColor(diskFill, sys.disk_pct || 0); } if (diskFill) { diskFill.style.width = (sys.disk_pct || 0) + '%'; diskFill.style.cssText = 'width:' + (sys.disk_pct || 0) + '%;' + setBarColor(diskFill, sys.disk_pct || 0); }
$('sysProcMem').textContent = fmtBytes(sys.proc_rss); var memGrowth = sys.proc_rss_growth || 0;
var memGrowthStr = memGrowth > 0 ? ' (+' + fmtBytes(memGrowth) + ')' : '';
$('sysProcMem').textContent = fmtBytes(sys.proc_rss) + memGrowthStr;
// Main stats - db stats are nested under d.db // Main stats - db stats are nested under d.db
var db = d.db || {}; var db = d.db || {};
@@ -535,9 +578,12 @@ function update(d) {
var thtml = ''; var thtml = '';
if (d.tor_pool && d.tor_pool.hosts) { if (d.tor_pool && d.tor_pool.hosts) {
d.tor_pool.hosts.forEach(function(h) { d.tor_pool.hosts.forEach(function(h) {
// Status: OK only if available AND has successes, WARN if available but 0%, DOWN if in backoff
var statusCls = !h.healthy ? 'tag-err' : (h.success_rate > 0 ? 'tag-ok' : 'tag-warn');
var statusTxt = !h.healthy ? 'DOWN' : (h.success_rate > 0 ? 'OK' : 'IDLE');
thtml += '<div class="c c-sm"><div class="host-card">'; thtml += '<div class="c c-sm"><div class="host-card">';
thtml += '<span class="host-addr">' + h.address + '</span>'; thtml += '<span class="host-addr">' + h.address + '</span>';
thtml += '<span class="tag ' + (h.healthy ? 'tag-ok' : 'tag-err') + '">' + (h.healthy ? 'OK' : 'DOWN') + '</span>'; thtml += '<span class="tag ' + statusCls + '">' + statusTxt + '</span>';
thtml += '</div><div class="host-stats">' + fmtMs(h.latency_ms) + ' / ' + fmtDec(h.success_rate, 0) + '% success</div></div>'; thtml += '</div><div class="host-stats">' + fmtMs(h.latency_ms) + ' / ' + fmtDec(h.success_rate, 0) + '% success</div></div>';
}); });
} }
@@ -673,6 +719,7 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
<h1>PPF Dashboard</h1> <h1>PPF Dashboard</h1>
<div class="status"> <div class="status">
<span class="mode-badge mode-ssl" id="checktypeBadge">-</span> <span class="mode-badge mode-ssl" id="checktypeBadge">-</span>
<span class="mode-badge mode-profile" id="profileBadge" style="display:none">PROFILING</span>
<div class="status-item"><div class="dot" id="dot"></div><span id="statusTxt">Connecting</span></div> <div class="status-item"><div class="dot" id="dot"></div><span id="statusTxt">Connecting</span></div>
<div class="status-item">Updated: <span id="lastUpdate">-</span></div> <div class="status-item">Updated: <span id="lastUpdate">-</span></div>
</div> </div>
@@ -698,7 +745,7 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
<div class="sub">of <span id="total">-</span> in database</div> <div class="sub">of <span id="total">-</span> in database</div>
</div> </div>
<div class="c"> <div class="c">
<div class="lbl">Tests This Session</div> <div class="lbl">Tests (Cumulative)</div>
<div class="val" id="tested">-</div> <div class="val" id="tested">-</div>
<div class="sub"><span class="grn" id="passed">-</span> passed / <span class="red" id="failed">-</span> failed</div> <div class="sub"><span class="grn" id="passed">-</span> passed / <span class="red" id="failed">-</span> failed</div>
</div> </div>