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>
29 lines
1.3 KiB
C#
29 lines
1.3 KiB
C#
using System.Collections.Generic;
|
|
namespace Age.Engine.Diagnostics;
|
|
|
|
/// <summary>Base for sinks that need to know the currently-executing script. Tracks the frame stack
|
|
/// from FrameEnter/FrameExit (which emit regardless of the TracingSteps gate) so a subclass can key
|
|
/// per-instruction facts by the REAL running script — including inside nested call-script frames.
|
|
/// This is the "which script is offset 0x1c3 actually in?" fix: a bare Step event only carries pc +
|
|
/// depth, not the script name.</summary>
|
|
public abstract class TraceSinkBase : ITraceSink
|
|
{
|
|
private readonly Stack<string> _frames = new();
|
|
|
|
/// <summary>Name of the innermost frame currently executing ("?" before the first FrameEnter).</summary>
|
|
protected string CurrentScript => _frames.Count > 0 ? _frames.Peek() : "?";
|
|
|
|
public abstract bool TracingSteps { get; }
|
|
|
|
public void Emit(in TraceEvent e)
|
|
{
|
|
// Push before dispatch so the FrameEnter itself is attributed to the entered frame; pop after
|
|
// dispatch so the FrameExit is still attributed to the exiting frame.
|
|
if (e.Kind == TraceEventKind.FrameEnter) _frames.Push(e.Name ?? "?");
|
|
OnEvent(e);
|
|
if (e.Kind == TraceEventKind.FrameExit && _frames.Count > 0) _frames.Pop();
|
|
}
|
|
|
|
protected abstract void OnEvent(in TraceEvent e);
|
|
}
|