Files
OpenMaidEngine/engine/Age.Engine/Diagnostics/CompositeTraceSink.cs
gamer147 a256b8e6d8 feat(diag): aggregating + filtered trace sinks (histogram, op-filter, per-script)
The existing trace framework only had a flat text formatter, so every question
became 'dump millions of lines, then grep'. This session that cost a long wrong
detour. Add, all observe-only (parity preserved):

- HistogramTraceSink: execution counts per opcode AND per call-site (script:pc)
  with a sample operand. Dumped sorted after the run. This is what instantly
  showed the 493k headless 'sleep's are INPUTNAME.BIN:0x1c3 (a name-entry poll
  loop), not the opening.
- TraceSinkBase: tracks the frame stack -> attributes each step to its REAL
  script (nested call-script frames included) = the 'which script is this pc in?'
  answer a bare step trace can't give.
- TextTraceSink: op-filter (--trace-ops sleep,draw-texture,...) + script:pc tags.
- CompositeTraceSink: fan-out (text + histogram + Godot's call-script queue).
- OpcodeTable.ByLabel: mnemonic -> opcode for --trace-ops.
- CLI: --trace-histogram, --trace-ops, robust --trace-file (mkdir -p).
- Godot: --trace-histogram <file> profiles the REAL run (headless flow diverges:
  real run to page 1 is 562 steps / 0 sleeps vs headless 2M steps / 493k sleeps).
- Also: --sleep-scale <f> debug knob to slow the paced opening for inspection.

Engine 56/56 (4 new); sweep parity 284/13; Godot builds + dogfooded end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:50:07 -04:00

18 lines
687 B
C#

namespace Age.Engine.Diagnostics;
/// <summary>Fans one event stream out to several sinks, so e.g. a live text trace and an aggregating
/// histogram (or Godot's call-script queue) can run together. TracingSteps is true if ANY child needs
/// steps, so the VM's cheap gate stays correct.</summary>
public sealed class CompositeTraceSink : ITraceSink
{
private readonly ITraceSink[] _sinks;
public CompositeTraceSink(params ITraceSink[] sinks) => _sinks = sinks;
public bool TracingSteps
{
get { foreach (var s in _sinks) if (s.TracingSteps) return true; return false; }
}
public void Emit(in TraceEvent e) { foreach (var s in _sinks) s.Emit(in e); }
}