From e3d886bbd6f0558e2ae54ae5b4b2162049d19921 Mon Sep 17 00:00:00 2001 From: Andreas Hollandt Date: Sat, 1 Aug 2020 20:58:07 +0200 Subject: [PATCH] implement DAP Goto Request --- .../CommandFactories/MICommandFactory.cs | 6 +++ src/MICore/CommandFactories/gdb.cs | 26 ++++++++++++- src/MICore/CommandFactories/lldb.cs | 13 +++++++ src/MIDebugEngine/AD7.Impl/AD7Engine.cs | 38 +++++++++++++++++++ .../AD7.Impl/AD7MemoryAddress.cs | 4 ++ src/MIDebugEngine/AD7.Impl/AD7Thread.cs | 24 +++++------- .../Engine.Impl/DebuggedProcess.cs | 10 +++++ src/OpenDebugAD7/AD7DebugSession.cs | 35 +++++++++++++++-- src/OpenDebugAD7/AD7Resources.Designer.cs | 20 +++++----- src/OpenDebugAD7/AD7Resources.resx | 6 +-- 10 files changed, 151 insertions(+), 31 deletions(-) diff --git a/src/MICore/CommandFactories/MICommandFactory.cs b/src/MICore/CommandFactories/MICommandFactory.cs index ff0d3d078..4ec9fd469 100644 --- a/src/MICore/CommandFactories/MICommandFactory.cs +++ b/src/MICore/CommandFactories/MICommandFactory.cs @@ -242,6 +242,12 @@ public async Task ExecNextInstruction(int threadId, ResultClass resultClass = Re await ThreadFrameCmdAsync(command, resultClass, threadId, 0); } + /// + /// Jumps to a specified target location + /// + abstract public Task ExecJump(string filename, int line); + abstract public Task ExecJump(ulong address); + /// /// Tells GDB to spawn a target process previous setup with -file-exec-and-symbols or similar /// diff --git a/src/MICore/CommandFactories/gdb.cs b/src/MICore/CommandFactories/gdb.cs index 4bcb24b30..5df01be1c 100644 --- a/src/MICore/CommandFactories/gdb.cs +++ b/src/MICore/CommandFactories/gdb.cs @@ -157,7 +157,7 @@ public override async Task ThreadInfo(uint? threadId = null) public override async Task> StartAddressesForLine(string file, uint line) { - string cmd = "info line " + file + ":" + line; + string cmd = "info line -s " + file + " -li " + line; var result = await _debugger.ConsoleCmdAsync(cmd, allowWhileRunning: false); List addresses = new List(); using (StringReader stringReader = new StringReader(result)) @@ -173,7 +173,7 @@ public override async Task> StartAddressesForLine(string file, uint { ulong address; string addrStr = resultLine.Substring(pos + 18); - if (MICommandFactory.SpanNextAddr(addrStr, out address) != null) + if (SpanNextAddr(addrStr, out address) != null) { addresses.Add(address); } @@ -183,6 +183,28 @@ public override async Task> StartAddressesForLine(string file, uint return addresses; } + private async Task JumpInternal(string target) + { + // temporary breakpoint + jump + // NB: the gdb docs state: "Resume execution at line linespec. Execution stops again immediately if there is a breakpoint there." + // We rely on this. If another thread hits a breakpoint before that we have a UX problem + // and would need to handle this via scheduler-locking for all-stop mode and ??? for non-stop mode. + await _debugger.CmdAsync("-break-insert -t " + target, ResultClass.done); + await _debugger.CmdAsync("-exec-jump " + target, ResultClass.running); + } + + public override Task ExecJump(string filename, int line) + { + string target = "--source " + filename + " --line " + line.ToString(CultureInfo.InvariantCulture); + return JumpInternal(target); + } + + public override Task ExecJump(ulong address) + { + string target = "*" + string.Format(CultureInfo.InvariantCulture, "0x{0:X}", address); + return JumpInternal(target); + } + public override Task EnableTargetAsyncOption() { // Linux attach TODO: GDB will fail this command when attaching. This is worked around diff --git a/src/MICore/CommandFactories/lldb.cs b/src/MICore/CommandFactories/lldb.cs index 3cb2d9467..e2181942c 100644 --- a/src/MICore/CommandFactories/lldb.cs +++ b/src/MICore/CommandFactories/lldb.cs @@ -177,6 +177,19 @@ public override Task Catch(string name, bool onlyOnce = false, ResultClass resul throw new NotImplementedException("lldb catch command"); } + // TODO: update these if they become available in lldb-mi + public override async Task ExecJump(string filename, int line) + { + string command = "jump " + filename + ":" + line; + await _debugger.CmdAsync(command, ResultClass.running); + } + + public override async Task ExecJump(ulong address) + { + string command = "jump *" + string.Format(CultureInfo.InvariantCulture, "0x{0:X}", address); + await _debugger.CmdAsync(command, ResultClass.running); + } + /// /// Assigns the value of an expression to a variable. /// Since LLDB only accepts assigning values to variables, the expression may need to be evaluated. diff --git a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs index a99f43dd4..49e179530 100755 --- a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs @@ -182,6 +182,44 @@ public object GetMetric(string metric) return _configStore.GetEngineMetric(metric); } + public int Jump(string filename, int line) + { + try + { + _debuggedProcess.WorkerThread.RunOperation(() => _debuggedProcess.Jump(filename, line)); + } + catch (InvalidCoreDumpOperationException) + { + return AD7_HRESULT.E_CRASHDUMP_UNSUPPORTED; + } + catch (Exception e) + { + _engineCallback.OnError(EngineUtils.GetExceptionDescription(e)); + return Constants.E_ABORT; + } + + return Constants.S_OK; + } + + public int Jump(ulong address) + { + try + { + _debuggedProcess.WorkerThread.RunOperation(() => _debuggedProcess.Jump(address)); + } + catch (InvalidCoreDumpOperationException) + { + return AD7_HRESULT.E_CRASHDUMP_UNSUPPORTED; + } + catch (Exception e) + { + _engineCallback.OnError(EngineUtils.GetExceptionDescription(e)); + return Constants.E_ABORT; + } + + return Constants.S_OK; + } + #region IDebugEngine2 Members // Attach the debug engine to a program. diff --git a/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs b/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs index 318e50e2e..09bc6a277 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs @@ -198,6 +198,10 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_FUNCTION; } } + if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_FUNCTIONOFFSET) != 0) + { + // TODO: + } return Constants.S_OK; } diff --git a/src/MIDebugEngine/AD7.Impl/AD7Thread.cs b/src/MIDebugEngine/AD7.Impl/AD7Thread.cs index b2b0a8d35..f5457c011 100644 --- a/src/MIDebugEngine/AD7.Impl/AD7Thread.cs +++ b/src/MIDebugEngine/AD7.Impl/AD7Thread.cs @@ -282,27 +282,23 @@ int IDebugThread2.Resume(out uint suspendCount) } // Sets the next statement to the given stack frame and code context. + // https://docs.microsoft.com/en-us/visualstudio/extensibility/debugger/reference/idebugthread2-setnextstatement int IDebugThread2.SetNextStatement(IDebugStackFrame2 stackFrame, IDebugCodeContext2 codeContext) { - ulong addr = ((AD7MemoryAddress)codeContext).Address; - AD7StackFrame frame = ((AD7StackFrame)stackFrame); - if (frame.ThreadContext.Level != 0 || frame.Thread != this || !frame.ThreadContext.pc.HasValue) - { + // VS does provide a frame so at least do some sanity checks + AD7StackFrame frame = stackFrame as AD7StackFrame; + if (frame != null && (frame.ThreadContext.Level != 0 || frame.Thread != this)) return Constants.S_FALSE; - } - string toFunc = EngineUtils.GetAddressDescription(_engine.DebuggedProcess, addr); - string fromFunc = EngineUtils.GetAddressDescription(_engine.DebuggedProcess, frame.ThreadContext.pc.Value); - if (toFunc != fromFunc) + + try { - return Constants.S_FALSE; + ulong addr = ((AD7MemoryAddress)codeContext).Address; + return _engine.Jump(addr); } - string result = frame.EvaluateExpression("$pc=" + EngineUtils.AsAddr(addr, _engine.DebuggedProcess.Is64BitArch)); - if (result != null) + catch (Exception) { - _engine.DebuggedProcess.ThreadCache.MarkDirty(); - return Constants.S_OK; + return Constants.S_FALSE; } - return Constants.S_FALSE; } // suspend a thread. diff --git a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs index eddad0aec..219c85e7d 100755 --- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs +++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs @@ -1601,6 +1601,16 @@ public Task Continue(DebuggedThread thread) return Execute(thread); } + public async Task Jump(string filename, int line) + { + await MICommandFactory.ExecJump(filename, line); + } + + public async Task Jump(ulong address) + { + await MICommandFactory.ExecJump(address); + } + public async Task Step(int threadId, enum_STEPKIND kind, enum_STEPUNIT unit) { this.VerifyNotDebuggingCoreDump(); diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs index b5eb9d671..d4c5cfbd0 100644 --- a/src/OpenDebugAD7/AD7DebugSession.cs +++ b/src/OpenDebugAD7/AD7DebugSession.cs @@ -1326,7 +1326,37 @@ protected override void HandlePauseRequestAsync(IRequestResponder responder) { - responder.SetError(new ProtocolException(AD7Resources.Error_NotImplementedSetNextStatement)); + var response = new GotoResponse(); + if (!m_isStopped) + { + responder.SetResponse(response); + return; + } + + var builder = new ErrorBuilder(() => AD7Resources.Error_UnableToSetNextStatement); + IDebugThread2 thread = null; + try + { + if (m_gotoCodeContexts.TryGetValue(responder.Arguments.TargetId, out IDebugCodeContext2 gotoTarget)) + { + lock (m_threads) + { + if (!m_threads.TryGetValue(responder.Arguments.ThreadId, out thread)) + throw new AD7Exception("Unknown thread id: " + responder.Arguments.ThreadId.ToString(CultureInfo.InvariantCulture)); + } + BeforeContinue(); + builder.CheckHR(thread.SetNextStatement(null, gotoTarget)); + } + } + catch (AD7Exception e) + { + m_isStopped = true; + responder.SetError(new ProtocolException(e.Message)); + return; + } + + responder.SetResponse(response); + FireStoppedEvent(thread, StoppedEvent.ReasonValue.Goto); } protected override void HandleGotoTargetsRequestAsync(IRequestResponder responder) @@ -1336,6 +1366,7 @@ protected override void HandleGotoTargetsRequestAsync(IRequestResponder - /// Looks up a localized string similar to Set next statement is not supported by the current debugger.. - /// - internal static string Error_NotImplementedSetNextStatement { - get { - return ResourceManager.GetString("Error_NotImplementedSetNextStatement", resourceCulture); - } - } - /// /// Looks up a localized string similar to {0}: property '{1}' is invalid.. /// @@ -406,6 +397,17 @@ internal static string Error_UnableToSetBreakpoint { } } + /// + /// Looks up a localized string similar to Error setting next statement. {0}. + /// + internal static string Error_UnableToSetNextStatement + { + get + { + return ResourceManager.GetString("Error_UnableToSetNextStatement", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}' cannot be assigned to. /// diff --git a/src/OpenDebugAD7/AD7Resources.resx b/src/OpenDebugAD7/AD7Resources.resx index bc916064d..42237a129 100644 --- a/src/OpenDebugAD7/AD7Resources.resx +++ b/src/OpenDebugAD7/AD7Resources.resx @@ -195,6 +195,9 @@ Error setting breakpoint. {0} + + Error setting next statement. {0} + Unable to parse 'logMessage'. @@ -298,7 +301,4 @@ Unable to retrieve stack trace. {0} - - Set next statement is not supported by the current debugger. - \ No newline at end of file