diff --git a/src/MICore/CommandFactories/MICommandFactory.cs b/src/MICore/CommandFactories/MICommandFactory.cs
index c73d5c088..e224f761a 100644
--- a/src/MICore/CommandFactories/MICommandFactory.cs
+++ b/src/MICore/CommandFactories/MICommandFactory.cs
@@ -246,6 +246,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/clrdbg.cs b/src/MICore/CommandFactories/clrdbg.cs
index 05523bf6e..13006e318 100644
--- a/src/MICore/CommandFactories/clrdbg.cs
+++ b/src/MICore/CommandFactories/clrdbg.cs
@@ -237,6 +237,16 @@ public override Task Catch(string name, bool onlyOnce = false, ResultClass resul
throw new NotImplementedException("clrdbg catch command");
}
+ public override Task ExecJump(string filename, int line)
+ {
+ throw new NotImplementedException("clrdbg jump command");
+ }
+
+ public override Task ExecJump(ulong address)
+ {
+ throw new NotImplementedException("clrdbg jump command");
+ }
+
public override string GetTargetArchitectureCommand()
{
return null;
diff --git a/src/MICore/CommandFactories/gdb.cs b/src/MICore/CommandFactories/gdb.cs
index 3382fbfe0..0a4cb0602 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,25 @@ public override async Task> StartAddressesForLine(string file, uint
return addresses;
}
+ private async Task JumpInternal(string target)
+ {
+ // temporary breakpoint + jump
+ 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 e571841ca..60bff3b68 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("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 f3f404244..f44448a38 100755
--- a/src/MIDebugEngine/AD7.Impl/AD7Engine.cs
+++ b/src/MIDebugEngine/AD7.Impl/AD7Engine.cs
@@ -187,6 +187,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 f478379ad..0be7d6e79 100644
--- a/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs
+++ b/src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs
@@ -2,11 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
-using System.Collections.Generic;
-using System.Text;
using Microsoft.VisualStudio.Debugger.Interop;
using MICore;
-using Microsoft.MIDebugEngine.Natvis;
namespace Microsoft.MIDebugEngine
{
@@ -14,7 +11,7 @@ namespace Microsoft.MIDebugEngine
// IDebugMemoryContext2 represents a position in the address space of the machine running the program being debugged.
// IDebugCodeContext2 represents the starting position of a code instruction.
// For most run-time architectures today, a code context can be thought of as an address in a program's execution stream.
- internal class AD7MemoryAddress : IDebugCodeContext2
+ internal sealed class AD7MemoryAddress : IDebugCodeContext2
{
private readonly AD7Engine _engine;
private readonly ulong _address;
@@ -42,6 +39,7 @@ public void SetDocumentContext(IDebugDocumentContext2 docContext)
// Adds a specified value to the current context's address to create a new context.
public int Add(ulong dwCount, out IDebugMemoryContext2 newAddress)
{
+ // FIXME: this is not correct for IDebugCodeContext2
newAddress = new AD7MemoryAddress(_engine, (uint)dwCount + _address, null);
return Constants.S_OK;
}
@@ -160,19 +158,15 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo)
{
pinfo[0].dwFields = 0;
- if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0)
+ if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0 ||
+ (dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE) != 0)
{
pinfo[0].bstrAddress = EngineUtils.AsAddr(_address, _engine.DebuggedProcess.Is64BitArch);
- pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS;
+ pinfo[0].bstrAddressAbsolute = pinfo[0].bstrAddress;
+ pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS | enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE;
}
-
// Fields not supported by the sample
if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSOFFSET) != 0) { }
- if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE) != 0)
- {
- pinfo[0].bstrAddressAbsolute = EngineUtils.AsAddr(_address, _engine.DebuggedProcess.Is64BitArch);
- pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE;
- }
if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL) != 0)
{
DebuggedModule module = _engine.DebuggedProcess.ResolveAddress(_address);
@@ -195,7 +189,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) { }
+ if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_FUNCTIONOFFSET) != 0)
+ {
+ // TODO:
+ }
return Constants.S_OK;
}
@@ -210,10 +207,10 @@ public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo)
}
// Gets the user-displayable name for this context
- // This is not supported by the sample engine.
public int GetName(out string pbstrName)
{
- throw new NotImplementedException();
+ pbstrName = _functionName ?? Engine.GetAddressDescription(_address);
+ return Constants.S_OK;
}
// Subtracts a specified value from the current context's address to create a new context.
diff --git a/src/MIDebugEngine/AD7.Impl/AD7Thread.cs b/src/MIDebugEngine/AD7.Impl/AD7Thread.cs
index 212845a2c..561743d34 100644
--- a/src/MIDebugEngine/AD7.Impl/AD7Thread.cs
+++ b/src/MIDebugEngine/AD7.Impl/AD7Thread.cs
@@ -276,26 +276,19 @@ int IDebugThread2.Resume(out uint suspendCount)
// Sets the next statement to the given stack frame and code context.
int IDebugThread2.SetNextStatement(IDebugStackFrame2 stackFrame, IDebugCodeContext2 codeContext)
{
- // CLRDBG TODO: This implementation should be changed to call an MI command
- ulong addr = ((AD7MemoryAddress)codeContext).Address;
- AD7StackFrame frame = ((AD7StackFrame)stackFrame);
- if (frame.ThreadContext.Level != 0 || frame.Thread != this || !frame.ThreadContext.pc.HasValue || _engine.DebuggedProcess.MICommandFactory.Mode == MIMode.Clrdbg)
- {
+ var infos = new CONTEXT_INFO[1];
+ if (codeContext.GetInfo(enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS, infos) != Constants.S_OK)
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 address = Convert.ToUInt64(infos[0].bstrAddress, 16);
+ return _engine.Jump(address);
}
- 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 b0db63117..bc7c33482 100755
--- a/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
+++ b/src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
@@ -1618,6 +1618,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 315a70396..16e35fa1a 100644
--- a/src/OpenDebugAD7/AD7DebugSession.cs
+++ b/src/OpenDebugAD7/AD7DebugSession.cs
@@ -27,7 +27,7 @@
namespace OpenDebugAD7
{
- internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEventCallback2
+ internal sealed class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEventCallback2
{
// This is a general purpose lock. Don't hold it across long operations.
private readonly object m_lock = new object();
@@ -42,6 +42,8 @@ internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEven
private readonly DebugEventLogger m_logger;
private readonly Dictionary> m_breakpoints;
+ private readonly List m_gotoCodeContexts = new List();
+
private Dictionary m_functionBreakpoints;
private readonly Dictionary m_threadFrameEnumInfos = new Dictionary();
private readonly HandleCollection m_frameHandles;
@@ -277,6 +279,7 @@ public void BeforeContinue()
m_variableManager.Reset();
m_frameHandles.Reset();
m_threadFrameEnumInfos.Clear();
+ m_gotoCodeContexts.Clear();
}
}
@@ -620,7 +623,8 @@ protected override void HandleInitializeRequestAsync(IRequestResponder new ExceptionBreakpointsFilter() { Default = item.@default, Filter = item.filter, Label = item.label }).ToList(),
SupportsClipboardContext = m_engineConfiguration.ClipboardContext,
SupportsLogPoints = true,
- SupportsReadMemoryRequest = true
+ SupportsReadMemoryRequest = true,
+ SupportsGotoTargetsRequest = true,
};
responder.SetResponse(initializeResponse);
@@ -1193,6 +1197,87 @@ protected override void HandlePauseRequestAsync(IRequestResponder responder)
+ {
+ var response = new GotoResponse();
+ if (!m_isStopped)
+ {
+ responder.SetResponse(response);
+ return;
+ }
+
+ var gotoTarget = m_gotoCodeContexts[responder.Arguments.TargetId];
+ IDebugThread2 thread = null;
+ lock (m_threads)
+ {
+ if (!m_threads.TryGetValue(responder.Arguments.ThreadId, out thread))
+ throw new AD7Exception("Could not find thread!");
+ }
+ BeforeContinue();
+ var builder = new ErrorBuilder(() => AD7Resources.Error_UnableToSetNextStatement);
+ try
+ {
+ builder.CheckHR(thread.SetNextStatement(null, gotoTarget));
+ }
+ catch (AD7Exception e)
+ {
+ m_isStopped = true;
+ responder.SetError(new ProtocolException(e.Message));
+ }
+
+ responder.SetResponse(response);
+ }
+
+ protected override void HandleGotoTargetsRequestAsync(IRequestResponder responder)
+ {
+ var response = new GotoTargetsResponse();
+
+ var source = responder.Arguments.Source;
+ // TODO: handle this for disassembly debugging
+ if (source.Path == null)
+ {
+ responder.SetResponse(response);
+ return;
+ }
+
+ try
+ {
+ string convertedPath = m_pathConverter.ConvertClientPathToDebugger(source.Path);
+ int line = m_pathConverter.ConvertClientLineToDebugger(responder.Arguments.Line);
+ var docPos = new AD7DocumentPosition(m_sessionConfig, convertedPath, line);
+
+ var targets = new List();
+
+ IEnumDebugCodeContexts2 codeContextsEnum;
+ if (m_program.EnumCodeContexts(docPos, out codeContextsEnum) == HRConstants.S_OK)
+ {
+ var codeContexts = new IDebugCodeContext2[1];
+ uint nProps = 0;
+ while (codeContextsEnum.Next(1, codeContexts, ref nProps) == HRConstants.S_OK)
+ {
+ var codeContext = codeContexts[0];
+ string contextName;
+ codeContext.GetName(out contextName);
+ m_gotoCodeContexts.Add(codeContext);
+ targets.Add(new GotoTarget(m_gotoCodeContexts.Count - 1, contextName, responder.Arguments.Line)); // TODO: get the real line
+ }
+ }
+
+ response.Targets = targets;
+ }
+ catch (Exception e)
+ {
+ e = Utilities.GetInnerMost(e);
+ if (Utilities.IsCorruptingException(e))
+ Utilities.ReportException(e);
+
+ responder.SetError(new ProtocolException(e.Message));
+ return;
+ }
+
+ responder.SetResponse(response);
+ }
protected override void HandleStackTraceRequestAsync(IRequestResponder responder)
{
diff --git a/src/OpenDebugAD7/AD7Resources.Designer.cs b/src/OpenDebugAD7/AD7Resources.Designer.cs
index c8a973fef..d2bb945d2 100644
--- a/src/OpenDebugAD7/AD7Resources.Designer.cs
+++ b/src/OpenDebugAD7/AD7Resources.Designer.cs
@@ -370,6 +370,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 b100bbd55..40621c371 100644
--- a/src/OpenDebugAD7/AD7Resources.resx
+++ b/src/OpenDebugAD7/AD7Resources.resx
@@ -192,6 +192,9 @@
Error setting breakpoint. {0}
+
+ Error setting next statement. {0}
+
Unable to parse 'logMessage'.