Skip to content

Commit a2f4182

Browse files
committed
Add test
1 parent 22fc892 commit a2f4182

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

Lib/test/test_perf_profiler.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import unittest
2+
import subprocess
3+
import re
4+
import sys
5+
import sysconfig
6+
from test.support.script_helper import make_script
7+
from test.support.os_helper import temp_dir
8+
9+
10+
def get_perf_version():
11+
try:
12+
cmd = ["perf", "--version"]
13+
proc = subprocess.Popen(
14+
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True
15+
)
16+
with proc:
17+
version, stderr = proc.communicate()
18+
19+
if proc.returncode:
20+
raise Exception(
21+
f"Command {' '.join(cmd)!r} failed "
22+
f"with exit code {proc.returncode}: "
23+
f"stdout={version!r} stderr={stderr!r}"
24+
)
25+
except OSError:
26+
raise unittest.SkipTest("Couldn't find perf on the path")
27+
28+
match = re.search(r"^perf version\s+(.*)", version)
29+
if match is None:
30+
raise Exception("unable to parse perf version: %r" % version)
31+
return (version, match.group(1))
32+
33+
34+
_, version = get_perf_version()
35+
36+
if not version:
37+
raise unittest.SkipTest("Could not find valid perf tool")
38+
39+
if "no-omit-frame-pointe" not in sysconfig.get_config_var("CFLAGS"):
40+
raise unittest.SkipTest("Unwinding without frame pointer is unreliable")
41+
42+
43+
def run_perf(cwd, *args, **env_vars):
44+
if env_vars:
45+
env = os.environ.copy()
46+
env.update(env_vars)
47+
else:
48+
env = None
49+
# -nx: Do not execute commands from any .gdbinit initialization files
50+
# (issue #22188)
51+
output_file = cwd + "/perf_output.perf"
52+
base_cmd = ("perf", "record", "-g", "--call-graph=fp", "-o", output_file, "--")
53+
proc = subprocess.Popen(
54+
base_cmd + args,
55+
# Redirect stdin to prevent GDB from messing with
56+
# the terminal settings
57+
stdout=subprocess.PIPE,
58+
stderr=subprocess.PIPE,
59+
env=env,
60+
)
61+
with proc:
62+
out, err = proc.communicate()
63+
base_cmd = ("perf", "script")
64+
proc = subprocess.Popen(
65+
("perf", "script", "-i", output_file),
66+
# Redirect stdin to prevent GDB from messing with
67+
# the terminal settings
68+
stdout=subprocess.PIPE,
69+
stderr=subprocess.PIPE,
70+
env=env,
71+
)
72+
with proc:
73+
out, err = proc.communicate()
74+
return out.decode("utf-8", "replace"), err.decode("utf-8", "replace")
75+
76+
77+
class TestPerfProfiler(unittest.TestCase):
78+
def test_python_calls_appear_in_the_stack_if_perf_activated(self):
79+
with temp_dir() as script_dir:
80+
code = """if 1:
81+
def foo(n):
82+
x = 0
83+
for i in range(n):
84+
x += i
85+
86+
def bar(n):
87+
foo(n)
88+
89+
def baz(n):
90+
bar(n)
91+
92+
baz(10000000)
93+
"""
94+
script = make_script(script_dir, "perftest", code)
95+
stdout, stderr = run_perf(script_dir, sys.executable, "-Xperf", script)
96+
self.assertEqual(stderr, "")
97+
98+
self.assertIn(f"py::foo:{script}", stdout)
99+
self.assertIn(f"py::bar:{script}", stdout)
100+
self.assertIn(f"py::baz:{script}", stdout)
101+
102+
def test_python_calls_do_not_appear_in_the_stack_if_perf_activated(self):
103+
with temp_dir() as script_dir:
104+
code = """if 1:
105+
def foo(n):
106+
x = 0
107+
for i in range(n):
108+
x += i
109+
110+
def bar(n):
111+
foo(n)
112+
113+
def baz(n):
114+
bar(n)
115+
116+
baz(10000000)
117+
"""
118+
script = make_script(script_dir, "perftest", code)
119+
stdout, stderr = run_perf(script_dir, sys.executable, script)
120+
self.assertEqual(stderr, "")
121+
122+
self.assertNotIn(f"py::foo:{script}", stdout)
123+
self.assertNotIn(f"py::bar:{script}", stdout)
124+
self.assertNotIn(f"py::baz:{script}", stdout)
125+
126+
127+
if __name__ == "__main__":
128+
unittest.main()

Makefile.pre.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -2320,7 +2320,7 @@ config.status: $(srcdir)/configure
23202320

23212321
.PRECIOUS: config.status $(BUILDPYTHON) Makefile Makefile.pre
23222322

2323-
Objects/asm_trampoline.o: $(srcdir)/Objects/asm_trampoline.sx
2323+
Objects/asm_trampoline.o: $(srcdir)/Objects/asm_trampoline.S
23242324
$(CC) -c $(PY_CORE_CFLAGS) -o $@ $<
23252325

23262326
# Some make's put the object file in the current directory

0 commit comments

Comments
 (0)