<Uri>https://github.com/dotnet/symstore</Uri>
<Sha>387e5a4d679b30fab748ddca70d088002f0034a8</Sha>
</Dependency>
- <Dependency Name="Microsoft.Diagnostics.Runtime" Version="2.3.411802">
+ <Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23153.5">
<Uri>https://github.com/microsoft/clrmd</Uri>
- <Sha>be3a3abce108bdb335e76fddb40d202568410343</Sha>
+ <Sha>ca77c991f8e4c1178152ec92000565c1b2b0f4dc</Sha>
</Dependency>
- <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="2.3.411802">
+ <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23153.5">
<Uri>https://github.com/microsoft/clrmd</Uri>
- <Sha>be3a3abce108bdb335e76fddb40d202568410343</Sha>
+ <Sha>ca77c991f8e4c1178152ec92000565c1b2b0f4dc</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<SystemReflectionMetadataVersion>5.0.0</SystemReflectionMetadataVersion>
<!-- Other libs -->
<MicrosoftBclAsyncInterfacesVersion>1.1.0</MicrosoftBclAsyncInterfacesVersion>
- <MicrosoftDiagnosticsRuntimeVersion>2.3.411802</MicrosoftDiagnosticsRuntimeVersion>
+ <MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23153.5</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiaSymReaderNativePackageVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftDiagnosticsTracingTraceEventVersion>3.0.7</MicrosoftDiagnosticsTracingTraceEventVersion>
<!-- Use pinned version to avoid picking up latest (which doesn't support netcoreapp3.1) during source-build -->
-namespace Microsoft.Diagnostics.DebugServices
+using System;
+
+namespace Microsoft.Diagnostics.DebugServices
{
public enum MemoryRegionType
{
MEM_RESERVE = 0x2000
}
+ [Flags]
public enum MemoryRegionProtection
{
PAGE_UNKNOWN = 0,
if (!TryGetSegmentMemoryRange(segment, generation, out var start, out var end))
continue;
- var currentObjectAddress = start;
- ClrObject currentObject;
- do
- {
- currentObject = _heap.GetObject(currentObjectAddress);
- if (currentObject.Type != null)
- yield return currentObject;
-
- currentObjectAddress = segment.GetNextObjectAddress(currentObject);
- } while (currentObjectAddress > 0 && currentObjectAddress < end);
+ foreach (ClrObject obj in _heap.EnumerateObjects(new MemoryRange(start, end)))
+ if (obj.IsValid)
+ yield return obj;
}
}
end = segment.Generation1.End;
return start != end;
case GCGeneration.Generation2:
- if (!(segment.IsLargeObjectSegment || segment.IsPinnedObjectSegment))
+ if (segment.Kind != GCSegmentKind.Large && segment.Kind != GCSegmentKind.Large && segment.Kind != GCSegmentKind.Frozen)
{
start = segment.Generation2.Start;
end = segment.Generation2.End;
}
return start != end;
case GCGeneration.LargeObjectHeap:
- if (segment.IsLargeObjectSegment)
+ if (segment.Kind == GCSegmentKind.Large)
{
start = segment.Start;
end = segment.End;
}
return start != end;
case GCGeneration.PinnedObjectHeap:
- if (segment.IsPinnedObjectSegment)
+ if (segment.Kind == GCSegmentKind.Pinned || segment.Kind == GCSegmentKind.Frozen)
{
start = segment.Start;
end = segment.End;
+++ /dev/null
-using Microsoft.Diagnostics.Runtime;
-using Microsoft.Diagnostics.Runtime.DacInterface;
-using System;
-using System.Collections.Generic;
-
-namespace Microsoft.Diagnostics.ExtensionCommands
-{
- internal sealed class ClrMemoryPointer
- {
- public ulong Address { get; }
-
- /// <summary>
- /// Size may be 0 if we do not know the size of a segment.
- /// </summary>
- public ulong Size { get; }
-
- public ClrMemoryKind Kind { get; }
-
- public ClrMemoryPointer(ulong address, ClrMemoryKind kind)
- {
- Address = address;
- Kind = kind;
- }
-
- public ClrMemoryPointer(ulong address, ulong length, ClrMemoryKind kind)
- {
- Address = address;
- Size = length;
- Kind = kind;
- }
-
- /// <summary>
- /// Enumerates pointers to various CLR heaps in memory.
- /// </summary>
- public static IEnumerable<ClrMemoryPointer> EnumerateClrMemoryAddresses(ClrRuntime runtime)
- {
- SOSDac sos = runtime.DacLibrary.SOSDacInterface;
- foreach (JitManagerInfo jitMgr in sos.GetJitManagers())
- {
- foreach (var handle in runtime.EnumerateHandles())
- yield return new ClrMemoryPointer(handle.Address, ClrMemoryKind.HandleTable);
-
- List<ClrMemoryPointer> heaps = new();
- foreach (var mem in sos.GetCodeHeapList(jitMgr.Address))
- {
- if (mem.Type == CodeHeapType.Loader)
- {
- sos.TraverseLoaderHeap(mem.Address, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.CodeHeap)));
- }
- else
- {
- yield return new ClrMemoryPointer(mem.Address, mem.Type switch
- {
- CodeHeapType.Loader => ClrMemoryKind.LoaderHeap,
- CodeHeapType.Host => ClrMemoryKind.Host,
- _ => ClrMemoryKind.UnknownCodeHeap
- });
- }
- }
-
- foreach (ClrMemoryPointer ptr in heaps)
- yield return ptr;
-
- heaps.Clear();
-
- foreach (var seg in runtime.Heap.Segments)
- {
- if (seg.CommittedMemory.Length > 0)
- yield return new ClrMemoryPointer(seg.CommittedMemory.Start, seg.CommittedMemory.Length, ClrMemoryKind.GCHeapSegment);
-
- if (seg.ReservedMemory.Length > 0)
- yield return new ClrMemoryPointer(seg.ReservedMemory.Start, seg.ReservedMemory.Length, ClrMemoryKind.GCHeapReserve);
- }
-
- HashSet<ulong> seen = new();
-
- if (runtime.SystemDomain is not null)
- AddAppDomainHeaps(runtime, sos, runtime.SystemDomain.Address, heaps);
-
- if (runtime.SharedDomain is not null)
- AddAppDomainHeaps(runtime, sos, runtime.SharedDomain.Address, heaps);
-
- foreach (var heap in heaps)
- if (seen.Add(heap.Address))
- yield return heap;
-
- foreach (ClrDataAddress address in sos.GetAppDomainList())
- {
- heaps.Clear();
- AddAppDomainHeaps(runtime, sos, address, heaps);
-
- foreach (var heap in heaps)
- if (seen.Add(heap.Address))
- yield return heap;
- }
- }
- }
-
- private enum VCSHeapType
- {
- IndcellHeap,
- LookupHeap,
- ResolveHeap,
- DispatchHeap,
- CacheEntryHeap
- }
-
- private static void AddAppDomainHeaps(ClrRuntime runtime, SOSDac sos, ClrDataAddress address, List<ClrMemoryPointer> heaps)
- {
- if (sos.GetAppDomainData(address, out AppDomainData domain))
- {
- sos.TraverseLoaderHeap(AdjustAddress(runtime, domain.StubHeap), (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.StubHeap)));
- sos.TraverseLoaderHeap(AdjustAddress(runtime, domain.HighFrequencyHeap), (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.HighFrequencyHeap)));
- sos.TraverseLoaderHeap(AdjustAddress(runtime, domain.LowFrequencyHeap), (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.LowFrequencyHeap)));
- sos.TraverseStubHeap(address, (int)VCSHeapType.IndcellHeap, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.IndcellHeap)));
- sos.TraverseStubHeap(address, (int)VCSHeapType.LookupHeap, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.LookupHeap)));
- sos.TraverseStubHeap(address, (int)VCSHeapType.ResolveHeap, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.ResolveHeap)));
- sos.TraverseStubHeap(address, (int)VCSHeapType.DispatchHeap, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.DispatchHeap)));
- sos.TraverseStubHeap(address, (int)VCSHeapType.CacheEntryHeap, (address, size, isCurrent) => heaps.Add(new ClrMemoryPointer(address, GetSize(size), ClrMemoryKind.CacheEntryHeap)));
- }
- }
-
- private static ulong AdjustAddress(ClrRuntime runtime, ulong address)
- {
- // .Net 7 has an issue where it changed the kind of LoaderHeap it expects in TraverseLoaderHeap.
- // On this runtime, we will shift the pointer forward to skip the vtable, as the type of heap
- // the dac expects to walk has the same layout of LoaderHeap, except for the vtable.
- if (runtime.ClrInfo.Flavor == ClrFlavor.Core && runtime.ClrInfo.Version.Major == 7)
- return address + (uint)runtime.DataTarget.DataReader.PointerSize;
-
- return address;
- }
-
- private static ulong GetSize(nint size)
- {
- // Some sanity checks on size in case we get bad data in the future.
- if (size <= 0 || size > int.MaxValue)
- return 0;
-
- return (ulong)size;
- }
- }
-
- internal enum ClrMemoryKind
- {
- None,
- LoaderHeap,
- Host,
- UnknownCodeHeap,
- GCHeapSegment,
- GCHeapReserve,
- StubHeap,
- HighFrequencyHeap,
- LowFrequencyHeap,
- IndcellHeap,
- LookupHeap,
- ResolveHeap,
- DispatchHeap,
- CacheEntryHeap,
- HandleTable,
- CodeHeap,
- }
-}
--- /dev/null
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime;
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+ [Command(Name = "eeheap", Help = "Displays information about native memory that CLR has allocated.")]
+ public class EEHeapCommand : CommandBase
+ {
+ [ServiceImport]
+ public IRuntimeService RuntimeService { get; set; }
+
+ [ServiceImport]
+ public IMemoryService MemoryService { get; set; }
+
+ [Option(Name = "--gc", Aliases = new string[] { "-gc" }, Help = "Only display the GC.")]
+ public bool ShowGC { get; set; }
+
+ [Option(Name = "--loader", Aliases = new string[] { "-loader" }, Help = "Only display the Loader.")]
+ public bool ShowLoader { get; set; }
+
+ public override void Invoke()
+ {
+ IRuntime[] runtimes = RuntimeService.EnumerateRuntimes().ToArray();
+
+ ulong totalBytes = 0;
+ StringBuilder stringBuilder = null;
+ foreach (IRuntime iRuntime in runtimes)
+ {
+ if (runtimes.Length > 1)
+ WriteDivider($"{iRuntime.RuntimeType} {iRuntime.RuntimeModule?.GetVersionData()}");
+
+ ClrRuntime clrRuntime = iRuntime.Services.GetService<ClrRuntime>();
+ totalBytes += PrintOneRuntime(ref stringBuilder, clrRuntime);
+ }
+
+ // Only print the total bytes if we walked everything.
+ if (runtimes.Length > 1 && !ShowGC && !ShowLoader)
+ WriteLine($"Total bytes consumed by all CLRs: {FormatMemorySize(totalBytes, "0")}");
+ }
+
+ private ulong PrintOneRuntime(ref StringBuilder stringBuilder, ClrRuntime clrRuntime)
+ {
+ TableOutput output = new(Console, (21, "x12"), (0, "x12"))
+ {
+ AlignLeft = true
+ };
+
+ HashSet<ulong> seen = new();
+
+ ulong totalSize = 0;
+
+ if (ShowLoader || !ShowGC)
+ {
+ totalSize += PrintAppDomains(output, clrRuntime, seen);
+ totalSize += PrintCodeHeaps(output, clrRuntime);
+ totalSize += PrintModuleThunkTable(output, ref stringBuilder, clrRuntime);
+ totalSize += PrintModuleLoaderAllocators(output, ref stringBuilder, clrRuntime, seen);
+ }
+
+ if (ShowGC || !ShowLoader)
+ totalSize += PrintGCHeap(clrRuntime);
+
+ // Only print the total bytes if we walked everything.
+ if (!ShowGC && !ShowLoader)
+ {
+ WriteLine();
+ WriteLine($"Total bytes consumed by CLR: {FormatMemorySize(totalSize, "0")}");
+ WriteLine();
+ }
+
+ return totalSize;
+ }
+
+ private ulong PrintAppDomains(TableOutput output, ClrRuntime clrRuntime, HashSet<ulong> loaderAllocatorsSeen)
+ {
+ Console.WriteLine("Loader Heap:");
+ WriteDivider();
+
+ ulong totalBytes = 0;
+
+ totalBytes += PrintAppDomain(output, clrRuntime.SystemDomain, "System Domain:", loaderAllocatorsSeen);
+ totalBytes += PrintAppDomain(output, clrRuntime.SharedDomain, "Shared Domain:", loaderAllocatorsSeen);
+
+ for (int i = 0; i < clrRuntime.AppDomains.Length; i++)
+ {
+ ClrAppDomain appDomain = clrRuntime.AppDomains[i];
+ totalBytes += PrintAppDomain(output, appDomain, $"Domain {i + 1}:", loaderAllocatorsSeen);
+ }
+
+ return totalBytes;
+ }
+
+ private ulong PrintAppDomain(TableOutput output, ClrAppDomain appDomain, string name, HashSet<ulong> loaderAllocatorsSeen)
+ {
+ if (appDomain is null)
+ return 0;
+
+ output.WriteRow(name, appDomain.Address);
+
+ // Starting on .Net 8 and beyond, we now have the LoaderAllocator for each domain. If we've previously
+ // seen this LoaderAllocator, we won't print it again. We also need to keep track of all LoaderAllocators
+ // we've seen so that we know when a ClrModule has a new/unique one. This does change the output of
+ // !eeheap, as we will no longer print duplicate heaps for new AppDomains.
+
+ if (appDomain.LoaderAllocator != 0)
+ {
+ output.WriteRow("LoaderAllocator:", appDomain.LoaderAllocator);
+
+ loaderAllocatorsSeen ??= new();
+ if (!loaderAllocatorsSeen.Add(appDomain.LoaderAllocator))
+ return 0;
+ }
+
+ var heapsByKind = from heap in appDomain.EnumerateLoaderAllocatorHeaps()
+ where loaderAllocatorsSeen.Add(heap.Address)
+ group heap by heap.Kind into g
+ orderby GetSortOrder(g.Key)
+ select g;
+
+ return PrintAppDomainHeapsByKind(output, heapsByKind);
+ }
+
+ private int GetSortOrder(NativeHeapKind key)
+ {
+ // Order heaps in a similar order to the old !eeheap
+ return key switch
+ {
+ NativeHeapKind.LowFrequencyHeap => 0,
+ NativeHeapKind.HighFrequencyHeap => 1,
+ NativeHeapKind.StubHeap => 2,
+ NativeHeapKind.ExecutableHeap => 3,
+ NativeHeapKind.FixupPrecodeHeap => 4,
+ NativeHeapKind.NewStubPrecodeHeap => 5,
+
+ NativeHeapKind.IndirectionCellHeap => 6,
+ NativeHeapKind.LookupHeap => 7,
+ NativeHeapKind.ResolveHeap => 8,
+ NativeHeapKind.DispatchHeap => 9,
+ NativeHeapKind.CacheEntryHeap => 10,
+ NativeHeapKind.VtableHeap => 11,
+
+ _ => 100 + (int)key
+ };
+ }
+
+ private ulong PrintAppDomainHeapsByKind(TableOutput output, IOrderedEnumerable<IGrouping<NativeHeapKind, ClrNativeHeapInfo>> heapsByKind)
+ {
+ // Just build and print the table.
+ ulong totalSize = 0;
+ ulong totalWasted = 0;
+ StringBuilder text = new(512);
+
+ foreach (var item in heapsByKind)
+ {
+ text.Clear();
+ NativeHeapKind kind = item.Key;
+ ulong heapSize = 0;
+ ulong heapWasted = 0;
+
+ foreach (ClrNativeHeapInfo heap in item)
+ {
+ if (text.Length > 0)
+ text.Append(" ");
+
+ (ulong size, ulong wasted) = CalculateSizeAndWasted(text, heap);
+
+ heapSize += size;
+ heapWasted += wasted;
+ }
+
+ text.Append(' ');
+ WriteSizeAndWasted(text, heapSize, heapWasted);
+ text.Append('.');
+
+ output.WriteRow($"{kind}:", text);
+
+ totalSize += heapSize;
+ totalWasted += heapWasted;
+ }
+
+ text.Clear();
+
+ if (totalSize > 0)
+ {
+ WriteSizeAndWasted(text, totalSize, totalWasted);
+ text.Append('.');
+ output.WriteRow("Total size:", text);
+ }
+ else
+ {
+ Console.WriteLine("No unique loader heaps found.");
+ }
+
+ WriteDivider();
+ return totalSize;
+ }
+
+ private ulong PrintCodeHeaps(TableOutput output, ClrRuntime clrRuntime)
+ {
+ ulong totalSize = 0;
+
+ StringBuilder text = new(512);
+ foreach (ClrJitManager jitManager in clrRuntime.EnumerateJitManagers())
+ {
+ output.WriteRow("JIT Manager:", jitManager.Address);
+
+ IEnumerable<ClrNativeHeapInfo> heaps = jitManager.EnumerateNativeHeaps().OrderBy(r => r.Kind).ThenBy(r => r.Address);
+
+ ulong jitMgrSize = 0, jitMgrWasted = 0;
+ foreach (ClrNativeHeapInfo heap in heaps)
+ {
+ text.Clear();
+
+ (ulong actualSize, ulong wasted) = CalculateSizeAndWasted(text, heap);
+ jitMgrSize += actualSize;
+ jitMgrWasted += wasted;
+
+ text.Append(' ');
+ WriteSizeAndWasted(text, actualSize, wasted);
+ text.Append('.');
+
+ output.WriteRow($"{heap.Kind}:", text);
+ }
+
+ text.Clear();
+ WriteSizeAndWasted(text, jitMgrSize, jitMgrWasted);
+ text.Append('.');
+
+ output.WriteRow("Total size:", text);
+ WriteDivider();
+
+ totalSize += jitMgrSize;
+ }
+
+ return totalSize;
+ }
+
+ private (ulong Size, ulong Wasted) CalculateSizeAndWasted(StringBuilder sb, ClrNativeHeapInfo heap)
+ {
+ sb.Append(heap.Address.ToString("x12"));
+
+ if (heap.Size is ulong size)
+ {
+ sb.Append('(');
+ sb.Append(size.ToString("x"));
+ sb.Append(':');
+ ulong actualSize = GetActualSize(heap.Address, size);
+ sb.Append(actualSize.ToString("x"));
+ sb.Append(')');
+
+ ulong wasted = 0;
+ if (actualSize < size && !heap.IsCurrentBlock)
+ wasted = size - actualSize;
+
+ return (actualSize, wasted);
+ }
+
+ return (0, 0);
+ }
+
+ private ulong PrintModuleThunkTable(TableOutput output, ref StringBuilder text, ClrRuntime clrRuntime)
+ {
+ IEnumerable<ClrModule> modulesWithThunks = clrRuntime.EnumerateModules().Where(r => r.ThunkHeap != 0);
+ if (!modulesWithThunks.Any())
+ return 0;
+
+ WriteDivider();
+ WriteLine("Module Thunk heaps:");
+
+ return PrintModules(output, ref text, modulesWithThunks);
+ }
+
+ private ulong PrintModuleLoaderAllocators(TableOutput output, ref StringBuilder text, ClrRuntime clrRuntime, HashSet<ulong> loaderAllocatorsSeen)
+ {
+ // On .Net Core, modules share their LoaderAllocator with their AppDomain (and AppDomain shares theirs
+ // with SystemDomain). Only collectable assemblies have unique loader allocators, and that's what we
+ // are essentially enumerating here.
+ IEnumerable<ClrModule> collectable = from module in clrRuntime.EnumerateModules()
+ where module.LoaderAllocator != 0
+ where loaderAllocatorsSeen is null || loaderAllocatorsSeen.Contains(module.LoaderAllocator)
+ select module;
+
+ if (!collectable.Any())
+ return 0;
+
+ WriteDivider();
+ WriteLine("Module LoaderAllocators:");
+
+ return PrintModules(output, ref text, collectable);
+ }
+
+ private ulong PrintModules(TableOutput output, ref StringBuilder text, IEnumerable<ClrModule> modules)
+ {
+ text ??= new(128);
+ ulong totalSize = 0, totalWasted = 0;
+ foreach (ClrModule module in modules)
+ {
+ ulong moduleSize = 0, moduleWasted = 0;
+
+ text.Clear();
+ foreach (ClrNativeHeapInfo info in module.EnumerateThunkHeap())
+ {
+ if (text.Length > 0)
+ text.Append(' ');
+
+ (ulong actualSize, ulong wasted) = CalculateSizeAndWasted(text, info);
+
+ moduleSize += actualSize;
+ moduleWasted += wasted;
+
+ }
+
+ text.Append(' ');
+ WriteSizeAndWasted(text, moduleSize, moduleWasted);
+ text.Append('.');
+
+ totalSize += moduleSize;
+ totalWasted += moduleWasted;
+ }
+
+ text.Clear();
+ WriteSizeAndWasted(text, totalSize, totalWasted);
+ output.WriteRow("Total size:", text);
+
+ return totalSize;
+ }
+
+ private static void WriteSizeAndWasted(StringBuilder sb, ulong heapSize, ulong heapWasted)
+ {
+ sb.Append("Size: ");
+ sb.Append(FormatMemorySize(heapSize));
+ sb.Append(" bytes total");
+
+ if (heapWasted > 0)
+ {
+ sb.Append(", ");
+ sb.Append(FormatMemorySize(heapWasted));
+ sb.Append(" bytes wasted");
+ }
+ }
+
+ private ulong GetActualSize(ulong address, ulong size)
+ {
+ const uint PageSize = 0x1000;
+ if (size > 0)
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent((int)PageSize);
+
+ ulong end = address + size;
+ ulong actualSize = 0;
+
+ while (address < end && MemoryService.ReadMemory(address, buffer, buffer.Length, out _))
+ {
+ actualSize += PageSize;
+ address += PageSize;
+ }
+
+ ArrayPool<byte>.Shared.Return(buffer);
+ return actualSize;
+ }
+
+ return 0;
+ }
+
+ private ulong PrintGCHeap(ClrRuntime clrRuntime)
+ {
+ Console.WriteLine();
+ ClrHeap heap = clrRuntime.Heap;
+
+ int pointerWidth = 16;
+ string pointerToStringFormat = "x16";
+ var pointerFormat = (pointerWidth, pointerToStringFormat);
+
+ int sizeWidth = Math.Max(15, heap.Segments.Max(seg => FormatMemorySize(seg.CommittedMemory.Length).Length));
+ var sizeFormat = (sizeWidth, "");
+
+ TableOutput gcOutput = new(Console, pointerFormat, pointerFormat, pointerFormat, pointerFormat, sizeFormat, sizeFormat);
+
+ WriteDivider('=');
+ Console.WriteLine($"Number of GC Heaps: {heap.SubHeaps.Length}");
+ WriteDivider();
+
+ foreach (var gc_heap in heap.SubHeaps)
+ {
+ if (heap.IsServer)
+ Console.WriteLine($"Heap {gc_heap.Index} ({gc_heap.Address:x16})");
+
+ if (!gc_heap.HasRegions)
+ {
+ for (int i = 0; i <= 2 && i < gc_heap.GenerationTable.Length; i++)
+ Console.WriteLine($"generation {i} starts at {gc_heap.GenerationTable[i].AllocationStart:x}");
+
+ Console.Write("ephemeral segment allocation context: ");
+ if (gc_heap.AllocationContext.Length > 0)
+ Console.WriteLine($"(0x{gc_heap.AllocationContext.Start:x}, 0x{gc_heap.AllocationContext.End})");
+ else
+ Console.WriteLine("none");
+ }
+
+ // Print gen 0-2
+ Console.WriteLine("Small object heap");
+ WriteSegmentHeader(gcOutput);
+
+ bool[] needToPrintGen = new bool[] { gc_heap.HasRegions, gc_heap.HasRegions, gc_heap.HasRegions };
+ IEnumerable<ClrSegment> ephemeralSegments = gc_heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Ephemeral || (seg.Kind >= GCSegmentKind.Generation0 && seg.Kind <= GCSegmentKind.Generation2));
+ IEnumerable<ClrSegment> segments = ephemeralSegments.OrderBy(seg => seg.Kind).ThenBy(seg => seg.Start);
+ foreach (ClrSegment segment in segments)
+ {
+ int genIndex = segment.Kind - GCSegmentKind.Generation0;
+ if (genIndex >= 0 && genIndex < needToPrintGen.Length && needToPrintGen[genIndex])
+ {
+ Console.WriteLine($"generation {genIndex}:");
+ needToPrintGen[genIndex] = false;
+ }
+
+ WriteSegment(gcOutput, segment);
+ }
+
+ // print frozen object heap
+ segments = gc_heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Frozen).OrderBy(seg => seg.Start);
+ if (segments.Any())
+ {
+ Console.WriteLine("Frozen object heap");
+ WriteSegmentHeader(gcOutput);
+
+ foreach (ClrSegment segment in segments)
+ WriteSegment(gcOutput, segment);
+ }
+
+ // print large object heap
+ if (gc_heap.HasRegions || gc_heap.GenerationTable.Length <= 3)
+ Console.WriteLine("Large object heap");
+ else
+ Console.WriteLine($"Large object heap starts at {gc_heap.GenerationTable[3].AllocationStart:x}");
+
+ segments = gc_heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Large).OrderBy(seg => seg.Start);
+ WriteSegmentHeader(gcOutput);
+
+ foreach (ClrSegment segment in segments)
+ WriteSegment(gcOutput, segment);
+
+ // print pinned object heap
+ segments = gc_heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Pinned).OrderBy(seg => seg.Start);
+ if (segments.Any())
+ {
+ if (gc_heap.HasRegions || gc_heap.GenerationTable.Length <= 3)
+ Console.WriteLine("Pinned object heap");
+ else
+ Console.WriteLine($"Pinned object heap starts at {gc_heap.GenerationTable[4].AllocationStart:x}");
+
+ WriteSegmentHeader(gcOutput);
+
+ foreach (ClrSegment segment in segments)
+ WriteSegment(gcOutput, segment);
+ }
+
+ Console.WriteLine($"Total Allocated Size: Size: {FormatMemorySize((ulong)gc_heap.Segments.Sum(r => (long)r.ObjectRange.Length))} bytes.");
+ Console.WriteLine($"Total Committed Size: Size: {FormatMemorySize((ulong)gc_heap.Segments.Sum(r => (long)r.CommittedMemory.Length))} bytes.");
+
+ Console.WriteLine("------------------------------");
+ }
+
+ ulong totalAllocated = (ulong)heap.SubHeaps.SelectMany(gc_heap => gc_heap.Segments).Sum(r => (long)r.ObjectRange.Length);
+ ulong totalCommitted = (ulong)heap.SubHeaps.SelectMany(gc_heap => gc_heap.Segments).Sum(r => (long)r.CommittedMemory.Length);
+
+ Console.WriteLine($"GC Allocated Heap Size: Size: {FormatMemorySize(totalAllocated)} bytes.");
+ Console.WriteLine($"GC Committed Heap Size: Size: {FormatMemorySize(totalCommitted)} bytes.");
+
+ return totalCommitted;
+ }
+
+ static void WriteSegmentHeader(TableOutput gcOutput)
+ {
+ gcOutput.WriteRow("segment", "begin", "allocated", "committed", "allocated size", "committed size");
+ }
+
+ static void WriteSegment(TableOutput gcOutput, ClrSegment segment)
+ {
+ gcOutput.WriteRow(segment.Address,
+ segment.ObjectRange.Start, segment.ObjectRange.End, segment.CommittedMemory.End,
+ FormatMemorySize(segment.ObjectRange.Length), FormatMemorySize(segment.CommittedMemory.Length));
+ }
+
+ static string FormatMemorySize(ulong length, string zeroValue = "")
+ {
+ if (length > 0)
+ return $"0x{length:x} ({length})";
+ return zeroValue;
+ }
+
+ private void WriteDivider(char c = '-', int width = 40) => WriteLine(new string(c, width));
+
+ private void WriteDivider(string header, int width = 120)
+ {
+ int lhs = (width - header.Length - 2) / 2;
+ if (lhs < 0)
+ {
+ WriteLine(header);
+ return;
+ }
+
+ int rhs = lhs;
+ if ((header.Length % 2) == 1)
+ rhs++;
+
+ StringBuilder sb = new(width + 1);
+ sb.Append('-', lhs);
+ sb.Append(' ');
+ sb.Append(header);
+ sb.Append(' ');
+ sb.Append('-', rhs);
+
+ WriteLine(sb.ToString());
+ }
+ }
+}
foreach ((ulong Pointer, DescribedRegion Range) found in pointersFound)
{
- if (found.Range.ClrMemoryKind == ClrMemoryKind.GCHeapSegment)
+ if (found.Range.ClrMemoryKind == ClrMemoryKind.GCHeap)
{
if (pinnedOnly)
{
pinned.Add(root.Object);
}
- foreach (ClrSegment seg in Runtime.Heap.Segments.Where(s => s.IsPinnedObjectSegment || s.IsLargeObjectSegment))
+ foreach (ClrSegment seg in Runtime.Heap.Segments.Where(s => s.IsPinned))
{
foreach (ClrObject obj in seg.EnumerateObjects().Where(o => seen.Add(o)))
{
{
bool printedTruncatedWarning = false;
- var addressResult = from region in MemoryRegionService.EnumerateRegions()
- where region.State != MemoryRegionState.MEM_FREE
- select new DescribedRegion(region, ModuleService.GetModuleFromAddress(region.Start));
+ IEnumerable<DescribedRegion> addressResult = from region in MemoryRegionService.EnumerateRegions()
+ where region.State != MemoryRegionState.MEM_FREE
+ select new DescribedRegion(region, ModuleService.GetModuleFromAddress(region.Start));
if (!includeReserveMemory)
addressResult = addressResult.Where(m => m.State != MemoryRegionState.MEM_RESERVE);
ClrRuntime clrRuntime = runtime.Services.GetService<ClrRuntime>();
if (clrRuntime is not null)
{
- foreach (ClrMemoryPointer mem in ClrMemoryPointer.EnumerateClrMemoryAddresses(clrRuntime))
+ foreach ((ulong Address, ulong? Size, ClrMemoryKind Kind) mem in EnumerateClrMemoryAddresses(clrRuntime))
{
- var found = rangeList.Where(r => r.Start <= mem.Address && mem.Address < r.End).ToArray();
+ DescribedRegion[] found = rangeList.Where(r => r.Start <= mem.Address && mem.Address < r.End).ToArray();
if (found.Length == 0 && mem.Kind != ClrMemoryKind.GCHeapReserve)
{
}
// Add the memory range if we know its size.
- if (mem.Size > 0)
+ if (mem.Size is ulong size && size > 0)
{
IModule module = ModuleService.GetModuleFromAddress(mem.Address);
rangeList.Add(new DescribedRegion()
{
Start = mem.Address,
- End = mem.Address + mem.Size,
+ End = mem.Address + size,
ClrMemoryKind = mem.Kind,
State = mem.Kind == ClrMemoryKind.GCHeapReserve ? MemoryRegionState.MEM_RESERVE : MemoryRegionState.MEM_COMMIT,
Module = module,
foreach (DescribedRegion region in found)
{
- if (mem.Kind == ClrMemoryKind.GCHeapReserve || mem.Kind == ClrMemoryKind.GCHeapSegment)
- {
- // GC heap segments are special. We only know a small chunk of memory on the actual allocated
- // region. We want to mark the whole region as GC/GCReserve and not try to divide up chunks for these.
- SetRegionKindWithWarning(mem, region);
- }
- else if (mem.Size == 0)
+ if (!mem.Size.HasValue || mem.Size.Value == 0)
{
// If we don't know the length of memory, just mark the Region with this tag.
SetRegionKindWithWarning(mem, region);
DescribedRegion middleRegion = new(region)
{
Start = mem.Address,
- End = mem.Address + mem.Size,
+ End = mem.Address + mem.Size.Value,
ClrMemoryKind = mem.Kind,
Usage = MemoryRegionUsage.CLR,
};
// Region is now the starting region of this set.
region.End = middleRegion.Start;
}
- else if (region.Size < mem.Size)
+ else if (region.Size < mem.Size.Value)
{
SetRegionKindWithWarning(mem, region);
// If we found no matching regions, expand the current region to be the right length.
if (!foundNext)
- region.End = mem.Address + mem.Size;
+ region.End = mem.Address + mem.Size.Value;
}
- else if (region.Size > mem.Size)
+ else if (region.Size > mem.Size.Value)
{
// The CLR memory segment is at the beginning of this region.
DescribedRegion newRange = new(region)
{
- End = mem.Address + mem.Size,
+ End = mem.Address + mem.Size.Value,
ClrMemoryKind = mem.Kind
};
}
}
- var ranges = rangeList.OrderBy(r => r.Start).ToArray();
+ DescribedRegion[] ranges = rangeList.OrderBy(r => r.Start).ToArray();
if (tagReserveMemoryHeuristically)
{
return ranges;
}
- private static void SetRegionKindWithWarning(ClrMemoryPointer mem, DescribedRegion region)
+ /// <summary>
+ /// Enumerates pointers to various CLR heaps in memory.
+ /// </summary>
+ private static IEnumerable<(ulong Address, ulong? Size, ClrMemoryKind Kind)> EnumerateClrMemoryAddresses(ClrRuntime runtime)
+ {
+ foreach (ClrNativeHeapInfo nativeHeap in runtime.EnumerateClrNativeHeaps())
+ yield return (nativeHeap.Address, nativeHeap.Size, nativeHeap.Kind == NativeHeapKind.Unknown ? ClrMemoryKind.None : (ClrMemoryKind)nativeHeap.Kind);
+
+ ulong prevHandle = 0;
+ ulong granularity = 0x100;
+ foreach (ClrHandle handle in runtime.EnumerateHandles())
+ {
+ // There can be a very large number of HandleTable entries. We don't need to enumerate every
+ // single one of them to find proper regions of memory. Instead, we'll skip handles that are
+ // "nearby" the previous handles we enumerated, but we will ensure that we always enumerate the
+ // next handle along an allocation granularity. We need to ensure that 'granularity' is less
+ // than the size of a handle table chunk, and is a power of 2.
+
+ if (handle.Address < prevHandle || handle.Address >= (prevHandle | (granularity - 1)))
+ {
+ yield return (handle.Address, null, ClrMemoryKind.HandleTable);
+ prevHandle = handle.Address;
+ }
+ }
+
+ // We don't really have the true bounds of the committed or reserved segments.
+ // Return null for the size so that we will mark the entire region with this type.
+ foreach (ClrSegment seg in runtime.Heap.Segments)
+ {
+ if (seg.CommittedMemory.Length > 0)
+ yield return (seg.CommittedMemory.Start, null, ClrMemoryKind.GCHeap);
+
+ if (seg.ReservedMemory.Length > 0)
+ yield return (seg.ReservedMemory.Start, null, ClrMemoryKind.GCHeapReserve);
+ }
+ }
+
+ private static void SetRegionKindWithWarning((ulong Address, ulong? Size, ClrMemoryKind Kind) mem, DescribedRegion region)
{
if (region.ClrMemoryKind != mem.Kind)
{
if (region.ClrMemoryKind != ClrMemoryKind.None
&& region.ClrMemoryKind != ClrMemoryKind.HighFrequencyHeap)
{
- Trace.WriteLine($"Warning: Overwriting range [{region.Start:x},{region.End:x}] {region.ClrMemoryKind} -> [{mem.Address:x},{mem.Address+mem.Size:x}] {mem.Kind}.");
+ if (mem.Size is not ulong size)
+ size = 0;
+
+ Trace.WriteLine($"Warning: Overwriting range [{region.Start:x},{region.End:x}] {region.ClrMemoryKind} -> [{mem.Address:x},{mem.Address+size:x}] {mem.Kind}.");
}
region.ClrMemoryKind = mem.Kind;
{
DescribedRegion range = FindMemory(ranges, sp);
if (range is not null)
- range.Description = "Stack";
+ range.Usage = MemoryRegionUsage.Stack;
}
}
}
if (nonReserved is null)
return null;
- mem.Description = nonReserved.Name;
+ mem.PrevRegionName = nonReserved.Name;
return nonReserved;
}
}
}
+ // intentionally has the same structure as NativeHeapKind. Only None/Unknown are in different spots
+ public enum ClrMemoryKind
+ {
+ None,
+ IndirectionCellHeap,
+ LookupHeap,
+ ResolveHeap,
+ DispatchHeap,
+ CacheEntryHeap,
+ VtableHeap,
+ LoaderCodeHeap,
+ HostCodeHeap,
+ StubHeap,
+ HighFrequencyHeap,
+ LowFrequencyHeap,
+
+ // Skip ahead so new ClrMD NativeHeapKind values don't break the enum.
+ Unknown = 100,
+ GCHeap,
+ GCHeapReserve,
+ HandleTable,
+ }
+
internal class DescribedRegion : IMemoryRegion
{
public DescribedRegion()
Protection = copyFrom.Protection;
Usage = copyFrom.Usage;
Image = copyFrom.Image;
- Description = copyFrom.Description;
ClrMemoryKind = copyFrom.ClrMemoryKind;
+ PrevRegionName = copyFrom.PrevRegionName;
}
public IModule Module { get; internal set; }
public string Image { get; internal set; }
- public string Description { get; internal set; }
-
public ClrMemoryKind ClrMemoryKind { get; internal set; }
public ulong Size => End <= Start ? 0 : End - Start;
+ /// <summary>
+ /// Only used for heuristically marking reserve regions with what it might
+ /// be reserved for.
+ /// </summary>
+ public string PrevRegionName { get; internal set; }
+
public string Name
{
get
return ClrMemoryKind.ToString();
}
- if (!string.IsNullOrWhiteSpace(Description))
- return Description;
+ if (Usage != MemoryRegionUsage.Unknown)
+ return Usage.ToString();
if (State == MemoryRegionState.MEM_RESERVE)
+ {
+ if (PrevRegionName is not null)
+ return $"[{PrevRegionName}Reserve]";
+
return "[RESERVED]";
+ }
else if (State == MemoryRegionState.MEM_FREE)
return "[FREE]";
public bool AlignLeft { get; set; } = false;
public IConsoleService Console { get; }
+ public int TotalWidth => 1 * (_formats.Length - 1) + _formats.Sum(c => Math.Abs(c.width));
private readonly (int width, string format)[] _formats;
if (obj is null)
return null;
+ if (obj is Enum)
+ return obj.ToString();
+
return obj switch
{
nint ni => ni.ToString(format),
long l => l.ToString(format),
uint ui => ui.ToString(format),
int i => i.ToString(format),
+ StringBuilder sb => sb.ToString(),
string s => s,
_ => throw new NotImplementedException(obj.GetType().ToString()),
};
// find the !address header
string[] split = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 0)
- foundHeader = split[0] == "BaseAddress" && split.Last() == "Usage";
+ foundHeader = (split[0] == "BaseAddress" || split[0] == "BaseAddr") && split.Last() == "Usage";
}
else if (!skipped)
{
[Command(Name = "dumpsigelem", DefaultOptions = "DumpSigElem", Help = "Dumps a single element of a signature object.")]
[Command(Name = "dumpstackobjects", DefaultOptions = "DumpStackObjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")]
[Command(Name = "dumpvc", DefaultOptions = "DumpVC", Help = "Displays info about the fields of a value class.")]
- [Command(Name = "eeheap", DefaultOptions = "EEHeap", Help = "Displays info about process memory consumed by internal runtime data structures.")]
[Command(Name = "eeversion", DefaultOptions = "EEVersion", Help = "Displays information about the runtime version.")]
[Command(Name = "ehinfo", DefaultOptions = "EHInfo", Help = "Displays the exception handling blocks in a JIT-ed method.")]
[Command(Name = "enummem", DefaultOptions = "enummem", Help = "ICLRDataEnumMemoryRegions.EnumMemoryRegions test command.")]
VERIFY:\s+LowFrequencyHeap:\s+<HEXVAL>.*bytes.*\s+
VERIFY:\s+HighFrequencyHeap:\s+<HEXVAL>.*bytes.*\s+
VERIFY:\s+Total size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
-VERIFY:\s+Jit code heap:\s+
-VERIFY:\s+LoaderCodeHeap:\s+<HEXVAL>.*bytes\.\s+
-VERIFY:\s+Total LoaderHeap size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
+VERIFY:\s+JIT Manager:\s+<HEXVAL>
+VERIFY:\s+(LoaderCodeHeap|HostCodeHeap):\s+<HEXVAL>.*bytes.*
+VERIFY:\s+Total size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
VERIFY:\s+Number of GC Heaps:\s+<DECVAL>\s+
VERIFY:\s+segment\s+begin\s+allocated\s+committed\s+allocated\s+size\s+committed\s+size\s+
-VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+0x<HEXVAL>\(<DECVAL>\)\s+0x<HEXVAL>\(<DECVAL>\)\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+0x<HEXVAL>\s*\(<DECVAL>\)\s+0x<HEXVAL>\s*\(<DECVAL>\)\s+
VERIFY:\s+Large object heap.*
VERIFY:\s+Pinned object heap.*
VERIFY:\s+LowFrequencyHeap:\s+<HEXVAL>.*bytes.*\s+
VERIFY:\s+HighFrequencyHeap:\s+<HEXVAL>.*bytes.*\s+
VERIFY:\s+Total size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
-VERIFY:\s+Jit code heap:\s+
-VERIFY:\s+LoaderCodeHeap:\s+<HEXVAL>.*bytes\.\s+
-VERIFY:\s+Total LoaderHeap size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
+VERIFY:\s+JIT Manager:\s+<HEXVAL>
+VERIFY:\s+(LoaderCodeHeap|HostCodeHeap):\s+<HEXVAL>.*bytes.*
+VERIFY:\s+Total size:\s+Size:\s+0x<HEXVAL>\s+\(<DECVAL>|lu\)\s+bytes\.\s+
VERIFY:\s+Number of GC Heaps:\s+<DECVAL>\s+
VERIFY:\s+segment\s+begin\s+allocated\s+committed\s+allocated\s+size\s+committed\s+size\s+
-VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+0x<HEXVAL>\(<DECVAL>\)\s+0x<HEXVAL>\(<DECVAL>\)\s+
+VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+0x<HEXVAL>\s*\(<DECVAL>\)\s+0x<HEXVAL>\s*\(<DECVAL>\)\s+
VERIFY:\s+Large object heap.*
# Continue to the next DebugBreak
}
return 2;
}
-
-
-DWORD_PTR g_trav_totalSize = 0;
-DWORD_PTR g_trav_wastedSize = 0;
-
-void LoaderHeapTraverse(CLRDATA_ADDRESS blockData,size_t blockSize,BOOL blockIsCurrentBlock)
-{
- DWORD_PTR dwAddr1;
- DWORD_PTR curSize = 0;
- char ch;
- for (dwAddr1 = (DWORD_PTR)blockData;
- dwAddr1 < (DWORD_PTR)blockData + blockSize;
- dwAddr1 += OSPageSize())
- {
- if (IsInterrupt())
- break;
- if (SafeReadMemory(dwAddr1, &ch, sizeof(ch), NULL))
- {
- curSize += OSPageSize();
- }
- else
- break;
- }
-
- if (!blockIsCurrentBlock)
- {
- g_trav_wastedSize += blockSize - curSize;
- }
-
- g_trav_totalSize += curSize;
- ExtOut("%p(%x:%x) ", SOS_PTR(blockData), blockSize, curSize);
-}
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the size for various heaps. *
-* total - the total size of the heap *
-* wasted - the amount of size wasted by the heap. *
-* *
-\**********************************************************************/
-void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted)
-{
- ExtOut("Size: 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "u) bytes", total, total);
- if (wasted)
- ExtOut(" total, 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "u) bytes wasted", wasted, wasted);
- ExtOut(".\n");
-}
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the size information for the JIT heap. *
-* *
-* Returns: The size of this heap. *
-* *
-\**********************************************************************/
-DWORD_PTR JitHeapInfo()
-{
- // walk ExecutionManager__m_pJitList
- unsigned int count = 0;
- if (FAILED(g_sos->GetJitManagerList(0, NULL, &count)))
- {
- ExtOut("Unable to get JIT info\n");
- return 0;
- }
-
- ArrayHolder<DacpJitManagerInfo> pArray = new DacpJitManagerInfo[count];
- if (pArray==NULL)
- {
- ReportOOM();
- return 0;
- }
-
- if (g_sos->GetJitManagerList(count, pArray, NULL) != S_OK)
- {
- ExtOut("Unable to get array of JIT Managers\n");
- return 0;
- }
-
- DWORD_PTR totalSize = 0;
- DWORD_PTR wasted = 0;
-
- for (unsigned int n=0; n < count; n++)
- {
- if (IsInterrupt())
- break;
-
- if (IsMiIL(pArray[n].codeType)) // JIT
- {
- unsigned int heapCount = 0;
- if (FAILED(g_sos->GetCodeHeapList(pArray[n].managerAddr, 0, NULL, &heapCount)))
- {
- ExtOut("Error getting EEJitManager code heaps\n");
- break;
- }
-
- if (heapCount > 0)
- {
- ArrayHolder<DacpJitCodeHeapInfo> codeHeapInfo = new DacpJitCodeHeapInfo[heapCount];
- if (codeHeapInfo == NULL)
- {
- ReportOOM();
- break;
- }
-
- if (g_sos->GetCodeHeapList(pArray[n].managerAddr, heapCount, codeHeapInfo, NULL) != S_OK)
- {
- ExtOut("Unable to get code heap info\n");
- break;
- }
-
- for (unsigned int iHeaps = 0; iHeaps < heapCount; iHeaps++)
- {
- if (IsInterrupt())
- break;
-
- if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_LOADER)
- {
- ExtOut("LoaderCodeHeap: ");
- totalSize += LoaderHeapInfo(codeHeapInfo[iHeaps].LoaderHeap, &wasted);
- }
- else if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_HOST)
- {
- ExtOut("HostCodeHeap: ");
- ExtOut("%p ", SOS_PTR(codeHeapInfo[iHeaps].HostData.baseAddr));
- DWORD dwSize = (DWORD)(codeHeapInfo[iHeaps].HostData.currentAddr - codeHeapInfo[iHeaps].HostData.baseAddr);
- PrintHeapSize(dwSize, 0);
- totalSize += dwSize;
- }
- }
- }
- }
- else if (!IsMiNative(pArray[n].codeType)) // ignore native heaps for now
- {
- ExtOut("Unknown Jit encountered, ignored\n");
- }
- }
-
- ExtOut("Total size: ");
- PrintHeapSize(totalSize, wasted);
-
- return totalSize;
-}
-
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the loader heap info for a single AD. *
-* pLoaderHeapAddr - pointer to the loader heap *
-* wasted - a pointer to store the number of bytes wasted in this *
-* VSDHeap (this pointer can be NULL) *
-* *
-* Returns: The size of this heap. *
-* *
-\**********************************************************************/
-DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted)
-{
- g_trav_totalSize = 0;
- g_trav_wastedSize = 0;
-
- if (pLoaderHeapAddr)
- g_sos->TraverseLoaderHeap(pLoaderHeapAddr, LoaderHeapTraverse);
-
- PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
-
- if (wasted)
- *wasted += g_trav_wastedSize;
- return g_trav_totalSize;
-}
-
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the heap info for a single VSDHeap. *
-* name - the name to print *
-* type - the type of heap *
-* appDomain - the app domain in which this resides *
-* wasted - a pointer to store the number of bytes wasted in this *
-* VSDHeap (this pointer can be NULL) *
-* *
-* Returns: The size of this heap. *
-* *
-\**********************************************************************/
-static DWORD_PTR PrintOneVSDHeap(const char *name, VCSHeapType type, CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
-{
- g_trav_totalSize = 0; g_trav_wastedSize = 0;
-
- ExtOut(name);
- g_sos->TraverseVirtCallStubHeap(appDomain, type, LoaderHeapTraverse);
-
- PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
- if (wasted)
- *wasted += g_trav_wastedSize;
- return g_trav_totalSize;
-}
-
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the heap info for VSDHeaps. *
-* appDomain - The AppDomain to print info for. *
-* wasted - a pointer to store the number of bytes wasted in this *
-* AppDomain (this pointer can be NULL) *
-* *
-* Returns: The size of this heap. *
-* *
-\**********************************************************************/
-DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
-{
- DWORD_PTR totalSize = 0;
-
- if (appDomain)
- {
- totalSize += PrintOneVSDHeap(" IndcellHeap: ", IndcellHeap, appDomain, wasted);
- totalSize += PrintOneVSDHeap(" LookupHeap: ", LookupHeap, appDomain, wasted);
- totalSize += PrintOneVSDHeap(" ResolveHeap: ", ResolveHeap, appDomain, wasted);
- totalSize += PrintOneVSDHeap(" DispatchHeap: ", DispatchHeap, appDomain, wasted);
- totalSize += PrintOneVSDHeap(" CacheEntryHeap: ", CacheEntryHeap, appDomain, wasted);
- }
-
- return totalSize;
-}
-
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the heap info for a domain *
-* name - the name of the domain (to be printed) *
-* adPtr - a pointer to the AppDomain to print info about *
-* outSize - a pointer to an int to store the size at (this may be *
-* NULL) *
-* outWasted - a pointer to an int to store the number of bytes this *
-* domain is wasting (this may be NULL) *
-* *
-* returns: SUCCESS if we successfully printed out the domain heap *
-* info, FAILED otherwise; if FAILED, outSize and *
-* outWasted are untouched. *
-* *
-\**********************************************************************/
-HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *outSize, DWORD_PTR *outWasted)
-{
- DacpAppDomainData appDomain;
- HRESULT hr = appDomain.Request(g_sos, adPtr);
- if (FAILED(hr))
- {
- ExtOut("Unable to get information for %s.\n", name);
- return hr;
- }
-
- ExtOut("--------------------------------------\n");
-
- const int column = 19;
- ExtOut("%s:", name);
- WhitespaceOut(column - (int)strlen(name) - 1);
- DMLOut("%s\n", DMLDomain(adPtr));
-
- DWORD_PTR domainHeapSize = 0;
- DWORD_PTR wasted = 0;
-
- ExtOut("LowFrequencyHeap: ");
- domainHeapSize += LoaderHeapInfo(appDomain.pLowFrequencyHeap, &wasted);
-
- ExtOut("HighFrequencyHeap: ");
- domainHeapSize += LoaderHeapInfo(appDomain.pHighFrequencyHeap, &wasted);
-
- ExtOut("StubHeap: ");
- domainHeapSize += LoaderHeapInfo(appDomain.pStubHeap, &wasted);
-
- ExtOut("Virtual Call Stub Heap:\n");
- domainHeapSize += VSDHeapInfo(appDomain.AppDomainPtr, &wasted);
-
- ExtOut("Total size: ");
- PrintHeapSize(domainHeapSize, wasted);
-
- if (outSize)
- *outSize += domainHeapSize;
- if (outWasted)
- *outWasted += wasted;
-
- return hr;
-}
-
-/**********************************************************************\
-* Routine Description: *
-* *
-* This function prints out the heap info for a list of modules. *
-* moduleList - an array of modules *
-* count - the number of modules in moduleList *
-* type - the type of heap *
-* outWasted - a pointer to store the number of bytes wasted in this *
-* heap (this pointer can be NULL) *
-* *
-* Returns: The size of this heap. *
-* *
-\**********************************************************************/
-DWORD_PTR PrintModuleHeapInfo(__out_ecount(count) DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *outWasted)
-{
- DWORD_PTR toReturn = 0;
- DWORD_PTR wasted = 0;
-
- if (IsMiniDumpFile())
- {
- ExtOut("<no information>\n");
- }
- else
- {
- DWORD_PTR thunkHeapSize = 0;
-
- for (int i = 0; i < count; i++)
- {
- CLRDATA_ADDRESS addr = moduleList[i];
- DacpModuleData dmd;
- if (dmd.Request(g_sos, addr) != S_OK)
- {
- ExtOut("Unable to read module %p\n", SOS_PTR(addr));
- }
- else
- {
- DMLOut("Module %s: ", DMLModule(addr));
- CLRDATA_ADDRESS heap = type == ModuleHeapType_ThunkHeap ? dmd.pThunkHeap : dmd.pLookupTableHeap;
- thunkHeapSize += LoaderHeapInfo(heap, &wasted);
- }
- }
-
- ExtOut("Total size: " WIN86_8SPACES);
- PrintHeapSize(thunkHeapSize, wasted);
-
- toReturn = thunkHeapSize;
- }
-
- if (outWasted)
- *outWasted += wasted;
-
- return toReturn;
-}
return hr;
}
+
+//
+// Executes managed extension commands
+//
+HRESULT ExecuteCommand(PCSTR commandName, PCSTR args)
+{
+ IHostServices* hostServices = GetHostServices();
+ if (hostServices != nullptr)
+ {
+ if (commandName != nullptr && strlen(commandName) > 0)
+ {
+ return hostServices->DispatchCommand(commandName, args);
+ }
+ }
+ return E_NOTIMPL;
+}
+
+
/**********************************************************************\
* Routine Description: *
* *
}
#endif // _DEBUG
-
-void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
-void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
-
#endif // FEATURE_PAL
/**********************************************************************\
\**********************************************************************/
DECLARE_API(EEHeap)
{
- INIT_API();
- MINIDUMP_NOT_SUPPORTED();
-
- BOOL dml = FALSE;
- BOOL showgc = FALSE;
- BOOL showloader = FALSE;
-
- CMDOption option[] =
- { // name, vptr, type, hasValue
- {"-gc", &showgc, COBOOL, FALSE},
- {"-loader", &showloader, COBOOL, FALSE},
- {"/d", &dml, COBOOL, FALSE},
- };
-
- if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
- {
- return Status;
- }
-
- EnableDMLHolder dmlHolder(dml);
- if (showloader || !showgc)
- {
- // Loader heap.
- DWORD_PTR allHeapSize = 0;
- DWORD_PTR wasted = 0;
- DacpAppDomainStoreData adsData;
- if ((Status=adsData.Request(g_sos))!=S_OK)
- {
- ExtOut("Unable to get AppDomain information\n");
- return Status;
- }
-
- // The first one is the system domain.
- ExtOut("Loader Heap:\n");
- IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
- if (adsData.sharedDomain != NULL)
- {
- IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
- }
-
- ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
-
- if (pArray==NULL)
- {
- ReportOOM();
- return Status;
- }
-
- if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
- {
- ExtOut("Unable to get the array of all AppDomains.\n");
- return Status;
- }
-
- for (int n=0;n<adsData.DomainCount;n++)
- {
- if (IsInterrupt())
- break;
-
- char domain[16];
- sprintf_s(domain, ARRAY_SIZE(domain), "Domain %d", n+1);
-
- IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
-
- }
-
- // Jit code heap
- ExtOut("--------------------------------------\n");
- ExtOut("Jit code heap:\n");
-
- if (IsMiniDumpFile())
- {
- ExtOut("<no information>\n");
- }
- else
- {
- allHeapSize += JitHeapInfo();
- }
-
-
- // Module Data
- {
- int numModule;
- ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
- if (moduleList == NULL)
- {
- ExtOut("Failed to request module list.\n");
- }
- else
- {
- // Module Thunk Heaps
- ExtOut("--------------------------------------\n");
- ExtOut("Module Thunk heaps:\n");
- allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
-
- // Module Lookup Table Heaps
- ExtOut("--------------------------------------\n");
- ExtOut("Module Lookup Table heaps:\n");
- allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
- }
- }
-
- ExtOut("--------------------------------------\n");
- ExtOut("Total LoaderHeap size: ");
- PrintHeapSize(allHeapSize, wasted);
- ExtOut("=======================================\n");
- }
-
- if (showgc || !showloader)
- {
- // GC Heap
- DWORD dwNHeaps = 1;
-
- if (!GetGcStructuresValid())
- {
- DisplayInvalidStructuresMessage();
- }
-
- DacpGcHeapData gcheap;
- if (gcheap.Request(g_sos) != S_OK)
- {
- ExtOut("Error requesting GC Heap data\n");
- return Status;
- }
-
- if (gcheap.bServerMode)
- {
- dwNHeaps = gcheap.HeapCount;
- }
-
- ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
- DWORD_PTR totalAllocatedSize = 0;
- DWORD_PTR totalCommittedSize = 0;
- if (!gcheap.bServerMode)
- {
- DacpGcHeapDetails heapDetails;
- if (heapDetails.Request(g_sos) != S_OK)
- {
- ExtOut("Error requesting details\n");
- return Status;
- }
-
- GCHeapInfo (heapDetails, totalAllocatedSize, totalCommittedSize);
- ExtOut("Total Allocated Size: ");
- PrintHeapSize(totalAllocatedSize, 0);
- ExtOut("Total Committed Size: ");
- PrintHeapSize(totalCommittedSize, 0);
- }
- else
- {
- DWORD dwAllocSize;
- if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
- {
- ExtOut("Failed to get GCHeaps: integer overflow\n");
- return Status;
- }
-
- CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
- if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
- {
- ExtOut("Failed to get GCHeaps\n");
- return Status;
- }
-
- DWORD n;
- for (n = 0; n < dwNHeaps; n ++)
- {
- DacpGcHeapDetails dacHeapDetails;
- if (dacHeapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
- {
- ExtOut("Error requesting details\n");
- return Status;
- }
- ExtOut("------------------------------\n");
- ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
- DWORD_PTR heapAllocSize = 0;
- DWORD_PTR heapCommitSize = 0;
- GCHeapDetails heapDetails(dacHeapDetails, heapAddrs[n]);
- GCHeapInfo (heapDetails, heapAllocSize, heapCommitSize);
- totalAllocatedSize += heapAllocSize;
- totalCommittedSize += heapCommitSize;
- ExtOut("Allocated Heap Size: " WIN86_8SPACES);
- PrintHeapSize(heapAllocSize, 0);
- ExtOut("Committed Heap Size: " WIN86_8SPACES);
- PrintHeapSize(heapCommitSize, 0);
- }
- }
- ExtOut("------------------------------\n");
- ExtOut("GC Allocated Heap Size: " WIN86_8SPACES);
- PrintHeapSize(totalAllocatedSize, 0);
- ExtOut("GC Committed Heap Size: " WIN86_8SPACES);
- PrintHeapSize(totalCommittedSize, 0);
- }
- return Status;
+ INIT_API_EXT();
+ return ExecuteCommand("eeheap", args);
}
void PrintGCStat(HeapStat *inStat, const char* label=NULL)
}
#ifdef HOST_WINDOWS
-
-//
-// Executes managed extension commands
-//
-HRESULT ExecuteCommand(PCSTR commandName, PCSTR args)
-{
- IHostServices* hostServices = GetHostServices();
- if (hostServices != nullptr)
- {
- if (commandName != nullptr && strlen(commandName) > 0)
- {
- return hostServices->DispatchCommand(commandName, args);
- }
- }
- return E_NOTIMPL;
-}
-
//
// Sets the symbol server path.
//
DWORD_PTR GetValueFromExpression (___in __in_z const char *const str);
void LoadRuntimeSymbols();
-enum ModuleHeapType
-{
- ModuleHeapType_ThunkHeap,
- ModuleHeapType_LookupTableHeap
-};
-
-HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *size, DWORD_PTR *wasted = 0);
-DWORD_PTR PrintModuleHeapInfo(DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *wasted = 0);
-void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted);
-void DomainInfo(DacpAppDomainData *pDomain);
-void AssemblyInfo(DacpAssemblyData *pAssembly);
-DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted = 0);
-DWORD_PTR JitHeapInfo();
-DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted = 0);
+void DomainInfo(DacpAppDomainData* pDomain);
+void AssemblyInfo(DacpAssemblyData* pAssembly);
size_t GetNumComponents(TADDR obj);
typedef unsigned int size_t;
typedef int ModuleMapType;
typedef int VCSHeapType;
+typedef int LoaderHeapKind;
cpp_quote("#endif")
cpp_quote("typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType;")
-cpp_quote("typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap} VCSHeapType;")
+cpp_quote("typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType;")
+cpp_quote("typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind;")
typedef void (*MODULEMAPTRAVERSE)(UINT index, CLRDATA_ADDRESS methodTable,LPVOID token);
typedef void (*VISITHEAP)(CLRDATA_ADDRESS blockData,size_t blockSize,BOOL blockIsCurrentBlock);
typedef enum SOSStackSourceType
{
- SOS_StackSourceIP, // Instuction pointer in managed code
+ SOS_StackSourceIP, // Instruction pointer in managed code
SOS_StackSourceFrame, // clr!Frame
} SOSStackSourceType;
HRESULT IsTrackedType(CLRDATA_ADDRESS objAddr, BOOL* isTrackedType, BOOL* hasTaggedMemory);
HRESULT GetTaggedMemory(CLRDATA_ADDRESS objAddr, CLRDATA_ADDRESS* taggedMemory, size_t* taggedMemorySizeInBytes);
}
+
+[
+ object,
+ local,
+ uuid(1b93bacc-8ca4-432d-943a-3e6e7ec0b0a3)
+]
+interface ISOSDacInterface12 : IUnknown
+{
+ HRESULT GetGlobalAllocationContext(CLRDATA_ADDRESS* allocPtr, CLRDATA_ADDRESS* allocLimit);
+}
+
+[
+ object,
+ local,
+ uuid(3176a8ed-597b-4f54-a71f-83695c6a8c5d)
+]
+interface ISOSDacInterface13 : IUnknown
+{
+ HRESULT TraverseLoaderHeap(CLRDATA_ADDRESS loaderHeapAddr, LoaderHeapKind kind, VISITHEAP pCallback);
+ HRESULT GetDomainLoaderAllocator(CLRDATA_ADDRESS domainAddress, CLRDATA_ADDRESS *pLoaderAllocator);
+ HRESULT GetLoaderAllocatorHeapNames(int count, const char **ppNames, int *pNeeded);
+ HRESULT GetLoaderAllocatorHeaps(CLRDATA_ADDRESS loaderAllocator, int count, CLRDATA_ADDRESS *pLoaderHeaps, LoaderHeapKind *pKinds, int *pNeeded);
+}
/* link this file in with the server and any clients */
- /* File created by MIDL compiler version 8.01.0622 */
-/* at Mon Jan 18 19:14:07 2038
- */
-/* Compiler settings for C:/ssd/runtime/src/coreclr/inc/sospriv.idl:
- Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622
+ /* File created by MIDL compiler version 8.01.0626 */
+/* Compiler settings for sospriv.idl:
+ Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0626
protocol : dce , ms_ext, c_ext, robust
error checks: allocation ref bounds_check enum stub_data
VC __declspec() decoration level:
MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface11,0x96BA1DB9,0x14CD,0x4492,0x80,0x65,0x1C,0xAA,0xEC,0xF6,0xE5,0xCF);
+
+MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface12,0x1b93bacc,0x8ca4,0x432d,0x94,0x3a,0x3e,0x6e,0x7e,0xc0,0xb0,0xa3);
+
+
+MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface13,0x3176a8ed,0x597b,0x4f54,0xa7,0x1f,0x83,0x69,0x5c,0x6a,0x8c,0x5d);
+
#undef MIDL_DEFINE_GUID
#ifdef __cplusplus
#endif
typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType;
-typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap} VCSHeapType;
+typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType;
+typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind;
typedef void ( *MODULEMAPTRAVERSE )(
UINT index,
CLRDATA_ADDRESS methodTable,
#endif /* __ISOSDacInterface12_INTERFACE_DEFINED__ */
+
+#ifndef __ISOSDacInterface13_INTERFACE_DEFINED__
+#define __ISOSDacInterface13_INTERFACE_DEFINED__
+
+/* interface ISOSDacInterface13 */
+/* [uuid][local][object] */
+
+
+EXTERN_C const IID IID_ISOSDacInterface13;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("3176a8ed-597b-4f54-a71f-83695c6a8c5d")
+ ISOSDacInterface13 : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE TraverseLoaderHeap(
+ CLRDATA_ADDRESS loaderHeapAddr,
+ LoaderHeapKind kind,
+ VISITHEAP pCallback) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetDomainLoaderAllocator(
+ CLRDATA_ADDRESS domainAddress,
+ CLRDATA_ADDRESS *pLoaderAllocator) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocatorHeapNames(
+ int count,
+ const char **ppNames,
+ int *pNeeded) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocatorHeaps(
+ CLRDATA_ADDRESS loaderAllocator,
+ int count,
+ CLRDATA_ADDRESS *pLoaderHeaps,
+ LoaderHeapKind *pKinds,
+ int *pNeeded) = 0;
+ };
+
+
+#else /* C style interface */
+
+ typedef struct ISOSDacInterface13Vtbl
+ {
+ BEGIN_INTERFACE
+
+ DECLSPEC_XFGVIRT(IUnknown, QueryInterface)
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ ISOSDacInterface13 * This,
+ /* [in] */ REFIID riid,
+ /* [annotation][iid_is][out] */
+ _COM_Outptr_ void **ppvObject);
+
+ DECLSPEC_XFGVIRT(IUnknown, AddRef)
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ ISOSDacInterface13 * This);
+
+ DECLSPEC_XFGVIRT(IUnknown, Release)
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ ISOSDacInterface13 * This);
+
+ DECLSPEC_XFGVIRT(ISOSDacInterface13, TraverseLoaderHeap)
+ HRESULT ( STDMETHODCALLTYPE *TraverseLoaderHeap )(
+ ISOSDacInterface13 * This,
+ CLRDATA_ADDRESS loaderHeapAddr,
+ LoaderHeapKind kind,
+ VISITHEAP pCallback);
+
+ END_INTERFACE
+ } ISOSDacInterface13Vtbl;
+
+ interface ISOSDacInterface13
+ {
+ CONST_VTBL struct ISOSDacInterface13Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define ISOSDacInterface13_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define ISOSDacInterface13_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define ISOSDacInterface13_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define ISOSDacInterface13_TraverseLoaderHeap(This,loaderHeapAddr,kind,pCallback) \
+ ( (This)->lpVtbl -> TraverseLoaderHeap(This,loaderHeapAddr,kind,pCallback) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __ISOSDacInterface13_INTERFACE_DEFINED__ */
+
+
/* Additional Prototypes for ALL interfaces */
/* end of Additional Prototypes */