未验证 提交 88b0528e 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Use Concord API to get SymReader for EnC instead of...

Use Concord API to get SymReader for EnC instead of IENCSymbolReaderProvider.GetSymbolReader. (#29177)
上级 c968575f
......@@ -2,6 +2,7 @@
using System;
using Microsoft.CodeAnalysis.EditAndContinue;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.EditAndContinue
......@@ -11,7 +12,7 @@ public sealed class DebuggeeModuleMetadataCacheTests
[Fact]
public void GetOrAddNull()
{
var cache = new DebuggeeModuleMetadataCache();
var cache = new DebuggeeModuleInfoCache();
var mvid = Guid.NewGuid();
Assert.Null(cache.GetOrAdd(mvid, m => { Assert.Equal(mvid, m); return default; }));
......@@ -20,31 +21,33 @@ public void GetOrAddNull()
[Fact]
public void GetOrAdd()
{
var cache = new DebuggeeModuleMetadataCache();
var cache = new DebuggeeModuleInfoCache();
var metadata1 = ModuleMetadata.CreateFromImage((IntPtr)1, 1);
var symReader1 = NotImplementedSymUnmanagedReader.Instance;
var metadata2 = ModuleMetadata.CreateFromImage((IntPtr)2, 1);
var symReader2 = NotImplementedSymUnmanagedReader.Instance;
var mvid1 = Guid.NewGuid();
var mvid2 = Guid.NewGuid();
var mvid3 = Guid.NewGuid();
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => metadata1));
Assert.Same(metadata2, cache.GetOrAdd(mvid2, _ => metadata2));
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => new DebuggeeModuleInfo(metadata1, symReader1)).Metadata);
Assert.Same(metadata2, cache.GetOrAdd(mvid2, _ => new DebuggeeModuleInfo(metadata2, symReader2)).Metadata);
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => throw null));
Assert.Same(metadata2, cache.GetOrAdd(mvid2, _ => throw null));
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => throw null).Metadata);
Assert.Same(metadata2, cache.GetOrAdd(mvid2, _ => throw null).Metadata);
}
[Fact]
public void Remove()
{
var cache = new DebuggeeModuleMetadataCache();
var cache = new DebuggeeModuleInfoCache();
Assert.False(cache.Remove(Guid.NewGuid()));
var mvid1 = Guid.NewGuid();
cache.GetOrAdd(mvid1, _ => ModuleMetadata.CreateFromImage((IntPtr)1, 1));
cache.GetOrAdd(mvid1, _ => new DebuggeeModuleInfo(ModuleMetadata.CreateFromImage((IntPtr)1, 1), NotImplementedSymUnmanagedReader.Instance));
Assert.True(cache.Remove(mvid1));
Assert.False(cache.Remove(mvid1));
......@@ -54,20 +57,22 @@ public void Remove()
[Fact]
public void RemoveAdd()
{
var cache = new DebuggeeModuleMetadataCache();
var cache = new DebuggeeModuleInfoCache();
Assert.False(cache.Remove(Guid.NewGuid()));
var mvid1 = Guid.NewGuid();
var metadata1 = ModuleMetadata.CreateFromImage((IntPtr)1, 1);
var symReader1 = NotImplementedSymUnmanagedReader.Instance;
var metadata2 = ModuleMetadata.CreateFromImage((IntPtr)2, 1);
var symReader2 = NotImplementedSymUnmanagedReader.Instance;
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => metadata1));
Assert.Same(metadata1, cache.GetOrAdd(mvid1, _ => new DebuggeeModuleInfo(metadata1, symReader1)).Metadata);
Assert.True(cache.Remove(mvid1));
Assert.Null(cache.GetOrAdd(mvid1, _ => default));
Assert.Same(metadata2, cache.GetOrAdd(mvid1, _ => metadata2));
Assert.Same(metadata2, cache.GetOrAdd(mvid1, _ => new DebuggeeModuleInfo(metadata2, symReader2)).Metadata);
}
}
}
......@@ -18,6 +18,7 @@
<ProjectReference Include="..\..\Interactive\EditorFeatures\CSharp\Microsoft.CodeAnalysis.CSharp.InteractiveEditorFeatures.csproj" />
<ProjectReference Include="..\..\Interactive\EditorFeatures\VisualBasic\Microsoft.CodeAnalysis.VisualBasic.InteractiveEditorFeatures.vbproj" />
<ProjectReference Include="..\..\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj" />
<ProjectReference Include="..\..\Test\PdbUtilities\Roslyn.Test.PdbUtilities.csproj" />
<ProjectReference Include="..\..\Test\Utilities\Portable\Roslyn.Test.Utilities.csproj" />
<ProjectReference Include="..\..\Workspaces\Core\Desktop\Microsoft.CodeAnalysis.Workspaces.Desktop.csproj">
<Aliases>global</Aliases>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.DiaSymReader;
namespace Microsoft.CodeAnalysis.EditAndContinue
{
internal sealed class DebuggeeModuleInfo : IDisposable
{
public ModuleMetadata Metadata { get; }
private ISymUnmanagedReader5 _symReader;
public ISymUnmanagedReader5 SymReader => _symReader;
public DebuggeeModuleInfo(ModuleMetadata metadata, ISymUnmanagedReader5 symReader)
{
Debug.Assert(metadata != null);
Debug.Assert(symReader != null);
Metadata = metadata;
_symReader = symReader;
}
public void Dispose()
{
Metadata?.Dispose();
var symReader = Interlocked.Exchange(ref _symReader, null);
if (symReader != null && Marshal.IsComObject(symReader))
{
Marshal.ReleaseComObject(symReader);
}
}
}
}
......@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.EditAndContinue
{
......@@ -12,19 +11,19 @@ namespace Microsoft.CodeAnalysis.EditAndContinue
/// A cache of metadata blobs loaded into processes being debugged.
/// Thread safe.
/// </summary>
internal sealed class DebuggeeModuleMetadataCache
internal sealed class DebuggeeModuleInfoCache
{
// Maps MVIDs to metadata blobs loaded to specific processes
private Dictionary<Guid, ModuleMetadata> _lazyCache;
private Dictionary<Guid, DebuggeeModuleInfo> _lazyCache;
/// <summary>
/// May return null if the provider returns null.
/// </summary>
public ModuleMetadata GetOrAdd(Guid mvid, Func<Guid, ModuleMetadata> provider)
public DebuggeeModuleInfo GetOrAdd(Guid mvid, Func<Guid, DebuggeeModuleInfo> provider)
{
if (_lazyCache == null)
{
Interlocked.CompareExchange(ref _lazyCache, new Dictionary<Guid, ModuleMetadata>(), null);
Interlocked.CompareExchange(ref _lazyCache, new Dictionary<Guid, DebuggeeModuleInfo>(), null);
}
var cache = _lazyCache;
......@@ -37,22 +36,22 @@ public ModuleMetadata GetOrAdd(Guid mvid, Func<Guid, ModuleMetadata> provider)
}
}
var newMetadata = provider(mvid);
if (newMetadata == null)
var newInfo = provider(mvid);
if (newInfo == null)
{
return null;
return default;
}
lock (cache)
{
if (cache.TryGetValue(mvid, out var existing))
{
newMetadata.Dispose();
newInfo.Dispose();
return existing;
}
cache.Add(mvid, newMetadata);
return newMetadata;
cache.Add(mvid, newInfo);
return newInfo;
}
}
......
......@@ -10,9 +10,9 @@ namespace Microsoft.CodeAnalysis.EditAndContinue
internal interface IDebuggeeModuleMetadataProvider
{
/// <summary>
/// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata.
/// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata and symbols.
/// Shall only be called while in debug mode.
/// </summary>
ModuleMetadata TryGetBaselineMetadata(Guid mvid);
DebuggeeModuleInfo TryGetBaselineModuleInfo(Guid mvid);
}
}
......@@ -108,6 +108,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
<PackageReference Include="Microsoft.DiaSymReader" Version="$(MicrosoftDiaSymReaderVersion)" />
</ItemGroup>
<Import Project="..\..\..\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems" Label="Shared" />
</Project>
\ No newline at end of file
......@@ -7,7 +7,7 @@
namespace Roslyn.Test.Utilities
{
internal sealed class NotImplementedSymUnmanagedReader : ISymUnmanagedReader, ISymUnmanagedReader2, ISymUnmanagedReader3
public sealed class NotImplementedSymUnmanagedReader : ISymUnmanagedReader5
{
public static readonly NotImplementedSymUnmanagedReader Instance = new NotImplementedSymUnmanagedReader();
......@@ -142,5 +142,32 @@ public int UpdateSymbolStore(string filename, IStream stream)
{
return HResult.E_NOTIMPL;
}
public int MatchesModule(Guid guid, uint stamp, int age, out bool result)
{
result = false;
return HResult.E_NOTIMPL;
}
public unsafe int GetPortableDebugMetadata(out byte* metadata, out int size)
{
metadata = null;
size = 0;
return HResult.E_NOTIMPL;
}
public unsafe int GetSourceServerData(out byte* data, out int size)
{
data = null;
size = 0;
return HResult.E_NOTIMPL;
}
public unsafe int GetPortableDebugMetadataByVersion(int version, out byte* metadata, out int size)
{
metadata = null;
size = 0;
return HResult.E_NOTIMPL;
}
}
}
......@@ -2,12 +2,16 @@
using System;
using System.Composition;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.DiaSymReader;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.ComponentInterfaces;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.EditAndContinue
{
......@@ -50,11 +54,11 @@ DkmCustomMessage IDkmCustomMessageForwardReceiver.SendLower(DkmCustomMessage cus
}
}
private readonly DebuggeeModuleMetadataCache _baselineMetadata;
private readonly DebuggeeModuleInfoCache _baselineMetadata;
public DebuggeeModuleMetadataProvider()
{
_baselineMetadata = new DebuggeeModuleMetadataCache();
_baselineMetadata = new DebuggeeModuleInfoCache();
}
private void OnModuleInstanceUnload(Guid mvid)
......@@ -63,9 +67,12 @@ private void OnModuleInstanceUnload(Guid mvid)
/// <summary>
/// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata.
/// Shall only be called while in debug mode.
/// Shall only be called on MTA thread.
/// </summary>
public ModuleMetadata TryGetBaselineMetadata(Guid mvid)
public DebuggeeModuleInfo TryGetBaselineModuleInfo(Guid mvid)
{
Contract.ThrowIfFalse(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);
return _baselineMetadata.GetOrAdd(mvid, m =>
{
using (DebuggerComponent.ManagedEditAndContinueService())
......@@ -76,8 +83,7 @@ public ModuleMetadata TryGetBaselineMetadata(Guid mvid)
return default;
}
var metadata = GetBaselineModuleMetadata(clrModuleInstance);
if (metadata == null)
if (!TryGetBaselineModuleInfo(clrModuleInstance, out var metadata, out var symReader))
{
return default;
}
......@@ -91,13 +97,15 @@ public ModuleMetadata TryGetBaselineMetadata(Guid mvid)
Parameter1: this,
Parameter2: clrModuleInstance).SendLower();
return metadata;
return new DebuggeeModuleInfo(metadata, symReader);
}
});
}
private static ModuleMetadata GetBaselineModuleMetadata(DkmClrModuleInstance module)
private static bool TryGetBaselineModuleInfo(DkmClrModuleInstance module, out ModuleMetadata metadata, out ISymUnmanagedReader5 symReader)
{
Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);
IntPtr metadataPtr;
uint metadataSize;
try
......@@ -106,10 +114,21 @@ private static ModuleMetadata GetBaselineModuleMetadata(DkmClrModuleInstance mod
}
catch (Exception e) when (DkmExceptionUtilities.IsBadOrMissingMetadataException(e))
{
return null;
metadata = null;
symReader = null;
return false;
}
symReader = module.GetSymUnmanagedReader() as ISymUnmanagedReader5;
if (symReader == null)
{
metadata = null;
symReader = null;
return false;
}
return ModuleMetadata.CreateFromMetadata(metadataPtr, (int)metadataSize);
metadata = ModuleMetadata.CreateFromMetadata(metadataPtr, (int)metadataSize);
return true;
}
private static DkmClrModuleInstance FindClrModuleInstance(Guid mvid)
......
......@@ -89,8 +89,6 @@ internal sealed class VsENCRebuildableProjectImpl
/// </summary>
private Guid _mvid;
private Lazy<ISymUnmanagedReader5> _pdbReader;
#endregion
private bool IsDebuggable => _mvid != Guid.Empty;
......@@ -338,8 +336,6 @@ public int StopDebuggingPE()
_committedBaseline = null;
_projectBeingEmitted = null;
ReleasePdbReader(Interlocked.Exchange(ref _pdbReader, null));
// The HResult is ignored by the debugger.
return VSConstants.S_OK;
}
......@@ -349,18 +345,6 @@ public int StopDebuggingPE()
}
}
private static void ReleasePdbReader(Lazy<ISymUnmanagedReader5> pdbReader)
{
if (pdbReader?.IsValueCreated == true)
{
var symReader = pdbReader.Value;
if (Marshal.IsComObject(symReader))
{
Marshal.ReleaseComObject(symReader);
}
}
}
private static void LogEncSession()
{
var sessionId = DebugLogMessage.GetNextId();
......@@ -765,18 +749,7 @@ public unsafe int BuildForEnc(object pUpdatePE)
// so we'll to emit an empty delta. See bug 839558.
Debug.Assert(_lastEditSessionSummary == ProjectAnalysisSummary.ValidInsignificantChanges ||
_lastEditSessionSummary == ProjectAnalysisSummary.ValidChanges);
var updater = (IDebugUpdateInMemoryPE3)pUpdatePE;
// Acquire PDB reader lazily. _pdbReader is cleared when the debugging session stops.
if (_pdbReader == null)
{
var previousPdbReader = Interlocked.Exchange(ref _pdbReader, MarshalPdbReader(updater));
// This method should only be called on UI thread, thus the previous value should be null.
Contract.ThrowIfFalse(previousPdbReader == null);
}
// ISymUnmanagedReader can only be accessed from an MTA thread,
// so dispatch emit to one of thread pool threads, which are MTA.
var emitTask = Task.Factory.SafeStartNew(EmitProjectDelta, CancellationToken.None, TaskScheduler.Default);
......@@ -808,6 +781,8 @@ public unsafe int BuildForEnc(object pUpdatePE)
}
_documentsWithEmitError = ImmutableArray<DocumentId>.Empty;
var updater = (IDebugUpdateInMemoryPE3)pUpdatePE;
SetFileUpdates(updater, delta.LineEdits);
updater.SetDeltaIL(delta.IL.Value, (uint)delta.IL.Value.Length);
......@@ -949,14 +924,14 @@ private Deltas EmitProjectDelta()
var baseline = _committedBaseline;
if (baseline == null)
{
var baselineMetadata = _moduleMetadataProvider.TryGetBaselineMetadata(_mvid);
if (baselineMetadata != null)
var info = _moduleMetadataProvider.TryGetBaselineModuleInfo(_mvid);
if (info != null)
{
baseline = EmitBaseline.CreateInitialBaseline(
baselineMetadata,
GetBaselineEncDebugInfo,
GetBaselineLocalSignature,
HasPortableMetadata(_pdbReader.Value));
info.Metadata,
h => GetBaselineEncDebugInfo(info.SymReader, h),
h => GetBaselineLocalSignature(info.SymReader, h),
HasPortableMetadata(info.SymReader));
}
}
......@@ -985,11 +960,11 @@ private Deltas EmitProjectDelta()
private unsafe bool HasPortableMetadata(ISymUnmanagedReader5 symReader)
=> symReader.GetPortableDebugMetadata(out _, out _) == 0;
private StandaloneSignatureHandle GetBaselineLocalSignature(MethodDefinitionHandle methodHandle)
private static StandaloneSignatureHandle GetBaselineLocalSignature(ISymUnmanagedReader5 symReader, MethodDefinitionHandle methodHandle)
{
Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);
var symMethod = (ISymUnmanagedMethod2)_pdbReader.Value.GetMethodByVersion(MetadataTokens.GetToken(methodHandle), methodVersion: 1);
var symMethod = (ISymUnmanagedMethod2)symReader.GetMethodByVersion(MetadataTokens.GetToken(methodHandle), methodVersion: 1);
// Compiler generated methods (e.g. async kick-off methods) might not have debug information.
return symMethod == null ? default : MetadataTokens.StandaloneSignatureHandle(symMethod.GetLocalSignatureToken());
......@@ -999,40 +974,20 @@ private StandaloneSignatureHandle GetBaselineLocalSignature(MethodDefinitionHand
/// Returns EnC debug information for initial version of the specified method.
/// </summary>
/// <exception cref="InvalidDataException">The debug information data is corrupt or can't be retrieved from the debugger.</exception>
private EditAndContinueMethodDebugInformation GetBaselineEncDebugInfo(MethodDefinitionHandle methodHandle)
private static EditAndContinueMethodDebugInformation GetBaselineEncDebugInfo(ISymUnmanagedReader5 symReader, MethodDefinitionHandle methodHandle)
{
Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);
return GetEditAndContinueMethodDebugInfo(_pdbReader.Value, methodHandle);
return GetEditAndContinueMethodDebugInfo(symReader, methodHandle);
}
// Unmarshal the symbol reader (being marshalled cross thread from STA -> MTA).
private static ISymUnmanagedReader5 UnmarshalSymReader(IntPtr stream)
{
Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);
try
{
return (ISymUnmanagedReader5)NativeMethods.GetObjectAndRelease(stream);
}
catch (Exception exception) when (FatalError.ReportWithoutCrash(exception))
{
throw new InvalidDataException(exception.Message, exception);
}
}
private static EditAndContinueMethodDebugInformation GetEditAndContinueMethodDebugInfo(ISymUnmanagedReader3 symReader, MethodDefinitionHandle methodHandle)
private static EditAndContinueMethodDebugInformation GetEditAndContinueMethodDebugInfo(ISymUnmanagedReader5 symReader, MethodDefinitionHandle methodHandle)
{
return TryGetPortableEncDebugInfo(symReader, methodHandle, out var info) ? info : GetNativeEncDebugInfo(symReader, methodHandle);
}
private static unsafe bool TryGetPortableEncDebugInfo(ISymUnmanagedReader symReader, MethodDefinitionHandle methodHandle, out EditAndContinueMethodDebugInformation info)
private static unsafe bool TryGetPortableEncDebugInfo(ISymUnmanagedReader5 symReader, MethodDefinitionHandle methodHandle, out EditAndContinueMethodDebugInformation info)
{
if (!(symReader is ISymUnmanagedReader5 symReader5))
{
info = default;
return false;
}
int hr = symReader5.GetPortableDebugMetadataByVersion(version: 1, metadata: out byte* metadata, size: out int size);
int hr = symReader.GetPortableDebugMetadataByVersion(version: 1, metadata: out byte* metadata, size: out int size);
Marshal.ThrowExceptionForHR(hr);
if (hr != 0)
......@@ -1075,7 +1030,7 @@ private static bool TryGetCustomDebugInformation(System.Reflection.Metadata.Meta
return foundAny;
}
private static EditAndContinueMethodDebugInformation GetNativeEncDebugInfo(ISymUnmanagedReader3 symReader, MethodDefinitionHandle methodHandle)
private static EditAndContinueMethodDebugInformation GetNativeEncDebugInfo(ISymUnmanagedReader5 symReader, MethodDefinitionHandle methodHandle)
{
int methodToken = MetadataTokens.GetToken(methodHandle);
......@@ -1145,42 +1100,6 @@ public int EncApplySucceeded(int hrApplyResult)
}
}
private static Lazy<ISymUnmanagedReader5> MarshalPdbReader(IDebugUpdateInMemoryPE2 updater)
{
// ISymUnmanagedReader can only be accessed from an MTA thread, however, we need
// fetch the IUnknown instance (call IENCSymbolReaderProvider.GetSymbolReader) here
// in the STA. To further complicate things, we need to return synchronously from
// this method. Waiting for the MTA thread to complete so we can return synchronously
// blocks the STA thread, so we need to make sure the CLR doesn't try to marshal
// ISymUnmanagedReader calls made in an MTA back to the STA for execution (if this
// happens we'll be deadlocked). We'll use CoMarshalInterThreadInterfaceInStream to
// achieve this. First, we'll marshal the object in a Stream and pass a Stream pointer
// over to the MTA. In the MTA, we'll get the Stream from the pointer and unmarshal
// the object. The reader object was originally created on an MTA thread, and the
// instance we retrieved in the STA was a proxy. When we unmarshal the Stream in the
// MTA, it "unwraps" the proxy, allowing us to directly call the implementation.
// Another way to achieve this would be for the symbol reader to implement IAgileObject,
// but the symbol reader we use today does not. If that changes, we should consider
// removing this marshal/unmarshal code.
updater.GetENCDebugInfo(out IENCDebugInfo debugInfo);
var symbolReaderProvider = (IENCSymbolReaderProvider)debugInfo;
symbolReaderProvider.GetSymbolReader(out object pdbReaderObjSta);
if (Marshal.IsComObject(pdbReaderObjSta))
{
int hr = NativeMethods.GetStreamForObject(pdbReaderObjSta, out IntPtr stream);
Marshal.ReleaseComObject(pdbReaderObjSta);
Marshal.ThrowExceptionForHR(hr);
return new Lazy<ISymUnmanagedReader5>(() => UnmarshalSymReader(stream));
}
else
{
var managedSymReader = (ISymUnmanagedReader5)pdbReaderObjSta;
return new Lazy<ISymUnmanagedReader5>(() => managedSymReader);
}
}
private enum InternalErrorCode
{
CantApplyChangesModuleHasBeenUnloaded = 1,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册