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:
53
httpd.py
53
httpd.py
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import division
|
||||
"""HTTP API server with advanced web dashboard for PPF."""
|
||||
|
||||
import BaseHTTPServer
|
||||
@@ -7,9 +8,16 @@ import json
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
import gc
|
||||
import mysqlite
|
||||
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():
|
||||
"""Collect system resource statistics."""
|
||||
@@ -74,6 +82,33 @@ def get_system_stats():
|
||||
stats['proc_rss'] = 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
|
||||
|
||||
|
||||
@@ -197,6 +232,7 @@ body {
|
||||
@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-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-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); }
|
||||
@@ -426,6 +462,11 @@ function update(d) {
|
||||
ctBadge.textContent = ct.toUpperCase();
|
||||
ctBadge.className = 'mode-badge mode-' + ct;
|
||||
}
|
||||
// Profiling badge
|
||||
var profBadge = $('profileBadge');
|
||||
if (profBadge) {
|
||||
profBadge.style.display = d.profiling ? 'inline-block' : 'none';
|
||||
}
|
||||
|
||||
// System monitor bar
|
||||
var sys = d.system || {};
|
||||
@@ -438,7 +479,9 @@ function update(d) {
|
||||
$('sysDiskPct').textContent = (sys.disk_pct || 0) + '%';
|
||||
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); }
|
||||
$('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
|
||||
var db = d.db || {};
|
||||
@@ -535,9 +578,12 @@ function update(d) {
|
||||
var thtml = '';
|
||||
if (d.tor_pool && d.tor_pool.hosts) {
|
||||
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 += '<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>';
|
||||
});
|
||||
}
|
||||
@@ -673,6 +719,7 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
|
||||
<h1>PPF Dashboard</h1>
|
||||
<div class="status">
|
||||
<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">Updated: <span id="lastUpdate">-</span></div>
|
||||
</div>
|
||||
@@ -698,7 +745,7 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
|
||||
<div class="sub">of <span id="total">-</span> in database</div>
|
||||
</div>
|
||||
<div class="c">
|
||||
<div class="lbl">Tests This Session</div>
|
||||
<div class="lbl">Tests (Cumulative)</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>
|
||||
|
||||
Reference in New Issue
Block a user