Skip to content

Commit 728c3a2

Browse files
committed
gh-115946: Add a new interface for stack retrieval for external profilers and debuggers
1 parent 0782499 commit 728c3a2

File tree

5 files changed

+35
-1
lines changed

5 files changed

+35
-1
lines changed

Include/cpython/pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ struct _ts {
7373
pycore_ceval.h. */
7474
uintptr_t eval_breaker;
7575

76+
Py_ssize_t n_eval_frames;
77+
Py_ssize_t eval_stack_size;
78+
PyObject** eval_stack;
79+
7680
struct {
7781
/* Has been initialized to a safe state.
7882

Include/internal/pycore_frame.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {
9999
f->stacktop++;
100100
}
101101

102+
static inline void _PyFrame_PushState(PyThreadState *tstate, _PyInterpreterFrame *f) {
103+
if (tstate->n_eval_frames >= tstate->eval_stack_size) {
104+
tstate->eval_stack_size *= 2;
105+
tstate->eval_stack = PyMem_RawRealloc(tstate->eval_stack, tstate->eval_stack_size * sizeof(PyObject*));
106+
if (tstate->eval_stack == NULL) {
107+
Py_FatalError("Failed to allocate eval stack");
108+
}
109+
}
110+
tstate->eval_stack[tstate->n_eval_frames] = f->f_executable;
111+
tstate->n_eval_frames += 1;
112+
}
113+
102114
#define FRAME_SPECIALS_SIZE ((int)((sizeof(_PyInterpreterFrame)-1)/sizeof(PyObject *)))
103115

104116
static inline int
@@ -270,6 +282,7 @@ _PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func, int null_l
270282
tstate->datastack_top += code->co_framesize;
271283
assert(tstate->datastack_top < tstate->datastack_limit);
272284
_PyFrame_Initialize(new_frame, func, NULL, code, null_locals_from);
285+
_PyFrame_PushState(tstate, new_frame);
273286
return new_frame;
274287
}
275288

@@ -294,6 +307,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
294307
frame->instr_ptr = _PyCode_CODE(code);
295308
frame->owner = FRAME_OWNED_BY_THREAD;
296309
frame->return_offset = 0;
310+
_PyFrame_PushState(tstate, frame);
297311
return frame;
298312
}
299313

Python/ceval.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,8 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
17011701
void
17021702
_PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
17031703
{
1704+
tstate->n_eval_frames -= 1;
1705+
assert(tstate->n_eval_frames >= 0);
17041706
if (frame->owner == FRAME_OWNED_BY_THREAD) {
17051707
clear_thread_frame(tstate, frame);
17061708
}
@@ -1722,6 +1724,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
17221724
goto fail;
17231725
}
17241726
_PyFrame_Initialize(frame, func, locals, code, 0);
1727+
_PyFrame_PushState(tstate, frame);
17251728
if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) {
17261729
assert(frame->owner == FRAME_OWNED_BY_THREAD);
17271730
clear_thread_frame(tstate, frame);

Python/pystate.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,9 @@ init_threadstate(_PyThreadStateImpl *_tstate,
13621362
}
13631363

13641364
tstate->_status.initialized = 1;
1365+
tstate->n_eval_frames = 0;
1366+
tstate->eval_stack_size = tstate->py_recursion_limit * 2;
1367+
tstate->eval_stack = NULL;
13651368
}
13661369

13671370
static void
@@ -1428,7 +1431,6 @@ new_threadstate(PyInterpreterState *interp, int whence)
14281431

14291432
init_threadstate(tstate, interp, id, whence);
14301433
add_threadstate(interp, (PyThreadState *)tstate, old_head);
1431-
14321434
HEAD_UNLOCK(runtime);
14331435
if (!used_newtstate) {
14341436
// Must be called with lock unlocked to avoid re-entrancy deadlock.
@@ -1440,6 +1442,15 @@ new_threadstate(PyInterpreterState *interp, int whence)
14401442
_Py_qsbr_register(tstate, interp, qsbr_idx);
14411443
#endif
14421444

1445+
// Initialize eval stack
1446+
PyThreadState* result = (PyThreadState*)(tstate);
1447+
if (result && result->eval_stack == NULL) {
1448+
result->eval_stack = PyMem_RawMalloc(result->py_recursion_limit * sizeof(PyObject*));
1449+
if (result->eval_stack == NULL) {
1450+
PyMem_RawFree(result);
1451+
return NULL;
1452+
}
1453+
}
14431454
return (PyThreadState *)tstate;
14441455
}
14451456

@@ -1650,6 +1661,8 @@ tstate_delete_common(PyThreadState *tstate)
16501661
clear_datastack(tstate);
16511662

16521663
tstate->_status.finalized = 1;
1664+
tstate->n_eval_frames = -1;
1665+
PyMem_RawFree(tstate->eval_stack);
16531666
}
16541667

16551668
static void

Python/stdlib_module_names.h.new

Whitespace-only changes.

0 commit comments

Comments
 (0)