From b7e7737ed120fe2a72d6a925d0714e57e80f79f4 Mon Sep 17 00:00:00 2001 From: Lee Culver Date: Fri, 31 Mar 2023 12:54:25 -0700 Subject: [PATCH] Add -list to !sos maddress (#3798) - Added a way to list all kinds of a specific memory type - Added caching to NativeAddressHelper, as it was recalculating every run --- .../MAddressCommand.cs | 50 ++++++++++++++++++- .../NativeAddressHelper.cs | 31 ++++++++++-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs index 670719f19..7e59762b9 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/MAddressCommand.cs @@ -17,6 +17,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands private const string ReserveFlag = "-reserve"; private const string ReserveHeuristicFlag = "-reserveHeuristic"; private const string ForceHandleTableFlag = "-forceHandleTable"; + private const string ListFlag = "-list"; [Option(Name = SummaryFlag, Aliases = new string[] { "-stat", }, Help = "Only print summary table.")] public bool Summary { get; set; } @@ -33,6 +34,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands [Option(Name = ForceHandleTableFlag, Help = "We only tag the HandleTable if we can do so efficiently on newer runtimes. This option ensures we always tag HandleTable memory, even if it will take a long time.")] public bool IncludeHandleTableIfSlow { get; set; } + [Option(Name = ListFlag, Help = "A separated list of regions to list allocations for.")] + public string List { get; set; } + [ServiceImport] public NativeAddressHelper AddressHelper { get; set; } @@ -59,7 +63,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands CollapseReserveRegions(ranges); } - if (!Summary) + if (!Summary && List is null) { int kindSize = ranges.Max(r => r.Type.ToString().Length); int stateSize = ranges.Max(r => r.State.ToString().Length); @@ -120,8 +124,46 @@ namespace Microsoft.Diagnostics.ExtensionCommands } - // Print summary table unconditionally + if (List is not null) { + // Print a list of the specified memory regions, ordered by size descending. + + string[] requested = List.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string kind in requested) + { + if (!ranges.Any(r => r.Name.Equals(kind, StringComparison.OrdinalIgnoreCase))) + { + Console.WriteLineError($"No memory regions match '{kind}'."); + } + else + { + Console.WriteLine($"{kind} Memory Regions:"); + + TableOutput output = new(Console, (16, "x12"), (16, "n0"), (8, ""), (12, ""), (12, "")); + output.WriteRow("Base Address", "Size (bytes)", "Size", "Mem State", "Mem Type", "Mem Protect"); + + ulong totalSize = 0; + int count = 0; + + IEnumerable matching = ranges.Where(r => r.Name.Equals(kind, StringComparison.OrdinalIgnoreCase)).OrderByDescending(s => s.Size); + foreach (DescribedRegion region in matching) + { + output.WriteRow(region.Start, region.Size, region.Size.ConvertToHumanReadable(), region.State, region.Type, region.Protection); + + count++; + totalSize += region.Size; + } + + Console.WriteLine($"{totalSize:n0} bytes ({totalSize.ConvertToHumanReadable()}) in {count:n0} regions"); + Console.WriteLine(); + } + } + } + + if (List is null || Summary) + { + // Show the summary table in almost every case, unless the user specified -list without -summary. + var grouped = from mem in ranges let name = mem.Name group mem by name into g @@ -192,6 +234,10 @@ Flags: that reserve region HeapReserve. Note that this is a heuristic and NOT intended to be completely accurate. This can be useful to try to figure out what is creating large amount of MEM_RESERVE regions. + + {ListFlag} + A separated list of region types (as maddress defines them) to print the base + addresses and sizes of. This list may be separated by , or ""in quotes"". "); } } diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs index 9d6f5f02e..8a2c03cff 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs +++ b/src/Microsoft.Diagnostics.ExtensionCommands/NativeAddressHelper.cs @@ -15,6 +15,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands [ServiceExport(Scope = ServiceScope.Target)] public sealed class NativeAddressHelper { + private ((bool, bool, bool, bool) Key, DescribedRegion[] Result) _previous; + [ServiceImport] public ITarget Target { get; set; } @@ -58,8 +60,27 @@ namespace Microsoft.Diagnostics.ExtensionCommands /// If !address fails we will throw InvalidOperationException. This is usually /// because symbols for ntdll couldn't be found. /// An enumerable of memory ranges. - internal IEnumerable EnumerateAddressSpace(bool tagClrMemoryRanges, bool includeReserveMemory, bool tagReserveMemoryHeuristically, bool includeHandleTableIfSlow) + public IEnumerable EnumerateAddressSpace(bool tagClrMemoryRanges, bool includeReserveMemory, bool tagReserveMemoryHeuristically, bool includeHandleTableIfSlow) + { + (bool, bool, bool, bool) key = (tagClrMemoryRanges, includeReserveMemory, tagReserveMemoryHeuristically, includeHandleTableIfSlow); + + if (_previous.Result is not null && _previous.Key == key) + { + return _previous.Result; + } + + DescribedRegion[] result = EnumerateAddressSpaceWorker(tagClrMemoryRanges, includeReserveMemory, tagReserveMemoryHeuristically, includeHandleTableIfSlow); + _previous = (key, result); + + // Use AsReadOnly to ensure no modifications to the cached value + return Array.AsReadOnly(result); + } + + private DescribedRegion[] EnumerateAddressSpaceWorker(bool tagClrMemoryRanges, bool includeReserveMemory, bool tagReserveMemoryHeuristically, bool includeHandleTableIfSlow) { + Console.WriteLineWarning("Enumerating and tagging the entire address space and caching the result..."); + Console.WriteLineWarning("Subsequent runs of this command should be faster."); + bool printedTruncatedWarning = false; IEnumerable addressResult = from region in MemoryRegionService.EnumerateRegions() @@ -90,9 +111,9 @@ namespace Microsoft.Diagnostics.ExtensionCommands if (!printedTruncatedWarning) { - Console.WriteLine($"Warning: Could not find a memory range for {mem.Address:x} - {mem.Kind}."); - Console.WriteLine($"This crash dump may not be a full dump!"); - Console.WriteLine(""); + Console.WriteLineWarning($"Warning: Could not find a memory range for {mem.Address:x} - {mem.Kind}."); + Console.WriteLineWarning($"This crash dump may not be a full dump!"); + Console.WriteLineWarning(""); printedTruncatedWarning = true; } @@ -477,7 +498,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands HandleTable, } - internal sealed class DescribedRegion : IMemoryRegion + public sealed class DescribedRegion : IMemoryRegion { public DescribedRegion() { -- 2.34.1