Add ICorDebug metadata locator for clrstack -i failures (#1554)
authorMike McLaughlin <mikem@microsoft.com>
Mon, 14 Sep 2020 21:22:14 +0000 (14:22 -0700)
committerGitHub <noreply@github.com>
Mon, 14 Sep 2020 21:22:14 +0000 (14:22 -0700)
This was also useful when testing the DBI changes to enable this callback for cross-OS DAC.

src/SOS/SOS.Hosting/SOSHost.cs
src/SOS/SOS.NETCore/MetadataHelper.cs
src/SOS/Strike/cordebugdatatarget.h
src/SOS/Strike/datatarget.cpp
src/SOS/Strike/hostcoreclr.cpp
src/SOS/Strike/hostcoreclr.h

index 38b0af2b51b257691184b3ca23db438b5b2561e2..2666f5723561c6d8a105d5c835779096cf89c951 100644 (file)
@@ -123,6 +123,14 @@ namespace SOS
             IntPtr buffer,
             IntPtr dataSize);
 
+        private delegate int GetICorDebugMetadataLocatorDelegate(
+            [MarshalAs(UnmanagedType.LPWStr)] string imagePath,
+            uint imageTimestamp,
+            uint imageSize,
+            uint pathBufferSize,
+            IntPtr pPathBufferSize,
+            IntPtr pPathBuffer);
+
         #endregion
 
         /// <summary>
@@ -143,6 +151,7 @@ namespace SOS
             public GetLocalVariableNameDelegate GetLocalVariableNameDelegate;
             public GetMetadataLocatorDelegate GetMetadataLocatorDelegate;
             public GetExpressionDelegate GetExpressionDelegate;
+            public GetICorDebugMetadataLocatorDelegate GetICorDebugMetadataLocatorDelegate;
         }
 
         static SOSNetCoreCallbacks s_callbacks = new SOSNetCoreCallbacks {
@@ -158,6 +167,7 @@ namespace SOS
             GetLocalVariableNameDelegate = SymbolReader.GetLocalVariableName,
             GetMetadataLocatorDelegate = MetadataHelper.GetMetadataLocator,
             GetExpressionDelegate = SOSHost.GetExpression,
+            GetICorDebugMetadataLocatorDelegate = MetadataHelper.GetICorDebugMetadataLocator
         };
 
         const string DesktopRuntimeModuleName = "clr";
index cb9915253f36771b82ff7582df58cecf6b0bbf08..b3a5a5138dc8d59c2d0e3f70da598b2bf4c000ad 100644 (file)
@@ -2,6 +2,7 @@
 // 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.SymbolStore;
 using Microsoft.SymbolStore.KeyGenerators;
 using System;
@@ -16,55 +17,150 @@ namespace SOS
 {
     public class MetadataHelper
     {
+        const int S_OK = 0;
+        const int E_FAIL = unchecked((int)0x80004005);
+
+        // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) 
+        const int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a);
+
+        /// <summary>
+        /// Metadata locator helper for the DAC.
+        /// </summary>
+        /// <param name="imagePath">file name and path to module</param>
+        /// <param name="imageTimestamp">module timestamp</param>
+        /// <param name="imageSize">module image</param>
+        /// <param name="mvid">not used</param>
+        /// <param name="mdRva">not used</param>
+        /// <param name="flags">not used</param>
+        /// <param name="bufferSize">size of incoming buffer (pMetadata)</param>
+        /// <param name="pMetadata">pointer to buffer</param>
+        /// <param name="pMetadataSize">size of outgoing metadata</param>
+        /// <returns>HRESULT</returns>
         public static int GetMetadataLocator(
             [MarshalAs(UnmanagedType.LPWStr)] string imagePath,
             uint imageTimestamp,
-            uint imageSize, 
-            [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] mvid, 
+            uint imageSize,
+            [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] byte[] mvid,
             uint mdRva,
             uint flags,
             uint bufferSize,
             IntPtr pMetadata,
             IntPtr pMetadataSize)
         {
-            int hr = unchecked((int)0x80004005);
+            int hr = S_OK;
             int dataSize = 0;
 
             Debug.Assert(pMetadata != IntPtr.Zero);
-
-            Stream peStream = null;
-            if (imagePath != null && File.Exists(imagePath))
+            try
             {
-                peStream = SymbolReader.TryOpenFile(imagePath);
-            }
-            else if (SymbolReader.IsSymbolStoreEnabled())
-            {
-                SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
-                peStream = SymbolReader.GetSymbolStoreFile(key)?.Stream;
-            }
-            if (peStream != null)
-            {
-                using (var peReader = new PEReader(peStream, PEStreamOptions.Default))
+                Stream peStream = null;
+                if (imagePath != null && File.Exists(imagePath))
+                {
+                    peStream = SymbolReader.TryOpenFile(imagePath);
+                }
+                else if (SymbolReader.IsSymbolStoreEnabled())
+                {
+                    SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
+                    peStream = SymbolReader.GetSymbolStoreFile(key)?.Stream;
+                }
+                if (peStream != null)
                 {
-                    if (peReader.HasMetadata)
+                    using (var peReader = new PEReader(peStream, PEStreamOptions.Default))
                     {
-                        PEMemoryBlock metadataInfo = peReader.GetMetadata();
-                        unsafe
+                        if (peReader.HasMetadata)
                         {
-                            int size = Math.Min((int)bufferSize, metadataInfo.Length);
-                            Marshal.Copy(metadataInfo.GetContent().ToArray(), 0, pMetadata, size);
+                            PEMemoryBlock metadataInfo = peReader.GetMetadata();
+                            dataSize = metadataInfo.Length;
+                            unsafe
+                            {
+                                int size = Math.Min((int)bufferSize, metadataInfo.Length);
+                                Marshal.Copy(metadataInfo.GetContent().ToArray(), 0, pMetadata, size);
+                            }
+                        }
+                        else
+                        {
+                            hr = E_FAIL;
                         }
-                        dataSize = metadataInfo.Length;
-                        hr = 0;
                     }
                 }
+                else
+                {
+                    hr = E_FAIL;
+                }
+            }
+            catch (Exception ex) when 
+                (ex is UnauthorizedAccessException || 
+                 ex is BadImageFormatException || 
+                 ex is InvalidVirtualAddressException || 
+                 ex is IOException)
+            {
+                hr = E_FAIL;
             }
-
             if (pMetadataSize != IntPtr.Zero)
             {
                 Marshal.WriteInt32(pMetadataSize, dataSize);
             }
             return hr;
         }
+
+        /// <summary>
+        /// Metadata locator helper for the DAC.
+        /// </summary>
+        /// <param name="imagePath">file name and path to module</param>
+        /// <param name="imageTimestamp">module timestamp</param>
+        /// <param name="imageSize">module image</param>
+        /// <param name="localFilePath">local file path of the module</param>
+        /// <returns>HRESULT</returns>
+        public static int GetICorDebugMetadataLocator(
+            [MarshalAs(UnmanagedType.LPWStr)] string imagePath,
+            uint imageTimestamp,
+            uint imageSize,
+            uint pathBufferSize,
+            IntPtr pPathBufferSize,
+            IntPtr pPathBuffer)
+        {
+            int hr = S_OK;
+            int actualSize = 0;
+
+            Debug.Assert(pPathBuffer != IntPtr.Zero);
+            try
+            {
+                if (SymbolReader.IsSymbolStoreEnabled())
+                {
+                    SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
+                    string localFilePath = SymbolReader.GetSymbolFile(key);
+                    localFilePath += "\0";              // null terminate the string
+                    actualSize = localFilePath.Length;
+
+                    if (pathBufferSize > actualSize)
+                    {
+                        Marshal.Copy(localFilePath.ToCharArray(), 0, pPathBuffer, actualSize);
+                    }
+                    else
+                    {
+                        hr = E_INSUFFICIENT_BUFFER;
+                    }
+                }
+                else
+                {
+                    hr = E_FAIL;
+                }
+            }
+            catch (Exception ex) when
+                (ex is UnauthorizedAccessException ||
+                 ex is BadImageFormatException ||
+                 ex is InvalidVirtualAddressException ||
+                 ex is IOException)
+            {
+                hr = E_FAIL;
+            }
+            
+            if (pPathBufferSize != IntPtr.Zero)
+            {
+                Marshal.WriteInt32(pPathBufferSize, actualSize);
+            }
+
+            return hr;
+        }
     }
 }
index 3a9bbd4cd1f94259c9b9837a4e23b996f821588e..069c77e7bce8082d421bb33facd13a2aea16c416 100644 (file)
@@ -175,11 +175,14 @@ public:
 
         // Prepare context structure
         ZeroMemory(context, contextSize);
-        ((CONTEXT*) context)->ContextFlags = contextFlags;
+        ((CONTEXT*)context)->ContextFlags = contextFlags;
 
         // Ok, do it!
         hr = g_ExtAdvanced->GetThreadContext((LPVOID) context, contextSize);
 
+        // GetThreadContext clears ContextFlags but DBI needs it set to know what registers to copy
+        ((CONTEXT*)context)->ContextFlags = contextFlags;
+
         // This is cleanup; failure here doesn't mean GetThreadContext should fail
         // (that's determined by hr).
         g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
@@ -230,7 +233,7 @@ public:
         /* [annotation][length_is][size_is][out] */ 
         _Out_writes_to_(cchPathBuffer, *pcchPathBuffer) WCHAR wszPathBuffer[])
     {
-        return E_NOTIMPL;
+        return ::GetICorDebugMetadataLocator(wszImagePath, dwImageTimeStamp, dwImageSize, cchPathBuffer, pcchPathBuffer, wszPathBuffer);
     }
 
     //
index 30fbce1b5ad80509f973ad6f6bf986c638ffe7ce..e75321dcfcb48f0bf6cab7b30c8abef80ccb7b7d 100644 (file)
@@ -262,6 +262,9 @@ DataTarget::GetThreadContext(
     // Ok, do it!
     hr = g_ExtAdvanced->GetThreadContext((LPVOID) context, contextSize);
 
+    // GetThreadContext clears ContextFlags
+    ((CONTEXT*)context)->ContextFlags = contextFlags;
+
     // This is cleanup; failure here doesn't mean GetThreadContext should fail
     // (that's determined by hr).
     g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
index 847493a1264e297350bf1a4ab80ad13a28da1245..66781efdcea79f6ce3f42db528801a6155eb4892 100644 (file)
@@ -630,6 +630,7 @@ HRESULT InitializeHosting()
     IfFailRet(createDelegate(hostHandle, domainId, SOSManagedDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&g_SOSNetCoreCallbacks.GetLocalVariableNameDelegate));
     IfFailRet(createDelegate(hostHandle, domainId, SOSManagedDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&g_SOSNetCoreCallbacks.GetLineByILOffsetDelegate));
     IfFailRet(createDelegate(hostHandle, domainId, SOSManagedDllName, MetadataHelperClassName, "GetMetadataLocator", (void **)&g_SOSNetCoreCallbacks.GetMetadataLocatorDelegate));
+    IfFailRet(createDelegate(hostHandle, domainId, SOSManagedDllName, MetadataHelperClassName, "GetICorDebugMetadataLocator", (void **)&g_SOSNetCoreCallbacks.GetICorDebugMetadataLocatorDelegate));
 
     g_hostingInitialized = true;
     return Status;
@@ -839,6 +840,24 @@ HRESULT GetMetadataLocator(
     return g_SOSNetCoreCallbacks.GetMetadataLocatorDelegate(imagePath, imageTimestamp, imageSize, mvid, mdRva, flags, bufferSize, buffer, dataSize);
 }
 
+/**********************************************************************\
+ * Returns the metadata from a local or downloaded assembly
+\**********************************************************************/
+HRESULT GetICorDebugMetadataLocator(
+    LPCWSTR imagePath,
+    ULONG32 imageTimestamp,
+    ULONG32 imageSize,
+    ULONG32 cchPathBuffer,
+    ULONG32 *pcchPathBuffer,
+    WCHAR wszPathBuffer[])
+{
+    HRESULT Status = S_OK;
+    IfFailRet(InitializeSymbolStore());
+
+    _ASSERTE(g_SOSNetCoreCallbacks.GetICorDebugMetadataLocatorDelegate != nullptr);
+    return g_SOSNetCoreCallbacks.GetICorDebugMetadataLocatorDelegate(imagePath, imageTimestamp, imageSize, cchPathBuffer, pcchPathBuffer, wszPathBuffer);
+}
+
 #ifndef FEATURE_PAL
 
 /**********************************************************************\
index d0eb31ebf97969540102e3f897ffb6f0d1b8ab5b..ff9b67d15994bfcdda5948daa234f33e624e35c9 100644 (file)
@@ -51,6 +51,15 @@ typedef  BOOL (*GetMetadataLocatorDelegate)(
     unsigned int* pMetadataSize
 );
 
+typedef  BOOL (*GetICorDebugMetadataLocatorDelegate)(
+    LPCWSTR imagePath,
+    unsigned int imageTimestamp,
+    unsigned int imageSize,
+    ULONG32 cchPathBuffer,
+    ULONG32* pcchPathBuffer,
+    WCHAR* pwszPathBuffer
+);
+
 struct SOSNetCoreCallbacks
 {
     InitializeSymbolStoreDelegate InitializeSymbolStoreDelegate;
@@ -65,6 +74,7 @@ struct SOSNetCoreCallbacks
     GetLocalVariableNameDelegate GetLocalVariableNameDelegate;
     GetMetadataLocatorDelegate GetMetadataLocatorDelegate;
     GetExpressionDelegate GetExpressionDelegate;
+    GetICorDebugMetadataLocatorDelegate GetICorDebugMetadataLocatorDelegate;
 };
 
 static const char *SOSManagedDllName = "SOS.NETCore";
@@ -115,6 +125,14 @@ extern HRESULT GetMetadataLocator(
     BYTE* buffer,
     ULONG32* dataSize);
 
+extern HRESULT GetICorDebugMetadataLocator(
+    LPCWSTR imagePath,
+    ULONG32 imageTimestamp,
+    ULONG32 imageSize,
+    ULONG32 cchPathBuffer,
+    ULONG32* pcchPathBuffer,
+    WCHAR wszPathBuffer[]);
+
 class SymbolReader
 {
 private: