| File: EnumMemory\MemoryEnumerator.cs | Web Access |
| Project: Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy) |
// 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 Microsoft.Diagnostics.DataContractReader.Contracts; using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; using Microsoft.Diagnostics.DataContractReader.Legacy; using ContractModuleHandle = Microsoft.Diagnostics.DataContractReader.Contracts.ModuleHandle; namespace Microsoft.Diagnostics.DataContractReader.Legacy.EnumMemory; internal enum DumpType { Mini, Heap, Triage, } internal sealed class MemoryEnumerator { private const int MaxSyncBlocks = 1_000_000; private const int MaxThreads = 1_000_000; private readonly CollectorTarget _collectorTarget; private readonly HashSet<TargetPointer> _loaderAllocators = []; private readonly MethodCollector _methods; private readonly ObjectCollector _objects; private readonly PEImageCollector _peImageCollector; private MemoryEnumerator(CollectorTarget target) { _collectorTarget = target; _methods = new(target); _objects = new(target, _methods); _peImageCollector = new(target); } public static int Enumerate(Target target, MemoryRegionEmitter emitter, DumpType dumpType) { if (target is not ContractDescriptorTarget descriptorTarget) return HResults.E_NOTIMPL; target.Flush(FlushScope.All); using IDisposable readScope = descriptorTarget.RegisterReadCallback((address, size) => { if (emitter.ShouldEmitTargetRead(address, size)) emitter.Add(address, size); }); foreach (TargetSpan range in descriptorTarget.EnumerateDescriptorMemory()) emitter.Add(range.Address.Value, range.Size); new MemoryEnumerator(new(target, dumpType, emitter)).EnumerateMemoryRegions(); return emitter.Result; } private void EnumerateMemoryRegions() { TryEnumerate(EnumerateNativeRuntimeModule); TryEnumerate(EnumerateStatics); TryEnumerate(EnumerateDebugger); TryEnumerate(EnumerateManagedModules); TryEnumerate(EnumerateThreadExceptions); TryEnumerate(EnumerateThreads); if (_collectorTarget.DumpType == DumpType.Heap) { TryEnumerate(EnumerateGC); TryEnumerate(EnumerateCodeAndLoaderHeaps); TryEnumerate(EnumerateSyncBlocks); TryEnumerate(EnumerateAuxiliarySymbols); TryEnumerate(EnumerateStressLog); } if (_collectorTarget.DumpType is DumpType.Mini or DumpType.Triage) TryEnumerate(_collectorTarget.MiniMetadata.Write); } private void EnumerateNativeRuntimeModule() { if (_collectorTarget.Target.TryGetRuntimeImageBase(out TargetPointer imageBase)) _peImageCollector.EnumerateMemoryRegions(imageBase, uint.MaxValue, isMapped: true, includeExportsAndResources: true); } private static void TryEnumerate(Action enumerate) { try { enumerate(); } catch (System.Exception ex) when (ex.HResult != HResults.COR_E_OPERATIONCANCELED) { // Dump creation is best-effort because the target may be partially unreadable or corrupt. } } private void EnumerateStatics() { IRuntimeTypeSystem rts = _collectorTarget.Target.Contracts.RuntimeTypeSystem; rts.GetWellKnownMethodTable(WellKnownMethodTable.Array); rts.GetWellKnownMethodTable(WellKnownMethodTable.Canon); rts.GetWellKnownMethodTable(WellKnownMethodTable.Exception); rts.GetWellKnownMethodTable(WellKnownMethodTable.Free); rts.GetWellKnownMethodTable(WellKnownMethodTable.Object); rts.GetWellKnownMethodTable(WellKnownMethodTable.String); } private void EnumerateDebugger() { IDebugger debugger = _collectorTarget.Target.Contracts.Debugger; debugger.TryGetDebuggerData(out _); debugger.GetAttachStateFlags(); _collectorTarget.Target.Contracts.ReJIT.IsEnabled(); } private void EnumerateManagedModules() { ILoader loader = _collectorTarget.Target.Contracts.Loader; IEcmaMetadata ecmaMetadata = _collectorTarget.Target.Contracts.EcmaMetadata; TargetPointer appDomain = loader.GetAppDomain(); loader.GetRootAssembly(); IEnumerable<ContractModuleHandle> modules = loader.GetModuleHandles( appDomain, AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution); foreach (ContractModuleHandle module in modules) { loader.GetModule(module); loader.GetAssembly(module); TargetPointer peAssembly = loader.GetPEAssembly(module); ModuleFlags flags = loader.GetFlags(module); loader.GetSimpleName(module); if (_collectorTarget.DumpType != DumpType.Triage) loader.GetPath(module); loader.GetFileName(module); loader.GetILBase(module); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.FieldDefToDesc); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.ManifestModuleReferences); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.MemberRefToDesc); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.MethodDefToDesc); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.TypeDefToMethodTable); loader.GetModuleLookupMapBase(module, ModuleLookupMapKind.TypeRefToMethodTable); if (loader.TryGetLoadedImageContents(module, out TargetPointer imageBase, out uint imageSize, out uint imageFlags)) { _collectorTarget.Emitter.RegisterMetadataRange(ecmaMetadata.GetReadOnlyMetadataAddress(module)); TryEnumerate(() => { const uint ImageFlagMapped = 1; _peImageCollector.EnumerateMemoryRegions(imageBase, imageSize, (imageFlags & ImageFlagMapped) != 0); }); } if (flags.HasFlag(ModuleFlags.ReflectionEmit)) _collectorTarget.Emitter.RegisterMetadataRange(ecmaMetadata.GetReadWriteSavedMetadataAddress(module)); // Smaller dumps do not include in-memory symbols unless they are otherwise referenced. if (loader.TryGetSymbolStream(module, out TargetPointer symbolBuffer, out uint symbolSize) && _collectorTarget.DumpType == DumpType.Heap) _collectorTarget.Emitter.Add(symbolBuffer.Value, symbolSize); if (peAssembly != TargetPointer.Null) ecmaMetadata.HasReadWriteMetadata(peAssembly); TargetPointer loaderAllocator = loader.GetLoaderAllocator(module); if (loaderAllocator != TargetPointer.Null) _loaderAllocators.Add(loaderAllocator); loader.IsProbeExtensionResultValid(module); } TargetPointer globalLoaderAllocator = loader.GetGlobalLoaderAllocator(); if (globalLoaderAllocator != TargetPointer.Null) { _loaderAllocators.Add(globalLoaderAllocator); loader.GetHighFrequencyHeap(globalLoaderAllocator); loader.GetLowFrequencyHeap(globalLoaderAllocator); } foreach (ContractModuleHandle module in loader.GetModuleHandles( appDomain, AssemblyIterationFlags.IncludeLoading | AssemblyIterationFlags.IncludeLoaded | AssemblyIterationFlags.IncludeExecution)) { loader.IsAssemblyLoaded(module); } foreach (ContractModuleHandle _ in loader.GetModuleHandles(appDomain, AssemblyIterationFlags.IncludeFailedToLoad)) { } } private IEnumerable<ThreadData> EnumerateThreadData() { IThread thread = _collectorTarget.Target.Contracts.Thread; ThreadStoreData threadStore = thread.GetThreadStoreData(); HashSet<TargetPointer> visited = []; TargetPointer threadAddress = threadStore.FirstThread; for (int i = 0; threadAddress != TargetPointer.Null && i < MaxThreads; i++) { if (!visited.Add(threadAddress)) break; ThreadData threadData = thread.GetThreadData(threadAddress); yield return threadData; threadAddress = threadData.NextThread; } } private void EnumerateThreadExceptions() { // Collect exceptions across all threads before ordinary stack walks so their type and saved-frame // method names get priority in the bounded mini-metadata stream. foreach (ThreadData threadData in EnumerateThreadData()) { if (threadData.LastThrownObjectHandle != TargetPointer.Null) { TargetPointer exceptionObject = _collectorTarget.Target.ReadPointer(threadData.LastThrownObjectHandle.Value); _objects.EnumerateObject(exceptionObject); } } } private void EnumerateThreads() { IThread thread = _collectorTarget.Target.Contracts.Thread; _ = thread.GetThreadCounts(); foreach (ThreadData threadData in EnumerateThreadData()) { thread.GetThreadAllocContext(threadData.ThreadAddress, out _, out _); EnumerateStack(threadData); } } private void EnumerateStack(ThreadData threadData) { IStackWalk stackWalk = _collectorTarget.Target.Contracts.StackWalk; foreach (IStackDataFrameHandle frame in stackWalk.CreateStackWalk(threadData)) { TargetCodePointer instructionPointer = stackWalk.GetInstructionPointer(frame); TargetPointer methodDesc = stackWalk.GetMethodDescPtr(frame); _methods.CaptureMethod(methodDesc, instructionPointer); } } private void EnumerateGC() { IGC gc = _collectorTarget.Target.Contracts.GC; if (!gc.GetGCStructuresValid()) return; string[] identifiers = gc.GetGCIdentifiers(); gc.GetGCHeapCount(); gc.GetMaxGeneration(); gc.GetGCBounds(out _, out _); gc.GetCurrentGCState(); gc.TryGetGCDynamicAdaptationMode(out _); gc.GetGlobalMechanisms(); gc.GetGlobalAllocationContext(out _, out _); if (Array.IndexOf(identifiers, GCIdentifiers.Workstation) >= 0) { gc.GetHeapData(); gc.GetOomData(); } else { foreach (TargetPointer heap in gc.GetGCHeaps()) { gc.GetHeapData(heap); gc.GetOomData(heap); } } foreach ((GCHeapSegmentInfo segment, _) in gc.EnumerateAllSegments()) { if (segment.End > segment.Start) _collectorTarget.Emitter.Add(segment.Start.Value, segment.End.Value - segment.Start.Value); } AddGCMemoryRegions(gc.GetHandleTableMemoryRegions()); AddGCMemoryRegions(gc.GetGCBookkeepingMemoryRegions()); AddGCMemoryRegions(gc.GetGCFreeRegions()); } private void AddGCMemoryRegions(IReadOnlyList<GCMemoryRegionData> regions) { foreach (GCMemoryRegionData region in regions) _collectorTarget.Emitter.Add(region.Start.Value, region.Size); } private void EnumerateCodeAndLoaderHeaps() { IExecutionManager executionManager = _collectorTarget.Target.Contracts.ExecutionManager; foreach (JitManagerKind kind in new[] { JitManagerKind.EE, JitManagerKind.Interpreter }) { foreach (ICodeHeapInfo codeHeap in executionManager.GetCodeHeapInfos(kind)) { switch (codeHeap) { case HostCodeHeapInfo host when host.CurrentAddress > host.BaseAddress: _collectorTarget.Emitter.Add(host.BaseAddress.Value, host.CurrentAddress.Value - host.BaseAddress.Value); break; case LoaderCodeHeapInfo loader: AddLoaderHeapBlocks(loader.LoaderHeapAddress); break; } } } ILoader loaderContract = _collectorTarget.Target.Contracts.Loader; foreach (TargetPointer loaderAllocator in _loaderAllocators) { IReadOnlyDictionary<LoaderAllocatorHeapType, TargetPointer> heaps = loaderContract.GetLoaderAllocatorHeaps(loaderAllocator); foreach (TargetPointer loaderHeap in heaps.Values) AddLoaderHeapBlocks(loaderHeap); } } private void AddLoaderHeapBlocks(TargetPointer loaderHeap) { if (loaderHeap == TargetPointer.Null) return; ILoader loader = _collectorTarget.Target.Contracts.Loader; foreach (LoaderHeapBlock block in loader.EnumerateLoaderHeapBlocks(loaderHeap)) _collectorTarget.Emitter.Add(block.Address.Value, block.Size.Value); } private void EnumerateSyncBlocks() { ISyncBlock syncBlock = _collectorTarget.Target.Contracts.SyncBlock; IThread thread = _collectorTarget.Target.Contracts.Thread; uint count = syncBlock.GetSyncBlockCount(); for (uint i = 1; i <= count; i++) { TargetPointer address = syncBlock.GetSyncBlock(i); if (address == TargetPointer.Null || syncBlock.IsSyncBlockFree(i)) continue; syncBlock.GetSyncBlockObject(i); if (syncBlock.TryGetLockInfo(address, out uint owningThreadId, out _)) thread.IdToThread(owningThreadId); syncBlock.GetAdditionalThreadCount(address); syncBlock.GetBuiltInComData(address, out _, out _, out _); } TargetPointer cleanup = syncBlock.GetSyncBlockFromCleanupList(); TraverseTargetLinkedList(cleanup, syncBlock.GetNextSyncBlock); } private void EnumerateAuxiliarySymbols() { foreach ((TargetCodePointer _, string _) in _collectorTarget.Target.Contracts.AuxiliarySymbols.EnumerateAuxiliarySymbols()) { } } internal static void TraverseTargetLinkedList( TargetPointer current, Func<TargetPointer, TargetPointer> getNext) { HashSet<TargetPointer> visited = []; for (int i = 0; current != TargetPointer.Null && i < MaxSyncBlocks && visited.Add(current); i++) { current = getNext(current); } } private void EnumerateStressLog() { IStressLog stressLog = _collectorTarget.Target.Contracts.StressLog; if (!stressLog.HasStressLog()) return; StressLogData data = stressLog.GetStressLogData(); foreach (StressLogMemoryRange range in stressLog.GetStressLogMemoryRanges(data)) _collectorTarget.Emitter.Add(range.Start.Value, range.Size); } }