@@ -2253,6 +2253,127 @@ def get_trace_with_opcodes(pid):
22532253 location .col_offset , location .end_col_offset )
22542254 self .assertIn (actual , valid_locations )
22552255
2256+ @skip_if_not_supported
2257+ @unittest .skipIf (sys ._is_gil_enabled (), "Requires free-threading" )
2258+ @unittest .skipIf (
2259+ sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
2260+ "Requires process_vm_readv" ,
2261+ )
2262+ def test_tlbc_cache_refresh_after_growth (self ):
2263+ # Reproducer from gh-157660.
2264+ script = textwrap .dedent ("""\
2265+ import os, threading
2266+ from _remote_debugging import RemoteUnwinder
2267+ from test import support
2268+
2269+ go = threading.Event()
2270+ stop = threading.Event()
2271+
2272+ def leaf():
2273+ stop.wait()
2274+
2275+ def wait_for_leaf_frames(u, expected_count):
2276+ for _ in support.sleeping_retry(
2277+ support.SHORT_TIMEOUT,
2278+ f"Expected {expected_count} leaf frames",
2279+ ):
2280+ try:
2281+ traces = u.get_stack_trace()
2282+ except RuntimeError as exc:
2283+ if str(exc) != "Failed to parse initial frame in chain":
2284+ raise
2285+ continue
2286+ count = sum(
2287+ f.funcname == "leaf"
2288+ for i in traces
2289+ for t in i.threads for f in t.frame_info
2290+ )
2291+ if count == expected_count:
2292+ return
2293+
2294+ threading.Thread(target=leaf, daemon=True).start()
2295+ for _ in range(16):
2296+ threading.Thread(target=stop.wait, daemon=True).start()
2297+ threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2298+
2299+ u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2300+ wait_for_leaf_frames(u, 1)
2301+ go.set()
2302+ wait_for_leaf_frames(u, 2)
2303+ """ )
2304+ result = subprocess .run (
2305+ [sys .executable , "-X" , "gil=0" , "-X" , "tlbc=1" , "-c" , script ],
2306+ capture_output = True ,
2307+ text = True ,
2308+ timeout = SHORT_TIMEOUT ,
2309+ )
2310+ self .assertEqual (
2311+ result .returncode , 0 ,
2312+ f"stdout: { result .stdout } \n stderr: { result .stderr } " ,
2313+ )
2314+
2315+ @skip_if_not_supported
2316+ @unittest .skipIf (sys ._is_gil_enabled (), "Requires free-threading" )
2317+ @unittest .skipIf (
2318+ sys .platform == "linux" and not PROCESS_VM_READV_SUPPORTED ,
2319+ "Requires process_vm_readv" ,
2320+ )
2321+ def test_tlbc_cache_refresh_after_slot_fill (self ):
2322+ # Reproducer from gh-157660.
2323+ script = textwrap .dedent ("""\
2324+ import os, threading
2325+ from _remote_debugging import RemoteUnwinder
2326+
2327+ go = threading.Event()
2328+ stop = threading.Event()
2329+
2330+ def leaf():
2331+ stop.wait()
2332+
2333+ from test import support
2334+
2335+ def lines(u, expected_count):
2336+ for _ in support.sleeping_retry(
2337+ support.SHORT_TIMEOUT,
2338+ f"Expected {expected_count} leaf frames",
2339+ ):
2340+ try:
2341+ traces = u.get_stack_trace()
2342+ except RuntimeError as exc:
2343+ if str(exc) != "Failed to parse initial frame in chain":
2344+ raise
2345+ continue
2346+ result = sorted(
2347+ f.location.lineno
2348+ for i in traces
2349+ for t in i.threads for f in t.frame_info
2350+ if f.funcname == "leaf"
2351+ )
2352+ # A new frame can still point at the function definition.
2353+ if (len(result) == expected_count and
2354+ leaf.__code__.co_firstlineno not in result):
2355+ return result
2356+
2357+ threading.Thread(target=leaf, daemon=True).start()
2358+ threading.Thread(target=lambda: (go.wait(), leaf()), daemon=True).start()
2359+ u = RemoteUnwinder(os.getpid(), all_threads=True, cache_frames=False)
2360+ before = lines(u, 1)
2361+ assert before == [8], before
2362+ go.set()
2363+ cached = lines(u, 2)
2364+ assert cached == [8, 8], cached
2365+ """ )
2366+ result = subprocess .run (
2367+ [sys .executable , "-X" , "gil=0" , "-X" , "tlbc=1" , "-c" , script ],
2368+ capture_output = True ,
2369+ text = True ,
2370+ timeout = SHORT_TIMEOUT ,
2371+ )
2372+ self .assertEqual (
2373+ result .returncode , 0 ,
2374+ f"stdout: { result .stdout } \n stderr: { result .stderr } " ,
2375+ )
2376+
22562377
22572378class TestUnsupportedPlatformHandling (unittest .TestCase ):
22582379 @unittest .skipIf (
0 commit comments