Skip to content

Commit 5a6dcb8

Browse files
rcj1max-charlamb
andauthored
Adding TraverseModuleMap cDAC API (#118650)
* Adding TraverseModuleMap cDAC API Co-authored-by: Max Charlamb <[email protected]> --------- Co-authored-by: Max Charlamb <[email protected]>
1 parent 67d8ca6 commit 5a6dcb8

File tree

5 files changed

+153
-18
lines changed

5 files changed

+153
-18
lines changed

docs/design/datacontracts/Loader.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ TargetPointer GetILBase(ModuleHandle handle);
7575
TargetPointer GetAssemblyLoadContext(ModuleHandle handle);
7676
ModuleLookupTables GetLookupTables(ModuleHandle handle);
7777
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags);
78+
IEnumerable<(TargetPointer, uint)> EnumerateModuleLookupMap(TargetPointer table);
7879
bool IsCollectible(ModuleHandle handle);
7980
bool IsAssemblyLoaded(ModuleHandle handle);
8081
TargetPointer GetGlobalLoaderAllocator();
@@ -547,6 +548,33 @@ TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out Tar
547548
return TargetPointer.Null;
548549
}
549550

551+
IEnumerable<(TargetPointer, uint)> EnumerateModuleLookupMap(TargetPointer table)
552+
{
553+
Data.ModuleLookupMap lookupMap = new Data.ModuleLookupMap(table);
554+
// have to read lookupMap an extra time upfront because only the first map
555+
// has valid supportedFlagsMask
556+
TargetNUInt supportedFlagsMask = target.ReadNUInt(table + /* ModuleLookupMap::SupportedFlagsMask */);
557+
uint index = 1; // zero is invalid
558+
do
559+
{
560+
uint count = target.Read<uint>(table + /*ModuleLookupMap::Count*/);
561+
if (index < count)
562+
{
563+
TargetPointer entryAddress = target.ReadPointer(table + /*ModuleLookupMap::TableData*/) + (ulong)(index * target.PointerSize);
564+
TargetPointer rawValue = target.ReadPointer(entryAddress);
565+
ulong maskedValue = rawValue & ~(supportedFlagsMask.Value);
566+
if (maskedValue != 0)
567+
yield return (new TargetPointer(maskedValue), index);
568+
index++;
569+
}
570+
else
571+
{
572+
table = target.ReadPointer(table + /*ModuleLookupMap::Next*/);
573+
index -= count;
574+
}
575+
} while (table != TargetPointer.Null);
576+
}
577+
550578
bool IsCollectible(ModuleHandle handle)
551579
{
552580
TargetPointer assembly = target.ReadPointer(handle.Address + /*Module::Assembly*/);

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public interface ILoader : IContract
9898
TargetPointer GetAssemblyLoadContext(ModuleHandle handle) => throw new NotImplementedException();
9999
ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();
100100
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags) => throw new NotImplementedException();
101+
IEnumerable<(TargetPointer, uint)> EnumerateModuleLookupMap(TargetPointer table) => throw new NotImplementedException();
101102
bool IsCollectible(ModuleHandle handle) => throw new NotImplementedException();
102103
bool IsDynamic(ModuleHandle handle) => throw new NotImplementedException();
103104
bool IsAssemblyLoaded(ModuleHandle handle) => throw new NotImplementedException();

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -414,35 +414,64 @@ ModuleLookupTables ILoader.GetLookupTables(ModuleHandle handle)
414414
module.MethodDefToILCodeVersioningStateMap);
415415
}
416416

417-
TargetPointer ILoader.GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags)
417+
private static (bool Done, uint NextIndex) IterateLookupMap(uint index) => (false, index + 1);
418+
private static (bool Done, uint NextIndex) SearchLookupMap(uint index) => (true, index);
419+
private delegate (bool Done, uint NextIndex) Delegate(uint index);
420+
private IEnumerable<(TargetPointer, uint)> IterateModuleLookupMap(TargetPointer table, uint index, Delegate iterator)
418421
{
419-
uint rid = EcmaMetadataUtils.GetRowId(token);
420-
ArgumentOutOfRangeException.ThrowIfZero(rid);
421-
flags = new TargetNUInt(0);
422-
if (table == TargetPointer.Null)
423-
return TargetPointer.Null;
424-
uint index = rid;
425-
Data.ModuleLookupMap lookupMap = _target.ProcessedData.GetOrAdd<Data.ModuleLookupMap>(table);
426-
// have to read lookupMap an extra time upfront because only the first map
427-
// has valid supportedFlagsMask
428-
TargetNUInt supportedFlagsMask = lookupMap.SupportedFlagsMask;
422+
bool doneIterating;
429423
do
430424
{
431-
lookupMap = _target.ProcessedData.GetOrAdd<Data.ModuleLookupMap>(table);
425+
Data.ModuleLookupMap lookupMap = _target.ProcessedData.GetOrAdd<Data.ModuleLookupMap>(table);
432426
if (index < lookupMap.Count)
433427
{
434428
TargetPointer entryAddress = lookupMap.TableData + (ulong)(index * _target.PointerSize);
435429
TargetPointer rawValue = _target.ReadPointer(entryAddress);
436-
flags = new TargetNUInt(rawValue & supportedFlagsMask.Value);
437-
return rawValue & ~(supportedFlagsMask.Value);
430+
yield return (rawValue, index);
431+
(doneIterating, index) = iterator(index);
432+
if (doneIterating)
433+
yield break;
438434
}
439435
else
440436
{
441437
table = lookupMap.Next;
442438
index -= lookupMap.Count;
443439
}
444440
} while (table != TargetPointer.Null);
445-
return TargetPointer.Null;
441+
}
442+
443+
TargetPointer ILoader.GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags)
444+
{
445+
if (table == TargetPointer.Null)
446+
{
447+
flags = new TargetNUInt(0);
448+
return TargetPointer.Null;
449+
}
450+
451+
Data.ModuleLookupMap lookupMap = _target.ProcessedData.GetOrAdd<Data.ModuleLookupMap>(table);
452+
ulong supportedFlagsMask = lookupMap.SupportedFlagsMask.Value;
453+
454+
uint rid = EcmaMetadataUtils.GetRowId(token);
455+
ArgumentOutOfRangeException.ThrowIfZero(rid);
456+
(TargetPointer rval, uint _) = IterateModuleLookupMap(table, rid, SearchLookupMap).FirstOrDefault();
457+
flags = new TargetNUInt(rval & supportedFlagsMask);
458+
return rval & ~supportedFlagsMask;
459+
}
460+
461+
IEnumerable<(TargetPointer, uint)> ILoader.EnumerateModuleLookupMap(TargetPointer table)
462+
{
463+
if (table == TargetPointer.Null)
464+
yield break;
465+
Data.ModuleLookupMap lookupMap = _target.ProcessedData.GetOrAdd<Data.ModuleLookupMap>(table);
466+
ulong supportedFlagsMask = lookupMap.SupportedFlagsMask.Value;
467+
TargetNUInt flags = new TargetNUInt(0);
468+
uint index = 1; // zero is invalid
469+
foreach ((TargetPointer targetPointer, uint idx) in IterateModuleLookupMap(table, index, IterateLookupMap))
470+
{
471+
TargetPointer rval = targetPointer & ~supportedFlagsMask;
472+
if (rval != TargetPointer.Null)
473+
yield return (rval, idx);
474+
}
446475
}
447476

448477
bool ILoader.IsCollectible(ModuleHandle handle)

src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ internal struct DacpModuleData
138138
public ulong dwModuleIndex; // Always 0 - .NET no longer has this
139139
}
140140

141+
internal enum ModuleMapType
142+
{
143+
TYPEDEFTOMETHODTABLE = 0x0,
144+
TYPEREFTOMETHODTABLE = 0x1
145+
}
146+
141147
internal struct DacpMethodTableData
142148
{
143149
public int bIsFree; // everything else is NULL if this is true.
@@ -309,7 +315,7 @@ internal unsafe partial interface ISOSDacInterface
309315
[PreserveSig]
310316
int GetModuleData(ClrDataAddress moduleAddr, DacpModuleData* data);
311317
[PreserveSig]
312-
int TraverseModuleMap(/*ModuleMapType*/ int mmt, ClrDataAddress moduleAddr, /*MODULEMAPTRAVERSE*/ void* pCallback, void* token);
318+
int TraverseModuleMap(ModuleMapType mmt, ClrDataAddress moduleAddr, delegate* unmanaged[Stdcall]<uint, /*ClrDataAddress*/ ulong, void*, void> pCallback, void* token);
313319
[PreserveSig]
314320
int GetAssemblyModuleList(ClrDataAddress assembly, uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] modules, uint* pNeeded);
315321
[PreserveSig]

src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics;
77
using System.Linq;
88
using System.Runtime.InteropServices;
9+
using System.Runtime.CompilerServices;
910
using System.Runtime.InteropServices.Marshalling;
1011
using System.Text;
1112

@@ -2385,8 +2386,78 @@ int ISOSDacInterface.TraverseEHInfo(ClrDataAddress ip, void* pCallback, void* to
23852386
=> _legacyImpl is not null ? _legacyImpl.TraverseEHInfo(ip, pCallback, token) : HResults.E_NOTIMPL;
23862387
int ISOSDacInterface.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, void* pCallback)
23872388
=> _legacyImpl is not null ? _legacyImpl.TraverseLoaderHeap(loaderHeapAddr, pCallback) : HResults.E_NOTIMPL;
2388-
int ISOSDacInterface.TraverseModuleMap(int mmt, ClrDataAddress moduleAddr, void* pCallback, void* token)
2389-
=> _legacyImpl is not null ? _legacyImpl.TraverseModuleMap(mmt, moduleAddr, pCallback, token) : HResults.E_NOTIMPL;
2389+
2390+
#if DEBUG
2391+
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
2392+
private static void TraverseModuleMapCallback(uint index, ulong moduleAddr, void* expectedElements)
2393+
{
2394+
var expectedElementsDict = (Dictionary<ulong, uint>)GCHandle.FromIntPtr((nint)expectedElements).Target!;
2395+
if (expectedElementsDict.TryGetValue(moduleAddr, out uint expectedIndex) && expectedIndex == index)
2396+
{
2397+
expectedElementsDict[default]++; // Increment the count for verification
2398+
}
2399+
else
2400+
{
2401+
Debug.Assert(false, $"Unexpected module address {moduleAddr:x} at index {index}");
2402+
}
2403+
}
2404+
#endif
2405+
int ISOSDacInterface.TraverseModuleMap(ModuleMapType mmt, ClrDataAddress moduleAddr, delegate* unmanaged[Stdcall]<uint, ulong, void*, void> pCallback, void* token)
2406+
{
2407+
int hr = HResults.S_OK;
2408+
IEnumerable<(TargetPointer Address, uint Index)> elements = Enumerable.Empty<(TargetPointer, uint)>();
2409+
if (moduleAddr == 0)
2410+
hr = HResults.E_INVALIDARG;
2411+
else
2412+
{
2413+
try
2414+
{
2415+
Contracts.ILoader loader = _target.Contracts.Loader;
2416+
TargetPointer moduleAddrPtr = moduleAddr.ToTargetPointer(_target);
2417+
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(moduleAddrPtr);
2418+
Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
2419+
switch (mmt)
2420+
{
2421+
case ModuleMapType.TYPEDEFTOMETHODTABLE:
2422+
elements = loader.EnumerateModuleLookupMap(lookupTables.TypeDefToMethodTable);
2423+
break;
2424+
case ModuleMapType.TYPEREFTOMETHODTABLE:
2425+
elements = loader.EnumerateModuleLookupMap(lookupTables.TypeRefToMethodTable);
2426+
break;
2427+
default:
2428+
hr = HResults.E_INVALIDARG;
2429+
break;
2430+
}
2431+
if (hr == HResults.S_OK)
2432+
{
2433+
foreach ((TargetPointer element, uint index) in elements)
2434+
{
2435+
// Call the callback with each element
2436+
pCallback(index, element.ToClrDataAddress(_target).Value, token);
2437+
}
2438+
}
2439+
}
2440+
catch (System.Exception ex)
2441+
{
2442+
hr = ex.HResult;
2443+
}
2444+
}
2445+
#if DEBUG
2446+
if (_legacyImpl is not null)
2447+
{
2448+
Dictionary<ulong, uint> expectedElements = elements.ToDictionary(tuple => tuple.Address.ToClrDataAddress(_target).Value, tuple => tuple.Index);
2449+
expectedElements.Add(default, 0);
2450+
void* tokenDebug = GCHandle.ToIntPtr(GCHandle.Alloc(expectedElements)).ToPointer();
2451+
delegate* unmanaged[Stdcall]<uint, ulong, void*, void> callbackDebugPtr = &TraverseModuleMapCallback;
2452+
2453+
int hrLocal = _legacyImpl.TraverseModuleMap(mmt, moduleAddr, callbackDebugPtr, tokenDebug);
2454+
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
2455+
Debug.Assert(expectedElements[default] == elements.Count(), $"cDAC: {elements.Count()} elements, DAC: {expectedElements[default]} elements");
2456+
GCHandle.FromIntPtr((nint)tokenDebug).Free();
2457+
}
2458+
#endif
2459+
return hr;
2460+
}
23902461
int ISOSDacInterface.TraverseRCWCleanupList(ClrDataAddress cleanupListPtr, void* pCallback, void* token)
23912462
=> _legacyImpl is not null ? _legacyImpl.TraverseRCWCleanupList(cleanupListPtr, pCallback, token) : HResults.E_NOTIMPL;
23922463
int ISOSDacInterface.TraverseVirtCallStubHeap(ClrDataAddress pAppDomain, int heaptype, void* pCallback)

0 commit comments

Comments
 (0)