@@ -2486,46 +2486,53 @@ process_frame_chain(
24862486 return -1 ;
24872487 }
24882488 }
2489-
2489+ if (frame == NULL && PyList_GET_SIZE (frame_info ) == 0 ) {
2490+ // If the first frame is missing, the chain is broken:
2491+ const char * e = "Failed to parse initial frame in chain" ;
2492+ PyErr_SetString (PyExc_RuntimeError , e );
2493+ return -1 ;
2494+ }
2495+ _Py_DECLARE_STR (native , "<native>" );
2496+ _Py_DECLARE_STR (gc , "<GC>" );
2497+ PyObject * extra_frame = NULL ;
2498+ // This frame kicked off the current GC collection:
24902499 if (unwinder -> gc && frame_addr == gc_frame ) {
2491- _Py_DECLARE_STR (gc , "<GC>" );
2500+ extra_frame = & _Py_STR (gc );
2501+ }
2502+ // Otherwise, check for native frames to insert:
2503+ else if (unwinder -> native ) {
2504+ // Topmost frame spilled its stack pointer for a native call:
2505+ if (PyList_GET_SIZE (frame_info ) == 0 &&
2506+ GET_MEMBER (uintptr_t , frame_addr , unwinder -> debug_offsets .interpreter_frame .stackpointer ))
2507+ {
2508+ extra_frame = & _Py_STR (native );
2509+ }
2510+ // Or, we've reached an interpreter trampoline frame:
2511+ else if (frame == NULL &&
2512+ // Bottommost frame is always native, so skip that one:
2513+ next_frame_addr &&
2514+ // If the next frame will be reported as a GC frame, then
2515+ // don't add an extra native frame below it:
2516+ !(unwinder -> gc && next_frame_addr == gc_frame ))
2517+ {
2518+ extra_frame = & _Py_STR (native );
2519+ }
2520+ }
2521+ if (extra_frame ) {
24922522 // Use "~" as file and 0 as line, since that's what pstats uses:
2493- PyObject * gc_info = make_frame_info (unwinder , _Py_LATIN1_CHR ( '~' ),
2494- _PyLong_GetZero ( ), & _Py_STR ( gc ) );
2495- if (gc_info == NULL ) {
2523+ PyObject * extra_frame_info = make_frame_info (
2524+ unwinder , _Py_LATIN1_CHR ( '~' ), _PyLong_GetZero (), extra_frame );
2525+ if (extra_frame_info == NULL ) {
24962526 return -1 ;
24972527 }
2498- int error = PyList_Append (frame_info , gc_info );
2499- Py_DECREF (gc_info );
2528+ int error = PyList_Append (frame_info , extra_frame_info );
2529+ Py_DECREF (extra_frame_info );
25002530 if (error ) {
2501- const char * e = "Failed to append GC to frame info list" ;
2531+ const char * e = "Failed to append extra frame to frame info list" ;
25022532 set_exception_cause (unwinder , PyExc_RuntimeError , e );
25032533 return -1 ;
25042534 }
25052535 }
2506- if (frame == NULL ) {
2507- if (PyList_GET_SIZE (frame_info ) == 0 ) {
2508- // If the first frame is missing, the chain is broken:
2509- const char * e = "Failed to parse initial frame in chain" ;
2510- PyErr_SetString (PyExc_RuntimeError , e );
2511- return -1 ;
2512- }
2513- if (unwinder -> native &&
2514- // The last frame is always native, so skip that one:
2515- next_frame_addr &&
2516- // If the next frame will be reported as a GC frame, then don't
2517- // add an extra native frame below it:
2518- !(unwinder -> gc && next_frame_addr == gc_frame ))
2519- {
2520- _Py_DECLARE_STR (native , "<native>" );
2521- // Use "~" as file and 0 as line, since that's what pstats uses:
2522- frame = make_frame_info (unwinder , _Py_LATIN1_CHR ('~' ),
2523- _PyLong_GetZero (), & _Py_STR (native ));
2524- if (frame == NULL ) {
2525- return -1 ;
2526- }
2527- }
2528- }
25292536 if (frame ) {
25302537 if (prev_frame_addr && frame_addr != prev_frame_addr ) {
25312538 const char * f = "Broken frame chain: expected frame at 0x%lx, got 0x%lx" ;
0 commit comments