Skip to content

Commit 81b29a0

Browse files
committed
pythongh-91048: Refactor common data into context object in Modukes/_remote_debugging
1 parent 568a819 commit 81b29a0

File tree

5 files changed

+172
-164
lines changed

5 files changed

+172
-164
lines changed

Modules/_remote_debugging/_remote_debugging.h

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,38 @@ typedef struct {
279279
size_t count;
280280
} StackChunkList;
281281

282+
/*
283+
* Context for frame chain traversal operations.
284+
* Stack-allocated, passed by pointer to frame walking functions.
285+
* Groups related parameters that are commonly passed together.
286+
*/
287+
typedef struct {
288+
/* Inputs */
289+
uintptr_t frame_addr; // Starting frame address
290+
uintptr_t base_frame_addr; // Sentinel at bottom (for validation)
291+
uintptr_t gc_frame; // GC frame address (0 if not tracking)
292+
uintptr_t last_profiled_frame; // Last cached frame (0 if no cache)
293+
StackChunkList *chunks; // Pre-copied stack chunks
294+
295+
/* Outputs */
296+
PyObject *frame_info; // List to append FrameInfo objects
297+
uintptr_t *frame_addrs; // Array of visited frame addresses
298+
Py_ssize_t num_addrs; // Count of addresses collected
299+
Py_ssize_t max_addrs; // Capacity of frame_addrs array
300+
uintptr_t last_frame_visited; // Last frame address visited
301+
int stopped_at_cached_frame; // Whether we stopped at cached frame
302+
} FrameWalkContext;
303+
304+
/*
305+
* Context for code object parsing.
306+
* Groups parameters needed to resolve a code object from a frame.
307+
*/
308+
typedef struct {
309+
uintptr_t code_addr; // Code object address in remote process
310+
uintptr_t instruction_pointer; // Current instruction pointer
311+
int32_t tlbc_index; // Thread-local bytecode index (free-threading)
312+
} CodeObjectContext;
313+
282314
/* Function pointer types for iteration callbacks */
283315
typedef int (*thread_processor_func)(
284316
RemoteUnwinderObject *unwinder,
@@ -343,10 +375,7 @@ extern long read_py_long(RemoteUnwinderObject *unwinder, uintptr_t address);
343375
extern int parse_code_object(
344376
RemoteUnwinderObject *unwinder,
345377
PyObject **result,
346-
uintptr_t address,
347-
uintptr_t instruction_pointer,
348-
uintptr_t *previous_frame,
349-
int32_t tlbc_index
378+
const CodeObjectContext *ctx
350379
);
351380

352381
extern PyObject *make_location_info(
@@ -420,17 +449,7 @@ extern void *find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr);
420449

421450
extern int process_frame_chain(
422451
RemoteUnwinderObject *unwinder,
423-
uintptr_t initial_frame_addr,
424-
StackChunkList *chunks,
425-
PyObject *frame_info,
426-
uintptr_t base_frame_addr,
427-
uintptr_t gc_frame,
428-
uintptr_t last_profiled_frame,
429-
int *stopped_at_cached_frame,
430-
uintptr_t *frame_addrs,
431-
Py_ssize_t *num_addrs,
432-
Py_ssize_t max_addrs,
433-
uintptr_t *out_last_frame_addr
452+
FrameWalkContext *ctx
434453
);
435454

436455
/* Frame cache functions */
@@ -460,12 +479,7 @@ extern int frame_cache_store(
460479

461480
extern int collect_frames_with_cache(
462481
RemoteUnwinderObject *unwinder,
463-
uintptr_t frame_addr,
464-
StackChunkList *chunks,
465-
PyObject *frame_info,
466-
uintptr_t base_frame_addr,
467-
uintptr_t gc_frame,
468-
uintptr_t last_profiled_frame,
482+
FrameWalkContext *ctx,
469483
uint64_t thread_id);
470484

471485
/* ============================================================================

Modules/_remote_debugging/code_objects.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t
7676
PyErr_SetString(PyExc_RuntimeError, "TLBC array size exceeds maximum limit");
7777
return 0; // Invalid size
7878
}
79+
assert(tlbc_size > 0 && tlbc_size <= MAX_TLBC_SIZE);
7980

8081
// Allocate and read the entire TLBC array
8182
size_t array_data_size = tlbc_size * sizeof(void*);
@@ -156,8 +157,11 @@ parse_linetable(const uintptr_t addrq, const char* linetable, int firstlineno, L
156157
const uint8_t* ptr = (const uint8_t*)(linetable);
157158
uintptr_t addr = 0;
158159
int computed_line = firstlineno; // Running accumulator, separate from output
160+
const size_t MAX_LINETABLE_ENTRIES = 65536;
161+
size_t entry_count = 0;
159162

160-
while (*ptr != '\0') {
163+
while (*ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) {
164+
entry_count++;
161165
uint8_t first_byte = *(ptr++);
162166
uint8_t code = (first_byte >> 3) & 15;
163167
size_t length = (first_byte & 7) + 1;
@@ -277,12 +281,9 @@ make_frame_info(RemoteUnwinderObject *unwinder, PyObject *file, PyObject *locati
277281
int
278282
parse_code_object(RemoteUnwinderObject *unwinder,
279283
PyObject **result,
280-
uintptr_t address,
281-
uintptr_t instruction_pointer,
282-
uintptr_t *previous_frame,
283-
int32_t tlbc_index)
284+
const CodeObjectContext *ctx)
284285
{
285-
void *key = (void *)address;
286+
void *key = (void *)ctx->code_addr;
286287
CachedCodeMetadata *meta = NULL;
287288
PyObject *func = NULL;
288289
PyObject *file = NULL;
@@ -291,9 +292,9 @@ parse_code_object(RemoteUnwinderObject *unwinder,
291292
#ifdef Py_GIL_DISABLED
292293
// In free threading builds, code object addresses might have the low bit set
293294
// as a flag, so we need to mask it off to get the real address
294-
uintptr_t real_address = address & (~1);
295+
uintptr_t real_address = ctx->code_addr & (~1);
295296
#else
296-
uintptr_t real_address = address;
297+
uintptr_t real_address = ctx->code_addr;
297298
#endif
298299

299300
if (unwinder && unwinder->code_object_cache != NULL) {
@@ -360,12 +361,12 @@ parse_code_object(RemoteUnwinderObject *unwinder,
360361
linetable = NULL;
361362
}
362363

363-
uintptr_t ip = instruction_pointer;
364+
uintptr_t ip = ctx->instruction_pointer;
364365
ptrdiff_t addrq;
365366

366367
#ifdef Py_GIL_DISABLED
367368
// Handle thread-local bytecode (TLBC) in free threading builds
368-
if (tlbc_index == 0 || unwinder->debug_offsets.code_object.co_tlbc == 0 || unwinder == NULL) {
369+
if (ctx->tlbc_index == 0 || unwinder->debug_offsets.code_object.co_tlbc == 0 || unwinder == NULL) {
369370
// No TLBC or no unwinder - use main bytecode directly
370371
addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive;
371372
goto done_tlbc;
@@ -383,10 +384,12 @@ parse_code_object(RemoteUnwinderObject *unwinder,
383384
tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation);
384385
}
385386

386-
if (tlbc_entry && tlbc_index < tlbc_entry->tlbc_array_size) {
387+
if (tlbc_entry && ctx->tlbc_index < tlbc_entry->tlbc_array_size) {
388+
assert(ctx->tlbc_index >= 0);
389+
assert(tlbc_entry->tlbc_array_size > 0);
387390
// Use cached TLBC data
388391
uintptr_t *entries = (uintptr_t *)((char *)tlbc_entry->tlbc_array + sizeof(Py_ssize_t));
389-
uintptr_t tlbc_bytecode_addr = entries[tlbc_index];
392+
uintptr_t tlbc_bytecode_addr = entries[ctx->tlbc_index];
390393

391394
if (tlbc_bytecode_addr != 0) {
392395
// Calculate offset from TLBC bytecode
@@ -401,8 +404,6 @@ parse_code_object(RemoteUnwinderObject *unwinder,
401404
done_tlbc:
402405
#else
403406
// Non-free-threaded build, always use the main bytecode
404-
(void)tlbc_index; // Suppress unused parameter warning
405-
(void)unwinder; // Suppress unused parameter warning
406407
addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive;
407408
#endif
408409
; // Empty statement to avoid C23 extension warning

Modules/_remote_debugging/frame_cache.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ frame_cache_find(RemoteUnwinderObject *unwinder, uint64_t thread_id)
4444
return NULL;
4545
}
4646
for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) {
47+
assert(i >= 0 && i < FRAME_CACHE_MAX_THREADS);
4748
if (unwinder->frame_cache[i].thread_id == thread_id) {
49+
assert(unwinder->frame_cache[i].num_addrs <= FRAME_CACHE_MAX_FRAMES);
4850
return &unwinder->frame_cache[i];
4951
}
5052
}
@@ -154,6 +156,8 @@ frame_cache_lookup_and_extend(
154156
return 0;
155157
}
156158

159+
assert(entry->num_addrs >= 0 && entry->num_addrs <= FRAME_CACHE_MAX_FRAMES);
160+
157161
// Find the index where last_profiled_frame matches
158162
Py_ssize_t start_idx = -1;
159163
for (Py_ssize_t i = 0; i < entry->num_addrs; i++) {
@@ -166,6 +170,7 @@ frame_cache_lookup_and_extend(
166170
if (start_idx < 0) {
167171
return 0; // Not found
168172
}
173+
assert(start_idx < entry->num_addrs);
169174

170175
Py_ssize_t num_frames = PyList_GET_SIZE(entry->frame_list);
171176

@@ -225,6 +230,7 @@ frame_cache_store(
225230
if (num_addrs > FRAME_CACHE_MAX_FRAMES) {
226231
num_addrs = FRAME_CACHE_MAX_FRAMES;
227232
}
233+
assert(num_addrs >= 0 && num_addrs <= FRAME_CACHE_MAX_FRAMES);
228234

229235
FrameCacheEntry *entry = frame_cache_alloc_slot(unwinder, thread_id);
230236
if (!entry) {
@@ -245,6 +251,8 @@ frame_cache_store(
245251
entry->thread_id = thread_id;
246252
memcpy(entry->addrs, addrs, num_addrs * sizeof(uintptr_t));
247253
entry->num_addrs = num_addrs;
254+
assert(entry->num_addrs == num_addrs);
255+
assert(entry->thread_id == thread_id);
248256

249257
return 1;
250258
}

0 commit comments

Comments
 (0)