@@ -34,6 +34,21 @@ def supports_trampoline_profiling():
3434 raise unittest .SkipTest ("perf trampoline profiling not supported" )
3535
3636
37+ def _perf_env (** env_vars ):
38+ env = os .environ .copy ()
39+ # Keep perf's output stable regardless of the builder's perf config.
40+ env .update (
41+ {
42+ "DEBUGINFOD_URLS" : "" ,
43+ "PERF_CONFIG" : os .devnull ,
44+ }
45+ )
46+ if env_vars :
47+ env .update (env_vars )
48+ env ["PYTHON_JIT" ] = "0"
49+ return env
50+
51+
3752class TestPerfTrampoline (unittest .TestCase ):
3853 def setUp (self ):
3954 super ().setUp ()
@@ -63,13 +78,12 @@ def baz():
6378 """
6479 with temp_dir () as script_dir :
6580 script = make_script (script_dir , "perftest" , code )
66- env = {** os .environ , "PYTHON_JIT" : "0" }
6781 with subprocess .Popen (
6882 [sys .executable , "-Xperf" , script ],
6983 text = True ,
7084 stderr = subprocess .PIPE ,
7185 stdout = subprocess .PIPE ,
72- env = env ,
86+ env = _perf_env () ,
7387 ) as process :
7488 stdout , stderr = process .communicate ()
7589
@@ -132,13 +146,12 @@ def baz():
132146 """
133147 with temp_dir () as script_dir :
134148 script = make_script (script_dir , "perftest" , code )
135- env = {** os .environ , "PYTHON_JIT" : "0" }
136149 with subprocess .Popen (
137150 [sys .executable , "-Xperf" , script ],
138151 text = True ,
139152 stderr = subprocess .PIPE ,
140153 stdout = subprocess .PIPE ,
141- env = env ,
154+ env = _perf_env () ,
142155 ) as process :
143156 stdout , stderr = process .communicate ()
144157
@@ -198,13 +211,12 @@ def test_trampoline_works_after_fork_with_many_code_objects(self):
198211 """
199212 with temp_dir () as script_dir :
200213 script = make_script (script_dir , "perftest" , code )
201- env = {** os .environ , "PYTHON_JIT" : "0" }
202214 with subprocess .Popen (
203215 [sys .executable , "-Xperf" , script ],
204216 text = True ,
205217 stderr = subprocess .PIPE ,
206218 stdout = subprocess .PIPE ,
207- env = env ,
219+ env = _perf_env () ,
208220 ) as process :
209221 stdout , stderr = process .communicate ()
210222
@@ -242,13 +254,12 @@ def baz():
242254 code = set_eval_hook + code
243255 with temp_dir () as script_dir :
244256 script = make_script (script_dir , "perftest" , code )
245- env = {** os .environ , "PYTHON_JIT" : "0" }
246257 with subprocess .Popen (
247258 [sys .executable , script ],
248259 text = True ,
249260 stderr = subprocess .PIPE ,
250261 stdout = subprocess .PIPE ,
251- env = env ,
262+ env = _perf_env () ,
252263 ) as process :
253264 stdout , stderr = process .communicate ()
254265
@@ -345,9 +356,12 @@ def perf_command_works():
345356 "-c" ,
346357 'print("hello")' ,
347358 )
348- env = {** os .environ , "PYTHON_JIT" : "0" }
349359 stdout = subprocess .check_output (
350- cmd , cwd = script_dir , text = True , stderr = subprocess .STDOUT , env = env
360+ cmd ,
361+ cwd = script_dir ,
362+ text = True ,
363+ stderr = subprocess .STDOUT ,
364+ env = _perf_env (),
351365 )
352366 except (subprocess .SubprocessError , OSError ):
353367 return False
@@ -359,10 +373,7 @@ def perf_command_works():
359373
360374
361375def run_perf (cwd , * args , use_jit = False , ** env_vars ):
362- env = os .environ .copy ()
363- if env_vars :
364- env .update (env_vars )
365- env ["PYTHON_JIT" ] = "0"
376+ env = _perf_env (** env_vars )
366377 output_file = cwd + "/perf_output.perf"
367378 if not use_jit :
368379 base_cmd = (
@@ -385,6 +396,8 @@ def run_perf(cwd, *args, use_jit=False, **env_vars):
385396 "--call-graph=dwarf,65528" ,
386397 "-F99" ,
387398 "-k1" ,
399+ "-m" ,
400+ "4M" ,
388401 "-o" ,
389402 output_file ,
390403 "--" ,
@@ -425,54 +438,77 @@ def run_perf(cwd, *args, use_jit=False, **env_vars):
425438
426439
427440class TestPerfProfilerMixin :
428- def run_perf (self , script_dir , perf_mode , script ):
441+ PERF_CAPTURE_ATTEMPTS = 3
442+
443+ def run_perf (self , script_dir , script , activate_trampoline = True ):
429444 raise NotImplementedError ()
430445
446+ def run_perf_with_retries (
447+ self , script_dir , script , expected_symbols = (), activate_trampoline = True
448+ ):
449+ stdout = stderr = ""
450+ for _ in range (self .PERF_CAPTURE_ATTEMPTS ):
451+ stdout , stderr = self .run_perf (
452+ script_dir , script , activate_trampoline = activate_trampoline
453+ )
454+ if activate_trampoline and any (
455+ symbol not in stdout for symbol in expected_symbols
456+ ):
457+ continue
458+ break
459+ return stdout , stderr
460+
431461 def test_python_calls_appear_in_the_stack_if_perf_activated (self ):
432462 with temp_dir () as script_dir :
433463 code = """if 1:
464+ from itertools import repeat
465+
434466 def foo(n):
435- x = 0
436- for i in range(n):
437- x += i
467+ for _ in repeat(None, n):
468+ pass
438469
439470 def bar(n):
440471 foo(n)
441472
442473 def baz(n):
443474 bar(n)
444475
445- baz(10000000 )
476+ baz(40000000 )
446477 """
447478 script = make_script (script_dir , "perftest" , code )
448- stdout , stderr = self .run_perf (script_dir , script )
449- self .assertEqual (stderr , "" )
479+ expected_symbols = [
480+ f"py::foo:{ script } " ,
481+ f"py::bar:{ script } " ,
482+ f"py::baz:{ script } " ,
483+ ]
484+ stdout , _ = self .run_perf_with_retries (
485+ script_dir , script , expected_symbols
486+ )
450487
451- self .assertIn (f"py::foo:{ script } " , stdout )
452- self .assertIn (f"py::bar:{ script } " , stdout )
453- self .assertIn (f"py::baz:{ script } " , stdout )
488+ for expected_symbol in expected_symbols :
489+ self .assertIn (expected_symbol , stdout )
454490
455491 def test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated (self ):
456492 with temp_dir () as script_dir :
457493 code = """if 1:
494+ from itertools import repeat
495+
458496 def foo(n):
459- x = 0
460- for i in range(n):
461- x += i
497+ for _ in repeat(None, n):
498+ pass
462499
463500 def bar(n):
464501 foo(n)
465502
466503 def baz(n):
467504 bar(n)
468505
469- baz(10000000 )
506+ baz(40000000 )
470507 """
471508 script = make_script (script_dir , "perftest" , code )
472- stdout , stderr = self .run_perf (
509+ stdout , _ = self .run_perf_with_retries (
473510 script_dir , script , activate_trampoline = False
474511 )
475- self .assertEqual (stderr , "" )
476512
477513 self .assertNotIn (f"py::foo:{ script } " , stdout )
478514 self .assertNotIn (f"py::bar:{ script } " , stdout )
@@ -542,13 +578,12 @@ def compile_trampolines_for_all_functions():
542578
543579 with temp_dir () as script_dir :
544580 script = make_script (script_dir , "perftest" , code )
545- env = {** os .environ , "PYTHON_JIT" : "0" }
546581 with subprocess .Popen (
547582 [sys .executable , "-Xperf" , script ],
548583 universal_newlines = True ,
549584 stderr = subprocess .PIPE ,
550585 stdout = subprocess .PIPE ,
551- env = env ,
586+ env = _perf_env () ,
552587 ) as process :
553588 stdout , stderr = process .communicate ()
554589
0 commit comments