Skip to content

Commit

Permalink
implement Goto Request
Browse files Browse the repository at this point in the history
  • Loading branch information
Trass3r committed Sep 6, 2020
1 parent e1b8dda commit a4cdd47
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 34 deletions.
6 changes: 6 additions & 0 deletions src/MICore/CommandFactories/MICommandFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ public async Task ExecNextInstruction(int threadId, ResultClass resultClass = Re
await ThreadFrameCmdAsync(command, resultClass, threadId, 0);
}

/// <summary>
/// Jumps to a specified target location
/// </summary>
abstract public Task ExecJump(string filename, int line);
abstract public Task ExecJump(ulong address);

/// <summary>
/// Tells GDB to spawn a target process previous setup with -file-exec-and-symbols or similar
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/MICore/CommandFactories/clrdbg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 21 additions & 2 deletions src/MICore/CommandFactories/gdb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public override async Task<Results> ThreadInfo(uint? threadId = null)

public override async Task<List<ulong>> 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<ulong> addresses = new List<ulong>();
using (StringReader stringReader = new StringReader(result))
Expand All @@ -173,7 +173,7 @@ public override async Task<List<ulong>> 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);
}
Expand All @@ -183,6 +183,25 @@ public override async Task<List<ulong>> 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
Expand Down
13 changes: 13 additions & 0 deletions src/MICore/CommandFactories/lldb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
/// Assigns the value of an expression to a variable.
/// Since LLDB only accepts assigning values to variables, the expression may need to be evaluated.
Expand Down
38 changes: 38 additions & 0 deletions src/MIDebugEngine/AD7.Impl/AD7Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
27 changes: 12 additions & 15 deletions src/MIDebugEngine/AD7.Impl/AD7MemoryAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@
// 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
{
// And implementation of IDebugCodeContext2 and IDebugMemoryContext2.
// 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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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.
Expand Down
23 changes: 8 additions & 15 deletions src/MIDebugEngine/AD7.Impl/AD7Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
89 changes: 87 additions & 2 deletions src/OpenDebugAD7/AD7DebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -42,6 +42,8 @@ internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEven

private readonly DebugEventLogger m_logger;
private readonly Dictionary<string, Dictionary<int, IDebugPendingBreakpoint2>> m_breakpoints;
private readonly List<IDebugCodeContext2> m_gotoCodeContexts = new List<IDebugCodeContext2>();

private Dictionary<string, IDebugPendingBreakpoint2> m_functionBreakpoints;
private readonly Dictionary<int, ThreadFrameEnumInfo> m_threadFrameEnumInfos = new Dictionary<int, ThreadFrameEnumInfo>();
private readonly HandleCollection<IDebugStackFrame2> m_frameHandles;
Expand Down Expand Up @@ -277,6 +279,7 @@ public void BeforeContinue()
m_variableManager.Reset();
m_frameHandles.Reset();
m_threadFrameEnumInfos.Clear();
m_gotoCodeContexts.Clear();
}
}

Expand Down Expand Up @@ -620,7 +623,8 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
ExceptionBreakpointFilters = m_engineConfiguration.ExceptionSettings.ExceptionBreakpointFilters.Select(item => 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);
Expand Down Expand Up @@ -1193,6 +1197,87 @@ protected override void HandlePauseRequestAsync(IRequestResponder<PauseArguments
m_program.CauseBreak();
responder.SetResponse(new PauseResponse());
}

protected override void HandleGotoRequestAsync(IRequestResponder<GotoArguments> 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<GotoTargetsArguments, GotoTargetsResponse> 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<GotoTarget>();

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<StackTraceArguments, StackTraceResponse> responder)
{
Expand Down
Loading

0 comments on commit a4cdd47

Please sign in to comment.