@@ -340,6 +340,82 @@ def baz(n):
340340 self .assertNotIn (f"py::bar:{ script } " , stdout )
341341 self .assertNotIn (f"py::baz:{ script } " , stdout )
342342
343+ def test_pre_fork_compile (self ):
344+ code = """if 1:
345+ import sys
346+ import os
347+ import sysconfig
348+ from _testinternalcapi import (
349+ compile_perf_trampoline_entry,
350+ perf_trampoline_set_persist_after_fork,
351+ )
352+
353+ def foo_fork():
354+ pass
355+
356+ def bar_fork():
357+ foo_fork()
358+
359+ def foo():
360+ pass
361+
362+ def bar():
363+ foo()
364+
365+ def compile_trampolines_for_all_functions():
366+ perf_trampoline_set_persist_after_fork(1)
367+ for _, obj in globals().items():
368+ if callable(obj) and hasattr(obj, '__code__'):
369+ compile_perf_trampoline_entry(obj.__code__)
370+
371+ if __name__ == "__main__":
372+ compile_trampolines_for_all_functions()
373+ pid = os.fork()
374+ if pid == 0:
375+ print(os.getpid())
376+ bar_fork()
377+ else:
378+ bar()
379+ """
380+
381+ with temp_dir () as script_dir :
382+ script = make_script (script_dir , "perftest" , code )
383+ with subprocess .Popen (
384+ [sys .executable , "-Xperf" , script ],
385+ universal_newlines = True ,
386+ stderr = subprocess .PIPE ,
387+ stdout = subprocess .PIPE ,
388+ ) as process :
389+ stdout , stderr = process .communicate ()
390+
391+ self .assertEqual (process .returncode , 0 )
392+ self .assertNotIn ("Error:" , stderr )
393+ child_pid = int (stdout .strip ())
394+ perf_file = pathlib .Path (f"/tmp/perf-{ process .pid } .map" )
395+ perf_child_file = pathlib .Path (f"/tmp/perf-{ child_pid } .map" )
396+ self .assertTrue (perf_file .exists ())
397+ self .assertTrue (perf_child_file .exists ())
398+
399+ perf_file_contents = perf_file .read_text ()
400+ self .assertIn (f"py::foo:{ script } " , perf_file_contents )
401+ self .assertIn (f"py::bar:{ script } " , perf_file_contents )
402+ self .assertIn (f"py::foo_fork:{ script } " , perf_file_contents )
403+ self .assertIn (f"py::bar_fork:{ script } " , perf_file_contents )
404+
405+ child_perf_file_contents = perf_child_file .read_text ()
406+ self .assertIn (f"py::foo_fork:{ script } " , child_perf_file_contents )
407+ self .assertIn (f"py::bar_fork:{ script } " , child_perf_file_contents )
408+
409+ # Pre-compiled perf-map entries of a forked process must be
410+ # identical in both the parent and child perf-map files.
411+ perf_file_lines = perf_file_contents .split ("\n " )
412+ for line in perf_file_lines :
413+ if (
414+ f"py::foo_fork:{ script } " in line
415+ or f"py::bar_fork:{ script } " in line
416+ ):
417+ self .assertIn (line , child_perf_file_contents )
418+
343419
344420if __name__ == "__main__" :
345421 unittest .main ()
0 commit comments