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
<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>
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+// 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,
+ }
+ }
+}
<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>
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
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);
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;
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,
{
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();
}
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;
}
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));
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+
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
)
{
ULONG64 ProcessPeb;
- g_ExtSystem->GetCurrentProcessPeb (&ProcessPeb);
+ if (FAILED(g_ExtSystem->GetCurrentProcessPeb(&ProcessPeb)))
+ {
+ return;
+ }
ULONG64 pLdrEntry;
ULONG64 PebLdrAddress;
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);
+ }
}
}
}
}
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;
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;
}
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;
}
}
-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;
};
_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), () => {