From: Lee Culver Date: Sat, 4 Mar 2023 17:15:28 +0000 (-0800) Subject: Update to ClrMD 3.0, ISOSDacInterface13 (#3675) X-Git-Tag: accepted/tizen/unified/riscv/20231226.055542~43^2~2^2~59 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cdc0ab1247594323d10db9163364b01ee34f0bb6;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Update to ClrMD 3.0, ISOSDacInterface13 (#3675) * Update dependencies from https://github.com/microsoft/clrmd build 20230219.1 Microsoft.Diagnostics.Runtime , Microsoft.Diagnostics.Runtime.Utilities From Version 2.3.411802 -> To Version 2.4.411901 * Use new ClrMD native heap enumeration code * Clean up a few x86 issues - Fixed !address header parsing on x86. - Removed "Description" from DescribedRegion. We now use .Usage instead. - Added a "PrevRevionName" for heuristic tagging. * Remove test code * Update native SOS to work with ISOSDacInterface13 * Remove OrderBy Removed test code. * Use IsRuntimeVersion * initial managed !eeheap implementation * Remove native eeheap code * Print total size of all heaps * Reorder code * Memory formatting * Add original !eeheap args * Minor compile issues * Update ClrMD * Fix minor build issues * Fix parameter issue * Output fixes * GC Output Cleanup * Fix width * Fix reserve memory output Accidently deleted .ReserveMemory when refactoring earlier. * Rename EEHeap -> EEHeapCommand * Remove uses of var * Reuse runtimes * Return ArrayPool buffer * Update to ClrMD 3.0-beta * Fix minor build issue * Fix tests * Update ClrMD version * Fix a few command issues. * Fix mismatched tag * Fix pinned heap * Fix ClrMD issue with .net 8 heaps pre-ISOSDacInterface13 * Fix ClrMD issue * More version nonsense --------- Co-authored-by: dotnet-maestro[bot] --- diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1a887696d..94651c880 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,13 +4,13 @@ https://github.com/dotnet/symstore 387e5a4d679b30fab748ddca70d088002f0034a8 - + https://github.com/microsoft/clrmd - be3a3abce108bdb335e76fddb40d202568410343 + ca77c991f8e4c1178152ec92000565c1b2b0f4dc - + https://github.com/microsoft/clrmd - be3a3abce108bdb335e76fddb40d202568410343 + ca77c991f8e4c1178152ec92000565c1b2b0f4dc diff --git a/eng/Versions.props b/eng/Versions.props index 39c692f0e..e4f99e0fb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ 5.0.0 1.1.0 - 2.3.411802 + 3.0.0-beta.23153.5 16.9.0-beta1.21055.5 3.0.7 diff --git a/src/Microsoft.Diagnostics.DebugServices/IMemoryRegion.cs b/src/Microsoft.Diagnostics.DebugServices/IMemoryRegion.cs index bfb0b2147..af9accb11 100644 --- a/src/Microsoft.Diagnostics.DebugServices/IMemoryRegion.cs +++ b/src/Microsoft.Diagnostics.DebugServices/IMemoryRegion.cs @@ -1,4 +1,6 @@ -namespace Microsoft.Diagnostics.DebugServices +using System; + +namespace Microsoft.Diagnostics.DebugServices { public enum MemoryRegionType { @@ -16,6 +18,7 @@ MEM_RESERVE = 0x2000 } + [Flags] public enum MemoryRegionProtection { PAGE_UNKNOWN = 0, diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs index e194fe476..56c0dcc01 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs @@ -720,16 +720,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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; } } @@ -748,21 +741,21 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMemoryPointer.cs b/src/Microsoft.Diagnostics.ExtensionCommands/ClrMemoryPointer.cs deleted file mode 100644 index d18054661..000000000 --- a/src/Microsoft.Diagnostics.ExtensionCommands/ClrMemoryPointer.cs +++ /dev/null @@ -1,163 +0,0 @@ -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; } - - /// - /// Size may be 0 if we do not know the size of a segment. - /// - 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; - } - - /// - /// Enumerates pointers to various CLR heaps in memory. - /// - public static IEnumerable 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 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 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 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, - } -} diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs new file mode 100644 index 000000000..2bf0f9b6a --- /dev/null +++ b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs @@ -0,0 +1,521 @@ +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(); + 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 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 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 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> 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 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 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 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 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 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.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.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 ephemeralSegments = gc_heap.Segments.Where(seg => seg.Kind == GCSegmentKind.Ephemeral || (seg.Kind >= GCSegmentKind.Generation0 && seg.Kind <= GCSegmentKind.Generation2)); + IEnumerable 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()); + } + } +} diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs index 287114ae6..0834e55d8 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs @@ -258,7 +258,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands foreach ((ulong Pointer, DescribedRegion Range) found in pointersFound) { - if (found.Range.ClrMemoryKind == ClrMemoryKind.GCHeapSegment) + if (found.Range.ClrMemoryKind == ClrMemoryKind.GCHeap) { if (pinnedOnly) { @@ -305,7 +305,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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))) { diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs index 5d3c9db06..0c7abe7ae 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs @@ -56,9 +56,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands { 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 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); @@ -71,9 +71,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands ClrRuntime clrRuntime = runtime.Services.GetService(); 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) { @@ -89,13 +89,13 @@ namespace Microsoft.Diagnostics.ExtensionCommands } // 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, @@ -113,13 +113,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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); @@ -141,7 +135,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands DescribedRegion middleRegion = new(region) { Start = mem.Address, - End = mem.Address + mem.Size, + End = mem.Address + mem.Size.Value, ClrMemoryKind = mem.Kind, Usage = MemoryRegionUsage.CLR, }; @@ -168,7 +162,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands // 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); @@ -186,14 +180,14 @@ namespace Microsoft.Diagnostics.ExtensionCommands // 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 }; @@ -209,7 +203,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands } } - var ranges = rangeList.OrderBy(r => r.Start).ToArray(); + DescribedRegion[] ranges = rangeList.OrderBy(r => r.Start).ToArray(); if (tagReserveMemoryHeuristically) { @@ -228,7 +222,44 @@ namespace Microsoft.Diagnostics.ExtensionCommands return ranges; } - private static void SetRegionKindWithWarning(ClrMemoryPointer mem, DescribedRegion region) + /// + /// Enumerates pointers to various CLR heaps in memory. + /// + 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) { @@ -238,7 +269,10 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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; @@ -256,7 +290,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands { DescribedRegion range = FindMemory(ranges, sp); if (range is not null) - range.Description = "Stack"; + range.Usage = MemoryRegionUsage.Stack; } } } @@ -322,7 +356,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands if (nonReserved is null) return null; - mem.Description = nonReserved.Name; + mem.PrevRegionName = nonReserved.Name; return nonReserved; } @@ -372,6 +406,29 @@ namespace Microsoft.Diagnostics.ExtensionCommands } } + // 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() @@ -400,8 +457,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands Protection = copyFrom.Protection; Usage = copyFrom.Usage; Image = copyFrom.Image; - Description = copyFrom.Description; ClrMemoryKind = copyFrom.ClrMemoryKind; + PrevRegionName = copyFrom.PrevRegionName; } public IModule Module { get; internal set; } @@ -420,12 +477,16 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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; + /// + /// Only used for heuristically marking reserve regions with what it might + /// be reserved for. + /// + public string PrevRegionName { get; internal set; } + public string Name { get @@ -438,11 +499,16 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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]"; diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs b/src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs index 60c9c71a1..7156e880a 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs @@ -12,6 +12,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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; @@ -117,6 +118,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands if (obj is null) return null; + if (obj is Enum) + return obj.ToString(); + return obj switch { nint ni => ni.ToString(format), @@ -124,6 +128,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands 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()), }; diff --git a/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs b/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs index 98b2b05ee..4ab800888 100644 --- a/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs +++ b/src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs @@ -38,7 +38,7 @@ namespace SOS.Extensions // 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) { diff --git a/src/SOS/SOS.Hosting/Commands/SOSCommand.cs b/src/SOS/SOS.Hosting/Commands/SOSCommand.cs index 1fbb30dd5..29161e329 100644 --- a/src/SOS/SOS.Hosting/Commands/SOSCommand.cs +++ b/src/SOS/SOS.Hosting/Commands/SOSCommand.cs @@ -33,7 +33,6 @@ namespace SOS.Hosting [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.")] diff --git a/src/SOS/SOS.UnitTests/Scripts/GCPOH.script b/src/SOS/SOS.UnitTests/Scripts/GCPOH.script index 3d6855a15..92c4fbb7b 100644 --- a/src/SOS/SOS.UnitTests/Scripts/GCPOH.script +++ b/src/SOS/SOS.UnitTests/Scripts/GCPOH.script @@ -57,12 +57,12 @@ VERIFY:\s+System Domain:\s+\s+ VERIFY:\s+LowFrequencyHeap:\s+.*bytes.*\s+ VERIFY:\s+HighFrequencyHeap:\s+.*bytes.*\s+ VERIFY:\s+Total size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ -VERIFY:\s+Jit code heap:\s+ -VERIFY:\s+LoaderCodeHeap:\s+.*bytes\.\s+ -VERIFY:\s+Total LoaderHeap size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ +VERIFY:\s+JIT Manager:\s+ +VERIFY:\s+(LoaderCodeHeap|HostCodeHeap):\s+.*bytes.* +VERIFY:\s+Total size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ VERIFY:\s+Number of GC Heaps:\s+\s+ VERIFY:\s+segment\s+begin\s+allocated\s+committed\s+allocated\s+size\s+committed\s+size\s+ -VERIFY:\s+\s+\s+\s+\s+0x\(\)\s+0x\(\)\s+ +VERIFY:\s+\s+\s+\s+\s+0x\s*\(\)\s+0x\s*\(\)\s+ VERIFY:\s+Large object heap.* VERIFY:\s+Pinned object heap.* diff --git a/src/SOS/SOS.UnitTests/Scripts/GCTests.script b/src/SOS/SOS.UnitTests/Scripts/GCTests.script index 72a2d5c9b..611b3ab1b 100644 --- a/src/SOS/SOS.UnitTests/Scripts/GCTests.script +++ b/src/SOS/SOS.UnitTests/Scripts/GCTests.script @@ -93,12 +93,12 @@ VERIFY:\s+System Domain:\s+\s+ VERIFY:\s+LowFrequencyHeap:\s+.*bytes.*\s+ VERIFY:\s+HighFrequencyHeap:\s+.*bytes.*\s+ VERIFY:\s+Total size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ -VERIFY:\s+Jit code heap:\s+ -VERIFY:\s+LoaderCodeHeap:\s+.*bytes\.\s+ -VERIFY:\s+Total LoaderHeap size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ +VERIFY:\s+JIT Manager:\s+ +VERIFY:\s+(LoaderCodeHeap|HostCodeHeap):\s+.*bytes.* +VERIFY:\s+Total size:\s+Size:\s+0x\s+\(|lu\)\s+bytes\.\s+ VERIFY:\s+Number of GC Heaps:\s+\s+ VERIFY:\s+segment\s+begin\s+allocated\s+committed\s+allocated\s+size\s+committed\s+size\s+ -VERIFY:\s+\s+\s+\s+\s+0x\(\)\s+0x\(\)\s+ +VERIFY:\s+\s+\s+\s+\s+0x\s*\(\)\s+0x\s*\(\)\s+ VERIFY:\s+Large object heap.* # Continue to the next DebugBreak diff --git a/src/SOS/Strike/eeheap.cpp b/src/SOS/Strike/eeheap.cpp index 2a31011cb..a7ac225e0 100644 --- a/src/SOS/Strike/eeheap.cpp +++ b/src/SOS/Strike/eeheap.cpp @@ -2050,343 +2050,3 @@ int GCHeapSnapshot::GetGeneration(CLRDATA_ADDRESS objectPointer) } 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 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 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("\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; -} diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 14462f05c..1eea825f2 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -374,6 +374,24 @@ GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames) 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: * * * @@ -3591,10 +3609,6 @@ DECLARE_API(DumpPermissionSet) } #endif // _DEBUG - -void GCPrintGenerationInfo(DacpGcHeapDetails &heap); -void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size); - #endif // FEATURE_PAL /**********************************************************************\ @@ -3605,200 +3619,8 @@ void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size); \**********************************************************************/ 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 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\n"); - } - else - { - allHeapSize += JitHeapInfo(); - } - - - // Module Data - { - int numModule; - ArrayHolder 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::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) @@ -16273,23 +16095,6 @@ DECLARE_API(runtimes) } #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. // diff --git a/src/SOS/Strike/util.h b/src/SOS/Strike/util.h index 39d943e5a..c9f989cb5 100644 --- a/src/SOS/Strike/util.h +++ b/src/SOS/Strike/util.h @@ -1771,20 +1771,8 @@ void isRetAddr(DWORD_PTR retAddr, DWORD_PTR* whereCalled); 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); diff --git a/src/shared/inc/sospriv.idl b/src/shared/inc/sospriv.idl index 102801f7e..b1a3b18e0 100644 --- a/src/shared/inc/sospriv.idl +++ b/src/shared/inc/sospriv.idl @@ -42,11 +42,13 @@ typedef int mdToken; 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); @@ -104,7 +106,7 @@ cpp_quote("#define _SOS_StackReference_") typedef enum SOSStackSourceType { - SOS_StackSourceIP, // Instuction pointer in managed code + SOS_StackSourceIP, // Instruction pointer in managed code SOS_StackSourceFrame, // clr!Frame } SOSStackSourceType; @@ -449,3 +451,26 @@ interface ISOSDacInterface11 : IUnknown 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); +} diff --git a/src/shared/pal/prebuilt/idl/sospriv_i.cpp b/src/shared/pal/prebuilt/idl/sospriv_i.cpp index 2f9afdc48..07f02d061 100644 --- a/src/shared/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/shared/pal/prebuilt/idl/sospriv_i.cpp @@ -5,11 +5,9 @@ /* 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: @@ -111,6 +109,12 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface10,0x90B8FCC3,0x7251,0x4B0A,0xAE,0x3D, 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 diff --git a/src/shared/pal/prebuilt/inc/sospriv.h b/src/shared/pal/prebuilt/inc/sospriv.h index 20496876b..cc3448099 100644 --- a/src/shared/pal/prebuilt/inc/sospriv.h +++ b/src/shared/pal/prebuilt/inc/sospriv.h @@ -196,7 +196,8 @@ typedef int VCSHeapType; #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, @@ -3071,6 +3072,110 @@ EXTERN_C const IID IID_ISOSDacInterface12; #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 */