Reimplement !dso and !fq in C# (#3837)
authorLee Culver <leculver@microsoft.com>
Mon, 24 Apr 2023 15:14:01 +0000 (08:14 -0700)
committerGitHub <noreply@github.com>
Mon, 24 Apr 2023 15:14:01 +0000 (08:14 -0700)
* Add initial managed !fq command

* Update ClrMD version

* Finish managed !fq implementation

* Implement managed !dumpstackobjs

* Remove !dso and !fq C++ implementations

* Remove more dead code

* More dead code

* Remove ObjectSize helpers

* Remove usages of g_snapshot

* Remove GCHeap logic from SOS

* Cleanup

* Add RcwCleanup output

* Ensure what we don't read past stack range

* Change validation

* Update validation

* Add registers, fix output

* Remove !dso heading in C++

* Fix dso regexes

* Always print the statistics table

* Always print dumpheap statistics

* Add proper flush check

* Restore previous DumpHeapService behavior

Only print header when we have objects.

* Update ClrMD version

* Code review feedback

* TESTONLY:  Add !runtimes to get more diagnostics

* TESTONLY:  Fix previous command

* TESTONLY: Write all warning/errors to normal

* TESTONLY: Printf debugging

* Fix DumpStackObject failures. Remove the test only code. Enable sos logging to a file

---------

Co-authored-by: Mike McLaughlin <mikem@microsoft.com>
26 files changed:
eng/Version.Details.xml
eng/Versions.props
src/Microsoft.Diagnostics.ExtensionCommands/DumpHeapService.cs
src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.ExtensionCommands/EEHeapCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.ExtensionCommands/GCRootCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/LiveObjectService.cs
src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs
src/SOS/SOS.Hosting/Commands/SOSCommand.cs
src/SOS/SOS.UnitTests/SOS.cs
src/SOS/SOS.UnitTests/SOSRunner.cs
src/SOS/SOS.UnitTests/Scripts/GCPOH.script
src/SOS/SOS.UnitTests/Scripts/GCTests.script
src/SOS/SOS.UnitTests/Scripts/OtherCommands.script
src/SOS/SOS.UnitTests/Scripts/StackAndOtherTests.script
src/SOS/SOS.UnitTests/Scripts/StackTests.script
src/SOS/Strike/disasm.cpp
src/SOS/Strike/disasm.h
src/SOS/Strike/eeheap.cpp
src/SOS/Strike/exts.cpp
src/SOS/Strike/exts.h
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h
src/SOS/lldbplugin/soscommand.cpp

index 6dd5b18a971d9afbada897ce69512d89ba832850..53bdf10a6d154c110482088e4437315ee692d4ff 100644 (file)
@@ -4,13 +4,13 @@
       <Uri>https://github.com/dotnet/symstore</Uri>
       <Sha>cfbac6b5f6f9a628e89dbd38c074cbd41665820e</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23214.2">
+    <Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23219.1">
       <Uri>https://github.com/microsoft/clrmd</Uri>
-      <Sha>a85ab0d3e53eb7aef4b3a2f1d595da18342e8696</Sha>
+      <Sha>80b19b666752ae64301e33f819c39d4279ec0727</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23214.2">
+    <Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23219.1">
       <Uri>https://github.com/microsoft/clrmd</Uri>
-      <Sha>a85ab0d3e53eb7aef4b3a2f1d595da18342e8696</Sha>
+      <Sha>80b19b666752ae64301e33f819c39d4279ec0727</Sha>
     </Dependency>
   </ProductDependencies>
   <ToolsetDependencies>
index 85f4d5074d500b754878acd0d58f4f9948d51070..36b9b1096596d1ac78f8bb3bf841453835172b50 100644 (file)
@@ -45,7 +45,7 @@
     <SystemReflectionMetadataVersion>5.0.0</SystemReflectionMetadataVersion>
     <!-- Other libs -->
     <MicrosoftBclAsyncInterfacesVersion>6.0.0</MicrosoftBclAsyncInterfacesVersion>
-    <MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23214.2</MicrosoftDiagnosticsRuntimeVersion>
+    <MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23219.1</MicrosoftDiagnosticsRuntimeVersion>
     <MicrosoftDiaSymReaderNativePackageVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativePackageVersion>
     <MicrosoftDiagnosticsTracingTraceEventVersion>3.0.7</MicrosoftDiagnosticsTracingTraceEventVersion>
     <MicrosoftExtensionsLoggingVersion>6.0.0</MicrosoftExtensionsLoggingVersion>
index f9687f8f33504c8d3e249d2e7f03978159a0e0d6..3fddb73587b1bd3ba05e5dc9ee2ee25afdc6ce4e 100644 (file)
@@ -42,11 +42,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             Dictionary<ulong, (int Count, ulong Size, string TypeName)> stats = new();
 
             TableOutput thinLockOutput = null;
-            TableOutput objectTable = new(Console, (12, "x12"), (12, "x12"), (12, ""), (0, ""));
-            if (!statsOnly && (displayKind is DisplayKind.Normal or DisplayKind.Strings))
-            {
-                objectTable.WriteRow("Address", "MT", "Size");
-            }
+            TableOutput objectTable = null;
 
             ClrObject lastFreeObject = default;
             foreach (ClrObject obj in objects)
@@ -77,6 +73,15 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                 ulong size = obj.IsValid ? obj.Size : 0;
                 if (!statsOnly)
                 {
+                    if (objectTable is null)
+                    {
+                        objectTable = new(Console, (12, "x12"), (12, "x12"), (12, ""), (0, ""));
+                        if (displayKind is DisplayKind.Normal or DisplayKind.Strings)
+                        {
+                            objectTable.WriteRow("Address", "MT", "Size");
+                        }
+                    }
+
                     objectTable.WriteRow(new DmlDumpObj(obj), new DmlDumpHeap(obj.Type?.MethodTable ?? 0), size, obj.IsFree ? "Free" : "");
                 }
 
@@ -192,6 +197,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             }
             else if (displayKind == DisplayKind.Normal)
             {
+                // Print statistics table
                 if (stats.Count != 0)
                 {
                     // Print statistics table
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpStackObjectsCommand.cs
new file mode 100644 (file)
index 0000000..69f20de
--- /dev/null
@@ -0,0 +1,381 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+    [Command(Name = "dumpstackobjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")]
+    public class DumpStackObjectsCommand : CommandBase
+    {
+        [ServiceImport]
+        public IMemoryService MemoryService { get; set; }
+
+        [ServiceImport]
+        public IThread CurrentThread { get; set; }
+
+        [ServiceImport]
+        public IThreadService ThreadService { get; set; }
+
+        [ServiceImport]
+        public ClrRuntime Runtime { get; set; }
+
+        [Option(Name = "-verify", Help = "Verify each object and only print ones that are valid objects.")]
+        public bool Verify { get; set; }
+
+        [Argument(Name = "StackBounds", Help = "The top and bottom of the stack (in hex).")]
+        public string[] Bounds { get; set; }
+
+        public override void Invoke()
+        {
+            if (Runtime.Heap.Segments.Length == 0)
+            {
+                throw new DiagnosticsException("Cannot walk heap.");
+            }
+
+            MemoryRange range;
+            if (Bounds is null || Bounds.Length == 0)
+            {
+                range = GetStackRange();
+            }
+            else if (Bounds.Length == 2)
+            {
+                ulong start = ParseAddress(Bounds[0]) ?? throw new ArgumentException($"Failed to parse start address '{Bounds[0]}'.");
+                ulong end = ParseAddress(Bounds[1]) ?? throw new ArgumentException($"Failed to parse end address '{Bounds[1]}'.");
+                if (start > end)
+                {
+                    (start, end) = (end, start);
+                }
+
+                range = new(AlignDown(start), AlignUp(end));
+            }
+            else
+            {
+                throw new ArgumentException("Invalid arguments.");
+            }
+
+            if (range.Start == 0 || range.End == 0)
+            {
+                throw new ArgumentException($"Invalid range {range.Start:x} - {range.End:x}");
+            }
+
+            PrintStackObjects(range);
+        }
+
+        private void PrintStackObjects(MemoryRange stack)
+        {
+            Console.WriteLine($"OS Thread Id: 0x{CurrentThread.ThreadId:x} ({CurrentThread.ThreadIndex})");
+
+            TableOutput output = new(Console, (16, "x12"), (16, "x12"));
+            output.WriteRow("SP/REG", "Object", "Name");
+
+            int regCount = ThreadService.Registers.Count();
+            foreach ((ulong address, ClrObject obj) in EnumerateValidObjectsWithinRange(stack).OrderBy(r => r.StackAddress))
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                if (address < (ulong)regCount)
+                {
+                    string registerName;
+                    if (ThreadService.TryGetRegisterInfo((int)address, out RegisterInfo regInfo))
+                    {
+                        registerName = regInfo.RegisterName;
+                    }
+                    else
+                    {
+                        registerName = $"reg{address}";
+                    }
+
+                    output.WriteRow(registerName, obj.Address, obj.Type?.Name);
+                }
+                else
+                {
+                    output.WriteRow(address, obj.Address, obj.Type?.Name);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Enumerates all valid objects (and the address they came from) within the given range.
+        /// </summary>
+        private IEnumerable<(ulong StackAddress, ClrObject Object)> EnumerateValidObjectsWithinRange(MemoryRange range)
+        {
+            // Note: This implementation is careful to enumerate only real objects and not generate a lot of native
+            //       exceptions within the dac.  A naïve implementation could simply read every pointer aligned address
+            //       and call ClrHeap.GetObject(objAddr).IsValid.  That approach will generate a lot of exceptions
+            //       within the dac trying to validate wild pointers as MethodTables, and it will often find old
+            //       pointers which the GC has already swept but not zeroed yet.
+
+            // Sort the list of potential objects so that we can go through each in segment order.
+            // Sorting this array saves us a lot of time by not searching for segments.
+            IEnumerable<(ulong StackAddress, ulong PotentialObject)> potentialObjects = EnumeratePointersWithinHeapBounds(range);
+            potentialObjects = potentialObjects.Concat(EnumerateRegistersWithinHeapBounds());
+            potentialObjects = potentialObjects.OrderBy(r => r.PotentialObject);
+
+            ClrSegment currSegment = null;
+            List<(ulong StackAddress, ulong PotentialObject)> withinCurrSegment = new(64);
+            int segmentIndex = 0;
+            foreach ((ulong _, ulong PotentialObject) entry in potentialObjects)
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                // Find the segment of the current potential object, or null if it doesn't live
+                // within a segment.
+                ClrSegment segment = GetSegment(entry.PotentialObject, ref segmentIndex);
+                if (segment is null)
+                {
+                    continue;
+                }
+
+                // If we are already processing this segment, just add the entry to the list
+                if (currSegment == segment)
+                {
+                    withinCurrSegment.Add(entry);
+                    continue;
+                }
+
+                // We are finished walking objects from "currSegment".  If we found any pointers
+                // within its range, walk the segment and return every valid object.
+                if (withinCurrSegment.Count > 0)
+                {
+                    foreach ((ulong StackAddress, ClrObject Object) validObject in EnumerateObjectsOnSegment(withinCurrSegment, currSegment))
+                    {
+                        yield return validObject;
+                    }
+
+                    withinCurrSegment.Clear();
+                }
+
+                // Update currSegment and add this entry to the processing list.
+                currSegment = segment;
+                withinCurrSegment.Add(entry);
+            }
+
+            // Process leftover items
+            if (withinCurrSegment.Count > 0)
+            {
+                foreach ((ulong StackAddress, ClrObject Object) validObject in EnumerateObjectsOnSegment(withinCurrSegment, currSegment))
+                {
+                    yield return validObject;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Simultaneously walks the withinCurrSegment list and objects on segment returning valid objects found.
+        /// </summary>
+        private IEnumerable<(ulong StackAddress, ClrObject Object)> EnumerateObjectsOnSegment(List<(ulong StackAddress, ulong PotentialObject)> withinCurrSegment, ClrSegment segment)
+        {
+            if (withinCurrSegment.Count == 0)
+            {
+                yield break;
+            }
+
+            int index = 0;
+            MemoryRange range = new(withinCurrSegment[0].PotentialObject, withinCurrSegment[withinCurrSegment.Count - 1].PotentialObject + 1);
+            foreach (ClrObject obj in segment.EnumerateObjects(range, carefully: true))
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                if (index >= withinCurrSegment.Count)
+                {
+                    yield break;
+                }
+
+                while (index < withinCurrSegment.Count && withinCurrSegment[index].PotentialObject < obj)
+                {
+                    Console.CancellationToken.ThrowIfCancellationRequested();
+
+                    index++;
+                }
+
+                while (index < withinCurrSegment.Count && obj == withinCurrSegment[index].PotentialObject)
+                {
+                    Console.CancellationToken.ThrowIfCancellationRequested();
+
+                    if (Verify)
+                    {
+                        if (!Runtime.Heap.IsObjectCorrupted(obj, out _))
+                        {
+                            yield return (withinCurrSegment[index].StackAddress, obj);
+                        }
+                    }
+                    else
+                    {
+                        yield return (withinCurrSegment[index].StackAddress, obj);
+                    }
+
+                    index++;
+                }
+            }
+        }
+
+        private ClrSegment GetSegment(ulong potentialObject, ref int segmentIndex)
+        {
+            ImmutableArray<ClrSegment> segments = Runtime.Heap.Segments;
+
+            // This function assumes that segmentIndex is always within the bounds of segments
+            // and that all objects passed to it are within the given
+            // range of segment bounds.
+            Debug.Assert(segmentIndex >= 0 && segmentIndex <= segments.Length);
+            Debug.Assert(segments[0].ObjectRange.Start < potentialObject);
+            Debug.Assert(potentialObject < segments[segments.Length - 1].ObjectRange.End);
+
+            for (; segmentIndex < segments.Length; segmentIndex++)
+            {
+                ClrSegment curr = segments[segmentIndex];
+                if (potentialObject < curr.Start)
+                {
+                    return null;
+                }
+                else if (potentialObject < curr.ObjectRange.End)
+                {
+                    return segments[segmentIndex];
+                }
+            }
+
+            // Unreachable.
+            Debug.Fail("Reached the end of the segment array.");
+            return null;
+        }
+
+        private IEnumerable<(ulong RegisterIndex, ulong PotentialObject)> EnumerateRegistersWithinHeapBounds()
+        {
+            ClrHeap heap = Runtime.Heap;
+
+            // Segments are always sorted by address
+            ulong minAddress = heap.Segments[0].ObjectRange.Start;
+            ulong maxAddress = heap.Segments[heap.Segments.Length - 1].ObjectRange.End - (uint)MemoryService.PointerSize;
+
+            int regCount = ThreadService.Registers.Count();
+            for (int i = 0; i < regCount; i++)
+            {
+                if (CurrentThread.TryGetRegisterValue(i, out ulong value))
+                {
+                    if (minAddress <= value && value < maxAddress)
+                    {
+                        yield return ((ulong)i, value);
+                    }
+                }
+            }
+        }
+
+        private IEnumerable<(ulong StackAddress, ulong PotentialObject)> EnumeratePointersWithinHeapBounds(MemoryRange stack)
+        {
+            Debug.Assert(AlignDown(stack.Start) == stack.Start);
+            Debug.Assert(AlignUp(stack.End) == stack.End);
+
+            uint pointerSize = (uint)MemoryService.PointerSize;
+            ClrHeap heap = Runtime.Heap;
+
+            // Segments are always sorted by address
+            ulong minAddress = heap.Segments[0].ObjectRange.Start;
+            ulong maxAddress = heap.Segments[heap.Segments.Length - 1].ObjectRange.End - pointerSize;
+
+            // Read in 64k chunks
+            byte[] buffer = ArrayPool<byte>.Shared.Rent(64 * 1024);
+            try
+            {
+                ulong address = stack.Start;
+                while (stack.Contains(address))
+                {
+                    Console.CancellationToken.ThrowIfCancellationRequested();
+
+                    if (!MemoryService.ReadMemory(address, buffer, out int read))
+                    {
+                        break;
+                    }
+
+                    read = AlignDown(read);
+                    if (read < pointerSize)
+                    {
+                        break;
+                    }
+
+                    for (int i = 0; i < read; i += (int)pointerSize)
+                    {
+                        Console.CancellationToken.ThrowIfCancellationRequested();
+
+                        ulong stackAddress = address + (uint)i;
+                        if (!stack.Contains(stackAddress))
+                        {
+                            yield break;
+                        }
+
+                        ulong potentialObj = GetIndex(buffer, i);
+                        if (minAddress <= potentialObj && potentialObj < maxAddress)
+                        {
+                            yield return (stackAddress, potentialObj);
+                        }
+                    }
+
+                    address += (uint)read;
+                }
+            }
+            finally
+            {
+                ArrayPool<byte>.Shared.Return(buffer);
+            }
+        }
+
+        private static ulong GetIndex(Span<byte> buffer, int i) => Unsafe.As<byte, nuint>(ref buffer[i]);
+
+        private MemoryRange GetStackRange()
+        {
+            ulong end = 0;
+
+            int spIndex = ThreadService.StackPointerIndex;
+            if (!CurrentThread.TryGetRegisterValue(spIndex, out ulong stackPointer))
+            {
+                throw new DiagnosticsException($"Unable to get the stack pointer for thread {CurrentThread.ThreadId:x}.");
+            }
+
+            // On Windows we have the TEB to know where to end the walk.
+            ulong teb = CurrentThread.GetThreadTeb();
+            if (teb != 0)
+            {
+                // The stack base is after the first pointer, see TEB and NT_TIB.
+                MemoryService.ReadPointer(teb + (uint)MemoryService.PointerSize, out end);
+            }
+
+            if (end == 0)
+            {
+                end = stackPointer + 0xFFFF;
+            }
+
+            return new(AlignDown(stackPointer), AlignUp(end));
+        }
+
+        private ulong AlignDown(ulong address)
+        {
+            ulong mask = ~((ulong)MemoryService.PointerSize - 1);
+            return address & mask;
+        }
+
+        private int AlignDown(int value)
+        {
+            int mask = ~(MemoryService.PointerSize - 1);
+            return value & mask;
+        }
+
+        private ulong AlignUp(ulong address)
+        {
+            ulong pointerSize = (ulong)MemoryService.PointerSize;
+            if (address > ulong.MaxValue - pointerSize)
+            {
+                return AlignDown(address);
+            }
+
+            ulong mask = ~(pointerSize - 1);
+            return (address + pointerSize - 1) & mask;
+        }
+    }
+}
index a7fe9e3dd4f9701d4a8b0fa9c2cfb134741af059..326f2efef2a47d92f1a9c919f2e7eb461b075a20 100644 (file)
@@ -151,7 +151,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
             IOrderedEnumerable<IGrouping<NativeHeapKind, ClrNativeHeapInfo>> filteredHeapsByKind = from heap in appDomain.EnumerateLoaderAllocatorHeaps()
                                                                                                    where IsIncludedInFilter(heap)
-                                                                                                   where loaderAllocatorsSeen.Add(heap.Address)
+                                                                                                   where loaderAllocatorsSeen.Add(heap.MemoryRange.Start)
                                                                                                    group heap by heap.Kind into g
                                                                                                    orderby GetSortOrder(g.Key)
                                                                                                    select g;
@@ -244,7 +244,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             {
                 output.WriteRow("JIT Manager:", jitManager.Address);
 
-                IEnumerable<ClrNativeHeapInfo> heaps = jitManager.EnumerateNativeHeaps().Where(IsIncludedInFilter).OrderBy(r => r.Kind).ThenBy(r => r.Address);
+                IEnumerable<ClrNativeHeapInfo> heaps = jitManager.EnumerateNativeHeaps().Where(IsIncludedInFilter).OrderBy(r => r.Kind).ThenBy(r => r.MemoryRange.Start);
 
                 ulong jitMgrSize = 0, jitMgrWasted = 0;
                 foreach (ClrNativeHeapInfo heap in heaps)
@@ -284,15 +284,15 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                 return true;
             }
 
-            if (filterRange.Contains(info.Address))
+            if (filterRange.Contains(info.MemoryRange.Start))
             {
                 return true;
             }
 
-            if (info.Size is ulong size && size > 0)
+            if (info.MemoryRange.Length > 0)
             {
                 // Check for the last valid address in the range
-                return filterRange.Contains(info.Address + size - 1);
+                return filterRange.Contains(info.MemoryRange.End - 1);
             }
 
             return false;
@@ -300,14 +300,15 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
         private (ulong Size, ulong Wasted) CalculateSizeAndWasted(StringBuilder sb, ClrNativeHeapInfo heap)
         {
-            sb.Append(heap.Address.ToString("x12"));
+            sb.Append(heap.MemoryRange.Start.ToString("x12"));
 
-            if (heap.Size is ulong size)
+            ulong size = heap.MemoryRange.Length;
+            if (size > 0)
             {
                 sb.Append('(');
                 sb.Append(size.ToString("x"));
                 sb.Append(':');
-                ulong actualSize = GetActualSize(heap.Address, size);
+                ulong actualSize = GetActualSize(heap.MemoryRange.Start, size);
                 sb.Append(actualSize.ToString("x"));
                 sb.Append(')');
 
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/FinalizeQueueCommand.cs
new file mode 100644 (file)
index 0000000..f413b5c
--- /dev/null
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+    [Command(Name = "finalizequeue", Help = "Displays all objects registered for finalization.")]
+    public class FinalizeQueueCommand : CommandBase
+    {
+        [Option(Name = "-detail", Help = "Will display extra information on any SyncBlocks that need to be cleaned up, and on any RuntimeCallableWrappers (RCWs) that await cleanup.  Both of these data structures are cached and cleaned up by the finalizer thread when it gets a chance to run.")]
+        public bool Detail { get; set; }
+
+        [Option(Name = "-allReady", Help = "Specifying this argument will allow for the display of all objects  that are ready for finalization, whether they are already marked by  the GC as such, or whether the next GC will.  The objects that are  not in the \"Ready for finalization\" list are finalizable objects that  are no longer rooted.  This option can be very expensive, as it  verifies whether all the objects in the finalizable queues are still  rooted or not.")]
+        public bool AllReady { get; set; }
+
+        [Option(Name = "-short", Help = "Limits the output to just the address of each object.  If used in conjunction with -allReady it enumerates all objects that have a finalizer that are no longer rooted.  If used independently it lists all objects in the finalizable and \"ready for finalization\" queues.")]
+        public bool Short { get; set; }
+
+        [Option(Name = "-mt", Help = "Limits the search for finalizable objects to only those matching the given MethodTable.")]
+        public string MethodTable { get; set; }
+
+        [Option(Name = "-stat", Aliases = new string[] { "-summary" }, Help = "Only print object statistics, not the list of all objects.")]
+        public bool Stat { get; set; }
+
+        [ServiceImport]
+        public LiveObjectService LiveObjects { get; set; }
+
+        [ServiceImport]
+        public RootCacheService RootCache { get; set; }
+
+        [ServiceImport]
+        public DumpHeapService DumpHeap { get; set; }
+
+        [ServiceImport]
+        public ClrRuntime Runtime { get; set; }
+
+        public override void Invoke()
+        {
+            ulong mt = 0;
+            if (!string.IsNullOrWhiteSpace(MethodTable))
+            {
+                mt = ParseAddress(MethodTable) ?? throw new ArgumentException($"Could not parse MethodTable: '{MethodTable}'");
+            }
+
+            if (Short && Stat)
+            {
+                throw new ArgumentException("Cannot specify both -short and -stat.");
+            }
+
+            // If we are going to search for only live objects, be sure to print a warning first
+            // in the output of the command instead of in between the rest of the output.
+            if (AllReady)
+            {
+                LiveObjects.PrintWarning = true;
+                LiveObjects.Initialize();
+            }
+
+            if (!Short)
+            {
+                PrintSyncBlockCleanupData();
+                PrintRcwCleanupData();
+                Console.WriteLine("----------------------------------");
+                Console.WriteLine();
+
+                PrintGenerationalRanges();
+
+                if (AllReady)
+                {
+                    Console.WriteLine("Statistics for all finalizable objects that are no longer rooted:");
+                }
+                else
+                {
+                    Console.WriteLine("Statistics for all finalizable objects (including all objects ready for finalization):");
+                }
+            }
+
+            IEnumerable<ClrObject> objects = EnumerateFinalizableObjects(AllReady, mt);
+            DumpHeapService.DisplayKind displayKind = Short ? DumpHeapService.DisplayKind.Short : DumpHeapService.DisplayKind.Normal;
+
+            DumpHeap.PrintHeap(objects, displayKind, Stat, printFragmentation: false);
+
+        }
+        private IEnumerable<ClrObject> EnumerateFinalizableObjects(bool allReady, ulong mt)
+        {
+            IEnumerable<ClrObject> result = EnumerateValidFinalizableObjectsWithTypeFilter(mt);
+
+            if (allReady)
+            {
+                HashSet<ulong> rootedByFinalizer = new();
+                foreach (ClrRoot root in Runtime.Heap.EnumerateFinalizerRoots())
+                {
+                    Console.CancellationToken.ThrowIfCancellationRequested();
+
+                    ClrObject obj = root.Object;
+                    if (obj.IsValid)
+                    {
+                        rootedByFinalizer.Add(obj);
+                    }
+                }
+
+                // We are trying to find all objects that are ready to be finalized, which is essentially
+                // all dead objects.  However, objects which were previously collected but waiting on
+                // the finalizer thread to process them are considered "live" because they are rooted by
+                // the finalizer queue.  So our result needs to be either dead objects or directly rooted
+                // by the finalizer queue.
+                result = result.Where(obj => rootedByFinalizer.Contains(obj) || !LiveObjects.IsLive(obj));
+            }
+
+            return result;
+        }
+
+        private IEnumerable<ClrObject> EnumerateValidFinalizableObjectsWithTypeFilter(ulong mt)
+        {
+            foreach (ClrObject obj in Runtime.Heap.EnumerateFinalizableObjects())
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                if (!obj.IsValid)
+                {
+                    continue;
+                }
+
+                if (mt != 0 && obj.Type.MethodTable != mt)
+                {
+                    continue;
+                }
+
+                yield return obj;
+            }
+        }
+
+        private void PrintSyncBlockCleanupData()
+        {
+            TableOutput output = null;
+            int total = 0;
+            foreach (ClrSyncBlockCleanupData cleanup in Runtime.EnumerateSyncBlockCleanupData())
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                if (output is null)
+                {
+                    output = new(Console, (16, "x12"), (16, "x12"), (16, "x12"));
+                    output.WriteRow("SyncBlock", "RCW", "CCW", "ComClassFactory");
+                }
+
+                output.WriteRow(cleanup.SyncBlock, cleanup.Rcw, cleanup.Ccw, cleanup.ClassFactory);
+                total++;
+            }
+
+            Console.WriteLine($"SyncBlocks to be cleaned up: {total:n0}");
+        }
+
+        private void PrintRcwCleanupData()
+        {
+            TableOutput output = null;
+            int freeThreadedCount = 0;
+            int mtaCount = 0;
+            int staCount = 0;
+
+            foreach (ClrRcwCleanupData cleanup in Runtime.EnumerateRcwCleanupData())
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                if (output is null)
+                {
+                    output = new(Console, (16, "x12"), (16, "x12"), (16, "x12"));
+                    output.WriteRow("RCW", "Context", "Thread", "Apartment");
+                }
+
+                string apartment;
+                if (cleanup.IsFreeThreaded)
+                {
+                    freeThreadedCount++;
+                    apartment = "(FreeThreaded)";
+                }
+                else if (cleanup.Thread == 0)
+                {
+                    mtaCount++;
+                    apartment = "(MTA)";
+                }
+                else
+                {
+                    staCount++;
+                    apartment = "(STA)";
+                }
+
+                output.WriteRow(cleanup.Rcw, cleanup.Context, cleanup.Thread, apartment);
+            }
+
+            Console.WriteLine($"Free-Threaded Interfaces to be released: {freeThreadedCount:n0}");
+            Console.WriteLine($"MTA Interfaces to be released: {mtaCount:n0}");
+            Console.WriteLine($"STA Interfaces to be released: {staCount:n0}");
+        }
+
+        private void PrintGenerationalRanges()
+        {
+            foreach (ClrSubHeap heap in Runtime.Heap.SubHeaps)
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+
+                Console.WriteLine($"Heap {heap.Index}");
+
+                WriteGeneration(heap, 0);
+                WriteGeneration(heap, 1);
+                WriteGeneration(heap, 2);
+
+                Console.WriteLine($"Ready for finalization {heap.FinalizerQueueRoots.Length / (uint)IntPtr.Size:n0} objects ({heap.FinalizerQueueRoots.Start:x}->{heap.FinalizerQueueRoots.End:x})");
+
+                Console.WriteLine("------------------------------");
+            }
+        }
+
+        private void WriteGeneration(ClrSubHeap heap, int gen)
+        {
+            MemoryRange range = heap.GenerationalFinalizableObjects[gen];
+            Console.WriteLine($"generation {gen} has {range.Length / (uint)IntPtr.Size:n0} objects ({range.Start:x}->{range.End:x})");
+        }
+    }
+}
index 4edb693492289a564a105d9b310a910daba7f009..abe025273c0ca04df4ddf1adda070896d8482151 100644 (file)
@@ -61,6 +61,22 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             if (AsGCGeneration.HasValue)
             {
                 int gen = AsGCGeneration.Value;
+
+                ClrSegment seg = Runtime.Heap.GetSegmentByAddress(address);
+                if (seg is null)
+                {
+                    Console.WriteLineError($"Address {address:x} is not in the managed heap.");
+                    return;
+                }
+
+                Generation objectGen = seg.GetGeneration(address);
+                if (gen < (int)objectGen)
+                {
+                    Console.WriteLine($"Object {address:x} will survive this collection:");
+                    Console.WriteLine($"    gen({address:x}) = {objectGen} > {gen} = condemned generation.");
+                    return;
+                }
+
                 if (gen < 0 || gen > 1)
                 {
                     // If not gen0 or gen1, treat it as a normal !gcroot
index ed39e0de0b5d36c6183d103c732b99a25f966e57..a77b997eba1acac6d6bc9a8e2c5f5d0f7de8db3e 100644 (file)
@@ -35,6 +35,11 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             return _liveObjs.Contains(obj);
         }
 
+        public void Initialize()
+        {
+            _liveObjs ??= CreateObjectSet();
+        }
+
         private HashSet<ulong> CreateObjectSet()
         {
             ClrHeap heap = Runtime.Heap;
index 2f48a813370e2f3744c2c40b1ea49cdbefa8e46f..9658d026e35c8f6c52d17d7c0ee21c7bdf81dc05 100644 (file)
@@ -13,10 +13,19 @@ using Microsoft.Diagnostics.Runtime;
 namespace Microsoft.Diagnostics.ExtensionCommands
 {
     [ServiceExport(Scope = ServiceScope.Target)]
-    public sealed class NativeAddressHelper
+    public sealed class NativeAddressHelper : IDisposable
     {
+        private readonly IDisposable _onFlushEvent;
         private ((bool, bool, bool, bool) Key, DescribedRegion[] Result) _previous;
 
+        public NativeAddressHelper(ITarget target)
+        {
+            Target = target;
+            _onFlushEvent = target.OnFlushEvent.Register(() => _previous = default);
+        }
+
+        public void Dispose() => _onFlushEvent.Dispose();
+
         [ServiceImport]
         public ITarget Target { get; set; }
 
@@ -301,7 +310,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                     _ => ClrMemoryKind.Unknown
                 };
 
-                yield return (nativeHeap.Address, nativeHeap.Size ?? 0, kind);
+                yield return (nativeHeap.MemoryRange.Start, nativeHeap.MemoryRange.Length, kind);
             }
 
             // .Net 8 and beyond has accurate HandleTable memory info.
index d398b194598629143fbd744d68b6c07880f3c70d..1190fd29abeff5ec0bd86c56d47f82edf88c5c58 100644 (file)
@@ -27,12 +27,10 @@ namespace SOS.Hosting
     [Command(Name = "dumpobj",           DefaultOptions = "DumpObj",             Aliases = new string[] { "do" }, Help = "Displays info about an object at the specified address.")]
     [Command(Name = "dumpsig",           DefaultOptions = "DumpSig",             Help = "Dumps the signature of a method or field specified by <sigaddr> <moduleaddr>.")]
     [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 = "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.")]
-    [Command(Name = "finalizequeue",     DefaultOptions = "FinalizeQueue",       Help = "Displays all objects registered for finalization.")]
     [Command(Name = "findappdomain",     DefaultOptions = "FindAppDomain",       Help = "Attempts to resolve the AppDomain of a GC object.")]
     [Command(Name = "gchandles",         DefaultOptions = "GCHandles",           Help = "Provides statistics about GCHandles in the process.")]
     [Command(Name = "gcinfo",            DefaultOptions = "GCInfo",              Help = "Displays JIT GC encoding for a method.")]
index ae55c129bf012290cc278d27e6604713782354c2..4332a63da8df930dbe73dbf59186291320746eef 100644 (file)
@@ -340,8 +340,7 @@ public class SOS
 
             // This debuggee needs the directory of the exes/dlls to load the SymbolTestDll assembly.
             await SOSTestHelpers.RunTest(
-                scriptName: "StackAndOtherTests.script",
-                new SOSRunner.TestInformation
+                scriptName: "StackAndOtherTests.script", new SOSRunner.TestInformation
                 {
                     TestConfiguration = currentConfig,
                     TestName = "SOS.StackAndOtherTests",
index e1f993f3be41c0a20f6fde4bc85bc4e46055491f..98366fb0d005ecde3705813f5de9981eb13473ac 100644 (file)
@@ -145,6 +145,8 @@ public class SOSRunner : IDisposable
 
         public string DumpNameSuffix { get; set; }
 
+        public bool EnableSOSLogging { get; set; } = true;
+
         public bool TestCrashReport
         {
             get { return _testCrashReport && DumpGenerator == DumpGenerator.CreateDump && OS.Kind != OSKind.Windows && TestConfiguration.RuntimeFrameworkVersionMajor >= 6; }
@@ -457,6 +459,7 @@ public class SOSRunner : IDisposable
         {
             // Setup the logging from the options in the config file
             outputHelper = TestRunner.ConfigureLogging(config, information.OutputHelper, information.TestName);
+            string sosLogFile = information.EnableSOSLogging ? Path.Combine(config.LogDirPath, $"{information.TestName}.{config.LogSuffix}.soslog") : null;
 
             // Figure out which native debugger to use
             NativeDebugger debugger = GetNativeDebuggerToUse(config, action);
@@ -669,6 +672,7 @@ public class SOSRunner : IDisposable
                     break;
             }
 
+
             // Create the native debugger process running
             ProcessRunner processRunner = new ProcessRunner(debuggerPath, ReplaceVariables(variables, arguments.ToString())).
                 WithEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0").
@@ -682,6 +686,11 @@ public class SOSRunner : IDisposable
                 processRunner.WithExpectedExitCode(0);
             }
 
+            if (sosLogFile != null)
+            {
+                processRunner.WithEnvironmentVariable("DOTNET_ENABLED_SOS_LOGGING", sosLogFile);
+            }
+
             // Disable W^E so that the bpmd command and the tests pass
             // Issue: https://github.com/dotnet/diagnostics/issues/3126
             processRunner.WithRuntimeConfiguration("EnableWriteXorExecute", "0");
@@ -1420,6 +1429,11 @@ public class SOSRunner : IDisposable
                 defines.Add("UNIX_SINGLE_FILE_APP");
             }
         }
+        string setHostRuntime = _config.SetHostRuntime();
+        if (!string.IsNullOrEmpty(setHostRuntime) && setHostRuntime == "-none")
+        {
+            defines.Add("HOST_RUNTIME_NONE");
+        }
         return defines;
     }
 
index 4873d7037b8df4bda4267b4b123d3e8de0fc4e26..2c223a6bf01e4834bf3434a5f9e3f1a3c5b88e05 100644 (file)
@@ -18,7 +18,7 @@ ENDIF:CDB
 LOADSOS
 
 SOSCOMMAND:DumpStackObjects
-VERIFY:<HEXVAL>\s+<HEXVAL>\s+System.Byte\[\]\s+
+VERIFY:\s*<HEXVAL>\s+<HEXVAL>\s+System.Byte\[\]\s+
 
 SOSCOMMAND:DumpObj <POUT>\w+\s+(<HEXVAL>)\s+(System.Byte\[\]!\$0_)*System.Byte\[\]\s+<POUT>
 VERIFY:\s+Name:        System.Byte\[\]\s+
index 96f558f78090b414b1aea6b94c4655431a2de742..38be904a97016251cd14ee538f05d2862861d477 100644 (file)
@@ -24,7 +24,7 @@ ENDIF:CDB
 LOADSOS
 
 SOSCOMMAND:DumpStackObjects
-VERIFY:<HEXVAL>\s+<HEXVAL>\s+System.IO.StringWriter\s+
+VERIFY:\s*<HEXVAL>\s+<HEXVAL>\s+System.IO.StringWriter\s+
 
 SOSCOMMAND:DumpObj <POUT>\w+\s+(<HEXVAL>)\s+(System.IO.StringWriter!\$0_)*System.IO.StringWriter\s+<POUT>
 IFDEF:MAJOR_RUNTIME_VERSION_2
@@ -36,7 +36,7 @@ VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+System\.IO.TextWriter\s+<DECVAL>\s+st
 ENDIF:MAJOR_RUNTIME_VERSION_GE_3
 
 SOSCOMMAND:DumpStackObjects
-VERIFY:<HEXVAL>\s<HEXVAL>\s([Gg][Cc]where!\$0_)*GCWhere\s+
+VERIFY:\s*<HEXVAL>\s+<HEXVAL>\s+([Gg][Cc]where!\$0_)*GCWhere\s+
 
 SOSCOMMAND:GCWhere <POUT>\w+\s+(<HEXVAL>)\s+([Gg][Cc]where!\$0_)*GCWhere\s+<POUT>
 # we care that the Gen is 0
index 62e5c9b35900803a2e30513498f7ba19b1bdf257..8274219dcd379034b6ba82634292c0a7caf9cc04 100644 (file)
@@ -126,9 +126,9 @@ SOSCOMMAND:FinalizeQueue
 VERIFY:\s*SyncBlocks to be cleaned up:\s+<DECVAL>\s+
 VERIFY:(\s*Free-Threaded Interfaces to be released:\s+<DECVAL>\s+)?
 VERIFY:\s*Statistics for all finalizable objects.*:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
+VERIFY:\s+Address\s+MT\s+Size\s+
 VERIFY:(\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*)?
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
+VERIFY:\s*Total\s+<DECVAL>\s+objects.*<DECVAL>\s+bytes\s*
 
 EXTCOMMAND:logopen %LOG_PATH%/%TEST_NAME%.%LOG_SUFFIX%.consolelog
 EXTCOMMAND:logging %LOG_PATH%/%TEST_NAME%.%LOG_SUFFIX%.diaglog
index 685b0d2edfb7ffd7dbec8a4f898e26d66cde890e..5c31e0ef1dd939d0fce3d75c5066618ed795bb6c 100644 (file)
@@ -180,19 +180,25 @@ VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+\[DEFAULT\] I4 SymbolTestApp\.Program\.Foo1\(.
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+\[DEFAULT\] Void SymbolTestApp\.Program\.Main\(.*\)\s+\(.*\)\s+
 VERIFY:.*\s+Stack walk complete.\s+
 
+SOSCOMMAND: runtimes
+
+!IFDEF:HOST_RUNTIME_NONE
+
 # Verify DumpStackObjects works
 SOSCOMMAND:DumpStackObjects
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
-VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+
-VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
-VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
+VERIFY:\s*SP/REG\s+Object\s+Name\s+
+VERIFY:.*\s*<HEXVAL>\s+<HEXVAL>\s+System\.String.*
+VERIFY:.*\s*<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
 
 # Verify DumpStackObjects -verify works
 SOSCOMMAND:DumpStackObjects -verify
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
-VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+
-VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
-VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
+VERIFY:\s*SP/REG\s+Object\s+Name\s+
+VERIFY:.*\s*<HEXVAL>\s+<HEXVAL>\s+System\.String.*
+VERIFY:.*\s*<HEXVAL>\s+<HEXVAL>\s+System\.String\[\].*
+
+ENDIF:HOST_RUNTIME_NONE
 
 ENDIF:NETCORE_OR_DOTNETDUMP
 
index 57a5d87ee3e1e865fb51e13ea001d84ce3ab87b2..002a019e0c4bb1e18b7de110542ff61e14d25928 100644 (file)
@@ -137,7 +137,7 @@ ENDIF:NETCORE_OR_DOTNETDUMP
 # 7) Verify DumpStackObjects works
 SOSCOMMAND:DumpStackObjects
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
-VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+
+VERIFY:\s*SP/REG\s+Object\s+Name\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.FormatException\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.InvalidOperationException\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
@@ -145,7 +145,7 @@ VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
 # 8) Verify DumpStackObjects -verify works
 SOSCOMMAND:DumpStackObjects -verify
 VERIFY:.*OS Thread Id:\s+0x<HEXVAL>\s+.*
-VERIFY:\s+([R|E])*SP/REG\s+Object\s+Name\s+
+VERIFY:\s*SP/REG\s+Object\s+Name\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.FormatException\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.InvalidOperationException\s+
 VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*
index 482f48f854299e97f09467024748504003546a4e..10590c165db740462560387e2fa6dabcbddfb8d7 100644 (file)
@@ -1039,7 +1039,6 @@ void DumpStackWorker (DumpStackFlag &DSFlag)
 /// X86Machine implementation
 ///
 LPCSTR X86Machine::s_DumpStackHeading = "ChildEBP RetAddr  Caller, Callee\n";
-LPCSTR X86Machine::s_DSOHeading       = "ESP/REG  Object   Name\n";
 LPCSTR X86Machine::s_GCRegs[7]        = {"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp"};
 LPCSTR X86Machine::s_SPName           = "ESP";
 
@@ -1081,7 +1080,6 @@ void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printf
 /// ARMMachine implementation
 ///
 LPCSTR ARMMachine::s_DumpStackHeading = "ChildFP  RetAddr  Caller, Callee\n";
-LPCSTR ARMMachine::s_DSOHeading       = "SP/REG  Object   Name\n";
 LPCSTR ARMMachine::s_GCRegs[14]       = {"r0", "r1", "r2",  "r3",  "r4",  "r5",  "r6",
                                          "r7", "r8", "r9",  "r10", "r11", "r12", "lr"};
 LPCSTR ARMMachine::s_SPName           = "sp";
@@ -1093,7 +1091,6 @@ LPCSTR ARMMachine::s_SPName           = "sp";
 /// AMD64Machine implementation
 ///
 LPCSTR AMD64Machine::s_DumpStackHeading = "Child-SP         RetAddr          Caller, Callee\n";
-LPCSTR AMD64Machine::s_DSOHeading       = "RSP/REG          Object           Name\n";
 LPCSTR AMD64Machine::s_GCRegs[15]       = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp",
                                            "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
 LPCSTR AMD64Machine::s_SPName           = "RSP";
@@ -1121,7 +1118,6 @@ void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, prin
 /// ARM64Machine implementation
 ///
 LPCSTR ARM64Machine::s_DumpStackHeading = "ChildFP          RetAddr          Caller, Callee\n";
-LPCSTR ARM64Machine::s_DSOHeading       = "SP/REG           Object           Name\n";
 // excluding x18, fp & lr as these will not contain object references
 LPCSTR ARM64Machine::s_GCRegs[28]       = {"x0", "x1", "x2",  "x3",  "x4",  "x5",  "x6",
                                            "x7", "x8", "x9",  "x10", "x11", "x12", "x13",
index 0bacb2b17efae4a71f56b261f859c4ab80c42fb5..a09e5abecf9a57a6d4fbdb0be91bbceff9e3b2ca 100644 (file)
@@ -171,7 +171,6 @@ public:
     virtual void  FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
 
     virtual LPCSTR GetDumpStackHeading() const          { return s_DumpStackHeading; }
-    virtual LPCSTR GetDumpStackObjectsHeading() const   { return s_DSOHeading; }
     virtual LPCSTR GetSPName() const                    { return s_SPName; }
     virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
     { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
@@ -188,7 +187,6 @@ private:
 
 private:
     static LPCSTR     s_DumpStackHeading;
-    static LPCSTR     s_DSOHeading;
     static LPCSTR     s_GCRegs[7];
     static LPCSTR     s_SPName;
 }; // class X86Machine
@@ -243,7 +241,6 @@ public:
     virtual void  FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
 
     virtual LPCSTR GetDumpStackHeading() const          { return s_DumpStackHeading; }
-    virtual LPCSTR GetDumpStackObjectsHeading() const   { return s_DSOHeading; }
     virtual LPCSTR GetSPName() const                    { return s_SPName; }
     virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
     { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
@@ -260,7 +257,6 @@ private:
 
 private:
     static LPCSTR     s_DumpStackHeading;
-    static LPCSTR     s_DSOHeading;
     static LPCSTR     s_GCRegs[14];
     static LPCSTR     s_SPName;
     static ARMMachine s_ARMMachineInstance;
@@ -316,7 +312,6 @@ public:
     virtual void  FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
 
     virtual LPCSTR GetDumpStackHeading() const          { return s_DumpStackHeading; }
-    virtual LPCSTR GetDumpStackObjectsHeading() const   { return s_DSOHeading; }
     virtual LPCSTR GetSPName() const                    { return s_SPName; }
     virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
     { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs); }
@@ -333,7 +328,6 @@ private:
 
 private:
     static LPCSTR       s_DumpStackHeading;
-    static LPCSTR       s_DSOHeading;
     static LPCSTR       s_GCRegs[15];
     static LPCSTR       s_SPName;
 }; // class AMD64Machine
@@ -386,7 +380,6 @@ public:
     virtual void  FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
     
     virtual LPCSTR GetDumpStackHeading() const          { return s_DumpStackHeading; }
-    virtual LPCSTR GetDumpStackObjectsHeading() const   { return s_DSOHeading; }
     virtual LPCSTR GetSPName() const                    { return s_SPName; }
     virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
     { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = ARRAY_SIZE(s_GCRegs);}
@@ -402,7 +395,6 @@ private:
     ARM64Machine & operator=(const ARM64Machine&);  // undefined
 
     static LPCSTR     s_DumpStackHeading;
-    static LPCSTR     s_DSOHeading;
     static LPCSTR     s_GCRegs[28];
     static LPCSTR     s_SPName;
 
index 3b89f7023d07257ab5be692168a4d2b69a9445d2..259cb42996c257d5b16bc0bfe7dffd5410b8927a 100644 (file)
@@ -529,440 +529,3 @@ BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible,
 
     return TRUE;
 }
-
-// This function expects stat to be valid, and ready to get statistics.
-void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort)
-{
-    DWORD_PTR dwAddr=0;
-    UINT m;
-
-    if (!bShort)
-    {
-        for (m = 0; m <= GetMaxGeneration(); m ++)
-        {
-            if (IsInterrupt())
-                return;
-
-            ExtOut("generation %d has %d finalizable objects ", m,
-                (SegQueueLimit(heapDetails,gen_segment(m)) - SegQueue(heapDetails,gen_segment(m))) / sizeof(size_t));
-
-            ExtOut ("(%p->%p)\n",
-                SOS_PTR(SegQueue(heapDetails,gen_segment(m))),
-                SOS_PTR(SegQueueLimit(heapDetails,gen_segment(m))));
-        }
-    }
-
-    if (bAllReady)
-    {
-        if (!bShort)
-        {
-            ExtOut ("Finalizable but not rooted:  ");
-        }
-
-        TADDR rngStart = (TADDR)SegQueue(heapDetails, gen_segment(GetMaxGeneration()));
-        TADDR rngEnd   = (TADDR)SegQueueLimit(heapDetails, gen_segment(0));
-
-        std::stringstream argsBuilder;
-        argsBuilder << std::hex << rngStart << " ";
-        argsBuilder << std::hex << rngEnd << " ";
-        argsBuilder << "-nofinalizer ";
-        if (bShort)
-            argsBuilder << "-short";
-
-        ExecuteCommand("notreachableinrange", argsBuilder.str().c_str());
-    }
-
-    if (!bShort)
-    {
-        ExtOut ("Ready for finalization %d objects ",
-                (SegQueueLimit(heapDetails,FinalizerListSeg)-SegQueue(heapDetails,CriticalFinalizerListSeg)) / sizeof(size_t));
-        ExtOut ("(%p->%p)\n",
-                SOS_PTR(SegQueue(heapDetails,CriticalFinalizerListSeg)),
-                SOS_PTR(SegQueueLimit(heapDetails,FinalizerListSeg)));
-    }
-
-    // if bAllReady we only count objects that are ready for finalization,
-    // otherwise we count all finalizable objects.
-    TADDR taddrLowerLimit = (bAllReady ? (TADDR)SegQueue(heapDetails, CriticalFinalizerListSeg) :
-        (DWORD_PTR)SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
-    for (dwAddr = taddrLowerLimit;
-         dwAddr < (DWORD_PTR)SegQueueLimit(heapDetails, FinalizerListSeg);
-         dwAddr += sizeof (dwAddr))
-    {
-        if (IsInterrupt())
-        {
-            return;
-        }
-
-        DWORD_PTR objAddr = NULL,
-                  MTAddr = NULL;
-
-        if (SUCCEEDED(MOVE(objAddr, dwAddr)) && SUCCEEDED(GetMTOfObject(objAddr, &MTAddr)) && MTAddr)
-        {
-            if (bShort)
-            {
-                DMLOut("%s\n", DMLObject(objAddr));
-            }
-            else
-            {
-                size_t s = ObjectSize(objAddr);
-                stat->Add(MTAddr, (DWORD)s);
-            }
-        }
-    }
-}
-
-GCHeapSnapshot::GCHeapSnapshot()
-{
-    m_isBuilt = FALSE;
-    m_heapDetails = NULL;
-}
-
-///////////////////////////////////////////////////////////
-SegmentLookup::SegmentLookup()
-{
-    m_iSegmentsSize = m_iSegmentCount = 0;
-
-    m_segments = new DacpHeapSegmentData[nSegLookupStgIncrement];
-    if (m_segments == NULL)
-    {
-        ReportOOM();
-    }
-    else
-    {
-        m_iSegmentsSize = nSegLookupStgIncrement;
-    }
-}
-
-BOOL SegmentLookup::AddSegment(DacpHeapSegmentData *pData)
-{
-    // appends the address of a new (initialized) instance of DacpHeapSegmentData to the list of segments
-    // (m_segments) adding  space for a segment when necessary.
-    // @todo Microsoft: The field name m_iSegmentSize is a little misleading. It's not the size in bytes,
-    // but the number of elements allocated for the array. It probably should have been named something like
-    // m_iMaxSegments instead.
-    if (m_iSegmentCount >= m_iSegmentsSize)
-    {
-        // expand buffer--allocate enough space to hold the elements we already have plus nSegLookupStgIncrement
-        // more elements
-        DacpHeapSegmentData *pNewBuffer = new DacpHeapSegmentData[m_iSegmentsSize+nSegLookupStgIncrement];
-        if (pNewBuffer==NULL)
-            return FALSE;
-
-        // copy the old elements into the new array
-        memcpy(pNewBuffer, m_segments, sizeof(DacpHeapSegmentData)*m_iSegmentsSize);
-
-        // record the new number of elements available
-        m_iSegmentsSize+=nSegLookupStgIncrement;
-
-        // delete the old array
-        delete [] m_segments;
-
-        // set m_segments to point to the new array
-        m_segments = pNewBuffer;
-    }
-
-    // add pData to the array
-    m_segments[m_iSegmentCount++] = *pData;
-
-    return TRUE;
-}
-
-SegmentLookup::~SegmentLookup()
-{
-    if (m_segments)
-    {
-        delete [] m_segments;
-        m_segments = NULL;
-    }
-}
-
-void SegmentLookup::Clear()
-{
-    m_iSegmentCount = 0;
-}
-
-CLRDATA_ADDRESS SegmentLookup::GetHeap(CLRDATA_ADDRESS object, BOOL& bFound)
-{
-    CLRDATA_ADDRESS ret = NULL;
-    bFound = FALSE;
-
-    // Visit our segments
-    for (int i=0; i<m_iSegmentCount; i++)
-    {
-        if (TO_TADDR(m_segments[i].mem) <= TO_TADDR(object) &&
-            TO_TADDR(m_segments[i].highAllocMark) > TO_TADDR(object))
-        {
-            ret = m_segments[i].gc_heap;
-            bFound = TRUE;
-            break;
-        }
-    }
-
-    return ret;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-BOOL GCHeapSnapshot::Build()
-{
-    Clear();
-
-    m_isBuilt = FALSE;
-
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    /// 1. Get some basic information such as the heap type (SVR or WKS), how many heaps there are, mode and max generation
-    /// (See code:ClrDataAccess::RequestGCHeapData)
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    if (m_gcheap.Request(g_sos) != S_OK)
-    {
-        ExtOut("Error requesting GC Heap data\n");
-        return FALSE;
-    }
-
-    ArrayHolder<CLRDATA_ADDRESS> heapAddrs = NULL;
-
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    /// 2. Get a list of the addresses of the heaps when we have multiple heaps in server mode
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    if (m_gcheap.bServerMode)
-    {
-        UINT AllocSize;
-        // allocate an array to hold the starting addresses of each heap when we're in server mode
-        if (!ClrSafeInt<UINT>::multiply(sizeof(CLRDATA_ADDRESS), m_gcheap.HeapCount, AllocSize) ||
-            (heapAddrs = new CLRDATA_ADDRESS [m_gcheap.HeapCount]) == NULL)
-        {
-            ReportOOM();
-            return FALSE;
-        }
-
-        // and initialize it with their addresses (see code:ClrDataAccess::RequestGCHeapList
-        // for details)
-        if (g_sos->GetGCHeapList(m_gcheap.HeapCount, heapAddrs, NULL) != S_OK)
-        {
-            ExtOut("Failed to get GCHeaps\n");
-            return FALSE;
-        }
-    }
-
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    ///  3. Get some necessary information about each heap, such as the card table location, the generation
-    ///  table, the heap bounds, etc., and retrieve the heap segments
-    ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-    // allocate an array to hold the information
-    m_heapDetails = new GCHeapDetails[m_gcheap.HeapCount];
-
-    if (m_heapDetails == NULL)
-    {
-        ReportOOM();
-        return FALSE;
-    }
-
-    // get the heap information for each heap
-    // See code:ClrDataAccess::RequestGCHeapDetails for details
-    for (UINT n = 0; n < m_gcheap.HeapCount; n ++)
-    {
-        if (m_gcheap.bServerMode)
-        {
-            DacpGcHeapDetails dacHeapDetails;
-            if (dacHeapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
-            {
-                ExtOut("Error requesting details\n");
-                return FALSE;
-            }
-
-            m_heapDetails[n].Set(dacHeapDetails, heapAddrs[n]);
-        }
-        else
-        {
-            DacpGcHeapDetails dacHeapDetails;
-            if (dacHeapDetails.Request(g_sos) != S_OK)
-            {
-                ExtOut("Error requesting details\n");
-                return FALSE;
-            }
-
-            m_heapDetails[n].Set(dacHeapDetails);
-        }
-
-        // now get information about the heap segments for this heap
-        if (!AddSegments(m_heapDetails[n]))
-        {
-            ExtOut("Failed to retrieve segments for gc heap\n");
-            return FALSE;
-        }
-    }
-
-    m_isBuilt = TRUE;
-    return TRUE;
-}
-
-BOOL GCHeapSnapshot::AddSegments(const GCHeapDetails& details)
-{
-    int n = 0;
-    DacpHeapSegmentData segment;
-
-    // This array of addresses gives us access to all the segments.
-    CLRDATA_ADDRESS AddrSegs[5];
-    if (details.has_regions)
-    {
-        // with regions, each generation has its own list of segments
-        for (unsigned gen = 0; gen <= GetMaxGeneration() + 1; gen++)
-        {
-            AddrSegs[gen] = details.generation_table[gen].start_segment;
-        }
-        if (details.has_poh)
-        {
-            AddrSegs[4] = details.generation_table[GetMaxGeneration() + 2].start_segment; // pinned object heap
-        }
-    }
-    else
-    {
-        // The generation segments are linked to each other, starting with the maxGeneration segment. 
-        // The second address gives us the large object heap, the third the pinned object heap
-
-        AddrSegs[0] = details.generation_table[GetMaxGeneration()].start_segment;
-        AddrSegs[1] = details.generation_table[GetMaxGeneration() + 1].start_segment;
-        AddrSegs[2] = NULL;
-        if (details.has_poh)
-        {
-            AddrSegs[2] = details.generation_table[GetMaxGeneration() + 2].start_segment; // pinned object heap
-        }
-        AddrSegs[3] = NULL;
-        AddrSegs[4] = NULL;
-    }
-
-    // this loop will get information for all the heap segments in this heap. The outer loop iterates once
-    // for the "normal" generation segments and once for the large object heap. The inner loop follows the chain
-    // of segments rooted at AddrSegs[i]
-    for (unsigned int i = 0; i < ARRAY_SIZE(AddrSegs); ++i)
-    {
-        if (AddrSegs[i] == NULL)
-        {
-            continue;
-        }
-
-        CLRDATA_ADDRESS AddrSeg = AddrSegs[i];
-
-        while (AddrSeg != NULL)
-        {
-            if (IsInterrupt())
-            {
-                return FALSE;
-            }
-            // Initialize segment by copying fields from the target's heap segment at AddrSeg.
-            // See code:ClrDataAccess::RequestGCHeapSegment for details.
-            if (segment.Request(g_sos, AddrSeg, details.original_heap_details) != S_OK)
-            {
-                ExtOut("Error requesting heap segment %p\n", SOS_PTR(AddrSeg));
-                return FALSE;
-            }
-            // add the new segment to the array of segments. This will expand the array if necessary
-            if (!m_segments.AddSegment(&segment))
-            {
-                ExtOut("strike: Failed to store segment\n");
-                return FALSE;
-            }
-            // get the next segment in the chain
-            AddrSeg = segment.next;
-        }
-    }
-
-    return TRUE;
-}
-
-void GCHeapSnapshot::Clear()
-{
-    if (m_heapDetails != NULL)
-    {
-        delete [] m_heapDetails;
-        m_heapDetails = NULL;
-    }
-
-    m_segments.Clear();
-
-    m_isBuilt = FALSE;
-}
-
-GCHeapSnapshot g_snapshot;
-
-GCHeapDetails *GCHeapSnapshot::GetHeap(CLRDATA_ADDRESS objectPointer)
-{
-    // We need bFound because heap will be NULL if we are Workstation Mode.
-    // We still need a way to know if the address was found in our segment
-    // list.
-    BOOL bFound = FALSE;
-    CLRDATA_ADDRESS heap = m_segments.GetHeap(objectPointer, bFound);
-    if (heap)
-    {
-        for (UINT i=0; i<m_gcheap.HeapCount; i++)
-        {
-            if (m_heapDetails[i].heapAddr == heap)
-                return m_heapDetails + i;
-        }
-    }
-    else if (!m_gcheap.bServerMode)
-    {
-        if (bFound)
-        {
-            return m_heapDetails;
-        }
-    }
-
-    // Not found
-    return NULL;
-}
-
-// TODO: Do we need to handle the LOH here?
-int GCHeapSnapshot::GetGeneration(CLRDATA_ADDRESS objectPointer)
-{
-    GCHeapDetails *pDetails = GetHeap(objectPointer);
-    if (pDetails == NULL)
-    {
-        ExtOut("Object %p has no generation\n", SOS_PTR(objectPointer));
-        return 0;
-    }
-
-    TADDR taObj = TO_TADDR(objectPointer);
-    if (pDetails->has_regions)
-    {
-        for (int gen_num = 0; gen_num <= 1; gen_num++)
-        {
-            CLRDATA_ADDRESS dwAddrSeg = pDetails->generation_table[gen_num].start_segment;
-            while (dwAddrSeg != 0)
-            {
-                DacpHeapSegmentData segment;
-                if (segment.Request(g_sos, dwAddrSeg, pDetails->original_heap_details) != S_OK)
-                {
-                    ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
-                    return 0;
-                }
-                // The DAC doesn't fill the generation table with true CLRDATA_ADDRESS values
-                // but rather with ULONG64 values (i.e. non-sign-extended 64-bit values)
-                // We use the TO_TADDR below to ensure we won't break if this will ever
-                // be fixed in the DAC.
-                if (TO_TADDR(segment.mem) <= taObj && taObj < TO_TADDR(segment.highAllocMark))
-                {
-                    return gen_num;
-                }
-                dwAddrSeg = segment.next;
-            }
-        }
-    }
-    else
-    {
-        // The DAC doesn't fill the generation table with true CLRDATA_ADDRESS values
-        // but rather with ULONG64 values (i.e. non-sign-extended 64-bit values)
-        // We use the TO_TADDR below to ensure we won't break if this will ever
-        // be fixed in the DAC.
-        if (taObj >= TO_TADDR(pDetails->generation_table[0].allocation_start) &&
-            taObj <= TO_TADDR(pDetails->alloc_allocated))
-            return 0;
-
-        if (taObj >= TO_TADDR(pDetails->generation_table[1].allocation_start) &&
-            taObj <= TO_TADDR(pDetails->generation_table[0].allocation_start))
-            return 1;
-    }
-    return 2;
-}
index c72c91829eb9d50d489bc7358293a7c1bb02daae..3ebe83fde0e0cf44e83737d269ae4738be07574a 100644 (file)
@@ -409,3 +409,14 @@ HRESULT GetRuntime(IRuntime** ppRuntime)
 #endif
     return target->GetRuntime(ppRuntime);
 }
+
+void FlushCheck()
+{
+#ifndef FEATURE_PAL
+    SOSExtensions* extensions = (SOSExtensions*)Extensions::GetInstance();
+    if (extensions != nullptr)
+    {
+        extensions->FlushCheck();
+    }
+#endif // !FEATURE_PAL
+}
index 71916ec45a0663fe5069dc4b7b0a310f1e8af313..cf29ce595b9611bc69e06057a69cfeb4c756da31 100644 (file)
@@ -192,6 +192,7 @@ public:
 };
 
 extern HRESULT GetRuntime(IRuntime** ppRuntime);
+extern void FlushCheck();
 
 #ifndef MINIDUMP
  
@@ -322,7 +323,8 @@ inline void DACMessage(HRESULT Status)
     ControlC = FALSE;                                           \
     g_bDacBroken = TRUE;                                        \
     g_clrData = NULL;                                           \
-    g_sos = NULL;                                        
+    g_sos = NULL;                                               \
+    FlushCheck();
 
 // Also initializes the target machine
 #define INIT_API_NOEE()                                         \
@@ -464,7 +466,6 @@ public:
 
     // Retrieve some target specific output strings
     virtual LPCSTR GetDumpStackHeading() const = 0;
-    virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
     virtual LPCSTR GetSPName() const = 0;
     // Retrieves the non-volatile registers reported to the GC
     virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;
index f0efe93e8c37b4a5cf49a94be462bde14bfd6562..20feea9e7931395001491c1ea6b9850807130cae 100644 (file)
@@ -390,6 +390,7 @@ HRESULT ExecuteCommand(PCSTR commandName, PCSTR args)
             return hostServices->DispatchCommand(commandName, args);
         }
     }
+    ExtErr("Unrecognized command %s\n", commandName);
     return E_NOTIMPL;
 }
 
@@ -616,82 +617,6 @@ DECLARE_API (EEStack)
     return Status;
 }
 
-HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
-{
-    size_t StackTop = 0;
-    size_t StackBottom = 0;
-    if (nArg==0)
-    {
-        ULONG64 StackOffset;
-        g_ExtRegisters->GetStackOffset(&StackOffset);
-
-        StackTop = TO_TADDR(StackOffset);
-    }
-    else
-    {
-        StackTop = GetExpression(exprTop);
-        if (StackTop == 0)
-        {
-            ExtOut("wrong option: %s\n", exprTop);
-            return E_FAIL;
-        }
-
-        if (nArg==2)
-        {
-            StackBottom = GetExpression(exprBottom);
-            if (StackBottom == 0)
-            {
-                ExtOut("wrong option: %s\n", exprBottom);
-                return E_FAIL;
-            }
-        }
-    }
-
-#ifndef FEATURE_PAL
-    if (IsWindowsTarget())
-    {
-        NT_TIB teb;
-        ULONG64 dwTebAddr = 0;
-        HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
-        if (SUCCEEDED(hr) && SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
-        {
-            if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
-            {
-                if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
-                    StackBottom = TO_TADDR(teb.StackBase);
-            }
-        }
-    }
-#endif
-
-    if (StackBottom == 0)
-        StackBottom = StackTop + 0xFFFF;
-
-    if (StackBottom < StackTop)
-    {
-        ExtOut("Wrong option: stack selection wrong\n");
-        return E_FAIL;
-    }
-
-    // We can use the gc snapshot to eliminate object addresses that are
-    // not on the gc heap.
-    if (!g_snapshot.Build())
-    {
-        ExtOut("Unable to determine bounds of gc heap\n");
-        return E_FAIL;
-    }
-
-    // Print thread ID.
-    ULONG id = 0;
-    g_ExtSystem->GetCurrentThreadSystemId (&id);
-    ExtOut("OS Thread Id: 0x%x ", id);
-    g_ExtSystem->GetCurrentThreadId (&id);
-    ExtOut("(%d)\n", id);
-
-    DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
-    return S_OK;
-}
-
 /**********************************************************************\
 * Routine Description:                                                 *
 *                                                                      *
@@ -701,32 +626,10 @@ HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR e
 \**********************************************************************/
 DECLARE_API(DumpStackObjects)
 {
-    INIT_API();
+    INIT_API_EXT();
     MINIDUMP_NOT_SUPPORTED();
-    StringHolder exprTop, exprBottom;
-
-    BOOL bVerify = FALSE;
-    BOOL dml = FALSE;
-    CMDOption option[] =
-    {   // name, vptr, type, hasValue
-        {"-verify", &bVerify, COBOOL, FALSE},
-        {"/d", &dml, COBOOL, FALSE}
-    };
-    CMDValue arg[] =
-    {   // vptr, type
-        {&exprTop.data, COSTRING},
-        {&exprBottom.data, COSTRING}
-    };
-    size_t nArg;
-
-    if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
-    {
-        return Status;
-    }
 
-    EnableDMLHolder enableDML(dml);
-
-    return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
+    return ExecuteCommand("dumpstackobjects", args);
 }
 
 /**********************************************************************\
@@ -890,13 +793,7 @@ DECLARE_API(DumpIL)
         return DecodeILFromAddress(NULL, dwStartAddr);
     }
 
-    if (!g_snapshot.Build())
-    {
-        ExtOut("Unable to build snapshot of the garbage collector state\n");
-        return Status;
-    }
-
-    if (g_snapshot.GetHeap(dwStartAddr) != NULL)
+    if (sos::IsObject(dwStartAddr))
     {
         dwDynamicMethodObj = dwStartAddr;
     }
@@ -4027,138 +3924,10 @@ DECLARE_API(RCWCleanupList)
 \**********************************************************************/
 DECLARE_API(FinalizeQueue)
 {
-    INIT_API();
+    INIT_API_EXT();
     MINIDUMP_NOT_SUPPORTED();
 
-    BOOL bDetail = FALSE;
-    BOOL bAllReady = FALSE;
-    BOOL bShort    = FALSE;
-    BOOL dml = FALSE;
-    TADDR taddrMT  = 0;
-
-    CMDOption option[] =
-    {   // name, vptr, type, hasValue
-        {"-detail",   &bDetail,   COBOOL, FALSE},
-        {"-allReady", &bAllReady, COBOOL, FALSE},
-        {"-short",    &bShort,    COBOOL, FALSE},
-        {"/d",        &dml,       COBOOL, FALSE},
-        {"-mt",       &taddrMT,   COHEX,  TRUE},
-    };
-
-    if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
-    {
-        return Status;
-    }
-
-    EnableDMLHolder dmlHolder(dml);
-    if (!bShort)
-    {
-        DacpSyncBlockCleanupData dsbcd;
-        CLRDATA_ADDRESS sbCurrent = NULL;
-        ULONG cleanCount = 0;
-        while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
-        {
-            if (bDetail)
-            {
-                if (cleanCount == 0) // print first time only
-                {
-                    ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
-                    ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
-                        "SyncBlock", "RCW", "CCW", "ComClassFactory");
-                }
-
-                ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
-                    (ULONG64) dsbcd.SyncBlockPointer,
-                    (ULONG64) dsbcd.blockRCW,
-                    (ULONG64) dsbcd.blockCCW,
-                    (ULONG64) dsbcd.blockClassFactory);
-            }
-
-            cleanCount++;
-            sbCurrent = dsbcd.nextSyncBlock;
-            if (sbCurrent == NULL)
-            {
-                break;
-            }
-        }
-
-        ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
-
-#ifdef FEATURE_COMINTEROP
-        VisitRcwArgs travArgs;
-        ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
-        travArgs.bDetail = bDetail;
-        g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
-        ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
-        ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
-        ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
-#endif // FEATURE_COMINTEROP
-
-// noRCW:
-        ExtOut("----------------------------------\n");
-    }
-
-    // GC Heap
-    DWORD dwNHeaps = GetGcHeapCount();
-
-    HeapStat hpStat;
-
-    if (!IsServerBuild())
-    {
-        DacpGcHeapDetails heapDetails;
-        if (heapDetails.Request(g_sos) != S_OK)
-        {
-            ExtOut("Error requesting details\n");
-            return Status;
-        }
-
-        GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
-    }
-    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;
-        }
-
-        for (DWORD n = 0; n < dwNHeaps; n ++)
-        {
-            DacpGcHeapDetails heapDetails;
-            if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
-            {
-                ExtOut("Error requesting details\n");
-                return Status;
-            }
-
-            ExtOut("------------------------------\n");
-            ExtOut("Heap %d\n", n);
-
-            GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
-        }
-    }
-
-    if (!bShort)
-    {
-        if (bAllReady)
-        {
-            PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
-        }
-        else
-        {
-            PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
-        }
-    }
-
-    return Status;
+    return ExecuteCommand("finalizequeue", args);
 }
 
 enum {
@@ -9020,26 +8789,6 @@ DECLARE_API(FindRoots)
             ExtOut("At this time %sgcroot should be used instead.\n", SOSPrefix);
             return Status;
         }
-        // validate argument
-        if (!g_snapshot.Build())
-        {
-            ExtOut("Unable to build snapshot of the garbage collector state\n");
-            return Status;
-        }
-
-        if (g_snapshot.GetHeap(taObj) == NULL)
-        {
-            ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
-            return Status;
-        }
-
-        int ogen = g_snapshot.GetGeneration(taObj);
-        if (ogen > CNotification::GetCondemnedGen())
-        {
-            DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
-                DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
-            return Status;
-        }
 
         std::stringstream argsBuilder;
         argsBuilder << "-gcgen " << CNotification::GetCondemnedGen() << " " << std::hex << taObj;
index 768c919ca099e72eeda1ab9f45511f2577323841..8ef8d8344f61ac6da902ed70aba7ccdbba81b708 100644 (file)
@@ -1927,109 +1927,6 @@ BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADD
     return FALSE;
 }
 
-void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
-{
-    // rule out pointers that are outside of the gc heap.
-    if (g_snapshot.GetHeap(objAddr) == NULL)
-        return;
-
-    DacpObjectData objectData;
-    if (objectData.Request(g_sos, TO_CDADDR(objAddr)) != S_OK)
-        return;
-
-    if (sos::IsObject(objAddr, verifyFields != FALSE)
-        && !sos::MethodTable::IsFreeMT(TO_TADDR(objectData.MethodTable)))
-    {
-        DMLOut("%-" POINTERSIZE "s %s ", location, DMLObject(objAddr));
-        if (g_sos->GetObjectClassName(TO_CDADDR(objAddr), mdNameLen, g_mdName, NULL)==S_OK)
-        {
-            ExtOut("%S", g_mdName);
-
-            if (IsStringObject(objAddr))
-            {
-                ExtOut("    ");
-                StringObjectContent(objAddr, FALSE, 40);
-            }
-            else if (IsObjectArray(objAddr) &&
-                     (g_sos->GetMethodTableName(objectData.ElementTypeHandle, mdNameLen, g_mdName, NULL) == S_OK))
-            {
-                ExtOut("    ");
-                ExtOut("(%S[])", g_mdName);
-            }
-        }
-        else
-        {
-            ExtOut("<unknown type>");
-        }
-        ExtOut("\n");
-    }
-}
-
-void DumpStackObjectsOutput(DWORD_PTR ptr, DWORD_PTR objAddr, BOOL verifyFields)
-{
-    char location[64];
-    sprintf_s(location, 64, "%p", (DWORD_PTR *)ptr);
-
-    DumpStackObjectsOutput(location, objAddr, verifyFields);
-}
-
-void DumpStackObjectsInternal(size_t StackTop, size_t StackBottom, BOOL verifyFields)
-{
-    for (DWORD_PTR ptr = StackTop; ptr <= StackBottom; ptr += sizeof(DWORD_PTR))
-    {
-        if (IsInterrupt())
-            return;
-
-        DWORD_PTR objAddr;
-        move_xp(objAddr, ptr);
-
-        DumpStackObjectsOutput(ptr, objAddr, verifyFields);
-    }
-}
-
-void DumpRegObjectHelper(const char *regName, BOOL verifyFields)
-{
-    DWORD_PTR reg;
-#ifdef FEATURE_PAL
-    if (FAILED(g_ExtRegisters->GetValueByName(regName, &reg)))
-        return;
-#else
-    DEBUG_VALUE value;
-    ULONG IREG;
-    if (FAILED(g_ExtRegisters->GetIndexByName(regName, &IREG)) ||
-        FAILED(g_ExtRegisters->GetValue(IREG, &value)))
-        return;
-
-#if defined(SOS_TARGET_X86) || defined(SOS_TARGET_ARM)
-    reg = (DWORD_PTR) value.I32;
-#elif defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
-    reg = (DWORD_PTR) value.I64;
-#else
-#error Unsupported target
-#endif
-#endif // FEATURE_PAL
-
-    DumpStackObjectsOutput(regName, reg, verifyFields);
-}
-
-void DumpStackObjectsHelper (
-                TADDR StackTop,
-                TADDR StackBottom,
-                BOOL verifyFields)
-{
-    ExtOut(g_targetMachine->GetDumpStackObjectsHeading());
-
-    LPCSTR* regs;
-    unsigned int cnt;
-    g_targetMachine->GetGCRegisters(&regs, &cnt);
-
-    for (size_t i = 0; i < cnt; ++i)
-        DumpRegObjectHelper(regs[i], verifyFields);
-
-    // Make certain StackTop is dword aligned:
-    DumpStackObjectsInternal(StackTop & ~ALIGNCONST, StackBottom, verifyFields);
-}
-
 void AddToModuleList(DWORD_PTR * &moduleList, int &numModule, int &maxList,
                      DWORD_PTR dwModuleAddr)
 {
@@ -3437,24 +3334,6 @@ BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo)
 
 #endif // !FEATURE_PAL
 
-size_t ObjectSize(DWORD_PTR obj,BOOL fIsLargeObject)
-{
-    DWORD_PTR dwMT;
-    MOVE(dwMT, obj);
-    return ObjectSize(obj, dwMT, FALSE, fIsLargeObject);
-}
-
-size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject)
-{
-    BOOL bContainsPointers;
-    size_t size = 0;
-    if (!GetSizeEfficient(obj, mt, fIsLargeObject, size, bContainsPointers))
-    {
-        return 0;
-    }
-    return size;
-}
-
 // This takes an array of values and sets every non-printable character
 // to be a period.
 void Flatten(__out_ecount(len) char *data, unsigned int len)
@@ -4080,83 +3959,6 @@ BOOL GetGcStructuresValid()
     return heapData.bGcStructuresValid;
 }
 
-void GetAllocContextPtrs(AllocInfo *pallocInfo)
-{
-    // gets the allocation contexts for all threads. This provides information about how much of
-    // the current allocation quantum has been allocated and the heap to which the quantum belongs.
-    // The allocation quantum is a fixed size chunk of zeroed memory from which allocations will come
-    // until it's filled. Each managed thread has its own allocation context.
-
-    pallocInfo->num = 0;
-    pallocInfo->array = NULL;
-
-    // get the thread store (See code:ClrDataAccess::RequestThreadStoreData for details)
-    DacpThreadStoreData ThreadStore;
-    if ( ThreadStore.Request(g_sos) != S_OK)
-    {
-        return;
-    }
-
-    int numThread = ThreadStore.threadCount;
-    if (numThread)
-    {
-        pallocInfo->array = new needed_alloc_context[numThread + 1];
-        if (pallocInfo->array == NULL)
-        {
-            return;
-        }
-    }
-
-    // get details for each thread in the thread store
-    CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
-    while (CurThread != NULL)
-    {
-        if (IsInterrupt())
-            return;
-
-        DacpThreadData Thread;
-        // Get information about the thread (we're getting the values of several of the
-        // fields of the Thread instance from the target) See code:ClrDataAccess::RequestThreadData for
-        // details
-        if (Thread.Request(g_sos, CurThread) != S_OK)
-        {
-            return;
-        }
-
-        if (Thread.allocContextPtr != 0)
-        {
-            // get a list of all the allocation contexts
-            int j;
-            for (j = 0; j < pallocInfo->num; j ++)
-            {
-                if (pallocInfo->array[j].alloc_ptr == (BYTE *) Thread.allocContextPtr)
-                    break;
-            }
-            if (j == pallocInfo->num)
-            {
-                pallocInfo->num ++;
-                pallocInfo->array[j].alloc_ptr = (BYTE *) Thread.allocContextPtr;
-                pallocInfo->array[j].alloc_limit = (BYTE *) Thread.allocContextLimit;
-            }
-        }
-
-        CurThread = Thread.nextThread;
-    }
-
-    CLRDATA_ADDRESS allocPtr;
-    CLRDATA_ADDRESS allocLimit;
-
-    ReleaseHolder<ISOSDacInterface12> sos12;
-    if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface12), &sos12)) &&
-        SUCCEEDED(sos12->GetGlobalAllocationContext(&allocPtr, &allocLimit)) &&
-        allocPtr != 0)
-    {
-        int j = pallocInfo->num ++;
-        pallocInfo->array[j].alloc_ptr = (BYTE *) allocPtr;
-        pallocInfo->array[j].alloc_limit = (BYTE *) allocLimit;
-    }
-}
-
 HRESULT ReadVirtualCache::Read(TADDR address, PVOID buffer, ULONG bufferSize, PULONG lpcbBytesRead)
 {
     // address can be any random ULONG64, as it can come from VerifyObjectMember(), and this
@@ -5824,88 +5626,3 @@ HRESULT GetMetadataMemory(CLRDATA_ADDRESS address, ULONG32 bufferSize, BYTE* buf
 }
 
 #endif // FEATURE_PAL
-
-///////////////////////////////////////////////////////////////////////////////////////////
-//
-// Miscellaneous helper methods
-//
-
-void EnumerateThreadPoolGlobalWorkItemConcurrentQueue(
-    DWORD_PTR workItemsConcurrentQueuePtr,
-    const char *queueName,
-    HeapStat *stats)
-{
-    // Get its head segment.
-    sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
-    int offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
-    if (offset <= 0)
-    {
-        return;
-    }
-
-    // Now, walk from segment to segment, each of which contains an array of work items.
-    DWORD_PTR segmentPtr;
-    MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
-    while (sos::IsObject(segmentPtr, false))
-    {
-        sos::Object segment = TO_TADDR(segmentPtr);
-
-        // Get the work items array.  It's an array of Slot structs, which starts with the T.
-        offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
-        if (offset <= 0)
-        {
-            break;
-        }
-
-        DWORD_PTR slotsPtr;
-        MOVE(slotsPtr, segment.GetAddress() + offset);
-        if (!sos::IsObject(slotsPtr, false))
-        {
-            break;
-        }
-
-        // Walk every element in the array, outputting details on non-null work items.
-        DacpObjectData slotsArray;
-        if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
-        {
-            for (int i = 0; i < slotsArray.dwNumComponents; i++)
-            {
-                DWORD_PTR workItemPtr;
-                MOVE(workItemPtr, slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize)); // the item object reference is at the beginning of the Slot
-                if (workItemPtr != NULL && sos::IsObject(TO_CDADDR(workItemPtr), false))
-                {
-                    sos::Object workItem = TO_TADDR(workItemPtr);
-                    stats->Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
-                    DMLOut("%" THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "s %s %S", queueName, DMLObject(workItem.GetAddress()), workItem.GetTypeName());
-                    if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
-                        (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
-                    {
-                        DWORD_PTR delegatePtr;
-                        MOVE(delegatePtr, workItem.GetAddress() + offset);
-                        CLRDATA_ADDRESS md;
-                        if (TryGetMethodDescriptorForDelegate(TO_CDADDR(delegatePtr), &md))
-                        {
-                            NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
-                            ExtOut(" => %S", g_mdName);
-                        }
-                    }
-                    ExtOut("\n");
-                }
-            }
-        }
-
-        // Move to the next segment.
-        DacpFieldDescData segmentField;
-        offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
-        if (offset <= 0)
-        {
-            break;
-        }
-
-        MOVE(segmentPtr, segment.GetAddress() + offset);
-        if (segmentPtr == NULL)
-        {
-            break;
-        }
-    }
-}
index 2fb1eb0bea639a9e450f5dbba70eba20c7f80d49..50467dd8ad41cf96c089103d55d4b53087f66d9f 100644 (file)
@@ -262,215 +262,6 @@ private:
     static StaticData<char, 4, 1024> cache;
 };
 
-class GCHeapDetails
-{
-private:
-    void GetGenerationTableSize(CLRDATA_ADDRESS svrHeapAddr, unsigned int *count)
-    {
-        HRESULT hr = S_OK;
-        bool success = false;
-        ReleaseHolder<ISOSDacInterface8> sos8;
-        if (!SUCCEEDED(hr = g_sos->QueryInterface(__uuidof(ISOSDacInterface8), &sos8))
-            || !SUCCEEDED(hr = sos8->GetNumberGenerations(count)))
-        {
-            // The runtime will either have the original 4 generations or implement ISOSDacInterface8
-            // if the call succeeded, count is already populated.
-            *count = DAC_NUMBERGENERATIONS;
-        }
-    }
-
-    // Fill the target array with either the details from heap or if this is a newer runtime that supports
-    // the pinned object heap (or potentially future GC generations), get that data too. This abstraction is
-    // necessary because the original GC heap APIs are hardcoded to 4 generations.
-    void FillGenerationTable(CLRDATA_ADDRESS svrHeapAddr, const DacpGcHeapDetails &heap, unsigned int count, DacpGenerationData *data)
-    {
-        HRESULT hr = S_OK;
-        bool success = false;
-        unsigned int generationCount;
-        ReleaseHolder<ISOSDacInterface8> sos8;
-        if (SUCCEEDED(hr = g_sos->QueryInterface(__uuidof(ISOSDacInterface8), &sos8)))
-        {
-            if (svrHeapAddr == NULL)
-            {
-                if (SUCCEEDED(hr = sos8->GetGenerationTable(count, data, &generationCount))
-                    && hr != S_FALSE)
-                {
-                    success = true;
-                    // Nothing else to do, data is already populated
-                }
-            }
-            else
-            {
-                if (SUCCEEDED(hr = sos8->GetGenerationTableSvr(svrHeapAddr, count, data, &generationCount))
-                    && hr != S_FALSE)
-                {
-                    success = true;
-                    // Nothing else to do, data is already populated
-                }
-            }
-
-            _ASSERTE(generationCount == count || !success);
-        }
-
-        if (!success)
-        {
-            // This would mean that there are additional, unaccounted for, generations
-            _ASSERTE(hr != S_FALSE);
-
-            // We couldn't get any data from the newer APIs, so fall back to the original data
-            memcpy(data, &(heap.generation_table), sizeof(DacpGenerationData) * DAC_NUMBERGENERATIONS);
-        }
-    }
-
-    // Fill the target array with either the details from heap or if this is a newer runtime that supports
-    // the pinned object heap (or potentially future GC generations), get that data too. This abstraction is
-    // necessary because the original GC heap APIs are hardcoded to 4 generations.
-    void FillFinalizationPointers(CLRDATA_ADDRESS svrHeapAddr, const DacpGcHeapDetails &heap, unsigned int count, CLRDATA_ADDRESS *data)
-    {
-        HRESULT hr = S_OK;
-        bool success = false;
-        unsigned int fillPointersCount;
-        ReleaseHolder<ISOSDacInterface8> sos8;
-        if (SUCCEEDED(hr = g_sos->QueryInterface(__uuidof(ISOSDacInterface8), &sos8)))
-        {
-            if (svrHeapAddr == NULL)
-            {
-                if (SUCCEEDED(hr = sos8->GetFinalizationFillPointers(count, data, &fillPointersCount))
-                    && hr != S_FALSE)
-                {
-                    success = true;
-                    // Nothing else to do, data is already populated
-                }
-            }
-            else
-            {
-                if (SUCCEEDED(hr = sos8->GetFinalizationFillPointersSvr(svrHeapAddr, count, data, &fillPointersCount))
-                    && hr != S_FALSE)
-                {
-                    success = true;
-                    // Nothing else to do, data is already populated
-                }
-            }
-
-            _ASSERTE(fillPointersCount == count);
-        }
-
-        if (!success)
-        {
-            // This would mean that there are additional, unaccounted for, generations
-            _ASSERTE(hr != S_FALSE);
-
-            // We couldn't get any data from the newer APIs, so fall back to the original data
-            memcpy(data, &(heap.finalization_fill_pointers), sizeof(CLRDATA_ADDRESS) * (DAC_NUMBERGENERATIONS + 2));
-        }
-    }
-
-public:
-    GCHeapDetails()
-    {
-        generation_table = NULL;
-        finalization_fill_pointers = NULL;
-    }
-
-    GCHeapDetails(const DacpGcHeapDetails  &dacGCDetails, CLRDATA_ADDRESS svrHeapAddr = NULL)
-    {
-        generation_table = NULL;
-        finalization_fill_pointers = NULL;
-
-        Set(dacGCDetails, svrHeapAddr);
-    }
-
-    ~GCHeapDetails()
-    {
-        if (generation_table != NULL)
-        {
-            delete[] generation_table;
-            generation_table = NULL;
-        }
-
-        if (finalization_fill_pointers != NULL)
-        {
-            delete[] finalization_fill_pointers;
-            finalization_fill_pointers = NULL;
-        }
-    }
-
-    // Due to the raw pointers, we are not a POD and have to be careful about lifetime
-    GCHeapDetails(const GCHeapDetails& other) = delete;
-    GCHeapDetails(GCHeapDetails&& other) = delete;
-    GCHeapDetails& operator=(const GCHeapDetails& other) = delete;
-    GCHeapDetails& operator=(GCHeapDetails&& other) = delete;
-
-    void Set(const DacpGcHeapDetails dacGCDetails, CLRDATA_ADDRESS svrHeapAddr = NULL)
-    {
-        original_heap_details = dacGCDetails;
-
-        GetGenerationTableSize(svrHeapAddr, &num_generations);
-        // Either we're pre POH and have 4, or post and have 5. If there's a different
-        // number it's either a bug or we need to update SOS.
-        _ASSERTE(num_generations == 4 || num_generations == 5);
-        has_poh = num_generations > 4;
-
-        if (generation_table != NULL)
-        {
-            delete[] generation_table;
-        }
-        generation_table = new DacpGenerationData[num_generations];
-        FillGenerationTable(svrHeapAddr, dacGCDetails, num_generations, generation_table);
-
-        if (finalization_fill_pointers != NULL)
-        {
-            delete[] finalization_fill_pointers;
-        }
-
-        unsigned int num_fill_pointers = num_generations + 2;
-        finalization_fill_pointers = new CLRDATA_ADDRESS[num_fill_pointers];
-        FillFinalizationPointers(svrHeapAddr, dacGCDetails, num_fill_pointers, finalization_fill_pointers);
-
-        heapAddr = svrHeapAddr;
-        alloc_allocated = dacGCDetails.alloc_allocated;
-        mark_array = dacGCDetails.mark_array;
-        current_c_gc_state = dacGCDetails.current_c_gc_state;
-        next_sweep_obj = dacGCDetails.next_sweep_obj;
-        saved_sweep_ephemeral_seg = dacGCDetails.saved_sweep_ephemeral_seg;
-        saved_sweep_ephemeral_start = dacGCDetails.saved_sweep_ephemeral_start;
-        background_saved_lowest_address = dacGCDetails.background_saved_lowest_address;
-        background_saved_highest_address = dacGCDetails.background_saved_highest_address;
-        ephemeral_heap_segment = dacGCDetails.ephemeral_heap_segment;
-        lowest_address = dacGCDetails.lowest_address;
-        highest_address = dacGCDetails.highest_address;
-        card_table = dacGCDetails.card_table;
-        has_regions = generation_table[0].start_segment != generation_table[1].start_segment;
-        has_background_gc = dacGCDetails.mark_array != -1;
-    }
-
-    DacpGcHeapDetails original_heap_details;
-    bool has_poh;
-    bool has_regions;
-    bool has_background_gc;
-    CLRDATA_ADDRESS heapAddr; // Only filled in in server mode, otherwise NULL
-    CLRDATA_ADDRESS alloc_allocated;
-
-    CLRDATA_ADDRESS mark_array;
-    CLRDATA_ADDRESS current_c_gc_state;
-    CLRDATA_ADDRESS next_sweep_obj;
-    CLRDATA_ADDRESS saved_sweep_ephemeral_seg;
-    CLRDATA_ADDRESS saved_sweep_ephemeral_start;
-    CLRDATA_ADDRESS background_saved_lowest_address;
-    CLRDATA_ADDRESS background_saved_highest_address;
-
-    // There are num_generations entries in generation_table and num_generations + 3 entries
-    // in finalization_fill_pointers
-    unsigned int num_generations;
-    DacpGenerationData *generation_table;
-    CLRDATA_ADDRESS ephemeral_heap_segment;
-    CLRDATA_ADDRESS *finalization_fill_pointers;
-    CLRDATA_ADDRESS lowest_address;
-    CLRDATA_ADDRESS highest_address;
-    CLRDATA_ADDRESS card_table;
-
-};
-
 // Things in this namespace should not be directly accessed/called outside of
 // the output-related functions.
 namespace Output
@@ -1915,33 +1706,6 @@ struct DumpArrayFlags
 
 HRESULT GetMTOfObject(TADDR obj, TADDR *mt);
 
-struct needed_alloc_context
-{
-    BYTE*   alloc_ptr;   // starting point for next allocation
-    BYTE*   alloc_limit; // ending point for allocation region/quantum
-};
-
-struct AllocInfo
-{
-    needed_alloc_context *array;
-    int num;                     // number of allocation contexts in array
-
-    AllocInfo()
-        : array(NULL)
-        , num(0)
-    {}
-    void Init()
-    {
-        extern void GetAllocContextPtrs(AllocInfo *pallocInfo);
-        GetAllocContextPtrs(this);
-    }
-    ~AllocInfo()
-    {
-        if (array != NULL)
-            delete[] array;
-    }
-};
-
 struct GCHandleStatistics
 {
     HeapStat hs;
@@ -1968,47 +1732,6 @@ struct GCHandleStatistics
     }
 };
 
-struct SegmentLookup
-{
-    DacpHeapSegmentData *m_segments;
-    int m_iSegmentsSize;
-    int m_iSegmentCount;
-
-    SegmentLookup();
-    ~SegmentLookup();
-
-    void Clear();
-    BOOL AddSegment(DacpHeapSegmentData *pData);
-    CLRDATA_ADDRESS GetHeap(CLRDATA_ADDRESS object, BOOL& bFound);
-};
-
-class GCHeapSnapshot
-{
-private:
-    BOOL m_isBuilt;
-    GCHeapDetails *m_heapDetails;
-    DacpGcHeapData m_gcheap;
-    SegmentLookup m_segments;
-
-    BOOL AddSegments(const GCHeapDetails& details);
-public:
-    GCHeapSnapshot();
-
-    BOOL Build();
-    void Clear();
-    BOOL IsBuilt() { return m_isBuilt; }
-
-    DacpGcHeapData *GetHeapData() { return &m_gcheap; }
-
-    int GetHeapCount() { return m_gcheap.HeapCount; }
-
-    GCHeapDetails *GetHeap(CLRDATA_ADDRESS objectPointer);
-    int GetGeneration(CLRDATA_ADDRESS objectPointer);
-
-
-};
-extern GCHeapSnapshot g_snapshot;
-
 BOOL IsSameModuleName (const char *str1, const char *str2);
 BOOL IsModule (DWORD_PTR moduleAddr);
 BOOL IsMethodDesc (DWORD_PTR value);
@@ -2050,33 +1773,10 @@ struct strobjInfo
     DWORD   m_StringLength;
 };
 
-// Just to make figuring out which fill pointer element matches a generation
-// a bit less confusing. This gen_segment function is ported from gc.cpp.
-inline unsigned int gen_segment (int gen)
-{
-    return (DAC_NUMBERGENERATIONS - gen - 1);
-}
-
-inline CLRDATA_ADDRESS SegQueue(DacpGcHeapDetails& heapDetails, int seg)
-{
-    return heapDetails.finalization_fill_pointers[seg - 1];
-}
-
-inline CLRDATA_ADDRESS SegQueueLimit(DacpGcHeapDetails& heapDetails, int seg)
-{
-    return heapDetails.finalization_fill_pointers[seg];
-}
-
-#define FinalizerListSeg (DAC_NUMBERGENERATIONS+1)
-#define CriticalFinalizerListSeg (DAC_NUMBERGENERATIONS)
-
-void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort);
-
 CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr);
 CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr);
 
 BOOL IsMTForFreeObj(DWORD_PTR pMT);
-void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields);
 
 HRESULT ExecuteCommand(PCSTR commandName, PCSTR args);
 
@@ -2102,7 +1802,6 @@ void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, BOOL fSt
 void GetDomainList(DWORD_PTR *&domainList, int &numDomain);
 HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread);
 CLRDATA_ADDRESS GetCurrentManagedThread(); // returns current managed thread if any
-void GetAllocContextPtrs(AllocInfo *pallocInfo);
 
 void ReloadSymbolWithLineInfo();
 
@@ -2122,10 +1821,6 @@ BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
 
 BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle);
 
-// ObjSize now uses the methodtable cache for its work too.
-size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE);
-size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE);
-
 void CharArrayContent(TADDR pos, ULONG num, bool widechar);
 void StringObjectContent (size_t obj, BOOL fLiteral=FALSE, const int length=-1);  // length=-1: dump everything in the string object.
 
@@ -2906,15 +2601,4 @@ private:
 };
 #include "sigparser.h"
 
-///////////////////////////////////////////////////////////////////////////////////////////
-//
-// Miscellaneous helper methods
-//
-
-#define THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "17"
-void EnumerateThreadPoolGlobalWorkItemConcurrentQueue(
-    DWORD_PTR workItemsConcurrentQueuePtr,
-    const char *queueName,
-    HeapStat *stats);
-
 #endif // __util_h__
index 21857783042a4bf0905481e81b2f0dd74327cd86..7f79ec8bcb58fa1ca48655194794fe303886a6de 100644 (file)
@@ -181,14 +181,14 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     g_services->AddCommand("dumpsig", new sosCommand("DumpSig"), "Dumps the signature of a method or field specified by '<sigaddr> <moduleaddr>'.");
     g_services->AddCommand("dumpsigelem", new sosCommand("DumpSigElem"), "Dumps a single element of a signature object.");
     g_services->AddCommand("dumpstack", new sosCommand("DumpStack"), "Displays a native and managed stack trace.");
-    g_services->AddCommand("dumpstackobjects", new sosCommand("DumpStackObjects"), "Displays all managed objects found within the bounds of the current stack.");
-    g_services->AddCommand("dso", new sosCommand("DumpStackObjects"), "Displays all managed objects found within the bounds of the current stack.");
+    g_services->AddManagedCommand("dumpstackobjects", "Displays all managed objects found within the bounds of the current stack.");
+    g_services->AddManagedCommand("dso", "Displays all managed objects found within the bounds of the current stack.");
     g_services->AddCommand("dumpvc", new sosCommand("DumpVC"), "Displays info about the fields of a value class.");
     g_services->AddManagedCommand("eeheap", "Displays info about process memory consumed by internal runtime data structures.");
     g_services->AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process.");
     g_services->AddCommand("eeversion", new sosCommand("EEVersion"), "Displays information about the runtime and SOS versions.");
     g_services->AddCommand("ehinfo", new sosCommand("EHInfo"), "Displays the exception handling blocks in a JIT-ed method.");
-    g_services->AddCommand("finalizequeue", new sosCommand("FinalizeQueue"), "Displays all objects registered for finalization.");
+    g_services->AddManagedCommand("finalizequeue", "Displays all objects registered for finalization.");
     g_services->AddCommand("findappdomain", new sosCommand("FindAppDomain"), "Attempts to resolve the AppDomain of a GC object.");
     g_services->AddCommand("findroots", new sosCommand("FindRoots"), "Finds and displays object roots across GC collections.");
     g_services->AddCommand("gchandles", new sosCommand("GCHandles"), "Displays statistics about garbage collector handles in the process.");