| File: PdbSourceDocument\PdbFileLocatorService.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// 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.Composition; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.PdbSourceDocument; [Export(typeof(IPdbFileLocatorService)), Shared] [method: ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code")] internal sealed class PdbFileLocatorService( [Import(AllowDefault = true)] IPdbSourceDocumentLogger? logger) : IPdbFileLocatorService { private const int SymbolLocatorTimeout = 2000; private readonly IPdbSourceDocumentLogger? _logger = logger; public async Task<DocumentDebugInfoReader?> GetDocumentDebugInfoReaderAsync(string dllPath, bool useDefaultSymbolServers, TelemetryMessage telemetry, ISourceLinkService? sourceLinkService, CancellationToken cancellationToken) { var dllStream = IOUtilities.PerformIO(() => ReadFileIfExists(dllPath)); if (dllStream is null) return null; Stream? pdbStream = null; DocumentDebugInfoReader? result = null; var peReader = new PEReader(dllStream); try { // Try to load the pdb file from disk, or embedded if (peReader.TryOpenAssociatedPortablePdb(dllPath, ReadFileIfExists, out var pdbReaderProvider, out var pdbFilePath)) { Contract.ThrowIfNull(pdbReaderProvider); if (pdbFilePath is null) { telemetry.SetPdbSource("embedded"); _logger?.Log(FeaturesResources.Found_embedded_PDB_file); } else { telemetry.SetPdbSource("ondisk"); _logger?.Log(FeaturesResources.Found_PDB_file_at_0, pdbFilePath); } result = new DocumentDebugInfoReader(peReader, pdbReaderProvider); } if (result is null) { if (sourceLinkService is null) { _logger?.Log(FeaturesResources.Could_not_find_PDB_on_disk_or_embedded); } else { var delay = Task.Delay(SymbolLocatorTimeout, cancellationToken); // Call the debugger to find the PDB from a symbol server etc. var pdbResultTask = sourceLinkService.GetPdbFilePathAsync(dllPath, peReader, useDefaultSymbolServers, cancellationToken); var winner = await Task.WhenAny(pdbResultTask, delay).ConfigureAwait(false); if (winner == pdbResultTask) { var pdbResult = await pdbResultTask.ConfigureAwait(false); if (pdbResult is not null) { pdbStream = IOUtilities.PerformIO(() => File.OpenRead(pdbResult.PdbFilePath)); if (pdbStream is not null) { var readerProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); telemetry.SetPdbSource("symbolserver"); result = new DocumentDebugInfoReader(peReader, readerProvider); _logger?.Log(FeaturesResources.Found_PDB_on_symbol_server); } else { _logger?.Log(FeaturesResources.Found_PDB_on_symbol_server_but_could_not_read_file); } } else { _logger?.Log(FeaturesResources.Could_not_find_PDB_on_disk_or_embedded_or_server); } } else { telemetry.SetPdbSource("timeout"); _logger?.Log(FeaturesResources.Timeout_symbol_server); } } } } catch (BadImageFormatException ex) { // If the PDB is corrupt in some way we can just ignore it, and let the system fall through to another provider _logger?.Log(FeaturesResources.Error_reading_PDB_0, ex.Message); result = null; } finally { // If we're returning a result then it will own the disposal of the reader, but if not // then we need to do it ourselves. if (result is null) { pdbStream?.Dispose(); peReader.Dispose(); } } return result; static FileStream? ReadFileIfExists(string fileName) { if (File.Exists(fileName)) return File.OpenRead(fileName); return null; } } }