Implement source/line number support for Exception.StackTrace. (dotnet/coreclr#5078)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 19 May 2016 17:14:56 +0000 (10:14 -0700)
committerMike McLaughlin <mikem@microsoft.com>
Thu, 19 May 2016 17:14:56 +0000 (10:14 -0700)
* Implement source/line number support for Exception.StackTrace.

Uses a new helper class (StackTraceSymbols) in System.Diagnostics.StackTrace assembly
to get source/line info for portable PDBs. On Windows both types of pdbs are support;
on xplat only portable PDBs.

The first part of the work is in the GetStackFramesInternal pinvoke to return the neccessary
info needed by the portable pdb reader: MethodToken, assembly path, loaded PE image address
and if in-memory symbol file, the address.

The second part is in the StackFrameHelper in mscorlib. It attempts to load the System.Diagnostics.StackTrace,
gets the new type and creates a delegate to the helper function which is called for each stack
frame.

* Code review feedback.

Commit migrated from https://github.com/dotnet/coreclr/commit/76837be7f1216bc0a970b20c30db006c9e6dacbe

src/coreclr/src/mscorlib/src/System/Diagnostics/Stackframe.cs
src/coreclr/src/mscorlib/src/System/Diagnostics/Stacktrace.cs
src/coreclr/src/vm/debugdebugger.cpp
src/coreclr/src/vm/debugdebugger.h
src/coreclr/src/vm/mscorlib.h

index de58e87..397c3e1 100644 (file)
@@ -322,25 +322,26 @@ namespace System.Diagnostics {
     
         private void BuildStackFrame(int skipFrames, bool fNeedFileInfo)
         {
-            StackFrameHelper StackF = new StackFrameHelper(fNeedFileInfo, null);
-    
-            StackTrace.GetStackFramesInternal (StackF, 0, null); 
-    
-            int iNumOfFrames = StackF.GetNumberOfFrames();
-    
-            skipFrames += StackTrace.CalculateFramesToSkip (StackF, iNumOfFrames);
-    
-            if ((iNumOfFrames - skipFrames) > 0)
+            using (StackFrameHelper StackF = new StackFrameHelper(null))
             {
-                method = StackF.GetMethodBase (skipFrames);
-                offset = StackF.GetOffset (skipFrames);
-                ILOffset = StackF.GetILOffset (skipFrames);
-                if (fNeedFileInfo)
+                StackF.InitializeSourceInfo(0, fNeedFileInfo, null);
+
+                int iNumOfFrames = StackF.GetNumberOfFrames();
+
+                skipFrames += StackTrace.CalculateFramesToSkip(StackF, iNumOfFrames);
+
+                if ((iNumOfFrames - skipFrames) > 0)
                 {
-                    strFileName = StackF.GetFilename (skipFrames);
-                    iLineNumber = StackF.GetLineNumber (skipFrames);
-                    iColumnNumber = StackF.GetColumnNumber (skipFrames);
-                }        
+                    method = StackF.GetMethodBase(skipFrames);
+                    offset = StackF.GetOffset(skipFrames);
+                    ILOffset = StackF.GetILOffset(skipFrames);
+                    if (fNeedFileInfo)
+                    {
+                        strFileName = StackF.GetFilename(skipFrames);
+                        iLineNumber = StackF.GetLineNumber(skipFrames);
+                        iColumnNumber = StackF.GetColumnNumber(skipFrames);
+                    }
+                }
             }
         }
     }
index 85ec034..97a8c49 100644 (file)
@@ -3,9 +3,10 @@
 // See the LICENSE file in the project root for more information.
 
 namespace System.Diagnostics {
+    using System;
+    using System.Collections;
     using System.Text;
     using System.Threading;
-    using System;
     using System.Security;
     using System.Security.Permissions;
     using System.IO;
@@ -22,7 +23,7 @@ namespace System.Diagnostics {
     // to the unmanaged definition of the StackFrameHelper class, in 
     // VM\DebugDebugger.h. The binder will catch some of these layout problems.
     [Serializable]
-    internal class StackFrameHelper
+    internal class StackFrameHelper : IDisposable
     {
         [NonSerialized]
         private Thread targetThread;
@@ -39,6 +40,13 @@ namespace System.Diagnostics {
 
         [NonSerialized]
         private IntPtr[] rgMethodHandle;
+        private String[] rgAssemblyPath;
+        private IntPtr[] rgLoadedPeAddress;
+        private int[] rgiLoadedPeSize;
+        private IntPtr[] rgInMemoryPdbAddress;
+        private int[] rgiInMemoryPdbSize;
+        // if rgiMethodToken[i] == 0, then don't attempt to get the portable PDB source/info
+        private int[] rgiMethodToken;
         private String[] rgFilename;
         private int[] rgiLineNumber;
         private int[] rgiColumnNumber;
@@ -46,21 +54,37 @@ namespace System.Diagnostics {
         [OptionalField]
         private bool[] rgiLastFrameFromForeignExceptionStackTrace;
 #endif // FEATURE_EXCEPTIONDISPATCHINFO
+        private GetSourceLineInfoDelegate getSourceLineInfo;
         private int iFrameCount;
-        private bool fNeedFileInfo;
 
+        private delegate void GetSourceLineInfoDelegate(string assemblyPath, IntPtr loadedPeAddress, int loadedPeSize,
+            IntPtr inMemoryPdbAddress, int inMemoryPdbSize, int methodToken, int ilOffset, 
+            out string sourceFile, out int sourceLine, out int sourceColumn);
+
+        private static Type s_symbolsType = null;
+        private static MethodInfo s_symbolsMethodInfo = null;
+
+        [ThreadStatic]
+        private static int t_reentrancy = 0;
         
-        public StackFrameHelper(bool fNeedFileLineColInfo, Thread target)
+        public StackFrameHelper(Thread target)
         {
             targetThread = target;
             rgMethodBase = null;
             rgMethodHandle = null;
+            rgiMethodToken = null;
             rgiOffset = null;
             rgiILOffset = null;
+            rgAssemblyPath = null;
+            rgLoadedPeAddress = null;
+            rgiLoadedPeSize = null;
+            rgInMemoryPdbAddress = null;
+            rgiInMemoryPdbSize = null;
+            dynamicMethods = null;
             rgFilename = null;
             rgiLineNumber = null;
             rgiColumnNumber = null;
-            dynamicMethods = null;
+            getSourceLineInfo = null;
 
 #if FEATURE_EXCEPTIONDISPATCHINFO
             rgiLastFrameFromForeignExceptionStackTrace = null;
@@ -74,10 +98,77 @@ namespace System.Diagnostics {
             // limit in the future, then we should expose it in the managed API so applications can 
             // override it.
             iFrameCount = 0;
+        }
 
-            fNeedFileInfo = fNeedFileLineColInfo;
+        //
+        // Initializes the stack trace helper. If fNeedFileInfo is true, initializes rgFilename, 
+        // rgiLineNumber and rgiColumnNumber fields using the portable PDB reader if not already
+        // done by GetStackFramesInternal (on Windows for old PDB format).
+        //
+        internal void InitializeSourceInfo(int iSkip, bool fNeedFileInfo, Exception exception)
+        {
+            StackTrace.GetStackFramesInternal(this, iSkip, fNeedFileInfo, exception);
+
+            if (!fNeedFileInfo)
+                return;
+
+            // Check if this function is being reentered because of an exception in the code below
+            if (t_reentrancy > 0)
+                return;
+
+            t_reentrancy++;
+            try
+            {
+                if (s_symbolsMethodInfo == null)
+                {
+                    s_symbolsType = Type.GetType("System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
+                    if (s_symbolsType == null)
+                        return;
+
+                    s_symbolsMethodInfo = s_symbolsType.GetMethod("GetSourceLineInfo");
+                    if (s_symbolsMethodInfo == null)
+                        return;
+                }
+
+                if (getSourceLineInfo == null)
+                {
+                    // Create an instance of System.Diagnostics.Stacktrace.Symbols
+                    object target = Activator.CreateInstance(s_symbolsType);
+
+                    // Create an instance delegate for the GetSourceLineInfo method
+                    getSourceLineInfo = (GetSourceLineInfoDelegate)s_symbolsMethodInfo.CreateDelegate(typeof(GetSourceLineInfoDelegate), target);
+                }
+
+                for (int index = 0; index < iFrameCount; index++)
+                {
+                    // If there was some reason not to try get get the symbols from the portable PDB reader like the module was
+                    // ENC or the source/line info was already retrieved, the method token is 0.
+                    if (rgiMethodToken[index] != 0)
+                    {
+                        getSourceLineInfo(rgAssemblyPath[index], rgLoadedPeAddress[index], rgiLoadedPeSize[index], 
+                            rgInMemoryPdbAddress[index], rgiInMemoryPdbSize[index], rgiMethodToken[index], 
+                            rgiILOffset[index], out rgFilename[index], out rgiLineNumber[index], out rgiColumnNumber[index]);
+                    }
+                }
+            }
+            finally
+            {
+                t_reentrancy--;
+            }
         }
-    
+
+        void IDisposable.Dispose()
+        {
+            if (getSourceLineInfo != null)
+            {
+                IDisposable disposable = getSourceLineInfo.Target as IDisposable;
+                if (disposable != null)
+                {
+                    disposable.Dispose();
+                }
+            }
+        }
+
         [System.Security.SecuritySafeCritical]
         public virtual MethodBase GetMethodBase(int i) 
         { 
@@ -98,9 +189,9 @@ namespace System.Diagnostics {
 
         public virtual int GetOffset(int i) { return rgiOffset[i];}
         public virtual int GetILOffset(int i) { return rgiILOffset[i];}
-        public virtual String GetFilename(int i) { return rgFilename[i];}
-        public virtual int GetLineNumber(int i) { return rgiLineNumber[i];}
-        public virtual int GetColumnNumber(int i) { return rgiColumnNumber[i];}
+        public virtual String GetFilename(int i) { return rgFilename == null ? null : rgFilename[i];}
+        public virtual int GetLineNumber(int i) { return rgiLineNumber == null ? 0 : rgiLineNumber[i];}
+        public virtual int GetColumnNumber(int i) { return rgiColumnNumber == null ? 0 : rgiColumnNumber[i];}
 
 #if FEATURE_EXCEPTIONDISPATCHINFO
         public virtual bool IsLastFrameFromForeignExceptionStackTrace(int i) 
@@ -342,11 +433,10 @@ namespace System.Diagnostics {
 
         [System.Security.SecuritySafeCritical]
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal static extern void GetStackFramesInternal(StackFrameHelper sfh, int iSkip, Exception e);
+        internal static extern void GetStackFramesInternal(StackFrameHelper sfh, int iSkip, bool fNeedFileInfo, Exception e);
     
         internal static int CalculateFramesToSkip(StackFrameHelper StackF, int iNumFrames)
         {
-    
             int iRetVal = 0;
             String PackageName = "System.Diagnostics";
     
@@ -376,63 +466,63 @@ namespace System.Diagnostics {
         // Retrieves an object with stack trace information encoded.
         // It leaves out the first "iSkip" lines of the stacktrace.
         //
-        private void CaptureStackTrace(int iSkip, bool fNeedFileInfo, Thread targetThread,
-                                       Exception e)
+        private void CaptureStackTrace(int iSkip, bool fNeedFileInfo, Thread targetThread, Exception e)
         {
             m_iMethodsToSkip += iSkip;
-    
-            StackFrameHelper StackF = new StackFrameHelper(fNeedFileInfo, targetThread);
-    
-            GetStackFramesInternal(StackF, 0, e);
-    
-            m_iNumOfFrames = StackF.GetNumberOfFrames();
-
-            if (m_iMethodsToSkip > m_iNumOfFrames)
-                m_iMethodsToSkip = m_iNumOfFrames;
 
-            if (m_iNumOfFrames != 0)
+            using (StackFrameHelper StackF = new StackFrameHelper(targetThread))
             {
-                frames = new StackFrame[m_iNumOfFrames];
+                StackF.InitializeSourceInfo(0, fNeedFileInfo, e);
 
-                for (int i = 0; i < m_iNumOfFrames; i++)
+                m_iNumOfFrames = StackF.GetNumberOfFrames();
+
+                if (m_iMethodsToSkip > m_iNumOfFrames)
+                    m_iMethodsToSkip = m_iNumOfFrames;
+
+                if (m_iNumOfFrames != 0)
                 {
-                    bool fDummy1 = true;
-                    bool fDummy2 = true;
-                    StackFrame sfTemp = new StackFrame(fDummy1, fDummy2);
+                    frames = new StackFrame[m_iNumOfFrames];
+
+                    for (int i = 0; i < m_iNumOfFrames; i++)
+                    {
+                        bool fDummy1 = true;
+                        bool fDummy2 = true;
+                        StackFrame sfTemp = new StackFrame(fDummy1, fDummy2);
 
-                    sfTemp.SetMethodBase(StackF.GetMethodBase(i));
-                    sfTemp.SetOffset(StackF.GetOffset(i));
-                    sfTemp.SetILOffset(StackF.GetILOffset(i));
+                        sfTemp.SetMethodBase(StackF.GetMethodBase(i));
+                        sfTemp.SetOffset(StackF.GetOffset(i));
+                        sfTemp.SetILOffset(StackF.GetILOffset(i));
 
 #if FEATURE_EXCEPTIONDISPATCHINFO
                     sfTemp.SetIsLastFrameFromForeignExceptionStackTrace(StackF.IsLastFrameFromForeignExceptionStackTrace(i));
 #endif // FEATURE_EXCEPTIONDISPATCHINFO
 
-                    if (fNeedFileInfo)
-                    {
-                        sfTemp.SetFileName(StackF.GetFilename (i));
-                        sfTemp.SetLineNumber(StackF.GetLineNumber(i));
-                        sfTemp.SetColumnNumber(StackF.GetColumnNumber(i));
-                    } 
+                        if (fNeedFileInfo)
+                        {
+                            sfTemp.SetFileName(StackF.GetFilename(i));
+                            sfTemp.SetLineNumber(StackF.GetLineNumber(i));
+                            sfTemp.SetColumnNumber(StackF.GetColumnNumber(i));
+                        }
 
-                    frames[i] = sfTemp;
-                }
+                        frames[i] = sfTemp;
+                    }
 
-                // CalculateFramesToSkip skips all frames in the System.Diagnostics namespace,
-                // but this is not desired if building a stack trace from an exception.
-                if (e == null)
-                    m_iMethodsToSkip += CalculateFramesToSkip(StackF, m_iNumOfFrames);
+                    // CalculateFramesToSkip skips all frames in the System.Diagnostics namespace,
+                    // but this is not desired if building a stack trace from an exception.
+                    if (e == null)
+                        m_iMethodsToSkip += CalculateFramesToSkip(StackF, m_iNumOfFrames);
 
-                m_iNumOfFrames -= m_iMethodsToSkip;
-                if (m_iNumOfFrames < 0)
-                   {
-                       m_iNumOfFrames = 0;
-                   }
-            }
+                    m_iNumOfFrames -= m_iMethodsToSkip;
+                    if (m_iNumOfFrames < 0)
+                    {
+                        m_iNumOfFrames = 0;
+                    }
+                }
 
-            // In case this is the same object being re-used, set frames to null
-            else
-                frames = null;
+                // In case this is the same object being re-used, set frames to null
+                else
+                    frames = null;
+            }
         }
     
         // Property to get the number of frames in the stack trace
index da47fd3..effd16f 100644 (file)
@@ -388,9 +388,10 @@ FCIMPL0(FC_BOOL_RET, DebugDebugger::IsLogging)
 FCIMPLEND
 
 
-FCIMPL3(void, DebugStackTrace::GetStackFramesInternal, 
+FCIMPL4(void, DebugStackTrace::GetStackFramesInternal, 
         StackFrameHelper* pStackFrameHelperUNSAFE, 
         INT32 iSkip, 
+        CLR_BOOL fNeedFileInfo,
         Object* pExceptionUNSAFE
        )
 {    
@@ -423,7 +424,7 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
     if (pException == NULL)
     {
         // Thread is NULL if it's the current thread.
-        data.TargetThread = pStackFrameHelper->TargetThread;
+        data.TargetThread = pStackFrameHelper->targetThread;
         GetStackFrames(NULL, (void*)-1, &data);
     }
     else
@@ -438,53 +439,72 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
 
     if (data.cElements != 0)
     {
-#ifdef FEATURE_COMINTEROP        
-        if (pStackFrameHelper->fNeedFileInfo)
+#if defined(FEATURE_ISYM_READER) && defined(FEATURE_COMINTEROP)
+        if (fNeedFileInfo)
         {
              // Calls to COM up ahead.
             EnsureComStarted();
         }
-#endif // FEATURE_COMINTEROP
-
-        MethodTable *pMT = MscorlibBinder::GetClass(CLASS__METHOD_HANDLE);
-        TypeHandle arrayHandle = ClassLoader::LoadArrayTypeThrowing(TypeHandle(pMT), ELEMENT_TYPE_SZARRAY);
+#endif // FEATURE_ISYM_READER && FEATURE_COMINTEROP
 
         // Allocate memory for the MethodInfo objects
-        BASEARRAYREF MethodInfoArray = (BASEARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I, data.cElements);
-        //printf("\nmethod table = %X\n", pMT);
-
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgMethodHandle), (OBJECTREF)MethodInfoArray,
+        BASEARRAYREF methodInfoArray = (BASEARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgMethodHandle), (OBJECTREF)methodInfoArray,
                             pStackFrameHelper->GetAppDomain());
 
         // Allocate memory for the Offsets 
-        OBJECTREF Offsets = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
-
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiOffset), (OBJECTREF)Offsets,
+        OBJECTREF offsets = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiOffset), (OBJECTREF)offsets,
                             pStackFrameHelper->GetAppDomain());
 
         // Allocate memory for the ILOffsets 
-        OBJECTREF ILOffsets = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        OBJECTREF ilOffsets = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiILOffset), (OBJECTREF)ilOffsets,
+                            pStackFrameHelper->GetAppDomain());
 
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiILOffset), (OBJECTREF)ILOffsets,
+        // Allocate memory for the array of assembly file names
+        PTRARRAYREF assemblyPathArray = (PTRARRAYREF) AllocateObjectArray(data.cElements, g_pStringClass);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgAssemblyPath), (OBJECTREF)assemblyPathArray,
                             pStackFrameHelper->GetAppDomain());
 
-        // if we need Filename, linenumber, etc., then allocate memory for the same
-        // Allocate memory for the Filename string objects
-        PTRARRAYREF FilenameArray = (PTRARRAYREF) AllocateObjectArray(data.cElements, g_pStringClass);
+        // Allocate memory for the LoadedPeAddress
+        BASEARRAYREF loadedPeAddressArray = (BASEARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgLoadedPeAddress), (OBJECTREF)loadedPeAddressArray,
+                            pStackFrameHelper->GetAppDomain());
 
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgFilename), (OBJECTREF)FilenameArray,
+        // Allocate memory for the LoadedPeSize
+        OBJECTREF loadedPeSizeArray = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiLoadedPeSize), (OBJECTREF)loadedPeSizeArray,
                             pStackFrameHelper->GetAppDomain());
 
-        // Allocate memory for the Offsets 
-        OBJECTREF LineNumbers = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        // Allocate memory for the InMemoryPdbAddress
+        BASEARRAYREF inMemoryPdbAddressArray = (BASEARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgInMemoryPdbAddress), (OBJECTREF)inMemoryPdbAddressArray,
+                            pStackFrameHelper->GetAppDomain());
 
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiLineNumber), (OBJECTREF)LineNumbers,
+        // Allocate memory for the InMemoryPdbSize
+        OBJECTREF inMemoryPdbSizeArray = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiInMemoryPdbSize), (OBJECTREF)inMemoryPdbSizeArray,
                             pStackFrameHelper->GetAppDomain());
 
-        // Allocate memory for the ILOffsets 
-        OBJECTREF ColumnNumbers = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        // Allocate memory for the MethodTokens
+        OBJECTREF methodTokens = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiMethodToken), (OBJECTREF)methodTokens,
+                            pStackFrameHelper->GetAppDomain());
+        
+        // Allocate memory for the Filename string objects
+        PTRARRAYREF filenameArray = (PTRARRAYREF) AllocateObjectArray(data.cElements, g_pStringClass);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgFilename), (OBJECTREF)filenameArray,
+                            pStackFrameHelper->GetAppDomain());
 
-        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiColumnNumber), (OBJECTREF)ColumnNumbers,
+        // Allocate memory for the LineNumbers
+        OBJECTREF lineNumbers = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiLineNumber), (OBJECTREF)lineNumbers,
+                            pStackFrameHelper->GetAppDomain());
+
+        // Allocate memory for the ColumnNumbers
+        OBJECTREF columnNumbers = AllocatePrimitiveArray(ELEMENT_TYPE_I4, data.cElements);
+        SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiColumnNumber), (OBJECTREF)columnNumbers,
                             pStackFrameHelper->GetAppDomain());
 
 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
@@ -501,12 +521,12 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
             IsLastFrameFromForeignStackTraceFlags = AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, data.cElements);
 
             SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiLastFrameFromForeignExceptionStackTrace), (OBJECTREF)IsLastFrameFromForeignStackTraceFlags,
-                            pStackFrameHelper->GetAppDomain());
+                                pStackFrameHelper->GetAppDomain());
         }
         else
         {
             SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->rgiLastFrameFromForeignExceptionStackTrace), NULL,
-                            pStackFrameHelper->GetAppDomain());
+                                pStackFrameHelper->GetAppDomain());
         }
 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
 
@@ -530,17 +550,14 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
         
         if (iNumDynamics)
         {            
-            PTRARRAYREF DynamicDataArray = (PTRARRAYREF) AllocateObjectArray(iNumDynamics, g_pObjectClass);
-            
-            SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->dynamicMethods), (OBJECTREF)DynamicDataArray,
+            PTRARRAYREF dynamicDataArray = (PTRARRAYREF) AllocateObjectArray(iNumDynamics, g_pObjectClass);
+            SetObjectReference( (OBJECTREF *)&(pStackFrameHelper->dynamicMethods), (OBJECTREF)dynamicDataArray,
                                 pStackFrameHelper->GetAppDomain());
         }
         
         int iNumValidFrames = 0;
-        for (int i=0; i<data.cElements; i++)
+        for (int i = 0; i < data.cElements; i++)
         {
-            size_t *pElem = (size_t*)pStackFrameHelper->rgMethodHandle->GetDataPtr();
-
             // The managed stacktrace classes always returns typical method definition, so we don't need to bother providing exact instantiation.
             // Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation(data.pElements[i].pFunc, data.pElements[i].pExactGenericArgsToken, &pExactMethod, &thExactType);
             MethodDesc* pFunc = data.pElements[i].pFunc;
@@ -550,17 +567,17 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
                 pFunc = pFunc->StripMethodInstantiation();
             _ASSERTE(pFunc->IsRuntimeMethodHandle());
 
+            // Method handle
+            size_t *pElem = (size_t*)pStackFrameHelper->rgMethodHandle->GetDataPtr();
             pElem[iNumValidFrames] = (size_t)pFunc;
 
-            // native offset
-            I4 *pI4 = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiOffset)
-                                        ->GetDirectPointerToNonObjectElements();
-            pI4 [iNumValidFrames] = data.pElements[i].dwOffset; 
+            // Native offset
+            I4 *pI4 = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiOffset)->GetDirectPointerToNonObjectElements();
+            pI4[iNumValidFrames] = data.pElements[i].dwOffset;
 
             // IL offset
-            I4 *pILI4 = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiILOffset)
-                                        ->GetDirectPointerToNonObjectElements();
-            pILI4 [iNumValidFrames] = data.pElements[i].dwILOffset; 
+            I4 *pILI4 = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiILOffset)->GetDirectPointerToNonObjectElements();
+            pILI4[iNumValidFrames] = data.pElements[i].dwILOffset;
 
 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
             if (data.fDoWeHaveAnyFramesFromForeignStackTrace)
@@ -572,7 +589,6 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
             }
 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
 
-            BOOL fFileInfoSet = FALSE;
             MethodDesc *pMethod = data.pElements[i].pFunc;
 
             // If there are any dynamic methods, and this one is one of them, store
@@ -585,18 +601,16 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
                     OBJECTREF pResolver = pDMD->GetLCGMethodResolver()->GetManagedResolver();
                     _ASSERTE(pResolver != NULL);
                     
-                    ((PTRARRAYREF)pStackFrameHelper->dynamicMethods)->SetAt (iCurDynamic++, pResolver);
+                    ((PTRARRAYREF)pStackFrameHelper->dynamicMethods)->SetAt(iCurDynamic++, pResolver);
                 }
-                else
-                if (pMethod->GetMethodTable()->Collectible())
+                else if (pMethod->GetMethodTable()->Collectible())
                 {
                     OBJECTREF pLoaderAllocator = pMethod->GetMethodTable()->GetLoaderAllocator()->GetExposedObject();
                     _ASSERTE(pLoaderAllocator != NULL);
-                    ((PTRARRAYREF)pStackFrameHelper->dynamicMethods)->SetAt (iCurDynamic++, pLoaderAllocator);
+                    ((PTRARRAYREF)pStackFrameHelper->dynamicMethods)->SetAt(iCurDynamic++, pLoaderAllocator);
                 }
             }
 
-#ifdef FEATURE_ISYM_READER
             Module *pModule = pMethod->GetModule();
 
             // If it's an EnC method, then don't give back any line info, b/c the PDB is out of date.
@@ -610,23 +624,23 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
 #ifdef EnC_SUPPORTED                
             if (pModule->IsEditAndContinueEnabled())
             {
-                EditAndContinueModule *eacm = (EditAndContinueModule *) pModule;
+                EditAndContinueModule *eacm = (EditAndContinueModule *)pModule;
                 if (eacm->GetApplyChangesCount() != 1)
                 {
-                    fIsEnc = true;        
-                }                    
+                    fIsEnc = true;
+                }
             }
 #endif
-            
-            // check if the user wants the filenumber, linenumber info...
-            if (!fIsEnc && pStackFrameHelper->fNeedFileInfo)
+            BOOL fFileInfoSet = FALSE;
+
+#ifdef FEATURE_ISYM_READER
+            // Check if the user wants the filenumber, linenumber info...
+            if (!fIsEnc && fNeedFileInfo)
             {
-                // Use the MethodDesc...
                 ULONG32 sourceLine = 0;
                 ULONG32 sourceColumn = 0;
                 WCHAR wszFileName[MAX_LONGPATH];
                 ULONG32 fileNameLength = 0;
-
                 {
                     // Note: we need to enable preemptive GC when accessing the unmanages symbol store.
                     GCX_PREEMP();
@@ -749,7 +763,6 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
                                         hr = documents [j]->GetURL (MAX_LONGPATH, &fileNameLength, wszFileName);
                                         _ASSERTE ( SUCCEEDED(hr) || (hr == E_OUTOFMEMORY) || (hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)) );
 
-
                                         // indicate that the requisite information has been set!
                                         fFileInfoSet = TRUE;
 
@@ -770,16 +783,14 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
 
                 } // GCX_PREEMP()
                 
-                if (fFileInfoSet == TRUE)
+                if (fFileInfoSet)
                 {
                     // Set the line and column numbers
-                    I4 *pI4Line = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiLineNumber)
-                        ->GetDirectPointerToNonObjectElements();
-                    I4 *pI4Column = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiColumnNumber)
-                        ->GetDirectPointerToNonObjectElements();
+                    I4 *pI4Line = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiLineNumber)->GetDirectPointerToNonObjectElements();
+                    pI4Line[iNumValidFrames] = sourceLine;  
 
-                    pI4Line [iNumValidFrames] = sourceLine;  
-                    pI4Column [iNumValidFrames] = sourceColumn;  
+                    I4 *pI4Column = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiColumnNumber)->GetDirectPointerToNonObjectElements();
+                    pI4Column[iNumValidFrames] = sourceColumn;  
 
                     // Set the file name
                     OBJECTREF obj = (OBJECTREF) StringObject::NewString(wszFileName);
@@ -788,34 +799,57 @@ FCIMPL3(void, DebugStackTrace::GetStackFramesInternal,
             }
 #endif // FEATURE_ISYM_READER
 
-            if (fFileInfoSet == FALSE)
+            // If the above isym reader code did NOT set the source info either because it is ifdef'ed out (on xplat)
+            // or because the pdb is the new portable format on Windows then set the information needed to called the
+            // portable pdb reader in the StackTraceHelper. The source/line info isn't valid on ENC'ed modules.
+            if (!fFileInfoSet && !fIsEnc)
             {
-                I4 *pI4Line = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiLineNumber)
-                                            ->GetDirectPointerToNonObjectElements();
-                I4 *pI4Column = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiColumnNumber)
-                                            ->GetDirectPointerToNonObjectElements();
-                pI4Line [iNumValidFrames] = 0;
-                pI4Column [iNumValidFrames] = 0;
+                // Save MethodToken for the function
+                I4 *pMethodToken = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiMethodToken)->GetDirectPointerToNonObjectElements();
+                pMethodToken[iNumValidFrames] = pMethod->GetMemberDef();
 
-                pStackFrameHelper->rgFilename->SetAt(iNumValidFrames, NULL);
-                
+                PEFile *pPEFile = pModule->GetFile();
+
+                // Get the address and size of the loaded PE image
+                COUNT_T peSize;
+                PTR_CVOID peAddress = pPEFile->GetLoadedImageContents(&peSize);
+
+                // Save the PE address and size
+                PTR_CVOID *pLoadedPeAddress = (PTR_CVOID *)pStackFrameHelper->rgLoadedPeAddress->GetDataPtr();
+                pLoadedPeAddress[iNumValidFrames] = peAddress;
+
+                I4 *pLoadedPeSize = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiLoadedPeSize)->GetDirectPointerToNonObjectElements();
+                pLoadedPeSize[iNumValidFrames] = (I4)peSize;
+
+                // If there is a in memory symbol stream
+                CGrowableStream* stream = pModule->GetInMemorySymbolStream();
+                if (stream != NULL)
+                {
+                    MemoryRange range = stream->GetRawBuffer();
+
+                    // Save the in-memory PDB address and size
+                    PTR_VOID *pInMemoryPdbAddress = (PTR_VOID *)pStackFrameHelper->rgInMemoryPdbAddress->GetDataPtr();
+                    pInMemoryPdbAddress[iNumValidFrames] = range.StartAddress();
+
+                    I4 *pInMemoryPdbSize = (I4 *)((I4ARRAYREF)pStackFrameHelper->rgiInMemoryPdbSize)->GetDirectPointerToNonObjectElements();
+                    pInMemoryPdbSize[iNumValidFrames] = (I4)range.Size();
+                }
+                else
+                {
+                    // Set the pdb path (assembly file name)
+                    const SString& assemblyPath = pPEFile->GetPath();
+                    if (!assemblyPath.IsEmpty())
+                    {
+                        OBJECTREF obj = (OBJECTREF)StringObject::NewString(assemblyPath);
+                        pStackFrameHelper->rgAssemblyPath->SetAt(iNumValidFrames, obj);
+                    }
+                }
             }
 
             iNumValidFrames++;
         }
 
         pStackFrameHelper->iFrameCount = iNumValidFrames;
-
-        /*
-        int *pArray = (int*)OBJECTREFToObject(pStackFrameHelper->rgMethodHandle);
-        printf("array { MT - %X, size = %d", pArray[0], pArray[1]);
-        for (int i=0; i<pArray[1]; i++)
-        {
-            printf(", method desc in array[%d] = %X", i, pArray[i + 2]);
-        }
-        printf("}\n");
-        */
-
     }
     else
     {
index 57fce04..9cf5c3c 100644 (file)
@@ -69,26 +69,35 @@ protected:
 
 
 
-class StackFrameHelper:public Object
+class StackFrameHelper : public Object
 {
     // READ ME:
     // Modifying the order or fields of this object may require other changes to the
     // classlib defintion of the StackFrameHelper class.
 public:
-    THREADBASEREF TargetThread;
+    THREADBASEREF targetThread;
     I4ARRAYREF rgiOffset;
     I4ARRAYREF rgiILOffset;
     BASEARRAYREF rgMethodBase; 
     PTRARRAYREF dynamicMethods;    
     BASEARRAYREF rgMethodHandle; 
+    PTRARRAYREF rgAssemblyPath;
+    BASEARRAYREF rgLoadedPeAddress;
+    I4ARRAYREF rgiLoadedPeSize;
+    BASEARRAYREF rgInMemoryPdbAddress;
+    I4ARRAYREF rgiInMemoryPdbSize;
+    // if rgiMethodToken[i] == 0, then don't attempt to get the portable PDB source/info
+    I4ARRAYREF rgiMethodToken;
     PTRARRAYREF rgFilename;
     I4ARRAYREF rgiLineNumber;
     I4ARRAYREF rgiColumnNumber;
+
 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
     BOOLARRAYREF rgiLastFrameFromForeignExceptionStackTrace;
 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
+
+    OBJECTREF getSourceLineInfo;
     int iFrameCount;
-    CLR_BOOL fNeedFileInfo;
 
 protected:
     StackFrameHelper() {}
@@ -187,10 +196,11 @@ public:
         }
     };
 
-    static FCDECL3(void, 
+    static FCDECL4(void, 
                    GetStackFramesInternal, 
                    StackFrameHelper* pStackFrameHelper, 
                    INT32 iSkip, 
+                   CLR_BOOL fNeedFileInfo,
                    Object* pException
                   );
 
index 9620627..5100aae 100644 (file)
@@ -1474,21 +1474,27 @@ DEFINE_CLASS(STACK_BUILDER_SINK,    Messaging,              StackBuilderSink)
 DEFINE_METHOD(STACK_BUILDER_SINK,   PRIVATE_PROCESS_MESSAGE,_PrivateProcessMessage,     IM_IntPtr_ArrObj_Obj_RefArrObj_RetObj)
 #endif
 
-DEFINE_CLASS_U(Diagnostics,            StackFrameHelper,           StackFrameHelper)
-DEFINE_FIELD_U(targetThread,               StackFrameHelper,   TargetThread)
+DEFINE_CLASS_U(Diagnostics,                StackFrameHelper,   StackFrameHelper)
+DEFINE_FIELD_U(targetThread,               StackFrameHelper,   targetThread)
 DEFINE_FIELD_U(rgiOffset,                  StackFrameHelper,   rgiOffset)
 DEFINE_FIELD_U(rgiILOffset,                StackFrameHelper,   rgiILOffset)
 DEFINE_FIELD_U(rgMethodBase,               StackFrameHelper,   rgMethodBase)
 DEFINE_FIELD_U(dynamicMethods,             StackFrameHelper,   dynamicMethods)
 DEFINE_FIELD_U(rgMethodHandle,             StackFrameHelper,   rgMethodHandle)
+DEFINE_FIELD_U(rgAssemblyPath,             StackFrameHelper,   rgAssemblyPath)
+DEFINE_FIELD_U(rgLoadedPeAddress,          StackFrameHelper,   rgLoadedPeAddress)
+DEFINE_FIELD_U(rgiLoadedPeSize,            StackFrameHelper,   rgiLoadedPeSize)
+DEFINE_FIELD_U(rgInMemoryPdbAddress,       StackFrameHelper,   rgInMemoryPdbAddress)
+DEFINE_FIELD_U(rgiInMemoryPdbSize,         StackFrameHelper,   rgiInMemoryPdbSize)
+DEFINE_FIELD_U(rgiMethodToken,             StackFrameHelper,   rgiMethodToken)
 DEFINE_FIELD_U(rgFilename,                 StackFrameHelper,   rgFilename)
 DEFINE_FIELD_U(rgiLineNumber,              StackFrameHelper,   rgiLineNumber)
 DEFINE_FIELD_U(rgiColumnNumber,            StackFrameHelper,   rgiColumnNumber)
 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
 DEFINE_FIELD_U(rgiLastFrameFromForeignExceptionStackTrace,            StackFrameHelper,   rgiLastFrameFromForeignExceptionStackTrace)
 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
+DEFINE_FIELD_U(getSourceLineInfo,          StackFrameHelper,   getSourceLineInfo)
 DEFINE_FIELD_U(iFrameCount,                StackFrameHelper,   iFrameCount)
-DEFINE_FIELD_U(fNeedFileInfo,              StackFrameHelper,   fNeedFileInfo)
 
 DEFINE_CLASS(STACK_TRACE,           Diagnostics,            StackTrace)
 DEFINE_METHOD(STACK_TRACE,          GET_MANAGED_STACK_TRACE_HELPER, GetManagedStackTraceStringHelper, SM_Bool_RetStr)