ServiceProvider = new ServiceProvider();
ServiceProvider.AddServiceFactoryWithNoCaching<PEFile>(() => GetPEInfo());
- ServiceProvider.AddServiceFactory<PEReader>(() => ModuleService.GetPEReader(this));
+ ServiceProvider.AddServiceFactory<PEReader>(() => Utilities.OpenPEReader(ModuleService.SymbolService.DownloadModule(this)));
if (target.OperatingSystem == OSPlatform.Linux) {
- ServiceProvider.AddServiceFactory<ELFFile>(() => ModuleService.GetELFFile(this));
+ ServiceProvider.AddServiceFactory<ELFFile>(() => Utilities.OpenELFFile(ModuleService.SymbolService.DownloadModule(this)));
}
if (target.OperatingSystem == OSPlatform.OSX) {
- ServiceProvider.AddServiceFactory<MachOFile>(() => ModuleService.GetMachOFile(this));
+ ServiceProvider.AddServiceFactory<MachOFile>(() => Utilities.OpenMachOFile(ModuleService.SymbolService.DownloadModule(this)));
}
_onChangeEvent = target.Services.GetService<ISymbolService>()?.OnChangeEvent.Register(() => {
ServiceProvider.RemoveService(typeof(MachOFile));
using Microsoft.FileFormats.ELF;
using Microsoft.FileFormats.MachO;
using Microsoft.FileFormats.PE;
-using Microsoft.SymbolStore;
-using Microsoft.SymbolStore.KeyGenerators;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text;
return null;
}
- /// <summary>
- /// Finds or downloads the module and creates a PEReader for it.
- /// </summary>
- /// <param name="module">module instance</param>
- /// <returns>reader or null</returns>
- internal PEReader GetPEReader(IModule module)
- {
- if (!module.IndexTimeStamp.HasValue || !module.IndexFileSize.HasValue)
- {
- Trace.TraceWarning($"GetPEReader: module {module.FileName} has no index timestamp/filesize");
- return null;
- }
-
- SymbolStoreKey moduleKey = PEFileKeyGenerator.GetKey(Path.GetFileName(module.FileName), module.IndexTimeStamp.Value, module.IndexFileSize.Value);
- if (moduleKey is null)
- {
- Trace.TraceWarning($"GetPEReader: no index generated for module {module.FileName} ");
- return null;
- }
-
- if (File.Exists(module.FileName))
- {
- Stream stream = OpenFile(module.FileName);
- if (stream is not null)
- {
- var peFile = new PEFile(new StreamAddressSpace(stream), false);
- var generator = new PEFileKeyGenerator(Tracer.Instance, peFile, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
- foreach (SymbolStoreKey key in keys)
- {
- if (moduleKey.Equals(key))
- {
- Trace.TraceInformation("GetPEReader: local file match {0}", module.FileName);
- return OpenPEReader(module.FileName);
- }
- }
- }
- }
-
- // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = SymbolService.DownloadFile(moduleKey);
- if (!string.IsNullOrEmpty(downloadFilePath))
- {
- Trace.TraceInformation("GetPEReader: downloaded {0}", downloadFilePath);
- return OpenPEReader(downloadFilePath);
- }
-
- return null;
- }
-
- /// <summary>
- /// Opens and returns an PEReader instance from the local file path
- /// </summary>
- /// <param name="filePath">PE file to open</param>
- /// <returns>PEReader instance or null</returns>
- private PEReader OpenPEReader(string filePath)
- {
- Stream stream = OpenFile(filePath);
- if (stream is not null)
- {
- try
- {
- var reader = new PEReader(stream);
- if (reader.PEHeaders == null || reader.PEHeaders.PEHeader == null)
- {
- Trace.TraceError($"OpenPEReader: PEReader invalid headers");
- return null;
- }
- return reader;
- }
- catch (Exception ex) when (ex is BadImageFormatException || ex is IOException)
- {
- Trace.TraceError($"OpenPEReader: PEReader exception {ex.Message}");
- }
- }
- return null;
- }
-
- /// <summary>
- /// Finds or downloads the ELF module and creates a ELFFile instance for it.
- /// </summary>
- /// <param name="module">module instance</param>
- /// <returns>ELFFile instance or null</returns>
- internal ELFFile GetELFFile(IModule module)
- {
- if (module.BuildId.IsDefaultOrEmpty)
- {
- Trace.TraceWarning($"GetELFFile: module {module.FileName} has no build id");
- return null;
- }
-
- SymbolStoreKey moduleKey = ELFFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
- if (moduleKey is null)
- {
- Trace.TraceWarning($"GetELFFile: no index generated for module {module.FileName} ");
- return null;
- }
-
- if (File.Exists(module.FileName))
- {
- ELFFile elfFile = OpenELFFile(module.FileName);
- if (elfFile is not null)
- {
- var generator = new ELFFileKeyGenerator(Tracer.Instance, elfFile, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
- foreach (SymbolStoreKey key in keys)
- {
- if (moduleKey.Equals(key))
- {
- Trace.TraceInformation("GetELFFile: local file match {0}", module.FileName);
- return elfFile;
- }
- }
- }
- }
-
- // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = SymbolService.DownloadFile(moduleKey);
- if (!string.IsNullOrEmpty(downloadFilePath))
- {
- Trace.TraceInformation("GetELFFile: downloaded {0}", downloadFilePath);
- return OpenELFFile(downloadFilePath);
- }
-
- return null;
- }
-
- /// <summary>
- /// Opens and returns an ELFFile instance from the local file path
- /// </summary>
- /// <param name="filePath">ELF file to open</param>
- /// <returns>ELFFile instance or null</returns>
- private ELFFile OpenELFFile(string filePath)
- {
- Stream stream = OpenFile(filePath);
- if (stream is not null)
- {
- try
- {
- ELFFile elfFile = new ELFFile(new StreamAddressSpace(stream), position: 0, isDataSourceVirtualAddressSpace: false);
- if (!elfFile.IsValid())
- {
- Trace.TraceError($"OpenELFFile: not a valid file");
- return null;
- }
- return elfFile;
- }
- catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
- {
- Trace.TraceError($"OpenELFFile: exception {ex.Message}");
- }
- }
- return null;
- }
-
- /// <summary>
- /// Finds or downloads the ELF module and creates a MachOFile instance for it.
- /// </summary>
- /// <param name="module">module instance</param>
- /// <returns>MachO file instance or null</returns>
- internal MachOFile GetMachOFile(IModule module)
- {
- if (module.BuildId.IsDefaultOrEmpty)
- {
- Trace.TraceWarning($"GetMachOFile: module {module.FileName} has no build id");
- return null;
- }
-
- SymbolStoreKey moduleKey = MachOFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
- if (moduleKey is null)
- {
- Trace.TraceWarning($"GetMachOFile: no index generated for module {module.FileName} ");
- return null;
- }
-
- if (File.Exists(module.FileName))
- {
- MachOFile machOFile = OpenMachOFile(module.FileName);
- if (machOFile is not null)
- {
- var generator = new MachOFileKeyGenerator(Tracer.Instance, machOFile, module.FileName);
- IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
- foreach (SymbolStoreKey key in keys)
- {
- if (moduleKey.Equals(key))
- {
- Trace.TraceInformation("GetMachOFile: local file match {0}", module.FileName);
- return machOFile;
- }
- }
- }
- }
-
- // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
- string downloadFilePath = SymbolService.DownloadFile(moduleKey);
- if (!string.IsNullOrEmpty(downloadFilePath))
- {
- Trace.TraceInformation("GetMachOFile: downloaded {0}", downloadFilePath);
- return OpenMachOFile(downloadFilePath);
- }
-
- return null;
- }
-
- /// <summary>
- /// Opens and returns an MachOFile instance from the local file path
- /// </summary>
- /// <param name="filePath">MachO file to open</param>
- /// <returns>MachOFile instance or null</returns>
- private MachOFile OpenMachOFile(string filePath)
- {
- Stream stream = OpenFile(filePath);
- if (stream is not null)
- {
- try
- {
- var machoFile = new MachOFile(new StreamAddressSpace(stream), position: 0, dataSourceIsVirtualAddressSpace: false);
- if (!machoFile.IsValid())
- {
- Trace.TraceError($"OpenMachOFile: not a valid file");
- return null;
- }
- return machoFile;
- }
- catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
- {
- Trace.TraceError($"OpenMachOFile: exception {ex.Message}");
- }
- }
- return null;
- }
-
- /// <summary>
- /// Opens and returns a file stream
- /// </summary>
- /// <param name="filePath">file to open</param>
- /// <returns>stream or null</returns>
- private Stream OpenFile(string filePath)
- {
- try
- {
- return File.OpenRead(filePath);
- }
- catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException || ex is UnauthorizedAccessException || ex is IOException)
- {
- Trace.TraceError($"OpenFile: OpenRead exception {ex.Message}");
- return null;
- }
- }
-
/// <summary>
/// Returns the ELF module build id or the MachO module uuid
/// </summary>
--- /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.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ internal sealed class SymbolFile : ISymbolFile, IDisposable
+ {
+ private readonly MetadataReaderProvider _provider;
+ private readonly MetadataReader _reader;
+
+ public SymbolFile(MetadataReaderProvider provider, MetadataReader reader)
+ {
+ Debug.Assert(provider != null);
+ Debug.Assert(reader != null);
+
+ _provider = provider;
+ _reader = reader;
+ }
+
+ public void Dispose() => _provider.Dispose();
+
+ /// <summary>
+ /// Returns method token and IL offset for given source line number.
+ /// </summary>
+ /// <param name="filePath">source file name and path</param>
+ /// <param name="lineNumber">source line number</param>
+ /// <param name="methodToken">method token return</param>
+ /// <param name="ilOffset">IL offset return</param>
+ /// <returns>true if information is available</returns>
+ public bool ResolveSequencePoint(
+ string filePath,
+ int lineNumber,
+ out int methodToken,
+ out int ilOffset)
+ {
+ methodToken = 0;
+ ilOffset = 0;
+ try
+ {
+ string fileName = SymbolService.GetFileName(filePath);
+ foreach (MethodDebugInformationHandle methodDebugInformationHandle in _reader.MethodDebugInformation)
+ {
+ MethodDebugInformation methodDebugInfo = _reader.GetMethodDebugInformation(methodDebugInformationHandle);
+ SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+ foreach (SequencePoint point in sequencePoints)
+ {
+ string sourceName = _reader.GetString(_reader.GetDocument(point.Document).Name);
+ if (point.StartLine == lineNumber && SymbolService.GetFileName(sourceName) == fileName)
+ {
+ methodToken = MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle());
+ ilOffset = point.Offset;
+ return true;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"ResolveSequencePoint: {ex.Message}");
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns source line number and source file name for given IL offset and method token.
+ /// </summary>
+ /// <param name="methodToken">method token</param>
+ /// <param name="ilOffset">IL offset</param>
+ /// <param name="lineNumber">source line number return</param>
+ /// <param name="fileName">source file name return</param>
+ /// <returns>true if information is available</returns>
+ public bool GetSourceLineByILOffset(
+ int methodToken,
+ long ilOffset,
+ out int lineNumber,
+ out string fileName)
+ {
+ lineNumber = 0;
+ fileName = null;
+ try
+ {
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return false;
+
+ MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ if (methodDebugHandle.IsNil)
+ return false;
+
+ MethodDebugInformation methodDebugInfo = _reader.GetMethodDebugInformation(methodDebugHandle);
+ SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+
+ SequencePoint? nearestPoint = null;
+ foreach (SequencePoint point in sequencePoints)
+ {
+ if (point.Offset > ilOffset)
+ break;
+
+ if (point.StartLine != 0 && !point.IsHidden)
+ nearestPoint = point;
+ }
+
+ if (nearestPoint.HasValue)
+ {
+ lineNumber = nearestPoint.Value.StartLine;
+ fileName = _reader.GetString(_reader.GetDocument(nearestPoint.Value.Document).Name);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"GetSourceLineByILOffset: {ex.Message}");
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns local variable name for given local index and IL offset.
+ /// </summary>
+ /// <param name="methodToken">method token</param>
+ /// <param name="localIndex">local variable index</param>
+ /// <param name="localVarName">local variable name return</param>
+ /// <returns>true if name has been found</returns>
+ public bool GetLocalVariableByIndex(
+ int methodToken,
+ int localIndex,
+ out string localVarName)
+ {
+ localVarName = null;
+ try
+ {
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return false;
+
+ MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ LocalScopeHandleCollection localScopes = _reader.GetLocalScopes(methodDebugHandle);
+ foreach (LocalScopeHandle scopeHandle in localScopes)
+ {
+ LocalScope scope = _reader.GetLocalScope(scopeHandle);
+ LocalVariableHandleCollection localVars = scope.GetLocalVariables();
+ foreach (LocalVariableHandle varHandle in localVars)
+ {
+ LocalVariable localVar = _reader.GetLocalVariable(varHandle);
+ if (localVar.Index == localIndex)
+ {
+ if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
+ return false;
+
+ localVarName = _reader.GetString(localVar.Name);
+ return true;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError($"GetLocalVariableByIndex: {ex.Message}");
+ }
+ return false;
+ }
+ }
+}
// See the LICENSE file in the project root for more information.
using Microsoft.FileFormats;
+using Microsoft.FileFormats.PE;
using Microsoft.SymbolStore;
using Microsoft.SymbolStore.KeyGenerators;
using Microsoft.SymbolStore.SymbolStores;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text;
OnChangeEvent = new ServiceEvent();
}
+ #region ISymbolService
+
/// <summary>
/// Invoked when anything changes in the symbol service (adding servers, caches, or directories, clearing store, etc.)
/// </summary>
SetSymbolStore(null);
}
+ /// <summary>
+ /// Downloads module file
+ /// </summary>
+ /// <param name="module">module interface</param>
+ /// <returns>module path or null</returns>
+ public string DownloadModule(IModule module)
+ {
+ string downloadFilePath = DownloadPE(module);
+ if (downloadFilePath is null)
+ {
+ if (module.Target.OperatingSystem == OSPlatform.Linux)
+ {
+ downloadFilePath = DownloadELF(module);
+ }
+ else if (module.Target.OperatingSystem == OSPlatform.OSX)
+ {
+ downloadFilePath = DownloadMachO(module);
+ }
+ }
+ return downloadFilePath;
+ }
+
/// <summary>
/// Download a file from the symbol stores/server.
/// </summary>
return downloadFilePath;
}
- /// <summary>
- /// Attempts to download/retrieve from cache the key.
- /// </summary>
- /// <param name="key">index of the file to retrieve</param>
- /// <returns>stream or null</returns>
- public SymbolStoreFile GetSymbolStoreFile(SymbolStoreKey key)
- {
- if (IsSymbolStoreEnabled)
- {
- try
- {
- return _symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult();
- }
- catch (Exception ex) when (ex is UnauthorizedAccessException || ex is BadImageFormatException || ex is IOException)
- {
- Trace.TraceError("Exception: {0}", ex.ToString());
- }
- }
- return null;
- }
-
/// <summary>
/// Returns the metadata for the assembly
/// </summary>
Stream peStream = null;
if (imagePath != null && File.Exists(imagePath))
{
- peStream = TryOpenFile(imagePath);
+ peStream = Utilities.TryOpenFile(imagePath);
}
else if (IsSymbolStoreEnabled)
{
return ImmutableArray<byte>.Empty;
}
+ /// <summary>
+ /// Returns the portable PDB reader for the assembly path
+ /// </summary>
+ /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param>
+ /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
+ /// <param name="peStream">in-memory PE stream or null</param>
+ /// <returns>symbol file or null</returns>
+ /// <remarks>
+ /// Assumes that neither PE image nor PDB loaded into memory can be unloaded or moved around.
+ /// </remarks>
+ public ISymbolFile OpenSymbolFile(string assemblyPath, bool isFileLayout, Stream peStream)
+ {
+ if (assemblyPath == null && peStream == null) throw new ArgumentNullException(nameof(assemblyPath));
+ if (peStream is not null && !peStream.CanSeek) throw new ArgumentException(nameof(peStream));
+
+ PEStreamOptions options = isFileLayout ? PEStreamOptions.Default : PEStreamOptions.IsLoadedImage;
+ if (peStream == null)
+ {
+ peStream = Utilities.TryOpenFile(assemblyPath);
+ if (peStream == null)
+ return null;
+
+ options = PEStreamOptions.Default;
+ }
+
+ try
+ {
+ using (var peReader = new PEReader(peStream, options))
+ {
+ ReadPortableDebugTableEntries(peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry);
+
+ // First try .pdb file specified in CodeView data (we prefer .pdb file on disk over embedded PDB
+ // since embedded PDB needs decompression which is less efficient than memory-mapping the file).
+ if (codeViewEntry.DataSize != 0)
+ {
+ var result = TryOpenReaderFromCodeView(peReader, codeViewEntry, assemblyPath);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ // if it failed try Embedded Portable PDB (if available):
+ if (embeddedPdbEntry.DataSize != 0)
+ {
+ return TryOpenReaderFromEmbeddedPdb(peReader, embeddedPdbEntry);
+ }
+ }
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ // nop
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Returns the portable PDB reader for the portable PDB stream
+ /// </summary>
+ /// <param name="pdbStream">portable PDB memory or file stream</param>
+ /// <returns>symbol file or null</returns>
+ /// <remarks>
+ /// Assumes that the PDB loaded into memory can be unloaded or moved around.
+ /// </remarks>
+ public ISymbolFile OpenSymbolFile(Stream pdbStream)
+ {
+ if (pdbStream != null) throw new ArgumentNullException(nameof(pdbStream));
+ if (!pdbStream.CanSeek) throw new ArgumentException(nameof(pdbStream));
+
+ byte[] buffer = new byte[sizeof(uint)];
+ pdbStream.Position = 0;
+ if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint))
+ {
+ return null;
+ }
+ uint signature = BitConverter.ToUInt32(buffer, 0);
+
+ // quick check to avoid throwing exceptions below in common cases:
+ const uint ManagedMetadataSignature = 0x424A5342;
+ if (signature != ManagedMetadataSignature)
+ {
+ // not a Portable PDB
+ return null;
+ }
+
+ SymbolFile result = null;
+ MetadataReaderProvider provider = null;
+ try
+ {
+ pdbStream.Position = 0;
+ provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ result = new SymbolFile(provider, provider.GetMetadataReader());
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Finds or downloads the PE module
+ /// </summary>
+ /// <param name="module">module instance</param>
+ /// <returns>module path or null</returns>
+ private string DownloadPE(IModule module)
+ {
+ if (!module.IndexTimeStamp.HasValue || !module.IndexFileSize.HasValue)
+ {
+ Trace.TraceWarning($"DownLoadPE: module {module.FileName} has no index timestamp/filesize");
+ return null;
+ }
+
+ SymbolStoreKey moduleKey = PEFileKeyGenerator.GetKey(Path.GetFileName(module.FileName), module.IndexTimeStamp.Value, module.IndexFileSize.Value);
+ if (moduleKey is null)
+ {
+ Trace.TraceWarning($"DownLoadPE: no index generated for module {module.FileName} ");
+ return null;
+ }
+
+ if (File.Exists(module.FileName))
+ {
+ using Stream stream = Utilities.TryOpenFile(module.FileName);
+ if (stream is not null)
+ {
+ var peFile = new PEFile(new StreamAddressSpace(stream), false);
+ var generator = new PEFileKeyGenerator(Tracer.Instance, peFile, module.FileName);
+ IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
+ foreach (SymbolStoreKey key in keys)
+ {
+ if (moduleKey.Equals(key))
+ {
+ Trace.TraceInformation("DownloadPE: local file match {0}", module.FileName);
+ return module.FileName;
+ }
+ }
+ }
+ }
+
+ // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
+ string downloadFilePath = DownloadFile(moduleKey);
+ if (!string.IsNullOrEmpty(downloadFilePath))
+ {
+ Trace.TraceInformation("DownloadPE: downloaded {0}", downloadFilePath);
+ return downloadFilePath;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Finds or downloads the ELF module
+ /// </summary>
+ /// <param name="module">module instance</param>
+ /// <returns>module path or null</returns>
+ private string DownloadELF(IModule module)
+ {
+ if (module.BuildId.IsDefaultOrEmpty)
+ {
+ Trace.TraceWarning($"DownloadELF: module {module.FileName} has no build id");
+ return null;
+ }
+
+ SymbolStoreKey moduleKey = ELFFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
+ if (moduleKey is null)
+ {
+ Trace.TraceWarning($"DownloadELF: no index generated for module {module.FileName} ");
+ return null;
+ }
+
+ if (File.Exists(module.FileName))
+ {
+ using Utilities.ELFModule elfModule = Utilities.OpenELFFile(module.FileName);
+ if (elfModule is not null)
+ {
+ var generator = new ELFFileKeyGenerator(Tracer.Instance, elfModule, module.FileName);
+ IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
+ foreach (SymbolStoreKey key in keys)
+ {
+ if (moduleKey.Equals(key))
+ {
+ Trace.TraceInformation("DownloadELF: local file match {0}", module.FileName);
+ return module.FileName;
+ }
+ }
+ }
+ }
+
+ // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
+ string downloadFilePath = DownloadFile(moduleKey);
+ if (!string.IsNullOrEmpty(downloadFilePath))
+ {
+ Trace.TraceInformation("DownloadELF: downloaded {0}", downloadFilePath);
+ return downloadFilePath;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Finds or downloads the MachO module.
+ /// </summary>
+ /// <param name="module">module instance</param>
+ /// <returns>module path or null</returns>
+ private string DownloadMachO(IModule module)
+ {
+ if (module.BuildId.IsDefaultOrEmpty)
+ {
+ Trace.TraceWarning($"DownloadMachO: module {module.FileName} has no build id");
+ return null;
+ }
+
+ SymbolStoreKey moduleKey = MachOFileKeyGenerator.GetKeys(KeyTypeFlags.IdentityKey, module.FileName, module.BuildId.ToArray(), symbolFile: false, symbolFileName: null).SingleOrDefault();
+ if (moduleKey is null)
+ {
+ Trace.TraceWarning($"DownloadMachO: no index generated for module {module.FileName} ");
+ return null;
+ }
+
+ if (File.Exists(module.FileName))
+ {
+ using Utilities.MachOModule machOModule = Utilities.OpenMachOFile(module.FileName);
+ if (machOModule is not null)
+ {
+ var generator = new MachOFileKeyGenerator(Tracer.Instance, machOModule, module.FileName);
+ IEnumerable<SymbolStoreKey> keys = generator.GetKeys(KeyTypeFlags.IdentityKey);
+ foreach (SymbolStoreKey key in keys)
+ {
+ if (moduleKey.Equals(key))
+ {
+ Trace.TraceInformation("DownloadMachO: local file match {0}", module.FileName);
+ return module.FileName;
+ }
+ }
+ }
+ }
+
+ // Now download the module from the symbol server if local file doesn't exists or doesn't have the right key
+ string downloadFilePath = DownloadFile(moduleKey);
+ if (!string.IsNullOrEmpty(downloadFilePath))
+ {
+ Trace.TraceInformation("DownloadMachO: downloaded {0}", downloadFilePath);
+ return downloadFilePath;
+ }
+
+ return null;
+ }
+
+ private void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry)
+ {
+ // See spec: https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
+
+ codeViewEntry = default;
+ embeddedPdbEntry = default;
+
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ {
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
+ {
+ if (entry.MinorVersion != ImageDebugDirectory.PortablePDBMinorVersion)
+ {
+ continue;
+ }
+ codeViewEntry = entry;
+ }
+ else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
+ {
+ embeddedPdbEntry = entry;
+ }
+ }
+ }
+
+ private SymbolFile TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath)
+ {
+ SymbolFile result = null;
+ MetadataReaderProvider provider = null;
+ try
+ {
+ CodeViewDebugDirectoryData data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry);
+ string pdbPath = data.Path;
+ Stream pdbStream = null;
+
+ if (assemblyPath != null)
+ {
+ try
+ {
+ pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), GetFileName(pdbPath));
+ }
+ catch
+ {
+ // invalid characters in CodeView path
+ return null;
+ }
+ pdbStream = Utilities.TryOpenFile(pdbPath);
+ }
+
+ if (pdbStream == null)
+ {
+ if (IsSymbolStoreEnabled)
+ {
+ Debug.Assert(codeViewEntry.MinorVersion == ImageDebugDirectory.PortablePDBMinorVersion);
+ SymbolStoreKey key = PortablePDBFileKeyGenerator.GetKey(pdbPath, data.Guid);
+ pdbStream = GetSymbolStoreFile(key)?.Stream;
+ }
+ if (pdbStream == null)
+ {
+ return null;
+ }
+ // Make sure the stream is at the beginning of the pdb.
+ pdbStream.Position = 0;
+ }
+
+ provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ MetadataReader reader = provider.GetMetadataReader();
+
+ // Validate that the PDB matches the assembly version
+ if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp))
+ {
+ result = new SymbolFile(provider, reader);
+ }
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
+ private SymbolFile TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry)
+ {
+ SymbolFile result = null;
+ MetadataReaderProvider provider = null;
+
+ try
+ {
+ // TODO: We might want to cache this provider globally (across stack traces),
+ // since decompressing embedded PDB takes some time.
+ provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);
+ result = new SymbolFile(provider, provider.GetMetadataReader());
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
/// <summary>
/// Displays the symbol server and cache configuration
/// </summary>
return sb.ToString();
}
+ /// <summary>
+ /// Attempts to download/retrieve from cache the key.
+ /// </summary>
+ /// <param name="key">index of the file to retrieve</param>
+ /// <returns>stream or null</returns>
+ private SymbolStoreFile GetSymbolStoreFile(SymbolStoreKey key)
+ {
+ Debug.Assert(IsSymbolStoreEnabled);
+ try
+ {
+ return _symbolStore.GetFile(key, CancellationToken.None).GetAwaiter().GetResult();
+ }
+ catch (Exception ex) when (ex is UnauthorizedAccessException || ex is BadImageFormatException || ex is IOException)
+ {
+ Trace.TraceError("Exception: {0}", ex.ToString());
+ }
+ return null;
+ }
+
/// <summary>
/// Sets a new store store head.
/// </summary>
return false;
}
+ /// <summary>
+ /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux
+ /// </summary>
+ /// <param name="pathName"> File path to be processed </param>
+ /// <returns>Last component of path</returns>
+ internal static string GetFileName(string pathName)
+ {
+ int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'});
+ if (pos < 0)
+ {
+ return pathName;
+ }
+ return pathName.Substring(pos + 1);
+ }
+
/// <summary>
/// Compares two file paths using OS specific casing.
/// </summary>
return string.Equals(path1, path2);
}
}
-
- /// <summary>
- /// Attempt to open a file stream.
- /// </summary>
- /// <param name="path">file path</param>
- /// <returns>stream or null if doesn't exist or error</returns>
- private Stream TryOpenFile(string path)
- {
- if (File.Exists(path))
- {
- try
- {
- return File.OpenRead(path);
- }
- catch (Exception ex) when (ex is UnauthorizedAccessException || ex is NotSupportedException || ex is IOException)
- {
- Trace.TraceError($"TryOpenFile: {ex.Message}");
- }
- }
- return null;
- }
}
}
// 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.FileFormats;
+using Microsoft.FileFormats.ELF;
+using Microsoft.FileFormats.MachO;
using Microsoft.FileFormats.PE;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection.PortableExecutable;
namespace Microsoft.Diagnostics.DebugServices.Implementation
{
{
return new PdbFileInfo(pdbInfo.Path, pdbInfo.Signature, pdbInfo.Age, pdbInfo.IsPortablePDB);
}
+
+ /// <summary>
+ /// Opens and returns an PEReader instance from the local file path
+ /// </summary>
+ /// <param name="filePath">PE file to open</param>
+ /// <returns>PEReader instance or null</returns>
+ public static PEReader OpenPEReader(string filePath)
+ {
+ Stream stream = TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ try
+ {
+ var reader = new PEReader(stream);
+ if (reader.PEHeaders == null || reader.PEHeaders.PEHeader == null)
+ {
+ Trace.TraceError($"OpenPEReader: PEReader invalid headers");
+ return null;
+ }
+ return reader;
+ }
+ catch (Exception ex) when (ex is BadImageFormatException || ex is IOException)
+ {
+ Trace.TraceError($"OpenPEReader: PEReader exception {ex.Message}");
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Disposable ELFFile wrapper
+ /// </summary>
+ public class ELFModule : ELFFile, IDisposable
+ {
+ private readonly Stream _stream;
+
+ public ELFModule(Stream stream) :
+ base(new StreamAddressSpace(stream), position: 0, isDataSourceVirtualAddressSpace: false)
+ {
+ _stream = stream;
+ }
+
+ public void Dispose() => _stream.Dispose();
+ }
+
+ /// <summary>
+ /// Opens and returns an ELFFile instance from the local file path
+ /// </summary>
+ /// <param name="filePath">ELF file to open</param>
+ /// <returns>ELFFile instance or null</returns>
+ public static ELFModule OpenELFFile(string filePath)
+ {
+ Stream stream = TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ try
+ {
+ ELFModule elfModule = new (stream);
+ if (!elfModule.IsValid())
+ {
+ Trace.TraceError($"OpenELFFile: not a valid file");
+ return null;
+ }
+ return elfModule;
+ }
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
+ {
+ Trace.TraceError($"OpenELFFile: exception {ex.Message}");
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Disposable MachOFile wrapper
+ /// </summary>
+ public class MachOModule : MachOFile, IDisposable
+ {
+ private readonly Stream _stream;
+
+ public MachOModule(Stream stream) :
+ base(new StreamAddressSpace(stream), position: 0, dataSourceIsVirtualAddressSpace: false)
+ {
+ _stream = stream;
+ }
+
+ public void Dispose() => _stream.Dispose();
+ }
+
+ /// <summary>
+ /// Opens and returns an MachOFile instance from the local file path
+ /// </summary>
+ /// <param name="filePath">MachO file to open</param>
+ /// <returns>MachOFile instance or null</returns>
+ public static MachOModule OpenMachOFile(string filePath)
+ {
+ Stream stream = TryOpenFile(filePath);
+ if (stream is not null)
+ {
+ try
+ {
+ var machoModule = new MachOModule(stream);
+ if (!machoModule.IsValid())
+ {
+ Trace.TraceError($"OpenMachOFile: not a valid file");
+ return null;
+ }
+ return machoModule;
+ }
+ catch (Exception ex) when (ex is InvalidVirtualAddressException || ex is BadInputFormatException || ex is IOException)
+ {
+ Trace.TraceError($"OpenMachOFile: exception {ex.Message}");
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Attempt to open a file stream.
+ /// </summary>
+ /// <param name="path">file path</param>
+ /// <returns>stream or null if doesn't exist or error</returns>
+ public static Stream TryOpenFile(string path)
+ {
+ if (path is not null && File.Exists(path))
+ {
+ try
+ {
+ return File.OpenRead(path);
+ }
+ catch (Exception ex) when (ex is UnauthorizedAccessException || ex is NotSupportedException || ex is IOException)
+ {
+ Trace.TraceError($"TryOpenFile: {ex.Message}");
+ }
+ }
+ return null;
+ }
}
}
--- /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.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ public interface ISymbolFile
+ {
+ /// <summary>
+ /// Returns method token and IL offset for given source line number.
+ /// </summary>
+ /// <param name="filePath">source file name and path</param>
+ /// <param name="lineNumber">source line number</param>
+ /// <param name="methodToken">method token return</param>
+ /// <param name="ilOffset">IL offset return</param>
+ /// <returns>true if information is available</returns>
+ bool ResolveSequencePoint(string filePath, int lineNumber, out int methodToken, out int ilOffset);
+
+ /// <summary>
+ /// Returns source line number and source file name for given IL offset and method token.
+ /// </summary>
+ /// <param name="methodToken">method token</param>
+ /// <param name="ilOffset">IL offset</param>
+ /// <param name="lineNumber">source line number return</param>
+ /// <param name="fileName">source file name return</param>
+ /// <returns>true if information is available</returns>
+ bool GetSourceLineByILOffset(int methodToken, long ilOffset, out int lineNumber, out string fileName);
+
+ /// <summary>
+ /// Returns local variable name for given local index and IL offset.
+ /// </summary>
+ /// <param name="methodToken">method token</param>
+ /// <param name="localIndex">local variable index</param>
+ /// <param name="localVarName">local variable name return</param>
+ /// <returns>true if name has been found</returns>
+ bool GetLocalVariableByIndex(int methodToken, int localIndex, out string localVarName);
+ }
+}
// See the LICENSE file in the project root for more information.
using Microsoft.SymbolStore;
+using System;
using System.Collections.Immutable;
+using System.IO;
namespace Microsoft.Diagnostics.DebugServices
{
public interface ISymbolService
{
+ /// <summary>
+ /// Symbol file reader instance
+ /// </summary>
+ public class SymbolFile : IDisposable
+ {
+ public virtual void Dispose()
+ {
+ }
+ }
+
/// <summary>
/// Invoked when anything changes in the symbol service (adding servers, caches, or directories, clearing store, etc.)
/// </summary>
/// </summary>
void DisableSymbolStore();
+ /// <summary>
+ /// Downloads module file
+ /// </summary>
+ /// <param name="module">module interface</param>
+ /// <returns>module path or null</returns>
+ string DownloadModule(IModule module);
+
/// <summary>
/// Download a file from the symbol stores/server.
/// </summary>
/// <returns>path to the downloaded file either in the cache or in the temp directory or null if error</returns>
string DownloadFile(SymbolStoreKey key);
- /// <summary>
- /// Attempts to download/retrieve from cache the key.
- /// </summary>
- /// <param name="key">index of the file to retrieve</param>
- /// <returns>stream or null</returns>
- SymbolStoreFile GetSymbolStoreFile(SymbolStoreKey key);
-
/// <summary>
/// Returns the metadata for the assembly
/// </summary>
/// <param name="imageSize">size of PE image</param>
/// <returns>metadata</returns>
ImmutableArray<byte> GetMetadata(string imagePath, uint imageTimestamp, uint imageSize);
+
+ /// <summary>
+ /// Returns the portable PDB reader for the assembly path
+ /// </summary>
+ /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param>
+ /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
+ /// <param name="peStream">in-memory PE stream</param>
+ /// <returns>symbol file or null</returns>
+ /// <remarks>
+ /// Assumes that neither PE image nor PDB loaded into memory can be unloaded or moved around.
+ /// </remarks>
+ ISymbolFile OpenSymbolFile(string assemblyPath, bool isFileLayout, Stream peStream);
+
+ /// <summary>
+ /// Returns the portable PDB reader for the portable PDB stream
+ /// </summary>
+ /// <param name="pdbStream">portable PDB memory or file stream</param>
+ /// <returns>symbol file or null</returns>
+ /// <remarks>
+ /// Assumes that the PDB loaded into memory can be unloaded or moved around.
+ /// </remarks>
+ ISymbolFile OpenSymbolFile(Stream pdbStream);
}
}
private int _targetIdFactory;
private ITarget _target;
private TargetWrapper _targetWrapper;
- private IMemoryService _memoryService;
/// <summary>
/// Enable the assembly resolver to get the right versions in the same directory as this assembly.
_serviceProvider.AddService<ISymbolService>(_symbolService);
_hostWrapper = new HostWrapper(this, () => _targetWrapper);
- _hostWrapper.AddServiceWrapper(IID_IHostServices, this);
- _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(this, () => _memoryService));
+ _hostWrapper.ServiceWrapper.AddServiceWrapper(IID_IHostServices, this);
VTableBuilder builder = AddInterface(IID_IHostServices, validate: false);
builder.AddMethod(new GetHostDelegate(GetHost));
protected override void Destroy()
{
Trace.TraceInformation("HostServices.Destroy");
- _hostWrapper.RemoveServiceWrapper(IID_IHostServices);
+ _hostWrapper.ServiceWrapper.RemoveServiceWrapper(IID_IHostServices);
_hostWrapper.Release();
}
if (target == _target)
{
_target = null;
- _memoryService = null;
if (_targetWrapper != null)
{
_targetWrapper.Release();
_target = new TargetFromDebuggerServices(DebuggerServices, this, _targetIdFactory++);
_contextService.SetCurrentTarget(_target);
_targetWrapper = new TargetWrapper(_contextService.Services);
- _memoryService = _contextService.Services.GetService<IMemoryService>();
+ _targetWrapper.ServiceWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(_symbolService, _target.Services.GetService<IMemoryService>()));
}
catch (Exception ex)
{
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime.Utilities;
using System;
-using System.Collections.Generic;
-using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SOS.Hosting
private readonly IHost _host;
private readonly Func<TargetWrapper> _getTarget;
- private readonly Dictionary<Guid, Func<COMCallableIUnknown>> _factories = new Dictionary<Guid, Func<COMCallableIUnknown>>();
- private readonly Dictionary<Guid, COMCallableIUnknown> _wrappers = new Dictionary<Guid, COMCallableIUnknown>();
+
+ public ServiceWrapper ServiceWrapper { get; } = new ServiceWrapper();
public IntPtr IHost { get; }
VTableBuilder builder = AddInterface(IID_IHost, validate: false);
builder.AddMethod(new GetHostTypeDelegate(GetHostType));
- builder.AddMethod(new GetServiceDelegate(GetService));
+ builder.AddMethod(new GetServiceDelegate(ServiceWrapper.GetService));
builder.AddMethod(new GetCurrentTargetDelegate(GetCurrentTarget));
IHost = builder.Complete();
AddRef();
}
- protected override void Destroy()
- {
- Trace.TraceInformation("HostWrapper.Destroy");
- foreach (var wrapper in _wrappers.Values)
- {
- wrapper.Release();
- }
- _wrappers.Clear();
- }
-
- /// <summary>
- /// Add service instance factory
- /// </summary>
- /// <param name="serviceId">guid</param>
- /// <param name="factory">factory delegate</param>
- public void AddServiceWrapper(in Guid serviceId, Func<COMCallableIUnknown> factory)
- {
- _factories.Add(serviceId, factory);
- }
-
- /// <summary>
- /// Add service instance
- /// </summary>
- /// <param name="serviceId">guid</param>
- /// <param name="service">instance</param>
- public void AddServiceWrapper(in Guid serviceId, COMCallableIUnknown service)
- {
- _wrappers.Add(serviceId, service);
- }
-
- /// <summary>
- /// Remove the service instance
- /// </summary>
- /// <param name="serviceId">guid</param>
- public void RemoveServiceWrapper(in Guid serviceId)
- {
- _factories.Remove(serviceId);
- _wrappers.Remove(serviceId);
- }
-
- /// <summary>
- /// Returns the wrapper instance for the guid
- /// </summary>
- /// <param name="serviceId">service guid</param>
- /// <returns>instance or null</returns>
- public COMCallableIUnknown GetServiceWrapper(in Guid serviceId)
- {
- if (!_wrappers.TryGetValue(serviceId, out COMCallableIUnknown service))
- {
- if (_factories.TryGetValue(serviceId, out Func<COMCallableIUnknown> factory))
- {
- service = factory();
- if (service != null)
- {
- _wrappers.Add(serviceId, service);
- }
- }
- }
- return service;
- }
+ protected override void Destroy() => ServiceWrapper.Dispose();
#region IHost
/// </summary>
private HostType GetHostType(IntPtr self) => _host.HostType;
- /// <summary>
- /// Returns the native service for the given interface id. There is
- /// only a limited set of services that can be queried through this
- /// function. Adds a reference like QueryInterface.
- /// </summary>
- /// <param name="serviceId">guid of the service</param>
- /// <param name="service">pointer to return service instance</param>
- /// <returns>S_OK or E_NOINTERFACE</returns>
- private HResult GetService(IntPtr self, in Guid guid, out IntPtr ptr)
- {
- ptr = IntPtr.Zero;
-
- COMCallableIUnknown wrapper = GetServiceWrapper(guid);
- if (wrapper == null) {
- return HResult.E_NOINTERFACE;
- }
- wrapper.AddRef();
- return COMHelper.QueryInterface(wrapper.IUnknownObject, guid, out ptr);
- }
-
/// <summary>
/// Returns the current target wrapper or null
/// </summary>
[In] IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate HResult GetServiceDelegate(
+ internal delegate HResult GetServiceDelegate(
[In] IntPtr self,
[In] in Guid guid,
[Out] out IntPtr ptr);
ModuleService = services.GetService<IModuleService>();
ThreadService = services.GetService<IThreadService>();
MemoryService = services.GetService<IMemoryService>();
+ TargetWrapper.ServiceWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(services.GetService<ISymbolService>(), MemoryService));
_ignoreAddressBitsMask = MemoryService.SignExtensionMask();
_sosLibrary = services.GetService<SOSLibrary>();
SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
_hostWrapper = new HostWrapper(host, () => GetSOSHost()?.TargetWrapper);
- _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(host, () => GetSOSHost()?.MemoryService));
}
/// <summary>
--- /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.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace SOS.Hosting
+{
+ public sealed class ServiceWrapper : IDisposable
+ {
+ private readonly Dictionary<Guid, Func<COMCallableIUnknown>> _factories = new Dictionary<Guid, Func<COMCallableIUnknown>>();
+ private readonly Dictionary<Guid, COMCallableIUnknown> _wrappers = new Dictionary<Guid, COMCallableIUnknown>();
+
+ public ServiceWrapper()
+ {
+ }
+
+ public void Dispose()
+ {
+ Trace.TraceInformation("ServiceWrapper.Dispose");
+ foreach (var wrapper in _wrappers.Values)
+ {
+ wrapper.Release();
+ }
+ _wrappers.Clear();
+ }
+
+ /// <summary>
+ /// Add service instance factory
+ /// </summary>
+ /// <param name="serviceId">guid</param>
+ /// <param name="factory">factory delegate</param>
+ public void AddServiceWrapper(in Guid serviceId, Func<COMCallableIUnknown> factory)
+ {
+ _factories.Add(serviceId, factory);
+ }
+
+ /// <summary>
+ /// Add service instance
+ /// </summary>
+ /// <param name="serviceId">guid</param>
+ /// <param name="service">instance</param>
+ public void AddServiceWrapper(in Guid serviceId, COMCallableIUnknown service)
+ {
+ _wrappers.Add(serviceId, service);
+ }
+
+ /// <summary>
+ /// Remove the service instance
+ /// </summary>
+ /// <param name="serviceId">guid</param>
+ public void RemoveServiceWrapper(in Guid serviceId)
+ {
+ _factories.Remove(serviceId);
+ _wrappers.Remove(serviceId);
+ }
+
+ /// <summary>
+ /// Returns the wrapper instance for the guid
+ /// </summary>
+ /// <param name="serviceId">service guid</param>
+ /// <returns>instance or null</returns>
+ public COMCallableIUnknown GetServiceWrapper(in Guid serviceId)
+ {
+ if (!_wrappers.TryGetValue(serviceId, out COMCallableIUnknown service))
+ {
+ if (_factories.TryGetValue(serviceId, out Func<COMCallableIUnknown> factory))
+ {
+ service = factory();
+ if (service != null)
+ {
+ _wrappers.Add(serviceId, service);
+ }
+ }
+ }
+ return service;
+ }
+
+ /// <summary>
+ /// Returns the native service for the given interface id. There is
+ /// only a limited set of services that can be queried through this
+ /// function. Adds a reference like QueryInterface.
+ /// </summary>
+ /// <param name="serviceId">guid of the service</param>
+ /// <param name="service">pointer to return service instance</param>
+ /// <returns>S_OK or E_NOINTERFACE</returns>
+ public HResult GetService(IntPtr self, in Guid guid, out IntPtr ptr)
+ {
+ ptr = IntPtr.Zero;
+
+ COMCallableIUnknown wrapper = GetServiceWrapper(guid);
+ if (wrapper == null) {
+ return HResult.E_NOINTERFACE;
+ }
+ wrapper.AddRef();
+ return COMHelper.QueryInterface(wrapper.IUnknownObject, guid, out ptr);
+ }
+ }
+}
using Microsoft.SymbolStore.KeyGenerators;
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.IO;
-using System.Linq;
-using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
-using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
OSXCore = 3
}
- private sealed class OpenedReader : IDisposable
- {
- public readonly MetadataReaderProvider Provider;
- public readonly MetadataReader Reader;
-
- public OpenedReader(MetadataReaderProvider provider, MetadataReader reader)
- {
- Debug.Assert(provider != null);
- Debug.Assert(reader != null);
-
- Provider = provider;
- Reader = reader;
- }
-
- public void Dispose() => Provider.Dispose();
- }
-
/// <summary>
/// Writeline delegate for symbol store logging
/// </summary>
public static readonly Guid IID_ISymbolService = new Guid("7EE88D46-F8B3-4645-AD3E-01FE7D4F70F1");
- private readonly Func<IMemoryService> _getMemoryService;
private readonly ISymbolService _symbolService;
+ private readonly IMemoryService _memoryService;
- public SymbolServiceWrapper(IHost host, Func<IMemoryService> getMemoryService)
+ public SymbolServiceWrapper(ISymbolService symbolService, IMemoryService memoryService)
{
- Debug.Assert(host != null);
- Debug.Assert(getMemoryService != null);
- _getMemoryService = getMemoryService;
- _symbolService = host.Services.GetService<ISymbolService>();
+ Debug.Assert(symbolService != null);
+ Debug.Assert(memoryService != null);
+ _symbolService = symbolService;
+ _memoryService = memoryService;
Debug.Assert(_symbolService != null);
VTableBuilder builder = AddInterface(IID_ISymbolService, validate: false);
KeyGenerator generator = null;
if (config == RuntimeConfiguration.UnixCore)
{
- Stream stream = MemoryService.CreateMemoryStream();
+ Stream stream = _memoryService.CreateMemoryStream();
var elfFile = new ELFFile(new StreamAddressSpace(stream), address, true);
generator = new ELFFileKeyGenerator(Tracer.Instance, elfFile, moduleFilePath);
}
else if (config == RuntimeConfiguration.OSXCore)
{
- Stream stream = MemoryService.CreateMemoryStream();
+ Stream stream = _memoryService.CreateMemoryStream();
var machOFile = new MachOFile(new StreamAddressSpace(stream), address, true);
generator = new MachOFileKeyGenerator(Tracer.Instance, machOFile, moduleFilePath);
}
else if (config == RuntimeConfiguration.WindowsCore || config == RuntimeConfiguration.WindowsDesktop)
{
- Stream stream = MemoryService.CreateMemoryStream(address, size);
+ Stream stream = _memoryService.CreateMemoryStream(address, size);
var peFile = new PEFile(new StreamAddressSpace(stream), true);
generator = new PEFileKeyGenerator(Tracer.Instance, peFile, moduleFilePath);
}
}
}
+ /// <summary>
+ /// Get expression helper for native SOS.
+ /// </summary>
+ /// <param name="expression">hex number</param>
+ /// <returns>value</returns>
+ internal static ulong GetExpressionValue(
+ IntPtr self,
+ string expression)
+ {
+ if (expression != null)
+ {
+ if (ulong.TryParse(expression.Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong result))
+ {
+ return result;
+ }
+ }
+ return 0;
+ }
+
/// <summary>
/// Checks availability of debugging information for given assembly.
/// </summary>
{
try
{
- Stream peStream = null;
+ ISymbolFile symbolFile = null;
if (loadedPeAddress != 0)
{
- peStream = MemoryService.CreateMemoryStream(loadedPeAddress, loadedPeSize);
+ Stream peStream = _memoryService.CreateMemoryStream(loadedPeAddress, loadedPeSize);
+ symbolFile = _symbolService.OpenSymbolFile(assemblyPath, isFileLayout, peStream);
}
- Stream pdbStream = null;
if (inMemoryPdbAddress != 0)
{
- pdbStream = MemoryService.CreateMemoryStream(inMemoryPdbAddress, inMemoryPdbSize);
+ Stream pdbStream = _memoryService.CreateMemoryStream(inMemoryPdbAddress, inMemoryPdbSize);
+ symbolFile = _symbolService.OpenSymbolFile(pdbStream);
}
- OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
- if (openedReader != null)
+ if (symbolFile != null)
{
- GCHandle gch = GCHandle.Alloc(openedReader);
+ GCHandle gch = GCHandle.Alloc(symbolFile);
return GCHandle.ToIntPtr(gch);
}
}
try
{
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- ((OpenedReader)gch.Target).Dispose();
+ if (gch.Target is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
gch.Free();
}
catch (Exception ex)
}
}
- /// <summary>
- /// Get expression helper for native SOS.
- /// </summary>
- /// <param name="expression">hex number</param>
- /// <returns>value</returns>
- internal static ulong GetExpressionValue(
- IntPtr self,
- string expression)
- {
- if (expression != null)
- {
- if (ulong.TryParse(expression.Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong result))
- {
- return result;
- }
- }
- return 0;
- }
-
/// <summary>
/// Returns method token and IL offset for given source line number.
/// </summary>
out int ilOffset)
{
Debug.Assert(symbolReaderHandle != IntPtr.Zero);
- methodToken = 0;
- ilOffset = 0;
-
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- MetadataReader reader = ((OpenedReader)gch.Target).Reader;
-
- try
- {
- string fileName = GetFileName(filePath);
- foreach (MethodDebugInformationHandle methodDebugInformationHandle in reader.MethodDebugInformation)
- {
- MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugInformationHandle);
- SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
- foreach (SequencePoint point in sequencePoints)
- {
- string sourceName = reader.GetString(reader.GetDocument(point.Document).Name);
- if (point.StartLine == lineNumber && GetFileName(sourceName) == fileName)
- {
- methodToken = MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle());
- ilOffset = point.Offset;
- return true;
- }
- }
- }
- }
- catch (Exception ex)
- {
- Trace.TraceError($"ResolveSequencePoint: {ex.Message}");
- }
- return false;
+ ISymbolFile symbolFile = (ISymbolFile)gch.Target;
+ return symbolFile.ResolveSequencePoint(filePath, lineNumber, out methodToken, out ilOffset);
}
/// <summary>
fileName = IntPtr.Zero;
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- OpenedReader openedReader = (OpenedReader)gch.Target;
- if (!GetSourceLineByILOffset(openedReader, methodToken, ilOffset, out lineNumber, out string sourceFileName))
+ ISymbolFile symbolFile = (ISymbolFile)gch.Target;
+ if (!symbolFile.GetSourceLineByILOffset(methodToken, ilOffset, out lineNumber, out string sourceFileName))
{
return false;
}
return true;
}
- /// <summary>
- /// Helper method to return source line number and source file name for given IL offset and method token.
- /// </summary>
- /// <param name="openedReader">symbol reader returned by LoadSymbolsForModule</param>
- /// <param name="methodToken">method token</param>
- /// <param name="ilOffset">IL offset</param>
- /// <param name="lineNumber">source line number return</param>
- /// <param name="fileName">source file name return</param>
- /// <returns> true if information is available</returns>
- private bool GetSourceLineByILOffset(
- OpenedReader openedReader,
- int methodToken,
- long ilOffset,
- out int lineNumber,
- out string fileName)
- {
- lineNumber = 0;
- fileName = null;
- MetadataReader reader = openedReader.Reader;
- try
- {
- Handle handle = MetadataTokens.Handle(methodToken);
- if (handle.Kind != HandleKind.MethodDefinition)
- return false;
-
- MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
- if (methodDebugHandle.IsNil)
- return false;
-
- MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
- SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
-
- SequencePoint? nearestPoint = null;
- foreach (SequencePoint point in sequencePoints)
- {
- if (point.Offset > ilOffset)
- break;
-
- if (point.StartLine != 0 && !point.IsHidden)
- nearestPoint = point;
- }
-
- if (nearestPoint.HasValue)
- {
- lineNumber = nearestPoint.Value.StartLine;
- fileName = reader.GetString(reader.GetDocument(nearestPoint.Value.Document).Name);
- return true;
- }
- }
- catch (Exception ex)
- {
- Trace.TraceError($"GetSourceLineByILOffset: {ex.Message}");
- }
- return false;
- }
-
/// <summary>
/// Returns local variable name for given local index and IL offset.
/// </summary>
localVarName = IntPtr.Zero;
GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
- OpenedReader openedReader = (OpenedReader)gch.Target;
- if (!GetLocalVariableByIndex(openedReader, methodToken, localIndex, out string localVar))
+ ISymbolFile symbolFile = (ISymbolFile)gch.Target;
+ if (!symbolFile.GetLocalVariableByIndex(methodToken, localIndex, out string localVar))
{
return false;
}
return true;
}
- /// <summary>
- /// Helper method to return local variable name for given local index and IL offset.
- /// </summary>
- /// <param name="openedReader">symbol reader returned by LoadSymbolsForModule</param>
- /// <param name="methodToken">method token</param>
- /// <param name="localIndex">local variable index</param>
- /// <param name="localVarName">local variable name return</param>
- /// <returns>true if name has been found</returns>
- private bool GetLocalVariableByIndex(
- OpenedReader openedReader,
- int methodToken,
- int localIndex,
- out string localVarName)
- {
- localVarName = null;
- MetadataReader reader = openedReader.Reader;
- try
- {
- Handle handle = MetadataTokens.Handle(methodToken);
- if (handle.Kind != HandleKind.MethodDefinition)
- return false;
-
- MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
- LocalScopeHandleCollection localScopes = reader.GetLocalScopes(methodDebugHandle);
- foreach (LocalScopeHandle scopeHandle in localScopes)
- {
- LocalScope scope = reader.GetLocalScope(scopeHandle);
- LocalVariableHandleCollection localVars = scope.GetLocalVariables();
- foreach (LocalVariableHandle varHandle in localVars)
- {
- LocalVariable localVar = reader.GetLocalVariable(varHandle);
- if (localVar.Index == localIndex)
- {
- if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
- return false;
-
- localVarName = reader.GetString(localVar.Name);
- return true;
- }
- }
- }
- }
- catch (Exception ex)
- {
- Trace.TraceError($"GetLocalVariableByIndex: {ex.Message}");
- }
- return false;
- }
-
/// <summary>
/// Metadata locator helper for the DAC.
/// </summary>
IntPtr pMetadata,
IntPtr pMetadataSize)
{
- Debug.Assert(imageTimestamp != 0);
- Debug.Assert(imageSize != 0);
-
- if (pMetadata == IntPtr.Zero) {
- return HResult.E_INVALIDARG;
- }
- int hr = HResult.S_OK;
- int dataSize = 0;
-
- ImmutableArray<byte> metadata = _symbolService.GetMetadata(imagePath, imageTimestamp, imageSize);
- if (!metadata.IsEmpty)
- {
- dataSize = metadata.Length;
- int size = Math.Min((int)bufferSize, dataSize);
- Marshal.Copy(metadata.ToArray(), 0, pMetadata, size);
- }
- else
- {
- hr = HResult.E_FAIL;
- }
-
- if (pMetadataSize != IntPtr.Zero) {
- Marshal.WriteInt32(pMetadataSize, dataSize);
- }
- return hr;
+ return _symbolService.GetMetadataLocator(
+ imagePath,
+ imageTimestamp,
+ imageSize,
+ mvid,
+ mdRva,
+ flags,
+ bufferSize,
+ pMetadata,
+ pMetadataSize);
}
/// <summary>
IntPtr pPathBufferSize,
IntPtr pwszPathBuffer)
{
- return _symbolService.GetICorDebugMetadataLocator(imagePath, imageTimestamp, imageSize, pathBufferSize, pPathBufferSize, pwszPathBuffer);
+ return _symbolService.GetICorDebugMetadataLocator(
+ imagePath,
+ imageTimestamp,
+ imageSize,
+ pathBufferSize,
+ pPathBufferSize,
+ pwszPathBuffer);
}
- /// <summary>
- /// Returns the portable PDB reader for the assembly path
- /// </summary>
- /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param>
- /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
- /// <param name="peStream">in-memory PE stream</param>
- /// <param name="pdbStream">optional in-memory PDB stream</param>
- /// <returns>reader/provider wrapper instance</returns>
- /// <remarks>
- /// Assumes that neither PE image nor PDB loaded into memory can be unloaded or moved around.
- /// </remarks>
- private OpenedReader GetReader(string assemblyPath, bool isFileLayout, Stream peStream, Stream pdbStream)
- {
- return (pdbStream != null) ? TryOpenReaderForInMemoryPdb(pdbStream) : TryOpenReaderFromAssembly(assemblyPath, isFileLayout, peStream);
- }
-
- private OpenedReader TryOpenReaderForInMemoryPdb(Stream pdbStream)
- {
- Debug.Assert(pdbStream != null);
-
- byte[] buffer = new byte[sizeof(uint)];
- if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint))
- {
- return null;
- }
- uint signature = BitConverter.ToUInt32(buffer, 0);
-
- // quick check to avoid throwing exceptions below in common cases:
- const uint ManagedMetadataSignature = 0x424A5342;
- if (signature != ManagedMetadataSignature)
- {
- // not a Portable PDB
- return null;
- }
-
- OpenedReader result = null;
- MetadataReaderProvider provider = null;
- try
- {
- pdbStream.Position = 0;
- provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
- result = new OpenedReader(provider, provider.GetMetadataReader());
- }
- catch (Exception e) when (e is BadImageFormatException || e is IOException)
- {
- return null;
- }
- finally
- {
- if (result == null)
- {
- provider?.Dispose();
- }
- }
-
- return result;
- }
-
- private OpenedReader TryOpenReaderFromAssembly(string assemblyPath, bool isFileLayout, Stream peStream)
- {
- if (assemblyPath == null && peStream == null)
- return null;
-
- PEStreamOptions options = isFileLayout ? PEStreamOptions.Default : PEStreamOptions.IsLoadedImage;
- if (peStream == null)
- {
- peStream = TryOpenFile(assemblyPath);
- if (peStream == null)
- return null;
-
- options = PEStreamOptions.Default;
- }
-
- try
- {
- using (var peReader = new PEReader(peStream, options))
- {
- ReadPortableDebugTableEntries(peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry);
-
- // First try .pdb file specified in CodeView data (we prefer .pdb file on disk over embedded PDB
- // since embedded PDB needs decompression which is less efficient than memory-mapping the file).
- if (codeViewEntry.DataSize != 0)
- {
- var result = TryOpenReaderFromCodeView(peReader, codeViewEntry, assemblyPath);
- if (result != null)
- {
- return result;
- }
- }
-
- // if it failed try Embedded Portable PDB (if available):
- if (embeddedPdbEntry.DataSize != 0)
- {
- return TryOpenReaderFromEmbeddedPdb(peReader, embeddedPdbEntry);
- }
- }
- }
- catch (Exception e) when (e is BadImageFormatException || e is IOException)
- {
- // nop
- }
-
- return null;
- }
-
- private void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry)
- {
- // See spec: https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md
-
- codeViewEntry = default;
- embeddedPdbEntry = default;
-
- foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
- {
- if (entry.Type == DebugDirectoryEntryType.CodeView)
- {
- if (entry.MinorVersion != ImageDebugDirectory.PortablePDBMinorVersion)
- {
- continue;
- }
- codeViewEntry = entry;
- }
- else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
- {
- embeddedPdbEntry = entry;
- }
- }
- }
-
- private OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath)
- {
- OpenedReader result = null;
- MetadataReaderProvider provider = null;
- try
- {
- CodeViewDebugDirectoryData data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry);
- string pdbPath = data.Path;
- Stream pdbStream = null;
-
- if (assemblyPath != null)
- {
- try
- {
- pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), GetFileName(pdbPath));
- }
- catch
- {
- // invalid characters in CodeView path
- return null;
- }
- pdbStream = TryOpenFile(pdbPath);
- }
-
- if (pdbStream == null)
- {
- if (_symbolService.IsSymbolStoreEnabled)
- {
- Debug.Assert(codeViewEntry.MinorVersion == ImageDebugDirectory.PortablePDBMinorVersion);
- SymbolStoreKey key = PortablePDBFileKeyGenerator.GetKey(pdbPath, data.Guid);
- pdbStream = _symbolService.GetSymbolStoreFile(key)?.Stream;
- }
- if (pdbStream == null)
- {
- return null;
- }
- // Make sure the stream is at the beginning of the pdb.
- pdbStream.Position = 0;
- }
-
- provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
- MetadataReader reader = provider.GetMetadataReader();
-
- // Validate that the PDB matches the assembly version
- if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp))
- {
- result = new OpenedReader(provider, reader);
- }
- }
- catch (Exception e) when (e is BadImageFormatException || e is IOException)
- {
- return null;
- }
- finally
- {
- if (result == null)
- {
- provider?.Dispose();
- }
- }
-
- return result;
- }
-
- private OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry)
- {
- OpenedReader result = null;
- MetadataReaderProvider provider = null;
-
- try
- {
- // TODO: We might want to cache this provider globally (across stack traces),
- // since decompressing embedded PDB takes some time.
- provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);
- result = new OpenedReader(provider, provider.GetMetadataReader());
- }
- catch (Exception e) when (e is BadImageFormatException || e is IOException)
- {
- return null;
- }
- finally
- {
- if (result == null)
- {
- provider?.Dispose();
- }
- }
-
- return result;
- }
-
- /// <summary>
- /// Attempt to open a file stream.
- /// </summary>
- /// <param name="path">file path</param>
- /// <returns>stream or null if doesn't exist or error</returns>
- private Stream TryOpenFile(string path)
- {
- if (File.Exists(path))
- {
- try
- {
- return File.OpenRead(path);
- }
- catch (Exception ex) when (ex is UnauthorizedAccessException || ex is NotSupportedException || ex is IOException)
- {
- }
- }
- return null;
- }
-
- /// <summary>
- /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux
- /// </summary>
- /// <param name="pathName"> File path to be processed </param>
- /// <returns>Last component of path</returns>
- private static string GetFileName(string pathName)
- {
- int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'});
- if (pos < 0)
- {
- return pathName;
- }
- return pathName.Substring(pos + 1);
- }
-
- private IMemoryService MemoryService => _getMemoryService() ?? throw new DiagnosticsException("SymbolServiceWrapper: no current target");
-
#region Symbol service delegates
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
OSX = 3,
}
- public IntPtr ITarget { get; }
-
public static readonly Guid IID_ITarget = new Guid("B4640016-6CA0-468E-BA2C-1FFF28DE7B72");
+ public ServiceWrapper ServiceWrapper { get; } = new ServiceWrapper();
+
+ public IntPtr ITarget { get; }
+
private readonly IServiceProvider _services;
private readonly ITarget _target;
private readonly Dictionary<IRuntime, RuntimeWrapper> _wrappers = new Dictionary<IRuntime, RuntimeWrapper>();
VTableBuilder builder = AddInterface(IID_ITarget, validate: false);
builder.AddMethod(new GetOperatingSystemDelegate(GetOperatingSystem));
+ builder.AddMethod(new HostWrapper.GetServiceDelegate(ServiceWrapper.GetService));
builder.AddMethod(new GetTempDirectoryDelegate(GetTempDirectory));
builder.AddMethod(new GetRuntimeDelegate(GetRuntime));
builder.AddMethod(new FlushDelegate(Flush));
protected override void Destroy()
{
Trace.TraceInformation("TargetWrapper.Destroy");
+ ServiceWrapper.Dispose();
foreach (RuntimeWrapper wrapper in _wrappers.Values)
{
wrapper.Release();
\**********************************************************************/
void Runtime::LoadRuntimeModules()
{
- HRESULT hr = InitializeSymbolService();
- if (SUCCEEDED(hr))
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService != nullptr)
{
if (m_runtimeInfo != nullptr)
{
- GetSymbolService()->LoadNativeSymbolsFromIndex(
+ symbolService->LoadNativeSymbolsFromIndex(
SymbolFileCallback,
this,
GetRuntimeConfiguration(),
}
else
{
- GetSymbolService()->LoadNativeSymbols(
+ symbolService->LoadNativeSymbols(
SymbolFileCallback,
this,
GetRuntimeConfiguration(),
#endif
}
+HRESULT Target::GetService(REFIID serviceId, PVOID* ppService)
+{
+ return E_NOINTERFACE;
+}
+
LPCSTR Target::GetTempDirectory()
{
if (m_tmpPath == nullptr)
OperatingSystem STDMETHODCALLTYPE GetOperatingSystem();
+ HRESULT STDMETHODCALLTYPE GetService(REFIID serviceId, PVOID* ppService);
+
LPCSTR STDMETHODCALLTYPE GetTempDirectory();
HRESULT STDMETHODCALLTYPE GetRuntime(IRuntime** pRuntime);
if (FAILED(hr)) {
return hr;
}
- // Ignore error so the C++ hosting fallback doesn't fail because there is no symbol service
- InitializeSymbolService();
+#ifndef FEATURE_PAL
+ // When SOS is hosted on dotnet-dump on Windows, the ExtensionApis are not set so
+ // the expression evaluation function needs to be supplied.
+ if (GetExpression == nullptr)
+ {
+ GetExpression = ([](const char* message) {
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService == nullptr)
+ {
+ return (ULONG64)0;
+ }
+ return symbolService->GetExpressionValue(message);
+ });
+ }
+#endif
return S_OK;
}
OnUnloadTask::Run();
}
-/**********************************************************************\
- * Get the symbol service callback entry points.
-\**********************************************************************/
-HRESULT InitializeSymbolService()
-{
- static bool initialized = false;
- if (!initialized)
- {
- ISymbolService* symbolService = GetSymbolService();
- if (symbolService == nullptr) {
- return E_NOINTERFACE;
- }
- initialized = true;
-#ifndef FEATURE_PAL
- // When SOS is hosted on dotnet-dump on Windows, the ExtensionApis are not set so
- // the expression evaluation function needs to be supplied.
- if (GetExpression == nullptr)
- {
- GetExpression = ([](const char* message) {
- return GetSymbolService()->GetExpressionValue(message);
- });
- }
-#endif
- OnUnloadTask::Register([]() {
- initialized = false;
- DisableSymbolStore();
- });
- }
- return S_OK;
-}
-
/**********************************************************************\
* Setup and initialize the symbol server support.
\**********************************************************************/
const char* windowsSymbolPath)
{
HRESULT Status = S_OK;
- IfFailRet(InitializeSymbolService());
- if (!GetSymbolService()->InitializeSymbolStore(
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService == nullptr)
+ {
+ return E_NOINTERFACE;
+ }
+ if (!symbolService->InitializeSymbolStore(
msdl,
symweb,
symbolServer,
}
if (windowsSymbolPath != nullptr)
{
- if (!GetSymbolService()->ParseSymbolPath(windowsSymbolPath))
+ if (!symbolService->ParseSymbolPath(windowsSymbolPath))
{
ExtErr("Error parsing symbol path %s\n", windowsSymbolPath);
return E_FAIL;
BYTE* buffer,
ULONG32* dataSize)
{
- HRESULT Status = S_OK;
- IfFailRet(InitializeSymbolService());
- return GetSymbolService()->GetMetadataLocator(imagePath, imageTimestamp, imageSize, mvid, mdRva, flags, bufferSize, buffer, dataSize);
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService == nullptr)
+ {
+ return E_NOINTERFACE;
+ }
+ return symbolService->GetMetadataLocator(imagePath, imageTimestamp, imageSize, mvid, mdRva, flags, bufferSize, buffer, dataSize);
}
/**********************************************************************\
ULONG32 *pcchPathBuffer,
WCHAR wszPathBuffer[])
{
- HRESULT Status = S_OK;
- IfFailRet(InitializeSymbolService());
- return GetSymbolService()->GetICorDebugMetadataLocator(imagePath, imageTimestamp, imageSize, cchPathBuffer, pcchPathBuffer, wszPathBuffer);
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService == nullptr)
+ {
+ return E_NOINTERFACE;
+ }
+ return symbolService->GetICorDebugMetadataLocator(imagePath, imageTimestamp, imageSize, cchPathBuffer, pcchPathBuffer, wszPathBuffer);
}
#ifndef FEATURE_PAL
HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
{
- HRESULT Status = S_OK;
- IfFailRet(InitializeSymbolService());
-
+ ISymbolService* symbolService = GetSymbolService();
+ if (symbolService == nullptr)
+ {
+ return E_NOINTERFACE;
+ }
m_symbolReaderHandle = GetSymbolService()->LoadSymbolsForModule(
pModuleName, isFileLayout, peAddress, (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize);
{
return E_FAIL;
}
-
- return Status;
+ return S_OK;
}
/**********************************************************************\
extern HRESULT LoadNativeSymbols(bool runtimeOnly = false);
#endif
-extern HRESULT InitializeSymbolService();
-
extern HRESULT InitializeSymbolStore(
BOOL msdl,
BOOL symweb,
{
if (m_pSymbolService == nullptr)
{
- GetHost()->GetService(__uuidof(ISymbolService), (void**)&m_pSymbolService);
+ ITarget* target = GetTarget();
+ if (target != nullptr)
+ {
+ target->GetService(__uuidof(ISymbolService), (void**)&m_pSymbolService);
+ }
}
return m_pSymbolService;
}
virtual HostType STDMETHODCALLTYPE GetHostType() = 0;
/// <summary>
- /// Returns the native service for the given interface id. There is
- /// only a limited set of services that can be queried through this
+ /// Returns the global native service for the given interface id. There
+ /// is only a limited set of services that can be queried through this
/// function. Adds a reference like QueryInterface.
/// </summary>
/// <param name="serviceId">guid of the service</param>
/// <returns>target operating system</returns>
virtual OperatingSystem STDMETHODCALLTYPE GetOperatingSystem() = 0;
+ /// <summary>
+ /// Returns the per-target native service for the given interface
+ /// id. There is only a limited set of services that can be queried
+ /// through this function. Adds a reference like QueryInterface.
+ /// </summary>
+ /// <param name="serviceId">guid of the service</param>
+ /// <param name="service">pointer to return service instance</param>
+ /// <returns>S_OK or E_NOINTERFACE</returns>
+ virtual HRESULT STDMETHODCALLTYPE GetService(REFIID serviceId, PVOID* service) = 0;
+
/// <summary>
/// Returns the unique temporary directory for this instance of SOS
/// </summary>