Fix dumpheap failures on certain dumps. (#561)
authorMike McLaughlin <mikem@microsoft.com>
Tue, 15 Oct 2019 21:58:46 +0000 (14:58 -0700)
committerGitHub <noreply@github.com>
Tue, 15 Oct 2019 21:58:46 +0000 (14:58 -0700)
Fix dumpheap failures on certain dumps.

Issue: https://github.com/dotnet/diagnostics/issues/503

Add MemoryService to package up all the read memory "mapping" logic.

Add MemoryCache to module mapping logic.

Rewrote ReadVirtualCache because of a couple of bugs that caused
some test failures on triage dumps making !pe displaying the exception
message string  not reliable.

Add SystemMemoryVersion property to Versions.props

eng/Versions.props
src/Microsoft.Diagnostics.DebugServices/MemoryCache.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices/MemoryService.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj
src/SOS/SOS.Hosting/SOSHost.cs
src/SOS/SOS.Hosting/dbgeng/DebugDataSpaces.cs
src/SOS/SOS.UnitTests/Scripts/WebApp.script
src/SOS/Strike/dllsext.cpp
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/Tools/dotnet-dump/Analyzer.cs

index d0bbaa2fbc0eca51bc2f63d845636ca37e805157..135ff4b6271c82fbaef46b6f7966962dfb0b5e76 100644 (file)
@@ -29,6 +29,7 @@
     <MicrosoftDiagnosticsTracingTraceEventVersion>2.0.44</MicrosoftDiagnosticsTracingTraceEventVersion>
     <SystemCommandLineExperimentalVersion>0.2.0-alpha.19254.1</SystemCommandLineExperimentalVersion>
     <SystemCommandLineRenderingVersion>0.2.0-alpha.19254.1</SystemCommandLineRenderingVersion>
+    <SystemMemoryVersion>4.5.3</SystemMemoryVersion>
 
     <XUnitVersion>2.4.1</XUnitVersion>
     <XUnitAbstractionsVersion>2.0.3</XUnitAbstractionsVersion>
diff --git a/src/Microsoft.Diagnostics.DebugServices/MemoryCache.cs b/src/Microsoft.Diagnostics.DebugServices/MemoryCache.cs
new file mode 100644 (file)
index 0000000..b3a4210
--- /dev/null
@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+    public sealed class MemoryCache
+    {
+        /// <summary>
+        /// This class represents a chunk of cached memory, more or less a page.
+        /// </summary>
+        class Cluster
+        {
+            internal const int Size = 4096;
+
+            /// <summary>
+            /// Empty cluster
+            /// </summary>
+            internal static Cluster Empty = new Cluster(Array.Empty<byte>());
+
+            /// <summary>
+            /// The cached data.
+            /// </summary>
+            private readonly byte[] _data;
+
+            /// <summary>
+            /// Creates a cluster for some data.
+            /// If the buffer is shorter than a page, it build a validity bitmap for it.
+            /// </summary>
+            /// <param name="data">the data to cache</param>
+            /// 
+            internal Cluster(byte[] data)
+            {
+                _data = data;
+            }
+
+            /// <summary>
+            /// Computes the base address of the cluster holding an address.
+            /// </summary>
+            /// <param name="address">input address</param>
+            /// <returns>start address of the cluster</returns>
+            internal static ulong GetBase(ulong address)
+            {
+                return address & ~(ulong)(Cluster.Size - 1);
+            }
+
+            /// <summary>
+            /// Computes the offset of an address inside of the cluster.
+            /// </summary>
+            /// <param name="address">input address</param>
+            /// <returns>offset of address</returns>
+            internal static int GetOffset(ulong address)
+            {
+                return unchecked((int)((uint)address & (Cluster.Size - 1)));
+            }
+
+            /// <summary>
+            /// Reads at up <paramref name="size"/> bytes from location <paramref name="address"/>.
+            /// </summary>
+            /// <param name="address">desired address</param>
+            /// <param name="buffer">buffer to read</param>
+            /// <param name="size">number of bytes to read</param>
+            /// <returns>bytes read</returns>
+            internal int ReadBlock(ulong address, Span<byte> buffer, int size)
+            {
+                int offset = GetOffset(address);
+                if (offset < _data.Length)
+                {
+                    size = Math.Min(_data.Length - offset, size);
+                    new Span<byte>(_data, offset, size).CopyTo(buffer);
+                }
+                else
+                {
+                    size = 0;
+                }
+                return size;
+            }
+        }
+
+        /// <summary>
+        /// After memory cache reaches the limit size, it gets flushed upon next access.
+        /// </summary>
+        private const int CacheSizeLimit = 64 * 1024 * 1024; // 64 MB
+
+        /// <summary>
+        /// The delegate to the actual read memory
+        /// </summary>
+        public delegate byte[] ReadMemoryDelegate(ulong address, int size);
+
+        private readonly Dictionary<ulong, Cluster> _map;
+        private readonly ReadMemoryDelegate _readMemory;
+
+        public MemoryCache(ReadMemoryDelegate readMemory)
+        {
+            _map = new Dictionary<ulong, Cluster>();
+            _readMemory = readMemory;
+        }
+
+        /// <summary>
+        /// Current size of this memory cache
+        /// </summary>
+        public long CacheSize { get; private set; }
+
+        /// <summary>
+        /// Flush this memory cache
+        /// </summary>
+        public void FlushCache()
+        {
+            Trace.TraceInformation("Flushed memory cache");
+            _map.Clear();
+            CacheSize = 0;
+        }
+
+        /// <summary>
+        /// Reads up to <paramref name="buffer.Length"/> bytes of memory at <paramref name="address"/>.
+        /// It walks the set of clusters to collect as much data as possible.
+        /// </summary>
+        /// <param name="address">address to read</param>
+        /// <param name="buffer">span of buffer to read memory</param>
+        /// <param name="bytesRead">The number of bytes actually read out of the target process</param>
+        /// <returns>true if read memory succeeded or partially succeeded</returns>
+        public bool ReadMemory(ulong address, Span<byte> buffer, out int bytesRead)
+        {
+            int bytesRequested = buffer.Length;
+            int offset = 0;
+
+            while (bytesRequested > 0)
+            {
+                Cluster cluster = GetCluster(address);
+                int read = cluster.ReadBlock(address, buffer.Slice(offset), bytesRequested);
+                if (read <= 0) {
+                    break;
+                }
+                address += (uint)read;
+                offset += read;
+                bytesRequested -= read;
+            }
+
+            bytesRead = offset;
+            return offset > 0;
+        }
+
+        /// <summary>
+        /// Ensures that an address is cached.
+        /// </summary>
+        /// <param name="address">target address</param>
+        /// <returns>It will resolve to an existing cluster or a newly-created one</returns>
+        private Cluster GetCluster(ulong address)
+        {
+            ulong baseAddress = Cluster.GetBase(address);
+
+            if (!_map.TryGetValue(baseAddress, out Cluster cluster))
+            {
+                if (CacheSize >= CacheSizeLimit)
+                {
+                    FlushCache();
+                }
+
+                // There are 3 things that can happen here:
+                // 1) Normal full size cluster read (== Cluster.Size). The full block memory is cached.
+                // 2) Partial cluster read (< Cluster.Size). The partial memory block is cached and the memory after it is invalid.
+                // 3) Data == null. Read failure. Failure is not cached.
+                byte[] data = _readMemory(baseAddress, Cluster.Size);
+                if (data == null)
+                {
+                    cluster = Cluster.Empty;
+                }
+                else
+                { 
+                    cluster = new Cluster(data);
+                    CacheSize += data.Length;
+                    _map[baseAddress] = cluster;
+                }
+            }
+
+            return cluster;
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices/MemoryService.cs b/src/Microsoft.Diagnostics.DebugServices/MemoryService.cs
new file mode 100644 (file)
index 0000000..3914258
--- /dev/null
@@ -0,0 +1,290 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.Runtime;
+using Microsoft.SymbolStore;
+using Microsoft.SymbolStore.KeyGenerators;
+using SOS;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+    /// <summary>
+    /// Memory service CLRMD implementation
+    /// </summary>
+    public class MemoryService
+    {
+        private readonly IDataReader _dataReader;
+        private readonly MemoryCache _memoryCache;
+        private readonly Dictionary<string, PEReader> _pathToPeReader = new Dictionary<string, PEReader>();
+
+        /// <summary>
+        /// Memory service constructor
+        /// </summary>
+        /// <param name="dataReader">CLRMD data reader</param>
+        public MemoryService(IDataReader dataReader)
+        {
+            _dataReader = dataReader;
+            _memoryCache = new MemoryCache(ReadMemoryFromModule);
+        }
+
+        /// <summary>
+        /// Read memory out of the target process.
+        /// </summary>
+        /// <param name="address">The address of memory to read</param>
+        /// <param name="buffer">The buffer to write to</param>
+        /// <param name="bytesRequested">The number of bytes to read</param>
+        /// <param name="bytesRead">The number of bytes actually read out of the target process</param>
+        /// <returns>true if any bytes were read at all, false if the read failed (and no bytes were read)</returns>
+        public bool ReadMemory(ulong address, byte[] buffer, int bytesRequested, out int bytesRead)
+        {
+            return ReadMemory(address, new Span<byte>(buffer, 0, bytesRequested), out bytesRead);
+        }
+
+        /// <summary>
+        /// Read memory out of the target process.
+        /// </summary>
+        /// <param name="address">The address of memory to read</param>
+        /// <param name="buffer">The buffer to read memory into</param>
+        /// <param name="bytesRequested">The number of bytes to read</param>
+        /// <param name="bytesRead">The number of bytes actually read out of the target process</param>
+        /// <returns>true if any bytes were read at all, false if the read failed (and no bytes were read)</returns>
+        public bool ReadMemory(ulong address, IntPtr buffer, int bytesRequested, out int bytesRead)
+        {
+            unsafe
+            {
+                return ReadMemory(address, new Span<byte>(buffer.ToPointer(), bytesRequested), out bytesRead);
+            }
+        }
+
+        /// <summary>
+        /// Read memory out of the target process.
+        /// </summary>
+        /// <param name="address">The address of memory to read</param>
+        /// <param name="buffer">The buffer to read memory into</param>
+        /// <param name="bytesRead">The number of bytes actually read out of the target process</param>
+        /// <returns>true if any bytes were read at all, false if the read failed (and no bytes were read)</returns>
+        public bool ReadMemory(ulong address, Span<byte> buffer, out int bytesRead)
+        {
+            int bytesRequested = buffer.Length;
+            bool result = false;
+            unsafe
+            {
+                fixed (byte* ptr = buffer)
+                {
+                    result = _dataReader.ReadMemory(address, new IntPtr(ptr), bytesRequested, out bytesRead);
+                }
+            }
+            // If the read failed or a successful partial read
+            if (!result || (bytesRequested != bytesRead))
+            {
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                {
+                    // Check if the memory is in a module and cache it if it is
+                    if (_memoryCache.ReadMemory(address + (uint)bytesRead, buffer.Slice(bytesRead), out int read))
+                    {
+                        bytesRead += read;
+                        result = true;
+                    }
+                }
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Read memory from a PE module for the memory cache. Finds locally or downloads a module 
+        /// and "maps" it into the address space. This function can return more than requested which 
+        /// means the block should not be cached.
+        /// </summary>
+        /// <param name="address">memory address</param>
+        /// <param name="bytesRequested">number of bytes</param>
+        /// <returns>bytes read or null if error</returns>
+        private byte[] ReadMemoryFromModule(ulong address, int bytesRequested)
+        {
+            // Check if there is a module that contains the address range being read and map it into the virtual address space.
+            foreach (ModuleInfo module in _dataReader.EnumerateModules())
+            {
+                ulong start = module.ImageBase;
+                ulong end = start + module.FileSize;
+                if (address >= start && address < end)
+                {
+                    Trace.TraceInformation("ReadMemory: address {0:X16} size {1:X8} found module {2}", address, bytesRequested, module.FileName);
+
+                    // We found a module that contains the memory requested. Now find or download the PE image.
+                    PEReader reader = GetPEReader(module);
+                    if (reader != null)
+                    {
+                        // Read the memory from the PE image.
+                        int rva = unchecked((int)(address - start));
+                        try
+                        {
+                            byte[] data = null;
+
+                            int sizeOfHeaders = reader.PEHeaders.PEHeader.SizeOfHeaders;
+                            if (rva >= 0 && rva < sizeOfHeaders)
+                            {
+                                // If the address isn't contained in one of the sections, assume that SOS is reader the PE headers directly.
+                                Trace.TraceInformation("ReadMemory: rva {0:X8} size {1:X8} in PE Header", rva, bytesRequested);
+                                data = reader.GetEntireImage().GetReader(rva, bytesRequested).ReadBytes(bytesRequested);
+                            }
+                            else
+                            {
+                                PEMemoryBlock block = reader.GetSectionData(rva);
+                                if (block.Length > 0)
+                                {
+                                    int size = Math.Min(block.Length, bytesRequested);
+                                    data = block.GetReader().ReadBytes(size);
+                                    ApplyRelocations(module, reader, rva, data);
+                                }
+                            }
+
+                            return data;
+                        }
+                        catch (Exception ex) when (ex is BadImageFormatException || ex is InvalidOperationException || ex is IOException)
+                        {
+                            Trace.TraceError("ReadMemory: exception {0}", ex);
+                        }
+                    }
+                    break;
+                }
+            }
+            return null;
+        }
+
+        private PEReader GetPEReader(ModuleInfo module)
+        {
+            if (!_pathToPeReader.TryGetValue(module.FileName, out PEReader reader))
+            {
+                Stream stream = null;
+
+                string downloadFilePath = module.FileName;
+                if (!File.Exists(downloadFilePath))
+                {
+                    if (SymbolReader.IsSymbolStoreEnabled())
+                    {
+                        SymbolStoreKey key = PEFileKeyGenerator.GetKey(Path.GetFileName(downloadFilePath), module.TimeStamp, module.FileSize);
+                        if (key != null)
+                        {
+                            // Now download the module from the symbol server
+                            downloadFilePath = SymbolReader.GetSymbolFile(key);
+                        }
+                    }
+                }
+
+                if (!string.IsNullOrEmpty(downloadFilePath))
+                {
+                    Trace.TraceInformation("GetPEReader: downloading {0}", downloadFilePath);
+                    try
+                    {
+                        stream = File.OpenRead(downloadFilePath);
+                    }
+                    catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException || ex is UnauthorizedAccessException || ex is IOException)
+                    {
+                        Trace.TraceError("GetPEReader: exception {0}", ex);
+                    }
+                    if (stream != null)
+                    {
+                        reader = new PEReader(stream);
+                        if (reader.PEHeaders == null || reader.PEHeaders.PEHeader == null) {
+                            reader = null;
+                        }
+                        _pathToPeReader.Add(module.FileName, reader);
+                    }
+                }
+            }
+            return reader;
+        }
+
+        private void ApplyRelocations(ModuleInfo module, PEReader reader, int dataVA, byte[] data)
+        {
+            PEMemoryBlock relocations = reader.GetSectionData(".reloc");
+            if (relocations.Length > 0)
+            {
+                ulong baseDelta = module.ImageBase - reader.PEHeaders.PEHeader.ImageBase;
+                Trace.TraceInformation("ApplyRelocations: dataVA {0:X8} dataCB {1} baseDelta: {2:X16}", dataVA, data.Length, baseDelta);
+
+                BlobReader blob = relocations.GetReader();
+                while (blob.RemainingBytes > 0)
+                {
+                    // Read IMAGE_BASE_RELOCATION struct
+                    int virtualAddress = blob.ReadInt32();
+                    int sizeOfBlock = blob.ReadInt32();
+
+                    // Each relocation block covers 4K
+                    if (dataVA >= virtualAddress && dataVA < (virtualAddress + 4096))
+                    {
+                        int entryCount = (sizeOfBlock - 8) / 2;     // (sizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD)
+                        Trace.TraceInformation("ApplyRelocations: reloc VirtualAddress {0:X8} SizeOfBlock {1:X8} entry count {2}", virtualAddress, sizeOfBlock, entryCount);
+
+                        int relocsApplied = 0;
+                        for (int e = 0; e < entryCount; e++)
+                        {
+                            // Read relocation type/offset
+                            ushort entry = blob.ReadUInt16();
+                            if (entry == 0) {
+                                break;
+                            }
+                            var type = (BaseRelocationType)(entry >> 12);       // type is 4 upper bits
+                            int relocVA = virtualAddress + (entry & 0xfff);     // offset is 12 lower bits
+
+                            // Is this relocation in the data?
+                            if (relocVA >= dataVA && relocVA < (dataVA + data.Length))
+                            {
+                                int offset = relocVA - dataVA;
+                                switch (type)
+                                {
+                                    case BaseRelocationType.ImageRelBasedAbsolute:
+                                        break;
+
+                                    case BaseRelocationType.ImageRelBasedHighLow:
+                                    {
+                                        uint value = BitConverter.ToUInt32(data, offset);
+                                        value += (uint)baseDelta;
+                                        byte[] source = BitConverter.GetBytes(value);
+                                        Array.Copy(source, 0, data, offset, source.Length);
+                                        break;
+                                    }
+                                    case BaseRelocationType.ImageRelBasedDir64:
+                                    {
+                                        ulong value = BitConverter.ToUInt64(data, offset);
+                                        value += baseDelta;
+                                        byte[] source = BitConverter.GetBytes(value);
+                                        Array.Copy(source, 0, data, offset, source.Length);
+                                        break;
+                                    }
+                                    default:
+                                        Debug.Fail($"ApplyRelocations: invalid relocation type {type}");
+                                        break;
+                                }
+                                relocsApplied++;
+                            }
+                        }
+                        Trace.TraceInformation("ApplyRelocations: relocs {0} applied", relocsApplied);
+                    }
+                    else
+                    {
+                        // Skip to the next relocation block
+                        blob.Offset += sizeOfBlock - 8;
+                    }
+                }
+            }
+        }
+
+        enum BaseRelocationType
+        {
+            ImageRelBasedAbsolute   = 0,
+            ImageRelBasedHigh       = 1,
+            ImageRelBasedLow        = 2,
+            ImageRelBasedHighLow    = 3,
+            ImageRelBasedHighAdj    = 4,
+            ImageRelBasedDir64      = 10,
+        }
+    }
+}
index 974db0a8851803caa26e2880029e3bf6c26cde06..49aedc5efb321d52930aaacddac45b6ae205af9b 100644 (file)
   
   <ItemGroup>
     <PackageReference Include="Microsoft.Diagnostics.Runtime" Version="$(MicrosoftDiagnosticsRuntimeVersion)" />
+    <PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" />
+    <PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
+  </ItemGroup>
+  
+  <ItemGroup>
+    <ProjectReference Include="..\SOS\SOS.NETCore\SOS.NETCore.csproj" />
   </ItemGroup>
 </Project>
index 0f26ec1121ead1880e351eafdbe070d0789d71bf..7035afafb1412f1bfb57b80acf1bc1706c9e79c9 100644 (file)
@@ -161,12 +161,12 @@ namespace SOS
 
         private readonly AnalyzeContext _analyzeContext;
         private readonly RegisterService _registerService;
+        private readonly MemoryService _memoryService;
         private readonly IConsoleService _console;
         private readonly COMCallableIUnknown _ccw;  
         private readonly IntPtr _interface;
         private readonly ReadVirtualCache _versionCache;
         private IntPtr _sosLibrary = IntPtr.Zero;
-        private Dictionary<string, PEReader> _pathToPeReader = new Dictionary<string, PEReader>();
 
         /// <summary>
         /// Enable the assembly resolver to get the right SOS.NETCore version (the one
@@ -202,8 +202,9 @@ namespace SOS
             DataReader = dataTarget.DataReader;
             _console = serviceProvider.GetService<IConsoleService>();
             _analyzeContext = serviceProvider.GetService<AnalyzeContext>();
+            _memoryService = serviceProvider.GetService<MemoryService>();
             _registerService = serviceProvider.GetService<RegisterService>();
-            _versionCache = new ReadVirtualCache(this);
+            _versionCache = new ReadVirtualCache(_memoryService);
 
             string rid = InstallHelper.GetRid();
             SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
@@ -459,7 +460,7 @@ namespace SOS
             uint bytesRequested,
             uint* pbytesRead)
         {
-            if (DataReader.ReadMemory(address, buffer, unchecked((int)bytesRequested), out int bytesRead))
+            if (_memoryService.ReadMemory(address, buffer, unchecked((int)bytesRequested), out int bytesRead))
             {
                 Write(pbytesRead, (uint)bytesRead);
                 return S_OK;
@@ -467,110 +468,6 @@ namespace SOS
             return E_FAIL;
         }
 
-        internal unsafe int ReadVirtualForWindows(
-            IntPtr self,
-            ulong address,
-            IntPtr buffer,
-            uint bytesRequested,
-            uint* pbytesRead)
-        {
-            if (DataReader.ReadMemory(address, buffer, unchecked((int)bytesRequested), out int bytesRead))
-            {
-                Write(pbytesRead, (uint)bytesRead);
-                return S_OK;
-            }
-
-            // The memory read failed. Check if there is a module that contains the 
-            // address range being read and map it into the virtual address space.
-            foreach (ModuleInfo module in DataReader.EnumerateModules())
-            {
-                ulong start = module.ImageBase;
-                ulong end = start + module.FileSize;
-                if (start <= address && end > address)
-                {
-                    Trace.TraceInformation("ReadVirtualForWindows: address {0:X16} size {1:X8} found module {2}", address, bytesRequested, module.FileName);
-
-                    // We found a module that contains the memory requested. Now find or download the PE image.
-                    PEReader reader = GetPEReader(module);
-                    if (reader != null)
-                    {
-                        // Read the memory from the PE image. There are a few limitions:
-                        // 1) Fix ups are NOT applied to the sections
-                        // 2) Memory regions that cross/contain heap memory into module image memory
-                        int rva = (int)(address - start);
-                        try
-                        {
-                            PEMemoryBlock block = reader.GetSectionData(rva);
-                            if (block.Pointer == null)
-                            {
-                                Trace.TraceInformation("ReadVirtualForWindows: rva {0:X8} not in any section; reading from entire image", rva);
-
-                                // If the address isn't contained in one of the sections, assume that SOS is reader the PE headers directly.
-                                block = reader.GetEntireImage();
-                            }
-                            else
-                            {
-                                rva = 0;
-                            }
-                            BlobReader blob = block.GetReader(rva, (int)bytesRequested);
-                            byte[] data = blob.ReadBytes((int)bytesRequested);
-
-                            Marshal.Copy(data, 0, buffer, data.Length);
-                            Write(pbytesRead, (uint)data.Length);
-                            return S_OK;
-                        }
-                        catch (Exception ex) when (ex is BadImageFormatException || ex is InvalidOperationException || ex is IOException)
-                        {
-                            Trace.TraceError("ReadVirtualForWindows: exception {0}", ex);
-                        }
-                    }
-                    break;
-                }
-            }
-
-            return E_FAIL;
-        }
-
-        private PEReader GetPEReader(ModuleInfo module)
-        {
-            if (!_pathToPeReader.TryGetValue(module.FileName, out PEReader reader))
-            {
-                Stream stream = null;
-
-                string downloadFilePath = module.FileName;
-                if (!File.Exists(downloadFilePath)) 
-                {
-                    if (SymbolReader.IsSymbolStoreEnabled())
-                    {
-                        SymbolStoreKey key = PEFileKeyGenerator.GetKey(Path.GetFileName(downloadFilePath), module.TimeStamp, module.FileSize);
-                        if (key != null)
-                        {
-                            // Now download the module from the symbol server
-                            downloadFilePath = SymbolReader.GetSymbolFile(key);
-                        }
-                    }
-                }
-
-                if (!string.IsNullOrEmpty(downloadFilePath))
-                {
-                    try
-                    {
-                        stream = File.OpenRead(downloadFilePath);
-                    }
-                    catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException || ex is UnauthorizedAccessException || ex is IOException)
-                    {
-                        Trace.TraceError("GetPEReader: exception {0}", ex);
-                    }
-                    if (stream != null)
-                    {
-                        reader = new PEReader(stream);
-                        _pathToPeReader.Add(module.FileName, reader);
-                    }
-                }
-            }
-            return reader;
-        }
-
         internal unsafe int WriteVirtual(
             IntPtr self,
             ulong address,
@@ -1256,15 +1153,15 @@ namespace SOS
     {
         private const int CACHE_SIZE = 4096;
 
-        private readonly SOSHost _soshost;
+        private readonly MemoryService _memoryService;
         private readonly byte[] _cache = new byte[CACHE_SIZE];
         private ulong _startCache;
         private bool _cacheValid;
         private int _cacheSize;
 
-        internal ReadVirtualCache(SOSHost soshost)
+        internal ReadVirtualCache(MemoryService memoryService)
         {
-            _soshost = soshost;
+            _memoryService = memoryService;
             Clear();
         }
 
@@ -1279,22 +1176,23 @@ namespace SOS
             if (bufferSize > CACHE_SIZE) 
             {
                 // Don't even try with the cache
-                return _soshost.DataReader.ReadMemory(address, buffer, bufferSize, out bytesRead);
+                return _memoryService.ReadMemory(address, buffer, bufferSize, out bytesRead);
             }
 
             if (!_cacheValid || (address < _startCache) || (address > (_startCache + (ulong)(_cacheSize - bufferSize))))
             {
                 _cacheValid = false;
                 _startCache = address;
-                if (!_soshost.DataReader.ReadMemory(_startCache, _cache, CACHE_SIZE, out int cbBytesRead)) {
+                if (!_memoryService.ReadMemory(_startCache, _cache, _cache.Length, out int cbBytesRead)) {
                     return false;
                 }
                 _cacheSize = cbBytesRead;
                 _cacheValid = true;
             }
 
-            Array.Copy(_cache, (int)(address - _startCache), buffer, 0, bufferSize);
-            bytesRead = bufferSize;
+            int size = Math.Min(bufferSize, _cacheSize);
+            Array.Copy(_cache, (int)(address - _startCache), buffer, 0, size);
+            bytesRead = size;
             return true;
         }
 
index ee6c82b11a0eac106a8842afe7343a04eff179b1..253d27f1190b1b7e198058769e0cff3d707202ad 100644 (file)
@@ -24,7 +24,7 @@ namespace SOS
 
         private static void AddDebugDataSpaces(VTableBuilder builder, SOSHost soshost)
         {
-            builder.AddMethod(new ReadVirtualDelegate(soshost.ReadVirtualForWindows));
+            builder.AddMethod(new ReadVirtualDelegate(soshost.ReadVirtual));
             builder.AddMethod(new WriteVirtualDelegate(soshost.WriteVirtual));
             builder.AddMethod(new SearchVirtualDelegate((self, offset, length, pattern, patternSize, patternGranularity, matchOffset) => DebugClient.NotImplemented));
             builder.AddMethod(new ReadVirtualUncachedDelegate((self, offset, buffer, bufferSize, bytesRead) => DebugClient.NotImplemented));
index 44dbb3d8ade0d5ba88a1697c6576b0ece888223e..3f48029970910f59621ecffa340dcd3c9f33b0bb 100644 (file)
@@ -106,10 +106,6 @@ VERIFY:\s+Hosted Runtime:\s+no\s+
 VERIFY:\s+ID\s+OSID\s+ThreadOBJ\s+State.*\s+
 VERIFY:\s+<DECVAL>\s+<DECVAL>\s+<HEXVAL>\s+<HEXVAL>.*\s+
 
-# dumpheap, dumpasync may not work on Windows and Alpine dotnet-dump
-# Issue: https://github.com/dotnet/diagnostics/issues/503
-!IFDEF:DOTNETDUMP
-
 SOSCOMMAND:DumpHeap -stat
 VERIFY:\s*Statistics:\s+
 VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
@@ -129,35 +125,4 @@ VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
 VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
 VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
 
-SOSCOMMAND:DumpAsync -mt <PREVOUT> -fields
-
-ENDIF:DOTNETDUMP
-
-# This duplication allows these commands to run on lldb/xplat
-# Issue: https://github.com/dotnet/diagnostics/issues/503
-!IFDEF:ALPINE
-!IFDEF:WINDOWS
-
-SOSCOMMAND:DumpHeap -stat
-VERIFY:\s*Statistics:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
-VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
-!VERIFY:.*UNKNOWN.*
-
-SOSCOMMAND:DumpAsync
-VERIFY:\s*Statistics:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
-VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
-
-SOSCOMMAND:DumpAsync -mt <POUT>\s+MT\s+Count\s+TotalSize\s+Class Name\s+(<HEXVAL>)\s+<DECVAL>\s+<DECVAL>\s+.*<POUT>
-VERIFY:\s*Statistics:\s+
-VERIFY:\s+MT\s+Count\s+TotalSize\s+Class Name\s+
-VERIFY:\s*<HEXVAL>\s+<DECVAL>\s+<DECVAL>\s+.*
-VERIFY:\s*Total\s+<DECVAL>\s+objects\s+
-
-SOSCOMMAND:DumpAsync -mt <PREVOUT> -fields
-
-ENDIF:WINDOWS
-ENDIF:ALPINE
\ No newline at end of file
+SOSCOMMAND:DumpAsync -mt <PREVOUT> -fields
\ No newline at end of file
index 390044f5896eeeeb59d050b4b055ce5c2152d14e..7517d739a1c9c7b89a95f3c4d027f0e69285b312 100644 (file)
@@ -53,7 +53,10 @@ static void DllsNameFromPeb(
     )
 {
     ULONG64 ProcessPeb;
-    g_ExtSystem->GetCurrentProcessPeb (&ProcessPeb);
+    if (FAILED(g_ExtSystem->GetCurrentProcessPeb(&ProcessPeb)))
+    {
+        return;
+    }
 
     ULONG64 pLdrEntry;
     ULONG64 PebLdrAddress;
index 2942af788831a3340d1b16c5740f650eb9c486f2..d286f75a549e549ef13964a4a1cea5d3aa5d7505 100644 (file)
@@ -415,16 +415,17 @@ void DumpStackInternal(DumpStackFlag *pDSFlag)
     if (pDSFlag->end == 0) {
         // Find the current stack range
         NT_TIB teb;
-        ULONG64 dwTebAddr=0;
-
-        g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
-        if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
+        ULONG64 dwTebAddr = 0;
+        if (SUCCEEDED(g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr)))
         {
-            if (pDSFlag->top > TO_TADDR(teb.StackLimit)
-            && pDSFlag->top <= TO_TADDR(teb.StackBase))
+            if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
             {
-                if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
-                    pDSFlag->end = TO_TADDR(teb.StackBase);
+                if (pDSFlag->top > TO_TADDR(teb.StackLimit)
+                    && pDSFlag->top <= TO_TADDR(teb.StackBase))
+                {
+                    if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
+                        pDSFlag->end = TO_TADDR(teb.StackBase);
+                }
             }
         }
     }
@@ -10402,10 +10403,13 @@ DECLARE_API (ProcInfo)
     }
 
     if (fProcInfo & INFO_ENV) {
+        ULONG64 pPeb;
+        if (FAILED(g_ExtSystem->GetCurrentProcessPeb(&pPeb)))
+        {
+            return Status;
+        }
         ExtOut("---------------------------------------\n");
         ExtOut("Environment\n");
-        ULONG64 pPeb;
-        g_ExtSystem->GetCurrentProcessPeb(&pPeb);
 
         static ULONG Offset_ProcessParam = -1;
         static ULONG Offset_Environment = -1;
@@ -10487,7 +10491,10 @@ DECLARE_API (ProcInfo)
     HANDLE hProcess = INVALID_HANDLE_VALUE;
     if (fProcInfo & (INFO_TIME | INFO_MEM)) {
         ULONG64 handle;
-        g_ExtSystem->GetCurrentProcessHandle(&handle);
+        if (FAILED(g_ExtSystem->GetCurrentProcessHandle(&handle)))
+        {
+            return Status;
+        }
         hProcess = (HANDLE)handle;
     }
     
index 1d7a79bc5ff544e1add0c1cc8b8fafe19eb411fe..30b8e526617cb2d44fc7b5c0f9dd4411530ef9fb 100644 (file)
@@ -3448,16 +3448,14 @@ void StringObjectContent(size_t obj, BOOL fLiteral, const int length)
         return;
     }
     
-    strobjInfo stInfo;
-
-    if (MOVE(stInfo,obj) != S_OK)
+    strobjInfo stInfo { 0, 0 };
+    if (MOVE(stInfo, obj) != S_OK)
     {
         ExtOut ("Error getting string data\n");
         return;
     }
 
-    if (objData.Size > 0x200000 ||
-        stInfo.m_StringLength > 0x200000)
+    if (objData.Size > 0x200000 || stInfo.m_StringLength > 0x200000)
     {
         ExtOut ("<String is invalid or too large to print>\n");
         return;
@@ -4675,62 +4673,47 @@ void GetAllocContextPtrs(AllocInfo *pallocInfo)
     }
 }
 
-HRESULT ReadVirtualCache::Read(TADDR taOffset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead)
+HRESULT ReadVirtualCache::Read(TADDR address, PVOID buffer, ULONG bufferSize, PULONG lpcbBytesRead)
 {
-    // sign extend the passed in Offset so we can use it in when calling 
-    // IDebugDataSpaces::ReadVirtual()
-
-    CLRDATA_ADDRESS Offset = TO_CDADDR(taOffset);
-    // Offset can be any random ULONG64, as it can come from VerifyObjectMember(), and this
+    // address can be any random ULONG64, as it can come from VerifyObjectMember(), and this
     // can pass random pointer values in case of GC heap corruption
-    HRESULT ret;
-    ULONG cbBytesRead = 0;
 
-    if (BufferSize == 0)
+    if (bufferSize == 0)
         return S_OK;
 
-    if (BufferSize > CACHE_SIZE)
+    if (bufferSize > CACHE_SIZE)
     {
         // Don't even try with the cache
-        return g_ExtData->ReadVirtual(Offset, Buffer, BufferSize, lpcbBytesRead);
+        return g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, bufferSize, lpcbBytesRead);
     }
 
-    if ((m_cacheValid)
-        && (taOffset >= m_startCache) 
-        && (taOffset <= m_startCache + m_cacheSize - BufferSize))
-
+    if (!m_cacheValid || (address < m_startCache) || (address > (m_startCache + (m_cacheSize - bufferSize))))
     {
-        // It is within the cache
-        memcpy(Buffer,(LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+        ULONG cbBytesRead = 0;
+
+        m_cacheValid = FALSE;
+        m_startCache = address;
 
-        if (lpcbBytesRead != NULL)
+        // Avoid an int overflow
+        if (m_startCache + CACHE_SIZE < m_startCache)
+            m_startCache = (TADDR)(-CACHE_SIZE);
+
+        HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
+        if (hr != S_OK)
         {
-           *lpcbBytesRead = BufferSize;
+            return hr;
         }
-        return S_OK;
-    }
-    m_cacheValid = FALSE;
-    m_startCache = taOffset;
-
-    // avoid an int overflow
-    if (m_startCache + CACHE_SIZE < m_startCache)
-        m_startCache = (TADDR)(-CACHE_SIZE);
 
-    ret = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
-    if (ret != S_OK)
-    {
-        return ret;
+        m_cacheSize = cbBytesRead;     
+        m_cacheValid = TRUE;
     }
-    
-    m_cacheSize = cbBytesRead;     
-    m_cacheValid = TRUE;
-    memcpy(Buffer, (LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+
+    int size = _min(bufferSize, m_cacheSize);
+    memcpy(buffer, (LPVOID) ((ULONG64)m_cache + (address - m_startCache)), size);
 
     if (lpcbBytesRead != NULL)
     {
-        *lpcbBytesRead = cbBytesRead;
+        *lpcbBytesRead = size;
     }
 
     return S_OK;
index 81c1a342bb3ff7c5802180c20849dad1ce064868..f560082ab518800957abc2324f12bfb4556b8c69 100644 (file)
@@ -122,10 +122,13 @@ namespace Microsoft.Diagnostics.Tools.Dump
             };
             _serviceProvider.AddService(analyzeContext);
 
-            // Add the register, SOSHost and ClrRuntime services
+            // Add the register, memory, SOSHost and ClrRuntime services
             var registerService = new RegisterService(target);
             _serviceProvider.AddService(registerService);
 
+            var memoryService = new MemoryService(target.DataReader);
+            _serviceProvider.AddService(memoryService);
+
             _serviceProvider.AddServiceFactory(typeof(ClrRuntime), () => CreateRuntime(target));
 
             _serviceProvider.AddServiceFactory(typeof(SOSHost), () => {