public async Task <DocumentDebugInfoReader?> GetDocumentDebugInfoReaderAsync(string dllPath, TelemetryMessage telemetry, CancellationToken cancellationToken) { var dllStream = IOUtilities.PerformIO(() => File.OpenRead(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, pdbPath => File.OpenRead(pdbPath), 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, 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); }
private SourceFileInfo?TryGetEmbeddedSourceFile(string tempFilePath, SourceDocument sourceDocument, Encoding encoding, TelemetryMessage telemetry) { if (sourceDocument.EmbeddedTextBytes is null) { return(null); } var filePath = Path.Combine(tempFilePath, Path.GetFileName(sourceDocument.FilePath)); // We might have already navigated to this file before, so it might exist, but // we still need to re-validate the checksum and make sure its not the wrong file if (File.Exists(filePath) && LoadSourceFile(filePath, sourceDocument, encoding, FeaturesResources.embedded, ignoreChecksum: false) is { } existing) { telemetry.SetSourceFileSource("embedded"); _logger?.Log(FeaturesResources._0_found_in_embedded_PDB_cached_source_file, sourceDocument.FilePath); return(existing); } var embeddedTextBytes = sourceDocument.EmbeddedTextBytes; var uncompressedSize = BitConverter.ToInt32(embeddedTextBytes, 0); var stream = new MemoryStream(embeddedTextBytes, sizeof(int), embeddedTextBytes.Length - sizeof(int)); if (uncompressedSize != 0) { var decompressed = new MemoryStream(uncompressedSize); using (var deflater = new DeflateStream(stream, CompressionMode.Decompress)) { deflater.CopyTo(decompressed); } if (decompressed.Length != uncompressedSize) { return(null); } stream = decompressed; } if (stream is not null) { // Even though Roslyn supports loading SourceTexts from a stream, Visual Studio requires // a file to exist on disk so we have to write embedded source to a temp file. using (stream) { try { stream.Position = 0; using (var file = File.OpenWrite(filePath)) { stream.CopyTo(file); } new FileInfo(filePath).IsReadOnly = true; } catch (IOException ex) { _logger?.Log(FeaturesResources._0_found_in_embedded_PDB_but_could_not_write_file_1, sourceDocument.FilePath, ex.Message); return(null); } } var result = LoadSourceFile(filePath, sourceDocument, encoding, FeaturesResources.embedded, ignoreChecksum: false); if (result is not null) { telemetry.SetSourceFileSource("embedded"); _logger?.Log(FeaturesResources._0_found_in_embedded_PDB, sourceDocument.FilePath); } else { _logger?.Log(FeaturesResources._0_found_in_embedded_PDB_but_checksum_failed, sourceDocument.FilePath); } return(result); } return(null); }
private SourceFileInfo?TryGetOriginalFile(SourceDocument sourceDocument, Encoding encoding, TelemetryMessage telemetry) { if (File.Exists(sourceDocument.FilePath)) { var result = LoadSourceFile(sourceDocument.FilePath, sourceDocument, encoding, FeaturesResources.external, ignoreChecksum: false); if (result is not null) { telemetry.SetSourceFileSource("ondisk"); _logger?.Log(FeaturesResources._0_found_in_original_location, sourceDocument.FilePath); } else { _logger?.Log(FeaturesResources._0_found_in_original_location_but_checksum_failed, sourceDocument.FilePath); } return(result); } return(null); }
public async Task <SourceFileInfo?> LoadSourceDocumentAsync(string tempFilePath, SourceDocument sourceDocument, Encoding encoding, TelemetryMessage telemetry, CancellationToken cancellationToken) { // First we try getting "local" files, either from embedded source or a local file on disk // and if they don't work we call the debugger to download a file from SourceLink info return(TryGetEmbeddedSourceFile(tempFilePath, sourceDocument, encoding, telemetry) ?? TryGetOriginalFile(sourceDocument, encoding, telemetry) ?? await TryGetSourceLinkFileAsync(sourceDocument, encoding, telemetry, cancellationToken).ConfigureAwait(false)); }
private async Task <SourceFileInfo?> TryGetSourceLinkFileAsync(SourceDocument sourceDocument, Encoding encoding, TelemetryMessage telemetry, CancellationToken cancellationToken) { if (sourceDocument.SourceLinkUrl is null || _sourceLinkService.Value is null) { return(null); } // This should ideally be the repo-relative path to the file, and come from SourceLink: https://github.com/dotnet/sourcelink/pull/699 var relativePath = Path.GetFileName(sourceDocument.FilePath); var delay = Task.Delay(SourceLinkTimeout, cancellationToken); var sourceFileTask = _sourceLinkService.Value.GetSourceFilePathAsync(sourceDocument.SourceLinkUrl, relativePath, cancellationToken); var winner = await Task.WhenAny(sourceFileTask, delay).ConfigureAwait(false); if (winner == sourceFileTask) { var sourceFile = await sourceFileTask.ConfigureAwait(false); if (sourceFile is not null) { // TODO: Don't ignore the checksum here: https://github.com/dotnet/roslyn/issues/55834 var result = LoadSourceFile(sourceFile.SourceFilePath, sourceDocument, encoding, "SourceLink", ignoreChecksum: true); if (result is not null) { telemetry.SetSourceFileSource("sourcelink"); _logger?.Log(FeaturesResources._0_found_via_SourceLink, sourceDocument.FilePath); } else { _logger?.Log(FeaturesResources._0_found_via_SourceLink_but_couldnt_read_file, sourceDocument.FilePath); } return(result); } else { telemetry.SetSourceFileSource("timeout"); _logger?.Log(FeaturesResources.Timeout_SourceLink); } } return(null); }