Skip to content

Commit

Permalink
[GDB] Support Modules window for OpenDebugAD7 scenarios (#1054)
Browse files Browse the repository at this point in the history
Adds support for MIF_TIMESTAMP in IDebugModule2.GetInfo.
Handles SupportsModulesRequest for DAP.
Thank you @Trass3r for the initial work!
Sends ModuleEvents when an IDebugModuleLoadEvent2 is received.
  • Loading branch information
gc46 authored Oct 19, 2020
1 parent cc361a8 commit 378be00
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 10 deletions.
29 changes: 22 additions & 7 deletions src/MIDebugEngine/AD7.Impl/AD7Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// 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 System.Diagnostics;
using System.Threading;
using MICore;
using System.Globalization;
using System.Threading.Tasks;
using System.IO;
using MICore;
using Microsoft.VisualStudio.Debugger.Interop;
using Microsoft.VisualStudio.OLE.Interop;

namespace Microsoft.MIDebugEngine
{
Expand Down Expand Up @@ -71,6 +69,23 @@ int IDebugModule2.GetInfo(enum_MODULE_INFO_FIELDS dwFields, MODULE_INFO[] infoAr
info.m_dwLoadOrder = this.DebuggedModule.GetLoadOrder();
info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_LOADORDER;
}
if (this.Process.LaunchOptions is LocalLaunchOptions localLaunchOptions &&
string.IsNullOrWhiteSpace(localLaunchOptions.MIDebuggerServerAddress) && string.IsNullOrWhiteSpace(localLaunchOptions.DebugServer) &&
(dwFields & enum_MODULE_INFO_FIELDS.MIF_TIMESTAMP) != 0 &&
this.DebuggedModule.Name != null)
{
try
{
long ft = File.GetLastWriteTimeUtc(this.DebuggedModule.Name).ToFileTime();
uint low = (uint) (ft & 0xFFFFFFFF);
uint high = (uint) ((ulong) (ft >> 32));
info.m_TimeStamp = new FILETIME(){
dwLowDateTime = low,
dwHighDateTime = high
};
info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_TIMESTAMP;
} catch {}
}
if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_URLSYMBOLLOCATION) != 0)
{
if (this.DebuggedModule.SymbolsLoaded)
Expand Down Expand Up @@ -167,7 +182,7 @@ int IDebugModule3.GetSymbolInfo(enum_SYMBOL_SEARCH_INFO_FIELDS dwFields, MODULE_
int IDebugModule3.IsUserCode(out int pfUser)
{
pfUser = 1;
return Constants.S_OK;
return Constants.E_NOTIMPL;
}

// Loads and initializes symbols for the current module when the user explicitly asks for them to load.
Expand Down
3 changes: 3 additions & 0 deletions src/MIDebugEngine/MIDebugEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.OLE.Interop">
<HintPath>$(NuGetPackagesDirectory)/Microsoft.VisualStudio.OLE.Interop.16.7.30328.74/lib/net45/Microsoft.VisualStudio.OLE.Interop.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.VisualStudio.Debugger.InteropA">
Expand Down
188 changes: 185 additions & 3 deletions src/OpenDebugAD7/AD7DebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DebugEngineHost;
using Microsoft.DebugEngineHost.VSCode;
using Microsoft.VisualStudio.Debugger.Interop;
using Microsoft.VisualStudio.Debugger.Interop.DAP;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol;
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Utilities;
Expand Down Expand Up @@ -67,6 +67,35 @@ internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEven

private static Guid s_guidFilterAllLocalsPlusArgs = new Guid("939729a8-4cb0-4647-9831-7ff465240d5f");

private int m_nextModuleHandle = 1;
private readonly Dictionary<IDebugModule2, int> m_moduleMap = new Dictionary<IDebugModule2, int>();

private object RegisterDebugModule(IDebugModule2 debugModule)
{
Debug.Assert(!m_moduleMap.ContainsKey(debugModule));
lock (m_moduleMap)
{
int moduleHandle = m_nextModuleHandle;
m_moduleMap[debugModule] = moduleHandle;
m_nextModuleHandle++;
return moduleHandle;
}
}
private int? ReleaseDebugModule(IDebugModule2 debugModule)
{
lock (m_moduleMap)
{
if (m_moduleMap.TryGetValue(debugModule, out int moduleId))
{
m_moduleMap.Remove(debugModule);
return moduleId;
} else {
Debug.Fail("Trying to unload a module that has not been registered.");
return null;
}
}
}

#region Constructor

public AD7DebugSession(Stream debugAdapterStdIn, Stream debugAdapterStdOut, List<LoggingCategory> loggingCategories)
Expand Down Expand Up @@ -264,6 +293,17 @@ ppBPRequest is AD7BreakPointRequest ad7BreakpointRequest &&
return tracepoints;
}

private static long FileTimeToPosix(FILETIME ft)
{
long date = ((long)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
// removes the diff between 1970 and 1601
// 100-nanoseconds = milliseconds * 10000
date -= 11644473600000L * 10000;

// converts back from 100-nanoseconds to seconds
return date / 10000000;
}

#endregion

#region AD7EventHandlers helper methods
Expand Down Expand Up @@ -610,6 +650,49 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
});
}

List<ColumnDescriptor> additionalModuleColumns = null;
string clientId = responder.Arguments.ClientID;
if (clientId == "visualstudio" || clientId == "liveshare-server-host")
{
additionalModuleColumns = new List<ColumnDescriptor>();
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsLoadAddress",
Label = "Load Address",
Format = "string",
Type = ColumnDescriptor.TypeValue.String
});
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsPreferredLoadAddress",
Label = "Preferred Load Address",
Format = "string",
Type = ColumnDescriptor.TypeValue.String
});
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsModuleSize",
Label = "Module Size",
Format = "string",
Type = ColumnDescriptor.TypeValue.Number
});
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsLoadOrder",
Label = "Order",
Format = "string",
Type = ColumnDescriptor.TypeValue.Number
});
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsTimestampUTC",
Label = "Timestamp",
Format = "string",
Type = ColumnDescriptor.TypeValue.UnixTimestampUTC
});
additionalModuleColumns.Add(new ColumnDescriptor(){
AttributeName = "vsIs64Bit",
Label = "64-bit",
Format = "string",
Type = ColumnDescriptor.TypeValue.Boolean
});
}

InitializeResponse initializeResponse = new InitializeResponse()
{
SupportsConfigurationDoneRequest = true,
Expand All @@ -620,7 +703,9 @@ 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,
SupportsModulesRequest = true,
AdditionalModuleColumns = additionalModuleColumns
};

responder.SetResponse(initializeResponse);
Expand Down Expand Up @@ -1546,6 +1631,82 @@ protected override void HandleThreadsRequestAsync(IRequestResponder<ThreadsArgum
responder.SetResponse(response);
}

private ProtocolMessages.Module ConvertToModule(in IDebugModule2 module, int moduleId)
{
var debugModuleInfos = new MODULE_INFO[1];
if (module.GetInfo(enum_MODULE_INFO_FIELDS.MIF_ALLFIELDS, debugModuleInfos) == HRConstants.S_OK)
{
var debugModuleInfo = debugModuleInfos[0];

var path = debugModuleInfo.m_bstrUrl;
var vsTimestampUTC = (debugModuleInfo.dwValidFields & enum_MODULE_INFO_FIELDS.MIF_TIMESTAMP) != 0 ? FileTimeToPosix(debugModuleInfo.m_TimeStamp).ToString(CultureInfo.InvariantCulture) : null;
var version = debugModuleInfo.m_bstrVersion;
var vsLoadAddress = debugModuleInfo.m_addrLoadAddress.ToString(CultureInfo.InvariantCulture);
var vsPreferredLoadAddress = debugModuleInfo.m_addrPreferredLoadAddress.ToString(CultureInfo.InvariantCulture);
var vsModuleSize = (int)debugModuleInfo.m_dwSize;
var vsLoadOrder = (int)debugModuleInfo.m_dwLoadOrder;
var symbolFilePath = debugModuleInfo.m_bstrUrlSymbolLocation;
var symbolStatus = debugModuleInfo.m_bstrDebugMessage;
var vsIs64Bit = (debugModuleInfo.m_dwModuleFlags & enum_MODULE_FLAGS.MODULE_FLAG_64BIT) != 0;

ProtocolMessages.Module mod = new ProtocolMessages.Module(moduleId, debugModuleInfo.m_bstrName)
{
Path = path, VsTimestampUTC = vsTimestampUTC, Version = version, VsLoadAddress = vsLoadAddress, VsPreferredLoadAddress = vsPreferredLoadAddress,
VsModuleSize = vsModuleSize, VsLoadOrder = vsLoadOrder, SymbolFilePath = symbolFilePath, SymbolStatus = symbolStatus, VsIs64Bit = vsIs64Bit
};

// IsOptimized and IsUserCode are not set by gdb
if((debugModuleInfo.m_dwModuleFlags & enum_MODULE_FLAGS.MODULE_FLAG_OPTIMIZED) != 0)
{
mod.IsOptimized = true;
} else if ((debugModuleInfo.m_dwModuleFlags & enum_MODULE_FLAGS.MODULE_FLAG_UNOPTIMIZED) != 0)
{
mod.IsOptimized = false;
}
if (module is IDebugModule3 module3 && module3.IsUserCode(out int isUserCode) == HRConstants.S_OK)
{
if (isUserCode == 0)
{
mod.IsUserCode = false;
}
else
{
mod.IsUserCode = true;
}
}

return mod;
}
return null;
}

protected override void HandleModulesRequestAsync(IRequestResponder<ModulesArguments, ModulesResponse> responder)
{
var response = new ModulesResponse();
IEnumDebugModules2 enumDebugModules;
if (m_program.EnumModules(out enumDebugModules) == HRConstants.S_OK)
{
var debugModules = new IDebugModule2[1];
uint numReturned = 0;
while (enumDebugModules.Next(1, debugModules, ref numReturned) == HRConstants.S_OK && numReturned == 1)
{
IDebugModule2 module = debugModules[0];
int moduleId;
lock (m_moduleMap)
{
if (!m_moduleMap.TryGetValue(module, out moduleId))
{
Debug.Fail("Missing ModuleLoadEvent?");
continue;
}
}
var mod = ConvertToModule(module, moduleId);
response.Modules.Add(mod);
}
}
responder.SetResponse(response);
}

protected override void HandleSetBreakpointsRequestAsync(IRequestResponder<SetBreakpointsArguments, SetBreakpointsResponse> responder)
{
SetBreakpointsResponse response = new SetBreakpointsResponse();
Expand Down Expand Up @@ -2359,8 +2520,29 @@ public void HandleIDebugModuleLoadEvent2(IDebugEngine2 pEngine, IDebugProcess2 p
string moduleLoadMessage = null;
int isLoad = 0;
((IDebugModuleLoadEvent2)pEvent).GetModule(out module, ref moduleLoadMessage, ref isLoad);

m_logger.WriteLine(LoggingCategory.Module, moduleLoadMessage);

int? moduleId = null;
ModuleEvent.ReasonValue reason = ModuleEvent.ReasonValue.Unknown;

if (isLoad != 0)
{
moduleId = (int?)RegisterDebugModule(module);
reason = ModuleEvent.ReasonValue.New;
} else {
moduleId = ReleaseDebugModule(module);
reason = ModuleEvent.ReasonValue.Removed;
}

if (moduleId != null)
{
var mod = ConvertToModule(module, (int)moduleId);
if (mod != null)
{
Protocol.SendEvent(new ModuleEvent(reason, mod));
}
}
}

public void HandleIDebugBreakpointBoundEvent2(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent)
Expand Down
5 changes: 5 additions & 0 deletions src/OpenDebugAD7/OpenDebugAD7.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualStudio.OLE.Interop">
<HintPath>$(NuGetPackagesDirectory)/Microsoft.VisualStudio.OLE.Interop.16.7.30328.74/lib/net45/Microsoft.VisualStudio.OLE.Interop.dll</HintPath>
<ForceIncludeInVSIX>true</ForceIncludeInVSIX>
</Reference>
<Reference Include="Microsoft.VisualStudio.Debugger.InteropA">
<HintPath>$(NuGetPackagesDirectory)/Microsoft.VisualStudio.Debugger.Interop.Portable.1.0.1/lib/portable-net45+net46+dnxcore50/Microsoft.VisualStudio.Debugger.InteropA.dll</HintPath>
<ForceIncludeInVSIX>true</ForceIncludeInVSIX>
Expand Down Expand Up @@ -174,6 +178,7 @@
<DropUnsignedFile Condition="'$(IsXPlat)' == 'true'" Include="$(OutputPath)\OpenDebugAD7.exe.mdb" />
</ItemGroup>
<ItemGroup Condition="'$(IsXPlat)' == 'false'">
<DropUnsignedFile Include="$(OutputPath)\Microsoft.VisualStudio.OLE.Interop.dll" />
<DropUnsignedFile Include="$(OutputPath)\Microsoft.VisualStudio.Debugger.InteropA.dll" />
<DropUnsignedFile Include="$(OutputPath)\Microsoft.VisualStudio.Debugger.Interop.10.0.dll" />
<DropUnsignedFile Include="$(OutputPath)\Microsoft.VisualStudio.Debugger.Interop.11.0.dll" />
Expand Down
1 change: 1 addition & 0 deletions src/OpenDebugAD7/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

<!-- Update the version number VsCodeDebugProtocol HintPath in OpenDebugAD7\OpenDebugAD7.csproj -->
<package id="Microsoft.VisualStudio.Shared.VsCodeDebugProtocol" version="16.7.40526.2" />
<package id="Microsoft.VisualStudio.OLE.Interop" version="16.7.30328.74" />
</packages>

0 comments on commit 378be00

Please sign in to comment.