Skip to content

Commit dc0c4b3

Browse files
Merge branch 'main' into expat-2.9.0
2 parents ab5bd53 + 114de19 commit dc0c4b3

5 files changed

Lines changed: 25 additions & 62 deletions

File tree

‎Lib/profiling/sampling/stack_collector.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ def export(self, filename):
6868
return True
6969

7070

71-
# Bounded by the unwinder's maximum captured stack depth (MAX_FRAMES).
72-
_FLAMEGRAPH_RECURSION_MARGIN = 2000
71+
# Allow for tree conversion and the dict/list frames in the Python JSON encoder.
72+
_FLAMEGRAPH_RECURSION_MARGIN = 6000
7373

7474

7575
class FlamegraphCollector(StackTraceCollector):
@@ -498,7 +498,12 @@ def _get_source_lines(self, func):
498498
return None
499499

500500
def _create_flamegraph_html(self, data):
501-
data_json = json.dumps(data)
501+
try:
502+
data_json = json.dumps(data)
503+
except RecursionError:
504+
# The C encoder can exhaust the C stack independently of the
505+
# Python recursion limit. iterencode() uses the Python encoder.
506+
data_json = "".join(json.JSONEncoder().iterencode(data))
502507

503508
template_dir = importlib.resources.files(__package__)
504509
vendor_dir = template_dir / "_vendor"

‎Lib/test/test_profiling/test_sampling_profiler/test_collectors.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import tempfile
99
import unittest
1010

11-
from test.support import is_emscripten, set_recursion_limit
11+
from test.support import is_emscripten, run_with_limited_c_stack, set_recursion_limit
1212

1313
try:
1414
import _remote_debugging # noqa: F401
@@ -611,6 +611,7 @@ def test_flamegraph_collector_empty_export_fails(self):
611611
self.assertFalse(export_ok)
612612
self.assertEqual(os.path.getsize(flamegraph_out.name), 0)
613613

614+
@run_with_limited_c_stack(size=1024 * 1024)
614615
def test_flamegraph_deep_stack_export(self):
615616
flamegraph_out = tempfile.NamedTemporaryFile(
616617
suffix=".html", delete=False
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix deep flamegraph exports on platforms with small C stacks by falling back
2+
to the Python JSON encoder when the C encoder runs out of stack space.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix macOS runtime section lookup for remote debugging when virtual memory
2+
region sizes differ from Mach-O segment file sizes.

‎Python/remote_debug.h‎

Lines changed: 11 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
297297
static uintptr_t
298298
return_section_address64(
299299
const char* section,
300-
mach_port_t proc_ref,
301300
uintptr_t base,
302301
void* map
303302
) {
@@ -307,37 +306,15 @@ return_section_address64(
307306
int cmd_cnt = 0;
308307
struct segment_command_64* cmd = map + sizeof(struct mach_header_64);
309308

310-
mach_vm_size_t size = 0;
311-
mach_msg_type_number_t count = sizeof(vm_region_basic_info_data_64_t);
312-
mach_vm_address_t address = (mach_vm_address_t)base;
313-
vm_region_basic_info_data_64_t r_info;
314-
mach_port_t object_name;
315309
uintptr_t vmaddr = 0;
316310

317311
for (int i = 0; cmd_cnt < 2 && i < ncmds; i++) {
318312
if (cmd->cmd == LC_SEGMENT_64 && strcmp(cmd->segname, "__TEXT") == 0) {
319313
vmaddr = cmd->vmaddr;
320314
}
321315
if (cmd->cmd == LC_SEGMENT_64 && strcmp(cmd->segname, "__DATA") == 0) {
322-
while (cmd->filesize != size) {
323-
address += size;
324-
kern_return_t ret = mach_vm_region(
325-
proc_ref,
326-
&address,
327-
&size,
328-
VM_REGION_BASIC_INFO_64,
329-
(vm_region_info_t)&r_info, // cppcheck-suppress [uninitvar]
330-
&count,
331-
&object_name
332-
);
333-
if (ret != KERN_SUCCESS) {
334-
PyErr_Format(PyExc_RuntimeError,
335-
"mach_vm_region failed while parsing 64-bit Mach-O binary "
336-
"at base address 0x%lx (kern_return_t: %d)",
337-
base, ret);
338-
return 0;
339-
}
340-
}
316+
// The section address only needs the image's ASLR slide.
317+
// VM regions need not match the segment's on-disk size.
341318

342319
int nsects = cmd->nsects;
343320
struct section_64* sec = (struct section_64*)(
@@ -360,7 +337,6 @@ return_section_address64(
360337
static uintptr_t
361338
return_section_address32(
362339
const char* section,
363-
mach_port_t proc_ref,
364340
uintptr_t base,
365341
void* map
366342
) {
@@ -370,37 +346,15 @@ return_section_address32(
370346
int cmd_cnt = 0;
371347
struct segment_command* cmd = map + sizeof(struct mach_header);
372348

373-
mach_vm_size_t size = 0;
374-
mach_msg_type_number_t count = sizeof(vm_region_basic_info_data_t);
375-
mach_vm_address_t address = (mach_vm_address_t)base;
376-
vm_region_basic_info_data_t r_info;
377-
mach_port_t object_name;
378349
uintptr_t vmaddr = 0;
379350

380351
for (int i = 0; cmd_cnt < 2 && i < ncmds; i++) {
381352
if (cmd->cmd == LC_SEGMENT && strcmp(cmd->segname, "__TEXT") == 0) {
382353
vmaddr = cmd->vmaddr;
383354
}
384355
if (cmd->cmd == LC_SEGMENT && strcmp(cmd->segname, "__DATA") == 0) {
385-
while (cmd->filesize != size) {
386-
address += size;
387-
kern_return_t ret = mach_vm_region(
388-
proc_ref,
389-
&address,
390-
&size,
391-
VM_REGION_BASIC_INFO,
392-
(vm_region_info_t)&r_info, // cppcheck-suppress [uninitvar]
393-
&count,
394-
&object_name
395-
);
396-
if (ret != KERN_SUCCESS) {
397-
PyErr_Format(PyExc_RuntimeError,
398-
"mach_vm_region failed while parsing 32-bit Mach-O binary "
399-
"at base address 0x%lx (kern_return_t: %d)",
400-
base, ret);
401-
return 0;
402-
}
403-
}
356+
// The section address only needs the image's ASLR slide.
357+
// VM regions need not match the segment's on-disk size.
404358

405359
int nsects = cmd->nsects;
406360
struct section* sec = (struct section*)(
@@ -423,7 +377,6 @@ return_section_address32(
423377
static uintptr_t
424378
return_section_address_fat(
425379
const char* section,
426-
mach_port_t proc_ref,
427380
uintptr_t base,
428381
void* map
429382
) {
@@ -473,11 +426,11 @@ return_section_address_fat(
473426
switch (hdr->magic) {
474427
case MH_MAGIC:
475428
case MH_CIGAM:
476-
return return_section_address32(section, proc_ref, base, (void*)hdr);
429+
return return_section_address32(section, base, (void*)hdr);
477430

478431
case MH_MAGIC_64:
479432
case MH_CIGAM_64:
480-
return return_section_address64(section, proc_ref, base, (void*)hdr);
433+
return return_section_address64(section, base, (void*)hdr);
481434

482435
default:
483436
PyErr_Format(PyExc_RuntimeError,
@@ -496,7 +449,7 @@ return_section_address_fat(
496449
}
497450

498451
static uintptr_t
499-
search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_size_t size, mach_port_t proc_ref)
452+
search_section_in_file(const char* secname, char* path, uintptr_t base)
500453
{
501454
int fd = open(path, O_RDONLY);
502455
if (fd == -1) {
@@ -533,15 +486,15 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
533486
switch (magic) {
534487
case MH_MAGIC:
535488
case MH_CIGAM:
536-
result = return_section_address32(secname, proc_ref, base, map);
489+
result = return_section_address32(secname, base, map);
537490
break;
538491
case MH_MAGIC_64:
539492
case MH_CIGAM_64:
540-
result = return_section_address64(secname, proc_ref, base, map);
493+
result = return_section_address64(secname, base, map);
541494
break;
542495
case FAT_MAGIC:
543496
case FAT_CIGAM:
544-
result = return_section_address_fat(secname, proc_ref, base, map);
497+
result = return_section_address_fat(secname, base, map);
545498
break;
546499
default:
547500
PyErr_Format(PyExc_RuntimeError,
@@ -644,7 +597,7 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
644597
if (strncmp(filename, substr, strlen(substr)) == 0) {
645598
PyErr_Clear();
646599
uintptr_t result = search_section_in_file(
647-
secname, map_filename, address, size, proc_ref);
600+
secname, map_filename, address);
648601
if (result != 0) {
649602
if (validator == NULL || validator(handle, result)) {
650603
return result;

0 commit comments

Comments
 (0)