| File: Debugging\DebugInformationReaderProvider.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.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.DiaSymReader; namespace Microsoft.CodeAnalysis.Debugging; /// <summary> /// An abstraction of a symbol reader that provides a reader of Edit and Continue debug information. /// Owns the underlying PDB reader. /// </summary> internal abstract class DebugInformationReaderProvider : IDisposable { private sealed class DummySymReaderMetadataProvider : ISymReaderMetadataProvider { public static readonly DummySymReaderMetadataProvider Instance = new(); public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length) => throw ExceptionUtilities.Unreachable(); public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespaceName, out string typeName, out TypeAttributes attributes) => throw ExceptionUtilities.Unreachable(); public bool TryGetTypeReferenceInfo(int typeReferenceToken, out string namespaceName, out string typeName) => throw ExceptionUtilities.Unreachable(); } private sealed class Portable(MetadataReaderProvider pdbReaderProvider) : DebugInformationReaderProvider { private readonly MetadataReaderProvider _pdbReaderProvider = pdbReaderProvider; public override EditAndContinueDebugInfoReader CreateEditAndContinueDebugInfoReader() => EditAndContinueDebugInfoReader.Create(_pdbReaderProvider.GetMetadataReader()); public override ValueTask CopyContentToAsync(Stream stream, CancellationToken cancellationToken) { var reader = _pdbReaderProvider.GetMetadataReader(); unsafe { using var metadataStream = new UnmanagedMemoryStream(reader.MetadataPointer, reader.MetadataLength); metadataStream.CopyTo(stream); } return ValueTask.CompletedTask; } public override void Dispose() => _pdbReaderProvider.Dispose(); } private sealed class Native(Stream stream, ISymUnmanagedReader5 symReader, int version) : DebugInformationReaderProvider { private readonly Stream _stream = stream; private readonly int _version = version; private ISymUnmanagedReader5? _symReader = symReader; public override EditAndContinueDebugInfoReader CreateEditAndContinueDebugInfoReader() => EditAndContinueDebugInfoReader.Create(_symReader ?? throw new ObjectDisposedException(GetType().FullName), _version); public override async ValueTask CopyContentToAsync(Stream stream, CancellationToken cancellationToken) { var position = _stream.Position; try { _stream.Position = 0; await _stream.CopyToAsync(stream, bufferSize: 4 * 1024, cancellationToken).ConfigureAwait(false); } finally { _stream.Position = position; } } public override void Dispose() { _stream.Dispose(); var symReader = Interlocked.Exchange(ref _symReader, null); if (symReader != null && Marshal.IsComObject(symReader)) { #if NET Debug.Assert(OperatingSystem.IsWindows()); #endif Marshal.ReleaseComObject(symReader); } } } public abstract void Dispose(); /// <summary> /// Creates EnC debug information reader. /// </summary> public abstract EditAndContinueDebugInfoReader CreateEditAndContinueDebugInfoReader(); public abstract ValueTask CopyContentToAsync(Stream stream, CancellationToken cancellationToken); /// <summary> /// Creates <see cref="DebugInformationReaderProvider"/> from a stream of Portable or Windows PDB. /// </summary> /// <returns> /// Provider instance, which keeps the <paramref name="stream"/> open until disposed. /// </returns> /// <remarks> /// Requires Microsoft.DiaSymReader.Native.{platform}.dll to be available for reading Windows PDB. /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="stream"/> does not support read and seek operations.</exception> /// <exception cref="Exception">Error reading debug information from <paramref name="stream"/>.</exception> public static DebugInformationReaderProvider CreateFromStream(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanRead || !stream.CanSeek) { throw new ArgumentException(CompilerExtensionsResources.StreamMustSupportReadAndSeek, nameof(stream)); } var isPortable = stream.ReadByte() == 'B' && stream.ReadByte() == 'S' && stream.ReadByte() == 'J' && stream.ReadByte() == 'B'; stream.Position = 0; if (isPortable) { return new Portable(MetadataReaderProvider.FromPortablePdbStream(stream)); } return CreateNative(stream); } // Do not inline to avoid loading Microsoft.DiaSymReader until it's actually needed. [MethodImpl(MethodImplOptions.NoInlining)] private static DebugInformationReaderProvider CreateNative(Stream stream) { // We can use DummySymReaderMetadataProvider since we do not need to decode signatures, // which is the only operation SymReader needs the provider for. return new Native(stream, SymUnmanagedReaderFactory.CreateReader<ISymUnmanagedReader5>( stream, DummySymReaderMetadataProvider.Instance, SymUnmanagedReaderCreationOptions.UseAlternativeLoadPath), version: 1); } /// <summary> /// Creates <see cref="DebugInformationReaderProvider"/> from a Portable PDB metadata reader provider. /// </summary> /// <returns> /// Provider instance, which takes ownership of the <paramref name="metadataProvider"/> until disposed. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="metadataProvider"/> is null.</exception> public static DebugInformationReaderProvider CreateFromMetadataReader(MetadataReaderProvider metadataProvider) => new Portable(metadataProvider ?? throw new ArgumentNullException(nameof(metadataProvider))); }