Skip to content

Commit 3583be6

Browse files
committed
bpo-115773: Add tests to exercise the _Py_DebugOffsets structure
Signed-off-by: Pablo Galindo <pablogsal@gmail.com>
1 parent 7b21403 commit 3583be6

9 files changed

+495
-1
lines changed

Include/internal/pycore_runtime.h

+7
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ typedef struct _Py_DebugOffsets {
123123
struct _tuple_object {
124124
off_t ob_item;
125125
} tuple_object;
126+
127+
// Unicode object offset;
128+
struct _unicode_object {
129+
off_t state;
130+
off_t length;
131+
size_t asciiobject_size;
132+
} unicode_object;
126133
} _Py_DebugOffsets;
127134

128135
/* Full Python runtime state */

Include/internal/pycore_runtime_init.h

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ extern PyTypeObject _PyExc_MemoryError;
8383
.tuple_object = { \
8484
.ob_item = offsetof(PyTupleObject, ob_item), \
8585
}, \
86+
.unicode_object = { \
87+
.state = offsetof(PyUnicodeObject, _base._base.state), \
88+
.length = offsetof(PyUnicodeObject, _base._base.length), \
89+
.asciiobject_size = sizeof(PyASCIIObject), \
90+
}, \
8691
}, \
8792
.allocators = { \
8893
.standard = _pymem_allocators_standard_INIT(runtime), \

Lib/test/test_external_inspection.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import unittest
2+
import os
3+
import textwrap
4+
import importlib
5+
import sys
6+
from test.support import os_helper, SHORT_TIMEOUT
7+
from test.support.script_helper import make_script
8+
9+
import subprocess
10+
11+
PROCESS_VM_READV_SUPPORTED = False
12+
13+
try:
14+
from _testexternalinspection import PROCESS_VM_READV_SUPPORTED
15+
from _testexternalinspection import get_stack_trace
16+
except ImportError:
17+
unittest.skip("Test only runs when _testexternalinspection is available")
18+
19+
def _make_test_script(script_dir, script_basename, source):
20+
to_return = make_script(script_dir, script_basename, source)
21+
importlib.invalidate_caches()
22+
return to_return
23+
24+
class TestGetStackTrace(unittest.TestCase):
25+
26+
@unittest.skipIf(sys.platform != "linux", "Test only runs on Linux")
27+
@unittest.skipIf(not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support")
28+
def test_stack_trace(self):
29+
# Spawn a process with some realistic Python code
30+
script = textwrap.dedent("""\
31+
import time, sys, os
32+
def bar():
33+
for x in range(100):
34+
if x == 50:
35+
baz()
36+
def baz():
37+
foo()
38+
39+
def foo():
40+
fifo = sys.argv[1]
41+
with open(sys.argv[1], "w") as fifo:
42+
fifo.write("ready")
43+
time.sleep(100)
44+
45+
bar()
46+
""")
47+
stack_trace = None
48+
with os_helper.temp_dir() as work_dir:
49+
script_dir = os.path.join(work_dir, "script_pkg")
50+
os.mkdir(script_dir)
51+
fifo = f"{work_dir}/the_fifo"
52+
os.mkfifo(fifo)
53+
script_name = _make_test_script(script_dir, 'script', script)
54+
try:
55+
p = subprocess.Popen([sys.executable, script_name, str(fifo)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, text=True)
56+
with open(fifo, "r") as fifo_file:
57+
response = fifo_file.read()
58+
self.assertEqual(response, "ready")
59+
stack_trace = get_stack_trace(p.pid)
60+
except PermissionError:
61+
self.skipTest("Insufficient permissions to read the stack trace")
62+
finally:
63+
os.remove(fifo)
64+
p.kill()
65+
p.terminate()
66+
p.kill()
67+
p.wait(timeout=SHORT_TIMEOUT)
68+
69+
70+
expected_stack_trace = [
71+
'foo',
72+
'baz',
73+
'bar',
74+
'<module>'
75+
]
76+
self.assertEqual(stack_trace, expected_stack_trace)
77+
78+
if __name__ == "__main__":
79+
unittest.main()

Modules/Setup

+1
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ PYTHONPATH=$(COREPYTHONPATH)
285285
#_testcapi _testcapimodule.c
286286
#_testimportmultiple _testimportmultiple.c
287287
#_testmultiphase _testmultiphase.c
288+
#_testexternalinspection _testexternalinspection.c
288289
#_testsinglephase _testsinglephase.c
289290

290291
# ---

Modules/Setup.stdlib.in

+1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
@MODULE__TESTIMPORTMULTIPLE_TRUE@_testimportmultiple _testimportmultiple.c
172172
@MODULE__TESTMULTIPHASE_TRUE@_testmultiphase _testmultiphase.c
173173
@MODULE__TESTMULTIPHASE_TRUE@_testsinglephase _testsinglephase.c
174+
@MODULE__TESTEXTERNALINSPECTION_TRUE@_testexternalinspection _testexternalinspection.c
174175
@MODULE__CTYPES_TEST_TRUE@_ctypes_test _ctypes/_ctypes_test.c
175176

176177
# Limited API template modules; must be built as shared modules.

0 commit comments

Comments
 (0)