!maddress misattributes memory to the GC (#4318)
authorLee Culver <leculver@microsoft.com>
Thu, 12 Oct 2023 18:59:35 +0000 (11:59 -0700)
committerGitHub <noreply@github.com>
Thu, 12 Oct 2023 18:59:35 +0000 (11:59 -0700)
Fixes an issue reported by the GC team.

On windows, VirtualQuery information (bounds of allocated segments) is
very accurate in dumps because we have an entire segment of the dump
format which describes it. On Linux, it seems like we smash together
regions which came from two different calls of VirtualAlloc (well, the
Linux equivalent).

So, only use a heuristic of attributing regions of allocated memory on
Windows. Additionally, we are 100% accurate in reporting every byte of
memory that the GC allocates as of .Net 8. We shouldn't be ever using
this heuristic to attribute any memory to the GC.

src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs

index 7de6a766afd851b5bfa654188b4dc79af658be78..409c85bcf226f98acb48e0378a2f590574ec9068 100644 (file)
@@ -196,7 +196,25 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                                         // by the other region kind.
                                         if (region.ClrMemoryKind == ClrMemoryKind.None)
                                         {
-                                            region.ClrMemoryKind = mem.Kind;
+                                            // On platforms other than Windows, we may not have accurate region begin/end
+                                            // locations.  For example, with Windows dumps, we will get two distinct regions
+                                            // when one call to virtual alloc maps 0x10000-0x20000 and a different call to
+                                            // virtual alloc maps 0x20000-0x30000.  On Linux, we seem to only get one region
+                                            // defined (0x10000-0x30000) even if that came from two different calls to
+                                            // the Linux equivalent of VirtualAlloc.  Therefore, we only use this heuristic
+                                            // to tag memory on Windows to avoid accidently over-attributing memory to CLR.
+                                            //
+                                            // Finally, we actually get very accurate data about GC structures, so never
+                                            // use this heuristic to tag memory as belonging to the GC because we know it
+                                            // doesn't.
+                                            if (Target.OperatingSystem == OSPlatform.Windows
+                                                && mem.Kind != ClrMemoryKind.GCHeap
+                                                && mem.Kind != ClrMemoryKind.GCHeapReserve
+                                                && mem.Kind != ClrMemoryKind.GCBookkeeping
+                                                && mem.Kind != ClrMemoryKind.GCHeapToBeFreed)
+                                            {
+                                                region.ClrMemoryKind = mem.Kind;
+                                            }
                                         }
 
                                         DescribedRegion middleRegion = new(region)