Skip to content

Commit 06a7bda

Browse files
committed
Add native frame tests
1 parent a280f97 commit 06a7bda

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

Lib/test/test_profiling/test_sampling_profiler.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3109,7 +3109,99 @@ def test_gc_frames_disabled(self):
31093109

31103110
# GC frames should NOT be present
31113111
self.assertNotIn("<GC>", output)
3112+
@requires_subprocess()
3113+
@skip_if_not_supported
3114+
class TestNativeFrameTracking(unittest.TestCase):
3115+
"""Tests for native frame tracking in the sampling profiler."""
3116+
3117+
@classmethod
3118+
def setUpClass(cls):
3119+
"""Create a static test script with native frames and CPU-intensive work."""
3120+
cls.native_test_script = '''
3121+
import operator
3122+
3123+
def python_to_c():
3124+
# Native code at the top of the stack:
3125+
sum(range(1_000_000))
3126+
# Python code at the top of the stack:
3127+
for _ in range(1_000_000):
3128+
pass
3129+
3130+
def main_loop():
3131+
while True:
3132+
# Native code in the middle of the stack:
3133+
operator.call(python_to_c)
3134+
3135+
if __name__ == "__main__":
3136+
main_loop()
3137+
'''
3138+
3139+
def test_native_frames_enabled(self):
3140+
"""Test that native frames appear when native tracking is enabled."""
3141+
collapsed_file = tempfile.NamedTemporaryFile(
3142+
suffix=".txt", delete=False
3143+
)
3144+
self.addCleanup(close_and_unlink, collapsed_file)
3145+
3146+
with (
3147+
test_subprocess(self.native_test_script) as subproc,
3148+
):
3149+
# Suppress profiler output when testing file export
3150+
with (
3151+
io.StringIO() as captured_output,
3152+
mock.patch("sys.stdout", captured_output),
3153+
):
3154+
try:
3155+
profiling.sampling.sample.sample(
3156+
subproc.process.pid,
3157+
duration_sec=1,
3158+
filename=collapsed_file.name,
3159+
output_format="collapsed",
3160+
sample_interval_usec=5000,
3161+
native=True,
3162+
)
3163+
except PermissionError:
3164+
self.skipTest("Insufficient permissions for remote profiling")
3165+
3166+
# Verify file was created and contains valid data
3167+
self.assertTrue(os.path.exists(collapsed_file.name))
3168+
self.assertGreater(os.path.getsize(collapsed_file.name), 0)
3169+
3170+
# Check file format
3171+
with open(collapsed_file.name, "r") as f:
3172+
content = f.read()
3173+
3174+
lines = [line.rsplit(" ", 1)[0] for line in content.strip().split("\n")]
3175+
self.assertGreater(len(lines), 0)
3176+
3177+
# All samples should have native code in the middle of the stack:
3178+
self.assertTrue(all(";<native>;" in line for line in lines))
3179+
# Some samples should have native code at the top of the stack:
3180+
self.assertTrue(any(line.endswith(";<native>") for line in lines))
3181+
# Some samples should have Python code at the top of the stack:
3182+
self.assertTrue(any(not line.endswith(";<native>") for line in lines))
3183+
3184+
def test_native_frames_disabled(self):
3185+
"""Test that native frames do not appear when native tracking is disabled."""
3186+
with (
3187+
test_subprocess(self.native_test_script) as subproc,
3188+
io.StringIO() as captured_output,
3189+
mock.patch("sys.stdout", captured_output),
3190+
):
3191+
try:
3192+
profiling.sampling.sample.sample(
3193+
subproc.process.pid,
3194+
duration_sec=1,
3195+
sample_interval_usec=5000,
3196+
show_summary=False,
3197+
)
3198+
except PermissionError:
3199+
self.skipTest("Insufficient permissions for remote profiling")
3200+
3201+
output = captured_output.getvalue()
31123202

3203+
# native frames should NOT be present
3204+
self.assertNotIn("<native>", output)
31133205

31143206
if __name__ == "__main__":
31153207
unittest.main()

0 commit comments

Comments
 (0)