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