@@ -494,6 +494,58 @@ void ControllerStackInfo::GetStackInfo(
494494 _ASSERTE (!ISREDIRECTEDTHREAD (thread));
495495 }
496496
497+ #ifdef FEATURE_INTERPRETER
498+ // When hitting interpreter breakpoints, InterpBreakpoint creates a synthetic context
499+ // with SP pointing to InterpMethodContextFrame instead of native stack.
500+ //
501+ // The interpreter context has:
502+ // - IP = bytecode address (interpreter IR)
503+ // - SP = InterpMethodContextFrame pointer
504+ // - First arg register = InterpreterFrame pointer
505+ //
506+ // The stack walker's interpreter frame handling (stackwalk.cpp:2413-2468) expects to
507+ // DISCOVER the InterpreterFrame during the walk, saving the native context before
508+ // switching to interpreter frame enumeration. When we start with a synthetic context,
509+ // the "native context" being saved is actually the synthetic interpreter context,
510+ // which has invalid values for native stack restoration.
511+ //
512+ // Fix: When we detect an interpreter synthetic context, temporarily clear the filter
513+ // context and mark context as invalid so the stack walker starts from the thread's
514+ // frame chain. The InterpreterFrame will be discovered naturally and handled correctly.
515+ CONTEXT *pSavedFilterContext = NULL ;
516+ bool fRestoredFilterContext = false ;
517+ if (contextValid)
518+ {
519+ PCODE ip = GetIP (pContext);
520+ EECodeInfo codeInfo (ip);
521+ if (codeInfo.IsValid () && codeInfo.IsInterpretedCode ())
522+ {
523+ TADDR interpreterFrameAddr = GetFirstArgReg (pContext);
524+ if (interpreterFrameAddr != 0 )
525+ {
526+ Frame *pFrame = (Frame*)interpreterFrameAddr;
527+ if (pFrame != FRAME_TOP && pFrame->GetFrameIdentifier () == FrameIdentifier::InterpreterFrame)
528+ {
529+ LOG ((LF_CORDB, LL_INFO10000, " CSI::GSI: Interpreter synthetic context detected - IP=%p, SP=%p, InterpreterFrame=%p. Using frame chain instead.\n " ,
530+ (void *)ip, (void *)GetSP (pContext), (void *)interpreterFrameAddr));
531+
532+ // Temporarily clear the filter context so the stack walker uses the thread's
533+ // frame chain. The InterpreterFrame is on the frame chain and will be found
534+ // and processed correctly, with proper native context saving/restoration.
535+ pSavedFilterContext = g_pEEInterface->GetThreadFilterContext (thread);
536+ if (pSavedFilterContext != NULL )
537+ {
538+ g_pEEInterface->SetThreadFilterContext (thread, NULL );
539+ fRestoredFilterContext = true ;
540+ }
541+ contextValid = FALSE ;
542+ pContext = &this ->m_tempContext ;
543+ }
544+ }
545+ }
546+ }
547+ #endif // FEATURE_INTERPRETER
548+
497549 // Mark this stackwalk as valid so that it can in turn be used to grandfather
498550 // in other stackwalks.
499551 INDEBUG (m_dbgExecuted = true );
@@ -514,6 +566,14 @@ void ControllerStackInfo::GetStackInfo(
514566 (void *) this ,
515567 FALSE );
516568
569+ #ifdef FEATURE_INTERPRETER
570+ // Restore the filter context if we temporarily cleared it for interpreter stack walk
571+ if (fRestoredFilterContext )
572+ {
573+ g_pEEInterface->SetThreadFilterContext (thread, pSavedFilterContext);
574+ }
575+ #endif // FEATURE_INTERPRETER
576+
517577 _ASSERTE (m_activeFound); // All threads have at least one unmanaged frame
518578
519579 if (result == SWA_DONE)
0 commit comments