diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs index 3e9d722d98eff..b7cc20df0b54a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs @@ -166,7 +166,10 @@ internal void InitializeSourceInfo(int iSkip, bool fNeedFileInfo, Exception? exc if (mh == IntPtr.Zero) return null; - IRuntimeMethodInfo? mhReal = RuntimeMethodHandle.GetTypicalMethodDefinition(new RuntimeMethodInfoStub(mh, this)); + IRuntimeMethodInfo? mhReal = + LocalAppContextSwitches.ShowGenericInstantiations + ? RuntimeMethodHandle.FromIntPtr(mh).GetMethodInfo() + : RuntimeMethodHandle.GetTypicalMethodDefinition(new RuntimeMethodInfoStub(mh, this)); return RuntimeType.GetMethodBase(mhReal); } diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index a6bdd075afc48..80342cae692d0 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -391,13 +391,15 @@ FCIMPL4(void, DebugStackTrace::GetStackFramesInternal, int iNumValidFrames = 0; for (int i = 0; i < data.cElements; i++) { - // 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; - // Strip the instantiation to make sure that the reflection never gets a bad method desc back. - if (pFunc->HasMethodInstantiation()) + // We need to strip the instantiations if the method desc is shared by generic instantiations + // to make sure the reflection never gets a bad method desc back. + // i.e. a method desc has System.__Canon in its instantiations + if (pFunc->HasClassOrMethodInstantiation() && pFunc->IsSharedByGenericInstantiations()) + { pFunc = pFunc->StripMethodInstantiation(); + } _ASSERTE(pFunc->IsRuntimeMethodHandle()); // Method handle diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index d206fb419fd75..789514e4bc07b 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1659,7 +1659,7 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT { pcfThisFrame->isFrameless = true; pcfThisFrame->pFunc = pcfThisFrame->codeInfo.GetMethodDesc(); - + pcfThisFrame->InitializeExactGenericInstantiations(); *puMethodStartPC = pcfThisFrame->codeInfo.GetStartAddress(); } else @@ -7583,7 +7583,7 @@ extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack e #if _DEBUG EECodeInfo codeInfo(ip); _ASSERTE(codeInfo.IsValid()); - _ASSERTE(pMD == codeInfo.GetMethodDesc()); + _ASSERTE(pMD->GetMemberDef() == codeInfo.GetMethodDesc()->GetMemberDef()); #endif // _DEBUG pExInfo->m_StackTraceInfo.AppendElement(canAllocateMemory, ip, sp, pMD, &pExInfo->m_frameIter.m_crawl); diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 507eb4122a2e2..e0232af367c2b 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -565,7 +565,7 @@ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRe { CONTRACT(InstantiatedMethodDesc *) { - THROWS; + NOTHROW; GC_NOTRIGGER; FORBID_FAULT; PRECONDITION(CheckPointer(pExactOrRepMT)); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 6bde54108a042..c6a549d3eb292 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7779,21 +7779,28 @@ static void getMethodInfoHelper( if (methInfo->options & CORINFO_GENERICS_CTXT_MASK) { -#if defined(PROFILING_SUPPORTED) - BOOL fProfilerRequiresGenericsContextForEnterLeave = FALSE; - { - BEGIN_PROFILER_CALLBACK(CORProfilerPresent()); - if ((&g_profControlBlock)->RequiresGenericsContextForEnterLeave()) - { - fProfilerRequiresGenericsContextForEnterLeave = TRUE; - } - END_PROFILER_CALLBACK(); - } - if (fProfilerRequiresGenericsContextForEnterLeave) + if (ftn->IsJitOptimizationDisabled()) { methInfo->options = CorInfoOptions(methInfo->options|CORINFO_GENERICS_CTXT_KEEP_ALIVE); } + else + { +#if defined(PROFILING_SUPPORTED) + BOOL fProfilerRequiresGenericsContextForEnterLeave = FALSE; + { + BEGIN_PROFILER_CALLBACK(CORProfilerPresent()); + if ((&g_profControlBlock)->RequiresGenericsContextForEnterLeave()) + { + fProfilerRequiresGenericsContextForEnterLeave = TRUE; + } + END_PROFILER_CALLBACK(); + } + if (fProfilerRequiresGenericsContextForEnterLeave) + { + methInfo->options = CorInfoOptions(methInfo->options|CORINFO_GENERICS_CTXT_KEEP_ALIVE); + } #endif // defined(PROFILING_SUPPORTED) + } } PCCOR_SIGNATURE pSig = NULL; diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 56e76cdf4949d..a14205976c8e4 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -290,6 +290,45 @@ PTR_VOID CrawlFrame::GetExactGenericArgsToken() } } +void CrawlFrame::InitializeExactGenericInstantiations() +{ + CONTRACTL { + SUPPORTS_DAC; + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + // Turn into cooperative GC mode + GCX_COOP(); + + if (pFunc != NULL && pFunc->HasClassOrMethodInstantiation() && pFunc->IsSharedByGenericInstantiations()) + { + // Get exact instantiations for shared generics where possible + PTR_VOID pExactGenericArgsToken = GetExactGenericArgsToken(); + + if (pExactGenericArgsToken != NULL) + { + TypeHandle th; + MethodDesc* pConstructedFunc = NULL; + if (Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pFunc, pExactGenericArgsToken, &th, &pConstructedFunc)) + { +#ifndef DACCESS_COMPILE + if (pConstructedFunc->IsSharedByGenericInstantiations()) + { + InstantiatedMethodDesc *pInstMD = InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(th.GetMethodTable(), pConstructedFunc->GetMemberDef(), Instantiation(), false); + + if (pInstMD != NULL) + { + pConstructedFunc = pInstMD; + } + } +#endif // !DACCESS_COMPILE + pFunc = pConstructedFunc; + } + } + } +} + /* Is this frame at a safe spot for GC? */ bool CrawlFrame::IsGcSafe() @@ -3025,6 +3064,7 @@ void StackFrameIterator::ProcessCurrentFrame(void) #endif // FEATURE_EH_FUNCLETS m_crawl.pFunc = m_crawl.codeInfo.GetMethodDesc(); + m_crawl.InitializeExactGenericInstantiations(); // Cache values which may be updated by CheckForSkippedFrames() m_cachedCodeInfo = m_crawl.codeInfo; diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index ac37c6679e83c..65951dfdf956a 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -144,6 +144,7 @@ class CrawlFrame These together carry the exact instantiation information. */ PTR_VOID GetExactGenericArgsToken(); + void InitializeExactGenericInstantiations(); inline CodeManState * GetCodeManState() { LIMITED_METHOD_DAC_CONTRACT; return & codeManState; } /* diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs index 67ba8fd483eb1..533e70894c0f1 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackFrameTests.cs @@ -116,8 +116,16 @@ public static IEnumerable ToString_TestData() yield return new object[] { new StackFrame(), "MoveNext at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; yield return new object[] { new StackFrame("FileName", 1, 2), "MoveNext at offset {offset} in file:line:column FileName:1:2" + Environment.NewLine }; yield return new object[] { new StackFrame(int.MaxValue), "" + Environment.NewLine }; - yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; - yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; + if (AppContext.TryGetSwitch("Switch.System.Diagnostics.StackTrace.ShowGenericInstantiations", out var showGenericInstantiations) && showGenericInstantiations) + { + yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; + yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; + } + else + { + yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; + yield return new object[] { GenericMethod(), "GenericMethod at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; + } yield return new object[] { new ClassWithConstructor().StackFrame, ".ctor at offset {offset} in file:line:column {fileName}:{lineNumber}:{column}" + Environment.NewLine }; } @@ -133,17 +141,17 @@ public void ToString_Invoke_ReturnsExpected(StackFrame stackFrame, string expect Assert.Equal(expectedToString, stackFrame.ToString()); } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static StackFrame GenericMethod() => new StackFrame(); - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static StackFrame GenericMethod() => new StackFrame(); private class ClassWithConstructor { public StackFrame StackFrame { get; } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public ClassWithConstructor() => StackFrame = new StackFrame(); } diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index 93bdf3840bbd1..c4053a8fadc12 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -285,8 +285,16 @@ public static IEnumerable ToString_TestData() yield return new object[] { NoParameters(), "System.Diagnostics.Tests.StackTraceTests.NoParameters()" }; yield return new object[] { OneParameter(1), "System.Diagnostics.Tests.StackTraceTests.OneParameter(Int32 x)" }; yield return new object[] { TwoParameters(1, null), "System.Diagnostics.Tests.StackTraceTests.TwoParameters(Int32 x, String y)" }; - yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[T]()" }; - yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[T,U]()" }; + if (AppContext.TryGetSwitch("Switch.System.Diagnostics.StackTrace.ShowGenericInstantiations", out var showGenericInstantiations) && showGenericInstantiations) + { + yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[System.Int32]()" }; + yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[System.Int32,System.String]()" }; + } + else + { + yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[T]()" }; + yield return new object[] { Generic(), "System.Diagnostics.Tests.StackTraceTests.Generic[T,U]()" }; + } yield return new object[] { new ClassWithConstructor().StackTrace, "System.Diagnostics.Tests.StackTraceTests.ClassWithConstructor..ctor()" }; // Methods belonging to the System.Diagnostics namespace are ignored. diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs index ff5463bb4cbe1..e5c4b163300cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs @@ -222,7 +222,12 @@ public override string ToString() else fFirstTyParam = false; - sb.Append(typars[k].Name); + string typeName = typars[k].ToString(); + for (int i = 0; i < typeName.Length; i++) + { + char ch = typeName[i]; + sb.Append(ch == '+' ? '.' : ch); + } k++; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index e71e88411f04a..90f9432d1b193 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -249,10 +249,10 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) if (declaringType != null) { // Append t.FullName, replacing '+' with '.' - string fullName = declaringType.FullName!; - for (int i = 0; i < fullName.Length; i++) + string fullNameWithoutAssemblyInfo = declaringType.ToString(); + for (int i = 0; i < fullNameWithoutAssemblyInfo.Length; i++) { - char ch = fullName[i]; + char ch = fullNameWithoutAssemblyInfo[i]; sb.Append(ch == '+' ? '.' : ch); } sb.Append('.'); @@ -273,7 +273,12 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) else fFirstTyParam = false; - sb.Append(typars[k].Name); + string typeName = typars[k].ToString(); + for (int i = 0; i < typeName.Length; i++) + { + char ch = typeName[i]; + sb.Append(ch == '+' ? '.' : ch); + } k++; } sb.Append(']'); diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs index 1e6e67af54bdc..b799490c1e857 100644 --- a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs +++ b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs @@ -80,5 +80,24 @@ public static bool ShowILOffsets [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetDefaultShowILOffsetSetting(); } + + private static int s_showGenericInstantiations; + private static bool GetDefaultShowGenericInstantiationsSetting() + { + if (s_showGenericInstantiations < 0) return false; + if (s_showGenericInstantiations > 0) return true; + + // Disabled by default. + bool isSwitchEnabled = AppContextConfigHelper.GetBooleanConfig("Switch.System.Diagnostics.StackTrace.ShowGenericInstantiations", false); + s_showGenericInstantiations = isSwitchEnabled ? 1 : -1; + + return isSwitchEnabled; + } + + public static bool ShowGenericInstantiations + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetDefaultShowGenericInstantiationsSetting(); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Environment.StackTrace.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Environment.StackTrace.cs index 255d03483505f..af3bc8f295ee8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Environment.StackTrace.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Environment.StackTrace.cs @@ -23,7 +23,13 @@ public void StackTraceTest() { "System.Tests.EnvironmentStackTrace.StaticFrame(Object obj)", "System.Tests.EnvironmentStackTrace.TestClass..ctor()", - "System.Tests.EnvironmentStackTrace.GenericFrame[T1,T2](T1 t1, T2 t2)", + AppContext.TryGetSwitch("Switch.System.Diagnostics.StackTrace.ShowGenericInstantiations", out var showGenericInstantiations) + ? + showGenericInstantiations + ? "System.Tests.EnvironmentStackTrace.GenericFrame[System.DateTime,System.Text.StringBuilder](DateTime t1, StringBuilder t2)" + : "System.Tests.EnvironmentStackTrace.GenericFrame[T1,T2](T1 t1, T2 t2)" + : "System.Tests.EnvironmentStackTrace.GenericFrame[T1,T2](T1 t1, T2 t2)" + , "System.Tests.EnvironmentStackTrace.TestFrame()" }; @@ -40,19 +46,19 @@ public void StackTraceTest() } } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private void TestFrame() { GenericFrame(DateTime.Now, null); } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private void GenericFrame(T1 t1, T2 t2) { new TestClass(); } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static void StaticFrame(object obj) { s_stackTrace = Environment.StackTrace; @@ -60,7 +66,7 @@ private static void StaticFrame(object obj) private class TestClass { - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public TestClass() { StaticFrame(null);