Skip to content

gh-149584: _remote_debugging: Stop rereading the base frame sentinel - #159124

Open
maurycy wants to merge 1 commit into
python:mainfrom
maurycy:remote-debugging-base-frame
Open

maurycy wants to merge 1 commit into
python:mainfrom
maurycy:remote-debugging-base-frame

Conversation

@maurycy

@maurycy maurycy commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Basically, process_frame_chain() reads frames until previous is NULL:

parse_result = parse_frame_object(
unwinder, &frame, frame_addr,
&address_of_code_object, &next_frame_addr);
}

The last one is always tstate->base_frame that is the sentinel set up once per thread with previous == NULL and owner == FRAME_OWNED_BY_INTERPRETER:

cpython/Python/pystate.c

Lines 1589 to 1599 in 565f76f

_tstate->base_frame.previous = NULL;
_tstate->base_frame.f_executable = PyStackRef_None;
_tstate->base_frame.f_funcobj = PyStackRef_NULL;
_tstate->base_frame.f_globals = NULL;
_tstate->base_frame.f_builtins = NULL;
_tstate->base_frame.f_locals = NULL;
_tstate->base_frame.frame_obj = NULL;
_tstate->base_frame.instr_ptr = NULL;
_tstate->base_frame.stackpointer = _tstate->base_frame.localsplus;
_tstate->base_frame.return_offset = 0;
_tstate->base_frame.owner = FRAME_OWNED_BY_INTERPRETER;

Its address is already in the thread state buffer:

uintptr_t base_frame_addr = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.base_frame);

so the remote read is not needed.

Tools/inspection/oracle_external_inspection.py with --runs 3, PGO+LTO, Ubuntu 24.04, AMD EPYC 4484PX, main at 182f323 vs the PR, on the same box one after another:

case mode main (182f323) 182f323 + patch
flat_alternating live-cache 8.04±0.03 us 7.13±0.02 us -11.3%
gen_alternating live-cache 7.95±0.02 us 7.36±0.03 us -7.4%
flat_alternating blocking-cache 14.59±0.06 us 13.73±0.02 us -5.9%
gen_alternating blocking-cache 15.11±0.05 us 14.31±0.09 us -5.3%
basic live-nocache 7.14±0.01 us 6.05±0.05 us -15.3%
code_object_reuse live-nocache 7.46±0.00 us 6.36±0.01 us -14.7%
flat_alternating live-nocache 7.45±0.01 us 6.37±0.01 us -14.5%
shared_leaf live-nocache 7.42±0.01 us 6.35±0.01 us -14.4%
nested_alternating live-nocache 7.45±0.01 us 6.47±0.09 us -13.2%
deep_static live-nocache 30.93±0.81 us 29.29±0.17 us -5.3%
basic blocking-nocache 17.55±0.08 us 16.63±0.03 us -5.2%
basic live-cache 3.16±0.01 us 3.28±0.05 us +3.8%
deep_static live-cache 5.59±0.07 us 5.82±0.08 us +4.1%
shared_leaf live-cache 3.36±0.01 us 3.52±0.01 us +4.8%

I'm not exactly sure why basic or deep_static are worse here. My guess it's just PGO noise.

main (`182f323`)
maurycy@taut cpython-A (182f323) % ./python ./Tools/inspection/oracle_external_inspection.py --runs 3
3.16.0a0 (tags/v3.15.0b1-2187-g182f323154:182f323154, Oct 10 2026, 12:34:48) [GCC 13.3.0]
cases=async_running_task,asyncio,basic,code_object_reuse,deep_recursion,deep_static,flat_alternating,gen_alternating,nested_alternating,oversized_chunk,shared_leaf,threads runs=3 duration=3.0 rate_khz=100.0 warmup=0.7 poisson_sampling=False

async_running_task (get_async_stack_trace) ref_keys=47
mode                 samples    stacks     empty           µs      errors% impossible  impossible%
blocking-nocache      370014    740028      3733   21.74±0.03    0.00±0.00          0    0.00±0.00
live-nocache          473351    946654      2591   16.29±0.04    2.01±0.36      20791    2.20±0.50

asyncio (get_async_stack_trace) ref_keys=92
mode                 samples    stacks     empty           µs      errors%
blocking-nocache      453715    564504      1720   18.45±0.22    0.00±0.00
live-nocache          550817    647128      2595   15.01±1.02    0.51±0.17

basic (get_stack_trace) ref_keys=31
mode                 samples           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      479886   17.55±0.08    0.00±0.00          ref          ref          -
blocking-cache        618009   13.36±0.06    0.00±0.00  0.001±0.000  0.000±0.000      0.000
live-cache            899992    3.16±0.01    0.00±0.00  0.010±0.001  0.009±0.001      0.000
live-nocache          899993    7.14±0.01    0.00±0.00  0.011±0.001  0.011±0.001      0.000

code_object_reuse (get_stack_trace) ref_keys=18
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      573963   14.00±0.03    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        773891    9.95±0.01    0.00±0.00          0    0.00±0.00  0.001±0.000 -0.000±0.000      0.001
live-cache            899996    3.36±0.01    0.00±0.00          0    0.00±0.00  0.405±0.007  0.404±0.007      0.001
live-nocache          899991    7.46±0.00    0.00±0.00          0    0.00±0.00  0.005±0.000  0.004±0.000      0.001

deep_recursion (get_stack_trace) ref_keys=737
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      222480   19.69±0.22    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        215715   20.80±0.26    0.00±0.00          0    0.00±0.00  0.050±0.014  0.034±0.013      0.016
live-cache             96744  62.16±14.76   23.14±4.85      26342   28.18±7.20  0.754±0.044  0.731±0.046      0.022
live-nocache          142593  48.35±36.21   12.05±1.11      18435   14.94±7.70  0.802±0.022  0.782±0.026      0.019

deep_static (get_stack_trace) ref_keys=1
mode                 samples           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache       56214   43.02±2.50    0.00±0.00          ref          ref          -
blocking-cache         65313   16.96±0.87    0.00±0.00  0.000±0.000  0.000±0.000      0.000
live-cache             76858    5.59±0.07    0.00±0.00  0.000±0.000  0.000±0.000      0.000
live-nocache           62809   30.93±0.81    0.00±0.00  0.000±0.000  0.000±0.000      0.000

flat_alternating (get_stack_trace) ref_keys=6
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      573954   13.98±0.05    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        552072   14.59±0.06    0.00±0.00          0    0.00±0.00  0.085±0.031  0.083±0.031      0.002
live-cache            899990    8.04±0.03    0.00±0.00       4228    0.47±0.03  0.069±0.004  0.067±0.004      0.002
live-nocache          899996    7.45±0.01    0.00±0.00       6388    0.71±0.07  0.136±0.015  0.135±0.015      0.002

gen_alternating (get_stack_trace) ref_keys=19
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      558000   14.79±0.06    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        545789   15.11±0.05    0.00±0.00          0    0.00±0.00  0.034±0.007  0.030±0.007      0.004
live-cache            713190    7.95±0.02   20.76±1.35     154784   21.67±3.13  0.282±0.043  0.278±0.043      0.004
live-nocache          723803    7.83±0.05   19.58±1.13     336208   46.43±4.08  0.488±0.100  0.483±0.099      0.005

nested_alternating (get_stack_trace) ref_keys=14
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      546604   14.08±0.03    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        724309   10.10±0.01    0.00±0.00          0    0.00±0.00  0.020±0.016  0.018±0.016      0.002
live-cache            899995    3.54±0.02    0.00±0.00          0    0.00±0.00  0.063±0.049  0.061±0.049      0.002
live-nocache          899997    7.45±0.01    0.00±0.00         11    0.00±0.00  0.067±0.038  0.065±0.038      0.002

oversized_chunk (get_stack_trace) ref_keys=17
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      563868   14.25±0.19    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        709827   10.92±0.01    0.00±0.00          0    0.00±0.00  0.026±0.014  0.023±0.014      0.002
live-cache            899991    4.71±0.00    0.00±0.00      12734    1.41±1.48  0.076±0.001  0.073±0.001      0.002
live-nocache          899991    7.62±0.05    0.00±0.00          0    0.00±0.00  0.109±0.006  0.107±0.006      0.002

shared_leaf (get_stack_trace) ref_keys=13
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      575934   14.03±0.05    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        779477    9.98±0.02    0.00±0.00          0    0.00±0.00  0.011±0.009  0.009±0.009      0.001
live-cache            899990    3.36±0.01    0.00±0.00        464    0.05±0.02  0.056±0.009  0.055±0.009      0.001
live-nocache          899996    7.42±0.01    0.00±0.00          0    0.00±0.00  0.044±0.028  0.043±0.028      0.001

threads (get_stack_trace) ref_keys=14
mode                 samples    stacks           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache       47805    544044 187.12±59.02    0.00±0.00          ref          ref          -
blocking-cache         66936    809486 128.93±34.61    0.00±0.00  0.033±0.008  0.032±0.008      0.001
live-cache            321457   2789805   21.69±7.90    0.00±0.00  0.055±0.030  0.054±0.030      0.001
live-nocache          124833   1209809  66.76±20.70    0.00±0.00  0.047±0.003  0.046±0.003      0.001
PR (`182f323` + patch)
maurycy@taut cpython-B (182f323*) % ./python ./Tools/inspection/oracle_external_inspection.py --runs 3
3.16.0a0 (tags/v3.15.0b1-2187-g182f323154-dirty:182f323154, Oct 10 2026, 14:15:59) [GCC 13.3.0]
cases=async_running_task,asyncio,basic,code_object_reuse,deep_recursion,deep_static,flat_alternating,gen_alternating,nested_alternating,oversized_chunk,shared_leaf,threads runs=3 duration=3.0 rate_khz=100.0 warmup=0.7 poisson_sampling=False

async_running_task (get_async_stack_trace) ref_keys=46
mode                 samples    stacks     empty           µs      errors% impossible  impossible%
blocking-nocache      348511    697022      3379   23.11±0.11    0.00±0.00          0    0.00±0.00
live-nocache          460515    921009      3251   16.70±0.05    1.92±0.17      14318    1.56±0.31

asyncio (get_async_stack_trace) ref_keys=95
mode                 samples    stacks     empty           µs      errors%
blocking-nocache      443576    553293      1683   18.90±0.10    0.00±0.00
live-nocache          594948    700929      2211   13.79±0.52    0.40±0.04

basic (get_stack_trace) ref_keys=32
mode                 samples           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      504897   16.63±0.03    0.00±0.00          ref          ref          -
blocking-cache        617583   13.39±0.14    0.00±0.00  0.001±0.000  0.001±0.000      0.000
live-cache            899992    3.28±0.05    0.00±0.00  0.012±0.000  0.012±0.000      0.000
live-nocache          899995    6.05±0.05    0.00±0.00  0.013±0.000  0.013±0.000      0.000

code_object_reuse (get_stack_trace) ref_keys=22
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      620199   12.81±0.22    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        767626   10.07±0.25    0.00±0.00          0    0.00±0.00  0.001±0.000 -0.001±0.000      0.001
live-cache            899994    3.47±0.06    0.00±0.00          0    0.00±0.00  0.349±0.025  0.347±0.025      0.001
live-nocache          899991    6.36±0.01    0.00±0.00          0    0.00±0.00  0.005±0.000  0.004±0.000      0.001

deep_recursion (get_stack_trace) ref_keys=769
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      228284   18.90±0.24    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        220124   20.12±0.41    0.00±0.00          0    0.00±0.00  0.036±0.010  0.020±0.010      0.016
live-cache             83004   71.18±6.10   25.86±5.92      31325   38.01±2.84  0.693±0.046  0.666±0.048      0.028
live-nocache          192223  28.96±16.09    7.58±3.68      16518    9.73±6.10  0.850±0.019  0.832±0.017      0.017

deep_static (get_stack_trace) ref_keys=1
mode                 samples           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache       56808   42.14±2.09    0.00±0.00          ref          ref          -
blocking-cache         65545   17.24±0.94    0.00±0.00  0.000±0.000  0.000±0.000      0.000
live-cache             76141    5.82±0.08    0.00±0.00  0.000±0.000  0.000±0.000      0.000
live-nocache           63351   29.29±0.17    0.00±0.00  0.000±0.000  0.000±0.000      0.000

flat_alternating (get_stack_trace) ref_keys=6
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      614231   12.92±0.03    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        582285   13.73±0.02    0.00±0.00          0    0.00±0.00  0.024±0.005  0.021±0.005      0.002
live-cache            899991    7.13±0.02    0.00±0.00       4204    0.47±0.14  0.077±0.013  0.076±0.013      0.002
live-nocache          899999    6.37±0.01    0.00±0.00       5548    0.62±0.04  0.111±0.017  0.110±0.017      0.002

gen_alternating (get_stack_trace) ref_keys=19
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      590747   13.84±0.05    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        573287   14.31±0.09    0.00±0.00          0    0.00±0.00  0.036±0.011  0.032±0.011      0.004
live-cache            724332    7.36±0.03   19.52±0.68     154090   21.29±4.24  0.287±0.025  0.283±0.025      0.004
live-nocache          725428    7.02±0.03   19.40±0.96     372262   51.34±4.62  0.638±0.059  0.633±0.059      0.005

nested_alternating (get_stack_trace) ref_keys=12
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      584517   13.01±0.01    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        714155   10.23±0.03    0.00±0.00          0    0.00±0.00  0.030±0.006  0.028±0.006      0.002
live-cache            899992    3.65±0.01    0.00±0.00         30    0.00±0.01  0.037±0.040  0.035±0.040      0.002
live-nocache          899998    6.47±0.09    0.00±0.00          4    0.00±0.00  0.067±0.030  0.065±0.030      0.002

oversized_chunk (get_stack_trace) ref_keys=17
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      608009   13.03±0.04    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        714629   10.83±0.09    0.00±0.00          0    0.00±0.00  0.024±0.011  0.021±0.011      0.002
live-cache            899993    4.59±0.01    0.00±0.00      24505    2.72±0.28  0.066±0.013  0.064±0.013      0.002
live-nocache          899989    6.52±0.01    0.00±0.00          1    0.00±0.00  0.095±0.010  0.092±0.010      0.002

shared_leaf (get_stack_trace) ref_keys=10
mode                 samples           µs      errors% impossible  impossible%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache      617170   12.96±0.02    0.00±0.00          0    0.00±0.00          ref          ref          -
blocking-cache        767359   10.12±0.02    0.00±0.00          0    0.00±0.00  0.032±0.024  0.031±0.024      0.001
live-cache            899993    3.52±0.01    0.00±0.00        685    0.08±0.06  0.071±0.047  0.070±0.047      0.001
live-nocache          899991    6.35±0.01    0.00±0.00          0    0.00±0.00  0.063±0.006  0.062±0.006      0.001

threads (get_stack_trace) ref_keys=8
mode                 samples    stacks           µs      errors%      raw_tvd   tvd_excess  tvd_floor
blocking-nocache       62150    576377 159.09±86.15    0.00±0.00          ref          ref          -
blocking-cache         76231    810134 113.85±35.56    0.00±0.00  0.035±0.037  0.034±0.037      0.001
live-cache            318426   2714810   19.70±2.44    0.00±0.00  0.042±0.017  0.041±0.017      0.001
live-nocache          127896   1394382   60.32±9.68    0.00±0.00  0.064±0.023  0.063±0.022      0.001

@maurycy
maurycy requested a review from pablogsal as a code owner October 10, 2026 15:17
@maurycy maurycy changed the title gh-149584: _remote_debugging: Stop reading the base frame sentinel on every sample gh-149584: _remote_debugging: Stop reading the base frame sentinel Oct 10, 2026
@maurycy maurycy changed the title gh-149584: _remote_debugging: Stop reading the base frame sentinel gh-149584: _remote_debugging: Stop rereading the base frame sentinel Oct 10, 2026
Comment on lines +329 to +330
if (frame_addr == ctx->base_frame_addr
&& !(unwinder->gc && frame_addr == ctx->gc_frame)) {

@maurycy maurycy Oct 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionally:

Suggested change
if (frame_addr == ctx->base_frame_addr
&& !(unwinder->gc && frame_addr == ctx->gc_frame)) {
// Skip the base frame sentinel unless we need to emit a <GC> marker
// because GC started with no Python frames on this thread.
if (frame_addr == ctx->base_frame_addr
&& !(unwinder->gc && frame_addr == ctx->gc_frame)) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant