Update to ClrMD 3.0, ISOSDacInterface13 (#3675)
authorLee Culver <leculver@microsoft.com>
Sat, 4 Mar 2023 17:15:28 +0000 (09:15 -0800)
committerGitHub <noreply@github.com>
Sat, 4 Mar 2023 17:15:28 +0000 (09:15 -0800)
* 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] <dotnet-maestro[bot]@users.noreply.github.com>
19 files changed:
eng/Version.Details.xml
eng/Versions.props
src/Microsoft.Diagnostics.DebugServices/IMemoryRegion.cs
src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs
src/Microsoft.Diagnostics.ExtensionCommands/ClrMemoryPointer.cs [deleted file]
src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.ExtensionCommands/FindPointersInCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs
src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs
src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs
src/SOS/SOS.Hosting/Commands/SOSCommand.cs
src/SOS/SOS.UnitTests/Scripts/GCPOH.script
src/SOS/SOS.UnitTests/Scripts/GCTests.script
src/SOS/Strike/eeheap.cpp
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.h
src/shared/inc/sospriv.idl
src/shared/pal/prebuilt/idl/sospriv_i.cpp
src/shared/pal/prebuilt/inc/sospriv.h

index 1a887696dfd6cb0b3baa4e14e354dcf717054d8b..94651c8808dc2a401fec62872373768c18950a16 100644 (file)
@@ -4,13 +4,13 @@
       <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>
index 39c692f0e13f4cbbb5e1bd3cf58b9ba4891ed2a7..e4f99e0fb49cbe89f0d6eea1b32a7c2c6a508e76 100644 (file)
@@ -45,7 +45,7 @@
     <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 -->
index bfb0b21474aafc78a16637fdf2f29f9aa0d7261a..af9accb11ea55f207b895655c137c5127bc124e2 100644 (file)
@@ -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,
index e194fe476286410e04b76c126d2d36608a756728..56c0dcc01b3e77736d07f8158caa544e6d9ce697 100644 (file)
@@ -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 (file)
index d180546..0000000
+++ /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; }
-
-        /// <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,
-    }
-}
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs
new file mode 100644 (file)
index 0000000..2bf0f9b
--- /dev/null
@@ -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<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());
+        }
+    }
+}
index 287114ae6650de1a282fb00bb45f8fcaea8fd3ce..0834e55d8d237dd1538d752cf855b41ec95875f6 100644 (file)
@@ -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)))
                 {
index 5d3c9db06d80299bf387f34aa8c36d5652a9bc1d..0c7abe7aebb464d461392d76ed99f3e9b1df7f64 100644 (file)
@@ -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<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);
@@ -71,9 +71,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                     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)
                             {
@@ -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)
+        /// <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)
             {
@@ -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;
 
+            /// <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
@@ -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]";
 
index 60c9c71a196867736c832b3f70825e9115fa001f..7156e880a05bdcd53a8be02a8283bedec43167af 100644 (file)
@@ -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()),
             };
index 98b2b05eee514f00dcfc906e4c51ed63cfea108b..4ab800888dc7fbb320a2fa39afb381835e065c8a 100644 (file)
@@ -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)
                 {
index 1fbb30dd50aca94c45db53970469bcaf0a3e7bd8..29161e329a1220e90b1e1be51a4942fc7eaaa87d 100644 (file)
@@ -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.")]
index 3d6855a155c96d6115b8882ddc509e4a4e3f4f34..92c4fbb7bdf5c1a160483d0818b275f43b55a0a0 100644 (file)
@@ -57,12 +57,12 @@ VERIFY:\s+System Domain:\s+<HEXVAL>\s+
 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.*
 
index 72a2d5c9bd521fac7981519cf35c17dd291f93cb..611b3ab1bf95e4659d2fd36553c979156c1abd62 100644 (file)
@@ -93,12 +93,12 @@ VERIFY:\s+System Domain:\s+<HEXVAL>\s+
 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
index 2a31011cbf8651d72fb6b09db8b07759e39a59b5..a7ac225e0283c42f3064de6a19e7036b2669bbb0 100644 (file)
@@ -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<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;
-}
index 14462f05caf73967ae724eab76c675145cfa0d60..1eea825f2f98744add500132aed7d08908d205b7 100644 (file)
@@ -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<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)
@@ -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.
 //
index 39d943e5a16f9d88307e12ea9d6de6beadd2a35a..c9f989cb5a59d609369466ea117bebea28a33f53 100644 (file)
@@ -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);
 
index 102801f7e5d3e78b75f121a6f780fde176e15d03..b1a3b18e06b8b9c28946c93c2bd19e051e1490af 100644 (file)
@@ -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);
+}
index 2f9afdc48e32919aa203f05fd69fb377cffb4717..07f02d061e86c450bb2d7f56dd91746cf94fe8c9 100644 (file)
@@ -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
index 20496876b04a49713df60444a0f593826bf24bb5..cc344809965608090921ef63da6776898bd465e4 100644 (file)
@@ -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 */