From fd0ade2071ae83a2b4c48755dffe0aaeecea4165 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 14 Aug 2024 13:51:28 +0800 Subject: [PATCH] fix all debug methods hitting source code breakpoints --- Fairy.Debugger.cs | 177 +++++++++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 58 deletions(-) diff --git a/Fairy.Debugger.cs b/Fairy.Debugger.cs index ee1b6db..f7b413d 100644 --- a/Fairy.Debugger.cs +++ b/Fairy.Debugger.cs @@ -10,6 +10,7 @@ namespace Neo.Plugins { public partial class Fairy { + [Flags] enum BreakReason { None = 0, @@ -153,26 +154,84 @@ private FairyEngine DebugRun(byte[] script, DataCache snapshot, out BreakReason return Execute(engine, out breakReason); } + private SourceFilenameAndLineNum GetCurrentSourceCode(FairyEngine engine) + { + UInt160 currentScriptHash = engine.CurrentScriptHash; + uint instructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + SourceFilenameAndLineNum currentSource = defaultSource; + if (contractScriptHashToAllSourceLineNums.ContainsKey(currentScriptHash) + && contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash].ContainsKey(instructionPointer) + && contractScriptHashToAllSourceLineNums[currentScriptHash] + .Contains(contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][instructionPointer])) + currentSource = contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][instructionPointer]; + return currentSource; + } + + private bool HitSourceCodeBreakpoint(FairyEngine engine) + { + UInt160 currentScriptHash = engine.CurrentScriptHash; + uint currentInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + return contractScriptHashToSourceCodeBreakpoints.ContainsKey(currentScriptHash) + && contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash].ContainsKey(currentInstructionPointer) + && contractScriptHashToSourceCodeBreakpoints[currentScriptHash] + .Contains(contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][currentInstructionPointer]); + } + + /// + /// Do not break for or + /// if we are at the same source code line as the starting position, or + /// if invocation stack count is same as prev, + /// and current source filename is different from prev, + /// and instruction pointer is greater than prev + /// + /// + /// + /// + /// + /// + /// + private bool ShouldBreakAtDifferentSourceCode(FairyEngine engine, BreakReason breakReason, + uint startInstructionPointer, SourceFilenameAndLineNum startSourceCode, int startInvocationStackCount) + { + if ((breakReason & BreakReason.SourceCodeBreakpoint) > 0 + || (breakReason & BreakReason.SourceCode) > 0) + { + uint currentInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + if (currentInstructionPointer <= startInstructionPointer) + return true; + if (engine.InvocationStack.Count != startInvocationStackCount) + return true; + SourceFilenameAndLineNum currentSourceCode = GetCurrentSourceCode(engine); + if (currentSourceCode.sourceFilename == startSourceCode.sourceFilename + && currentSourceCode.lineNum != startSourceCode.lineNum) + return true; + // Do not break when invocation stack count is same as prev, + // and current source filename is different from prev, + // and instruction pointer is greater than prev + } + return false; + } + private FairyEngine ExecuteAndCheck(FairyEngine engine, out BreakReason actualBreakReason, BreakReason requiredBreakReason = BreakReason.AssemblyBreakpoint | BreakReason.SourceCodeBreakpoint) { actualBreakReason = BreakReason.None; if (engine.State == VMState.HALT || engine.State == VMState.FAULT) return engine; - Instruction currentInstruction = engine.CurrentContext!.CurrentInstruction ?? Instruction.RET; - OpCode currentOpCode = currentInstruction.OpCode; + Instruction prevInstruction = engine.CurrentContext!.CurrentInstruction ?? Instruction.RET; + OpCode prevOpCode = prevInstruction.OpCode; if ((requiredBreakReason & BreakReason.Call) > 0 && - (currentOpCode == OpCode.CALL || currentOpCode == OpCode.CALLA || currentOpCode == OpCode.CALLT || currentOpCode == OpCode.CALL_L - || (currentOpCode == OpCode.SYSCALL && currentInstruction.TokenU32 == ApplicationEngine.System_Contract_Call.Hash))) + (prevOpCode == OpCode.CALL || prevOpCode == OpCode.CALLA || prevOpCode == OpCode.CALLT || prevOpCode == OpCode.CALL_L)) + //|| (prevOpCode == OpCode.SYSCALL && prevInstruction.TokenU32 == ApplicationEngine.System_Contract_Call.Hash))) { engine.ExecuteNext(); - if (currentInstruction.OpCode == OpCode.INITSLOT) + if ((engine.CurrentContext!.CurrentInstruction ?? Instruction.RET).OpCode == OpCode.INITSLOT) engine.ExecuteNext(); // Stopping at INITSLOT makes no good engine.State = VMState.BREAK; actualBreakReason |= BreakReason.Call; return engine; } - if ((requiredBreakReason & BreakReason.Return) > 0 && currentOpCode == OpCode.RET) + if ((requiredBreakReason & BreakReason.Return) > 0 && prevOpCode == OpCode.RET) { engine.ExecuteNext(); engine.State = VMState.BREAK; @@ -181,13 +240,10 @@ private FairyEngine ExecuteAndCheck(FairyEngine engine, out BreakReason actualBr } uint prevInstructionPointer = (uint)engine.CurrentContext.InstructionPointer; UInt160 prevScriptHash = engine.CurrentScriptHash; - SourceFilenameAndLineNum prevSource = defaultSource; - if (contractScriptHashToAllSourceLineNums.ContainsKey(prevScriptHash) - && contractScriptHashToAllInstructionPointerToSourceLineNum[prevScriptHash].ContainsKey(prevInstructionPointer) - && contractScriptHashToAllSourceLineNums[prevScriptHash] - .Contains(contractScriptHashToAllInstructionPointerToSourceLineNum[prevScriptHash][prevInstructionPointer])) - prevSource = contractScriptHashToAllInstructionPointerToSourceLineNum[prevScriptHash][prevInstructionPointer]; + SourceFilenameAndLineNum prevSource = GetCurrentSourceCode(engine); + engine.ExecuteNext(); + // Set coverage for the previous instruction if (contractScriptHashToInstructionPointerToCoverage.ContainsKey(prevScriptHash) && contractScriptHashToInstructionPointerToCoverage[prevScriptHash] @@ -209,31 +265,19 @@ private FairyEngine ExecuteAndCheck(FairyEngine engine, out BreakReason actualBr return engine; } } - if ((requiredBreakReason & BreakReason.SourceCodeBreakpoint) > 0) + if ((requiredBreakReason & BreakReason.SourceCodeBreakpoint) > 0 + && HitSourceCodeBreakpoint(engine)) { - if (contractScriptHashToSourceCodeBreakpoints.ContainsKey(currentScriptHash) - && contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash].ContainsKey(currentInstructionPointer) - && contractScriptHashToSourceCodeBreakpoints[currentScriptHash] - .Contains(contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][currentInstructionPointer]) - && prevSource != contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][currentInstructionPointer]) - { - engine.State = VMState.BREAK; - actualBreakReason |= BreakReason.SourceCodeBreakpoint; - return engine; - } + engine.State = VMState.BREAK; + actualBreakReason |= BreakReason.SourceCodeBreakpoint; + return engine; } - if ((requiredBreakReason & BreakReason.SourceCode) > 0) + if ((requiredBreakReason & BreakReason.SourceCode) > 0 + && prevSource != GetCurrentSourceCode(engine)) { - if (contractScriptHashToAllSourceLineNums.ContainsKey(currentScriptHash) - && contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash].ContainsKey(currentInstructionPointer) - && contractScriptHashToAllSourceLineNums[currentScriptHash] - .Contains(contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][currentInstructionPointer]) - && prevSource != contractScriptHashToAllInstructionPointerToSourceLineNum[currentScriptHash][currentInstructionPointer]) - { - engine.State = VMState.BREAK; - actualBreakReason |= BreakReason.SourceCode; - return engine; - } + engine.State = VMState.BREAK; + actualBreakReason |= BreakReason.SourceCode; + return engine; } return engine; } @@ -245,8 +289,21 @@ private FairyEngine Execute(FairyEngine engine, out BreakReason breakReason) breakReason = BreakReason.None; if (engine.State == VMState.BREAK) engine.State = VMState.NONE; + uint startInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + SourceFilenameAndLineNum startSourceCode = GetCurrentSourceCode(engine); + int startInvocationStackCount = engine.InvocationStack.Count; while (engine.State == VMState.NONE) + { engine = ExecuteAndCheck(engine, out breakReason); + if (engine.State == VMState.BREAK) + { + if ((breakReason & BreakReason.AssemblyBreakpoint) > 0) + break; + if (ShouldBreakAtDifferentSourceCode(engine, breakReason, startInstructionPointer, startSourceCode, startInvocationStackCount)) + break; + engine.State = VMState.NONE; + } + } return engine; } @@ -255,8 +312,23 @@ private FairyEngine StepInto(FairyEngine engine, out BreakReason breakReason) breakReason = BreakReason.None; if (engine.State == VMState.BREAK) engine.State = VMState.NONE; + uint startInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + SourceFilenameAndLineNum startSourceCode = GetCurrentSourceCode(engine); + int startInvocationStackCount = engine.InvocationStack.Count; while (engine.State == VMState.NONE) - engine = ExecuteAndCheck(engine, out breakReason, requiredBreakReason: BreakReason.AssemblyBreakpoint | BreakReason.SourceCodeBreakpoint | BreakReason.Call); + { + engine = ExecuteAndCheck(engine, out breakReason, requiredBreakReason: BreakReason.AssemblyBreakpoint | BreakReason.SourceCodeBreakpoint | BreakReason.SourceCode | BreakReason.Call); + if (engine.State == VMState.BREAK) + { + if ((breakReason & BreakReason.AssemblyBreakpoint) > 0) + break; + if ((breakReason & BreakReason.Call) > 0) + break; + if (ShouldBreakAtDifferentSourceCode(engine, breakReason, startInstructionPointer, startSourceCode, startInvocationStackCount)) + break; + engine.State = VMState.NONE; + } + } return engine; } @@ -278,15 +350,19 @@ private FairyEngine StepOut(FairyEngine engine, out BreakReason breakReason) breakReason = BreakReason.None; if (engine.State == VMState.BREAK) engine.State = VMState.NONE; - int invocationStackCount = engine.InvocationStack.Count; + uint startInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + SourceFilenameAndLineNum startSourceCode = GetCurrentSourceCode(engine); + int startInvocationStackCount = engine.InvocationStack.Count; while (engine.State == VMState.NONE) { engine = ExecuteAndCheck(engine, out breakReason, requiredBreakReason: BreakReason.AssemblyBreakpoint | BreakReason.SourceCodeBreakpoint | BreakReason.Return); if (engine.State == VMState.BREAK) { - if ((breakReason & BreakReason.AssemblyBreakpoint) > 0 || (breakReason & BreakReason.SourceCodeBreakpoint) > 0) + if ((breakReason & BreakReason.AssemblyBreakpoint) > 0) + break; + if ((breakReason & BreakReason.Return) > 0 && engine.InvocationStack.Count < startInvocationStackCount) break; - if ((breakReason & BreakReason.Return) > 0 && engine.InvocationStack.Count < invocationStackCount) + if (ShouldBreakAtDifferentSourceCode(engine, breakReason, startInstructionPointer, startSourceCode, startInvocationStackCount)) break; engine.State = VMState.NONE; } @@ -312,10 +388,9 @@ private FairyEngine StepOverSourceCode(FairyEngine engine, out BreakReason break breakReason = BreakReason.None; if (engine.State == VMState.BREAK) engine.State = VMState.NONE; - UInt160 prevScriptHash = engine.CurrentScriptHash; - uint prevInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; - SourceFilenameAndLineNum prevSourceCode = contractScriptHashToAllInstructionPointerToSourceLineNum[prevScriptHash][prevInstructionPointer]; - int prevInvocationStackCount = engine.InvocationStack.Count; + uint startInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; + SourceFilenameAndLineNum startSourceCode = GetCurrentSourceCode(engine); + int startInvocationStackCount = engine.InvocationStack.Count; while (engine.State == VMState.NONE) { engine = ExecuteAndCheck(engine, out breakReason, requiredBreakReason: BreakReason.AssemblyBreakpoint | BreakReason.SourceCodeBreakpoint | BreakReason.SourceCode); @@ -323,22 +398,8 @@ private FairyEngine StepOverSourceCode(FairyEngine engine, out BreakReason break { if ((breakReason & BreakReason.AssemblyBreakpoint) > 0) break; - if ((breakReason & BreakReason.SourceCodeBreakpoint) > 0 - || (breakReason & BreakReason.SourceCode) > 0) - { - uint currentInstructionPointer = (uint)engine.CurrentContext!.InstructionPointer; - if (currentInstructionPointer <= prevInstructionPointer) - break; - if (engine.InvocationStack.Count != prevInvocationStackCount) - break; - SourceFilenameAndLineNum currentSourceCode = contractScriptHashToAllInstructionPointerToSourceLineNum[engine.CurrentScriptHash][currentInstructionPointer]; - if (currentSourceCode.sourceFilename == prevSourceCode.sourceFilename - && currentSourceCode.lineNum != prevSourceCode.lineNum) - break; - // Do not break when invocation stack count is same as prev, - // and current source code is the same as prev, - // and instruction pointer is greater than prev - } + if (ShouldBreakAtDifferentSourceCode(engine, breakReason, startInstructionPointer, startSourceCode, startInvocationStackCount)) + break; engine.State = VMState.NONE; } }