fix: guard statistics calls in compare-backends.py against empty duration lists

When all runs for a backend fail, durations_by_backend[backend] is empty,
causing StatisticsError from statistics.quantiles (needs >= 2 points) and
statistics.mean (needs >= 1 point). Print placeholder messages instead.
This commit is contained in:
Willi Ballenthin
2026-04-22 22:21:24 +03:00
committed by Willi Ballenthin
parent 604fae3519
commit a938c87fa4
2 changed files with 39 additions and 18 deletions
+1
View File
@@ -49,6 +49,7 @@
- fix: Scopes.from_dict uses cls instead of self so subclasses return the correct type @williballenthin
- fix: correct wrong dict key in VMRay _compute_monitor_threads assertion (used thread_id instead of process_id) @williballenthin
- fix: replace assert with isinstance guard in get_callee for invalid MethodSpec tokens @williballenthin
- fix: guard statistics.quantiles/mean in compare-backends.py report() against empty duration lists @williballenthin (SURF-89)
- fix: replace zipfile with pyzipper in minimize_vmray_results.py so output archive is AES-encrypted @williballenthin (SURF-88)
- fix: assign yara_strings/yara_condition to empty string when Some has cmin=0 to prevent UnboundLocalError @williballenthin (SURF-87)
- fix: parenthesize s_type checks in capa2yara.py so kid.name != "Some" guard applies to And/Or/Not uniformly @williballenthin (SURF-86)
+38 -18
View File
@@ -252,33 +252,53 @@ def report(args):
console.print("durations:", style="bold")
console.print(" (10-quantiles, in seconds)", style="grey37")
for backend in BACKENDS:
q = statistics.quantiles(durations_by_backend[backend], n=10)
console.print(f" {backend: <8}: ", end="")
for i in range(9):
if i in (4, 8):
style = "bold"
else:
style = "default"
console.print(f"{q[i]: >6.1f}", style=style, end=" ")
console.print()
console.print(" ^-- 10% of samples took less than this ^", style="grey37")
console.print(" 10% of samples took more than this -----------------+", style="grey37")
durations = durations_by_backend[backend]
if len(durations) >= 2:
q = statistics.quantiles(durations, n=10)
console.print(f" {backend: <8}: ", end="")
for i in range(9):
if i in (4, 8):
style = "bold"
else:
style = "default"
console.print(f"{q[i]: >6.1f}", style=style, end=" ")
console.print()
else:
console.print(f" {backend: <8}: (no data)")
console.print(
" ^-- 10% of samples took less than this ^",
style="grey37",
)
console.print(
" 10% of samples took more than this -----------------+",
style="grey37",
)
console.print()
for backend in BACKENDS:
total = sum(durations_by_backend[backend])
successes = len(durations_by_backend[backend])
avg = statistics.mean(durations_by_backend[backend])
console.print(
f" {backend: <8}: {total: >7.0f} seconds across {successes: >4d} successful runs, {avg: >4.1f} average"
)
durations = durations_by_backend[backend]
if durations:
total = sum(durations)
avg = statistics.mean(durations)
console.print(
f" {backend: <8}: {total: >7.0f} seconds across {len(durations): >4d} successful runs, {avg: >4.1f} average"
)
else:
console.print(f" {backend: <8}: (no successful runs)")
console.print()
console.print("slowest samples:", style="bold")
for backend in BACKENDS:
durations = durations_by_backend[backend]
if not durations:
console.print(f" {backend: <8}: (no successful runs)")
continue
console.print(backend)
successful_keys = {k for k, v in doc[backend].items() if not v["err"]}
for duration, path in sorted(
((d["duration"], Path(d["path"]).name) for d in doc[backend].values()), reverse=True
((d["duration"], Path(d["path"]).name) for k, d in doc[backend].items() if k in successful_keys),
reverse=True,
)[:5]:
console.print(f" - {duration: >6.1f} {path}")