# /tmp/deep_profile.py
# Unified CPU + Memory + GC profiling script for the primary optimizer.
# This is the MANDATORY first step — gives the cross-domain view that
# single-domain agents lack.
#
# Usage: Adapt the "RUN TARGET HERE" section for your test/benchmark,
# then run with: $RUNNER /tmp/deep_profile.py

import cProfile, tracemalloc, gc, time, pstats, os, sys

# Track GC to quantify allocation→CPU interaction
gc_times = []
def gc_callback(phase, info):
    if phase == 'start':
        gc_callback._start = time.perf_counter()
    elif phase == 'stop':
        gc_times.append(time.perf_counter() - gc_callback._start)
gc.callbacks.append(gc_callback)

tracemalloc.start()
profiler = cProfile.Profile()

profiler.enable()
# === RUN TARGET HERE ===
profiler.disable()

mem_snapshot = tracemalloc.take_snapshot()
profiler.dump_stats('/tmp/deep_cpu.prof')

# Memory top allocators
print("=== MEMORY: Top allocators ===")
for stat in mem_snapshot.statistics('lineno')[:15]:
    print(stat)

# GC impact
total_gc = sum(gc_times)
print(f"\n=== GC: {len(gc_times)} collections, {total_gc:.3f}s total ===")

# CPU top functions (project-only)
print("\n=== CPU: Top project functions ===")
p = pstats.Stats('/tmp/deep_cpu.prof')
stats = p.stats
src = os.path.abspath('src')  # adjust to project source root
project_funcs = []
for (file, line, name), (cc, nc, tt, ct, callers) in stats.items():
    if not os.path.abspath(file).startswith(src):
        continue
    project_funcs.append((ct, tt, name, file, line))
project_funcs.sort(reverse=True)
total = project_funcs[0][0] if project_funcs else 1
if not os.path.exists('/tmp/deep_baseline_total'):
    with open('/tmp/deep_baseline_total', 'w') as f:
        f.write(str(total))
for ct, tt, name, file, line in project_funcs[:15]:
    pct = ct / total * 100
    print(f"  {name:30s} — {pct:5.1f}% cumtime, {tt:.3f}s self")
