Skip to content

Commit

Permalink
Reduce overhead of debugger support by not checking the debugger befo…
Browse files Browse the repository at this point in the history
…re every instruction
  • Loading branch information
Rohansi committed Feb 14, 2024
1 parent 4c2dab2 commit a0cc15b
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 85 deletions.
2 changes: 2 additions & 0 deletions Mond/Compiler/FunctionContext.Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 34,8 @@ public void Statement(Token start, Token end)
new ImmediateOperand(end.Line),
new ImmediateOperand(end.Column end.Contents.Length - 1)
}));

Emit(new Instruction(InstructionType.DebugCheckpoint));
}

public void Statement(Expression expression)
Expand Down
1 change: 1 addition & 0 deletions Mond/Compiler/Instruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 44,7 @@ enum InstructionType : byte
JmpTable, // jump to one of multiple locations

Breakpoint, // break if a debugger is attached
DebugCheckpoint, // break if a debugger is attached and wants to break here

// ----------------- //

Expand Down
9 changes: 0 additions & 9 deletions Mond/Debugger/MondDebugInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 168,6 @@ public IEnumerable<Statement> FindStatements(int startLine, int startColumn, int
(s.StartLineNumber > startLine && s.EndLineNumber < endLine));
}

public bool IsStatementStart(int address)
{
if (_statements == null)
return false;

var search = new Statement(address, 0, 0, 0, 0);
return _statements.BinarySearch(search, StatementAddressComparer) >= 0;
}

public Scope FindScope(int address)
{
if (_scopes == null)
Expand Down
6 changes: 6 additions & 0 deletions Mond/Debugger/MondDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 57,12 @@ protected void AddBreakpoint(MondProgram program, int address)
Programs.Add(program);
}

// snap breakpoints to the statement's address if it isn't already
// the VM will only check checkpoint instructions which align with statement addresses
var statement = program.DebugInfo?.FindStatement(address);
if (statement != null)
address = statement.Value.Address;

if (breakpoints.Contains(address))
return;

Expand Down
2 changes: 1 addition & 1 deletion Mond/MondProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 11,7 @@ namespace Mond
public sealed class MondProgram
{
private const uint MagicId = 0xFA57C0DE;
private const uint FormatVersion = 10;
private const uint FormatVersion = 11;

internal readonly int[] Bytecode;
internal readonly MondValue[] Numbers;
Expand Down
111 changes: 38 additions & 73 deletions Mond/VirtualMachine/Machine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 14,6 @@ partial class Machine

#if !NO_DEBUG
private MondDebugAction _debugAction;
private bool _debugSkip;
private bool _debugAlign;
private int _debugDepth;
internal MondDebugger Debugger;
#endif
Expand All @@ -28,8 26,6 @@ public Machine(MondState state)

#if !NO_DEBUG
_debugAction = MondDebugAction.Run;
_debugSkip = false;
_debugAlign = false;
_debugDepth = 0;
Debugger = null;
#endif
Expand Down Expand Up @@ -89,6 85,8 @@ public MondValue Call(MondValue function, params MondValue[] arguments)

PushCall(new ReturnAddress(closure.Program, closure.Address, argFrame, _evalStackSize));
PushLocal(closure.Locals);

DebuggerOnCall();
break;

case ClosureType.Native:
Expand Down Expand Up @@ -127,27 125,6 @@ private MondValue Run()
{
while (true)
{
#if !NO_DEBUG
if (Debugger != null)
{
var skip = _debugSkip;
_debugSkip = false;

var shouldStopAtStmt =
(_debugAction == MondDebugAction.StepInto) ||
(_debugAction == MondDebugAction.StepOver && _debugDepth == 0);

var shouldBreak =
(_debugAlign && program.DebugInfo == null) ||
(_debugAlign && program.DebugInfo.IsStatementStart(ip)) ||
(Debugger.ShouldBreak(program, ip)) ||
(shouldStopAtStmt && program.DebugInfo != null && program.DebugInfo.IsStatementStart(ip));

if (!skip && shouldBreak)
DebuggerBreak(program, locals, args, ip, initialCallDepth);
}
#endif

errorIp = ip;

var opcode = code[ip ];
Expand Down Expand Up @@ -684,7 661,9 @@ private MondValue Run()

args = _callStackSize >= 0 ? PeekCall().Arguments : null;
locals = _localStackSize >= 0 ? PeekLocal() : null;


DebuggerOnReturn();

if (_callStackSize == initialCallDepth)
{
var result = Pop();
Expand All @@ -699,11 678,6 @@ private MondValue Run()

ClearDirty();

#if !NO_DEBUG
if (Debugger != null && DebuggerCheckReturn())
DebuggerBreak(program, locals, args, ip, initialCallDepth);
#endif

break;
}

Expand Down Expand Up @@ -798,14 772,29 @@ private MondValue Run()
case (int)InstructionType.Breakpoint:
{
#if !NO_DEBUG
if (Debugger == null)
break;
if (Debugger != null)
{
DebuggerBreak(program, locals, args, ip, initialCallDepth);
}
#endif
break;
}

DebuggerBreak(program, locals, args, ip, initialCallDepth);
case (int)InstructionType.DebugCheckpoint:
{
#if !NO_DEBUG
if (Debugger != null)
{
var shouldStopAtStmt =
(_debugAction == MondDebugAction.StepInto) ||
(_debugAction == MondDebugAction.StepOver && _debugDepth == 0) ||
(_debugAction == MondDebugAction.StepOut && _debugDepth < 0);

var shouldBreak = shouldStopAtStmt || Debugger.ShouldBreak(program, ip);

// we stop for the statement *after* the debugger statement so we
// skip the next break opportunity, otherwise we break twice
_debugSkip = true;
if (shouldBreak)
DebuggerBreak(program, locals, args, ip, initialCallDepth);
}
#endif
break;
}
Expand Down Expand Up @@ -1025,11 1014,7 @@ private void DoCall(int argCount, ref int[] code, ref int ip, ref MondProgram pr
args = argFrame;
locals = closure.Locals;

#if !NO_DEBUG
if (Debugger != null)
DebuggerCheckCall();
#endif

DebuggerOnCall();
break;

case ClosureType.Native:
Expand Down Expand Up @@ -1086,53 1071,33 @@ private List<MondValue> UnpackArgs(int[] code, ref int ip, int argCount, int unp
return unpackedArgs;
}

#if !NO_DEBUG
private void DebuggerCheckCall()
private void DebuggerOnCall()
{
switch (_debugAction)
#if !NO_DEBUG
if (Debugger != null && _debugAction != MondDebugAction.Run)
{
case MondDebugAction.StepInto:
_debugAlign = true;
return;

case MondDebugAction.StepOver:
case MondDebugAction.StepOut:
_debugDepth ;
_debugAlign = false;
return;
_debugDepth ;
}
#endif
}

private bool DebuggerCheckReturn()
private void DebuggerOnReturn()
{
switch (_debugAction)
#if !NO_DEBUG
if (Debugger != null && _debugAction != MondDebugAction.Run)
{
case MondDebugAction.StepInto:
return !_debugAlign;

case MondDebugAction.StepOver:
--_debugDepth;

if (_debugDepth < 0)
return true;

_debugAlign = _debugDepth == 0;
return false;

case MondDebugAction.StepOut:
return --_debugDepth < 0;
_debugDepth--;
}

return false;
#endif
}

#if !NO_DEBUG
private void DebuggerBreak(MondProgram program, Frame locals, Frame args, int address, int initialCallDepth)
{
var context = new MondDebugContext(
_state, program, address, locals, args, _callStack, _callStackSize, initialCallDepth);

// so eval can work
_debugAlign = false;
_debugAction = MondDebugAction.Run;

_debugAction = Debugger.Break(context, address);
Expand Down
2 changes: 1 addition & 1 deletion TryMond/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 69,7 @@
{
Options = new MondCompilerOptions
{
DebugInfo = MondDebugInfoLevel.StackTrace,
DebugInfo = MondDebugInfoLevel.Full,
},
Libraries = new MondLibraryManager
{
Expand Down
2 changes: 1 addition & 1 deletion TryMond/AutoAbortDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 9,7 @@ public class AutoAbortDebugger : MondDebugger

protected override bool ShouldBreak(MondProgram program, int address)
{
return _count >= 5_000_000;
return _count >= 1_000_000;
}

protected override MondDebugAction OnBreak(MondDebugContext context, int address)
Expand Down

0 comments on commit a0cc15b

Please sign in to comment.