75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""harkaq-trace-norm — canoniza la traza cruda de harkaq-trace (plan-freebsd T1.2).
|
|
|
|
harkaq-trace-norm.py <traza-cruda> [--store <dir>] [--drop <prefijo>]... [--resumen]
|
|
|
|
Las reglas de normalización son la parte que FreeBSD tardó años en iterar (los `.meta` de
|
|
META MODE): el ruido no se CALLA, se clasifica con reglas explícitas — D3 aplica acá igual
|
|
que en el verdict. Reglas:
|
|
|
|
1. DROP por prefijo: /proc /sys /dev /tmp /out /src (el build escribe /out y lee /src por
|
|
contrato; lo interesante es lo demás). Ampliable con --drop.
|
|
2. DEDUPE + ORDEN ESTABLE: la traza cruda trae un evento por open(); el mismo path N veces
|
|
no es señal (el orden de llegada tampoco: readdir no garantiza orden — la lección
|
|
número uno de META MODE). Salida: paths únicos, ordenados.
|
|
3. ATRIBUCIÓN por dep (--store): un path store/<hash>-<nombre>/resto se atribuye a la dep
|
|
<nombre>. Con --resumen emite el conteo por dep — la señal de PODA (T1.4): una dep
|
|
declarada con 0 toques es grasa de la clausura.
|
|
|
|
El sellado (T1.3) NO es de este script: la salida canónica es determinista a propósito
|
|
(orden estable, sin timestamps) para que `b3sum` del worker la selle tal cual y viaje junto
|
|
al of_tree. Un script canoniza, el store sella — cada pieza tonta en lo suyo.
|
|
"""
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
# /cache: la cache content-addressed de zig DENTRO de la jaula — estado derivado del propio
|
|
# build (84 entradas en el piloto zlib), no entrada de la clausura. La lección META MODE
|
|
# repite: las reglas de ruido SON el producto; cada una entra medida, no adivinada.
|
|
DROP_DEFAULT = ["/proc", "/sys", "/dev", "/tmp", "/out", "/src", "/run", "/cache"]
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("traza")
|
|
ap.add_argument("--store", default=None,
|
|
help="raíz del store para atribuir paths a deps (store/<hash>-<nombre>/…)")
|
|
ap.add_argument("--drop", action="append", default=[],
|
|
help="prefijo adicional a descartar (repetible)")
|
|
ap.add_argument("--resumen", action="store_true",
|
|
help="emite conteo por dep en vez de la lista canónica")
|
|
args = ap.parse_args()
|
|
|
|
drops = DROP_DEFAULT + args.drop
|
|
store = str(Path(args.store).resolve()) if args.store else None
|
|
# store/<hash-hex>-<nombre>/… — el nombre puede traer guiones; el hash no.
|
|
re_dep = re.compile(re.escape(store) + r"/[0-9a-f]{16,}-([^/]+)/") if store else None
|
|
|
|
unicos = set()
|
|
por_dep = Counter()
|
|
with open(args.traza) as f:
|
|
for linea in f:
|
|
p = linea.strip()
|
|
if not p or any(p == d or p.startswith(d + "/") for d in drops):
|
|
continue
|
|
if p not in unicos and re_dep:
|
|
m = re_dep.match(p)
|
|
if m:
|
|
por_dep[m.group(1)] += 1
|
|
unicos.add(p)
|
|
|
|
if args.resumen:
|
|
for dep, n in sorted(por_dep.items()):
|
|
print(f"{n:6d} {dep}")
|
|
else:
|
|
for p in sorted(unicos):
|
|
print(p)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|