Fix stack trace population to get proper source/line info for tier 1 methods (#16302)
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Mon, 12 Feb 2018 00:57:09 +0000 (16:57 -0800)
committerNoah Falk <noahfalk@users.noreply.github.com>
Mon, 12 Feb 2018 00:57:08 +0000 (16:57 -0800)
Fixes https://github.com/dotnet/coreclr/issues/16224

src/debug/ee/debugger.cpp
src/debug/ee/debugger.h
src/debug/ee/functioninfo.cpp
src/vm/codeman.cpp
src/vm/codeman.h
src/vm/eedbginterface.h
src/vm/eedbginterfaceimpl.cpp
src/vm/eedbginterfaceimpl.h
tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.cs [new file with mode: 0644]
tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.csproj [new file with mode: 0644]

index 94792da..c5b8a63 100644 (file)
@@ -14139,6 +14139,9 @@ bool Debugger::GetILOffsetFromNative (MethodDesc *pFunc, const BYTE *pbAddr,
     }
     CONTRACTL_END;
 
+    _ASSERTE(pFunc != NULL);
+    _ASSERTE(pbAddr != NULL);
+
     if (!HasLazyData())
     {
         DebuggerLockHolder dbgLockHolder(this);
@@ -14152,23 +14155,36 @@ bool Debugger::GetILOffsetFromNative (MethodDesc *pFunc, const BYTE *pbAddr,
         pFunc = pFunc->GetWrappedMethodDesc();
     }
 
-    DebuggerJitInfo *jitInfo =
-            GetJitInfo(pFunc, (const BYTE *)pbAddr);
+    if (pFunc->IsDynamicMethod())
+    {
+        return false;
+    }
 
-    if (jitInfo != NULL)
+    DebuggerMethodInfo *methodInfo = GetOrCreateMethodInfo(pFunc->GetModule(), pFunc->GetMemberDef());
+    if (methodInfo == NULL)
     {
-        CorDebugMappingResult map;
-        DWORD whichIDontCare;
+        return false;
+    }
 
-        *ilOffset = jitInfo->MapNativeOffsetToIL(
-                                        nativeOffset,
-                                        &map,
-                                        &whichIDontCare);
+    PCODE methodStartAddress = g_pEEInterface->GetNativeCodeStartAddress((PCODE)pbAddr);
+    if (methodStartAddress == NULL)
+    {
+        return false;
+    }
 
-        return true;
+    DebuggerJitInfo *jitInfo = methodInfo->FindOrCreateInitAndAddJitInfo(pFunc, methodStartAddress);
+    if (jitInfo == NULL)
+    {
+        return false;
     }
 
-    return false;
+    CorDebugMappingResult map;
+    DWORD whichIDontCare;
+    *ilOffset = jitInfo->MapNativeOffsetToIL(
+                                    nativeOffset,
+                                    &map,
+                                    &whichIDontCare);
+    return true;
 }
 
 /******************************************************************************
index 25e4682..6ba4e69 100644 (file)
@@ -1010,7 +1010,7 @@ public:
     DebuggerJitInfo * FindJitInfo(MethodDesc * pMD, TADDR addrNativeStartAddr);
 
     // Creating the Jit-infos.
-    DebuggerJitInfo *FindOrCreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr);
+    DebuggerJitInfo *FindOrCreateInitAndAddJitInfo(MethodDesc* fd, PCODE startAddr);
     DebuggerJitInfo *CreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr, BOOL* jitInfoWasCreated);
 
 
index fc443f9..d452111 100644 (file)
@@ -1548,7 +1548,7 @@ DebuggerJitInfo * DebuggerMethodInfo::FindJitInfo(MethodDesc * pMD,
  *
  */
 
-DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr)
+DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* fd, PCODE startAddr)
 {
     CONTRACTL
     {
@@ -1569,7 +1569,7 @@ DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* f
     if (startAddr == NULL)
     {
         // This will grab the start address for the current code version.
-        startAddr = (TADDR)g_pEEInterface->GetFunctionAddress(fd);
+        startAddr = g_pEEInterface->GetFunctionAddress(fd);
         if (startAddr == NULL)
         {
             return NULL;
@@ -1577,7 +1577,7 @@ DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* f
     }
     else
     {
-        _ASSERTE(g_pEEInterface->GetNativeCodeMethodDesc((PCODE)startAddr) == fd);
+        _ASSERTE(g_pEEInterface->GetNativeCodeMethodDesc(startAddr) == fd);
     }
 
     // Check the lsit to see if we've already populated an entry for this JitInfo.
index b3ff114..ec5313f 100644 (file)
@@ -4156,6 +4156,19 @@ ExecutionManager::FindCodeRangeWithLock(PCODE currentPC)
     return GetRangeSection(currentPC);
 }
 
+
+//**************************************************************************
+PCODE ExecutionManager::GetCodeStartAddress(PCODE currentPC)
+{
+    WRAPPER_NO_CONTRACT;
+    _ASSERTE(currentPC != NULL);
+
+    EECodeInfo codeInfo(currentPC);
+    if (!codeInfo.IsValid())
+        return NULL;
+    return (PCODE)codeInfo.GetStartAddress();
+}
+
 //**************************************************************************
 MethodDesc * ExecutionManager::GetCodeMethodDesc(PCODE currentPC)
 {
index 983e2ca..0663d59 100644 (file)
@@ -1245,6 +1245,9 @@ public:
     // Special version with profiler hook
     static BOOL IsManagedCode(PCODE currentPC, HostCallPreference hostCallPreference, BOOL *pfFailedReaderLock);
 
+    // Returns method's start address for a given PC
+    static PCODE GetCodeStartAddress(PCODE currentPC);
+
     // Returns methodDesc for given PC
     static MethodDesc * GetCodeMethodDesc(PCODE currentPC);
 
index 241ef33..3c5bdc8 100644 (file)
@@ -135,6 +135,8 @@ public:
 
 #endif // #ifndef DACCESS_COMPILE
 
+    virtual PCODE GetNativeCodeStartAddress(PCODE address) = 0;
+
     virtual MethodDesc *GetNativeCodeMethodDesc(const PCODE address) = 0;
 
 #ifndef DACCESS_COMPILE
index 976e6b6..69fe3bd 100644 (file)
@@ -469,6 +469,14 @@ BOOL EEDbgInterfaceImpl::IsManagedNativeCode(const BYTE *address)
     return ExecutionManager::IsManagedCode((PCODE)address);
 }
 
+PCODE EEDbgInterfaceImpl::GetNativeCodeStartAddress(PCODE address)
+{
+    WRAPPER_NO_CONTRACT;
+    _ASSERTE(address != NULL);
+
+    return ExecutionManager::GetCodeStartAddress(address);
+}
+
 MethodDesc *EEDbgInterfaceImpl::GetNativeCodeMethodDesc(const PCODE address)
 { 
     CONTRACT(MethodDesc *)
index 7451246..7172e9a 100644 (file)
@@ -122,6 +122,8 @@ public:
 
     BOOL IsManagedNativeCode(const BYTE *address);
 
+    PCODE GetNativeCodeStartAddress(PCODE address) DAC_UNEXPECTED();
+
     MethodDesc *GetNativeCodeMethodDesc(const PCODE address) DAC_UNEXPECTED();
 
 #ifndef USE_GC_INFO_DECODER
diff --git a/tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.cs b/tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.cs
new file mode 100644 (file)
index 0000000..e5eba33
--- /dev/null
@@ -0,0 +1,58 @@
+// 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.Runtime.CompilerServices;
+using System.Threading;
+
+internal static class Program
+{
+    private static int Main()
+    {
+        const int Pass = 100, Fail = 1;
+
+        string tier0StackTrace = Capture(true);
+        PromoteToTier1(() => Capture(false));
+        string tier1StackTrace = Capture(true);
+        return tier0StackTrace == tier1StackTrace ? Pass : Fail;
+    }
+
+    private static void PromoteToTier1(Action action)
+    {
+        // Call the method once to register a call for call counting
+        action();
+
+        // Allow time for call counting to begin
+        Thread.Sleep(500);
+
+        // Call the method enough times to trigger tier 1 promotion
+        for (int i = 0; i < 100; i++)
+        {
+            action();
+        }
+
+        // Allow time for the method to be jitted at tier 1
+        Thread.Sleep(500);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static string Capture(bool doWork)
+    {
+        if (!doWork)
+        {
+            return null;
+        }
+
+        string stackTrace = new StackTrace(true).ToString().Trim();
+
+        // Remove the last line of the stack trace, which would correspond with Main()
+        int lastNewLineIndex = stackTrace.LastIndexOf('\n');
+        if (lastNewLineIndex == -1)
+        {
+            return null;
+        }
+        return stackTrace.Substring(0, lastNewLineIndex).Trim();
+    }
+}
diff --git a/tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.csproj b/tests/src/baseservices/exceptions/stacktrace/Tier1StackTrace.csproj
new file mode 100644 (file)
index 0000000..c426338
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{8758BFAC-7D36-4244-8A36-4C464C0AFA6D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <LangVersion>latest</LangVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <Optimize>true</Optimize>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="Tier1StackTrace.cs" />
+  </ItemGroup>
+  <PropertyGroup>
+    <CLRTestBatchPreCommands><![CDATA[
+$(CLRTestBatchPreCommands)
+set COMPlus_EXPERIMENTAL_TieredCompilation=1
+]]></CLRTestBatchPreCommands>
+    <BashCLRTestPreCommands><![CDATA[
+$(BashCLRTestPreCommands)
+export COMPlus_EXPERIMENTAL_TieredCompilation=1
+]]></BashCLRTestPreCommands>
+  </PropertyGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>