Skip to content

Commit 5a59e0b

Browse files
committed
helper
1 parent cb6ed34 commit 5a59e0b

File tree

2 files changed

+130
-273
lines changed

2 files changed

+130
-273
lines changed

Lib/test/test_profiling/test_sampling_profiler/test_collectors.py

Lines changed: 128 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,25 @@ def find_child_by_name(children, strings, substr):
5858
return None
5959

6060

61+
def _jsonl_tables(records):
62+
meta = next(record for record in records if record["type"] == "meta")
63+
end = next(record for record in records if record["type"] == "end")
64+
agg = next(record for record in records if record["type"] == "agg")
65+
str_defs = {
66+
item["str_id"]: item["value"]
67+
for record in records
68+
if record["type"] == "str_def"
69+
for item in record["defs"]
70+
}
71+
frame_defs = [
72+
item
73+
for record in records
74+
if record["type"] == "frame_def"
75+
for item in record["defs"]
76+
]
77+
return meta, str_defs, frame_defs, agg, end
78+
79+
6180
class TestSampleProfilerComponents(unittest.TestCase):
6281
"""Unit tests for individual profiler components."""
6382

@@ -1666,14 +1685,12 @@ def test_diff_flamegraph_load_baseline(self):
16661685
self.assertAlmostEqual(cold_node["diff"], -1.0)
16671686
self.assertAlmostEqual(cold_node["diff_pct"], -50.0)
16681687

1669-
def test_jsonl_collector_export(self):
1688+
def test_jsonl_collector_export_exact_output(self):
16701689
jsonl_out = tempfile.NamedTemporaryFile(delete=False)
16711690
self.addCleanup(close_and_unlink, jsonl_out)
16721691

16731692
collector = JsonlCollector(1000)
1674-
run_id = collector.run_id
1675-
1676-
self.assertIsNotNone(run_id)
1693+
collector.run_id = "run-123"
16771694

16781695
test_frames1 = [
16791696
MockInterpreterInfo(
@@ -1705,46 +1722,74 @@ def test_jsonl_collector_export(self):
17051722
collector.collect(test_frames2)
17061723
collector.collect(test_frames3)
17071724

1708-
with captured_stdout(), captured_stderr():
1709-
collector.export(jsonl_out.name)
1725+
collector.export(jsonl_out.name)
17101726

1711-
# Check file contents
1712-
with open(jsonl_out.name, "r") as f:
1727+
with open(jsonl_out.name, "r", encoding="utf-8") as f:
17131728
content = f.read()
17141729

1715-
lines = content.strip().split("\n")
1716-
self.assertEqual(len(lines), 5)
1717-
1718-
def jsonl(obj):
1719-
return json.dumps(obj, separators=(",", ":"))
1720-
1721-
expected = [
1722-
jsonl({"type": "meta", "v": 1, "run_id": run_id,
1723-
"sample_interval_usec": 1000}),
1724-
jsonl({"type": "str_def", "v": 1, "run_id": run_id,
1725-
"defs": [{"str_id": 1, "value": "func1"},
1726-
{"str_id": 2, "value": "file.py"},
1727-
{"str_id": 3, "value": "func2"},
1728-
{"str_id": 4, "value": "other_func"},
1729-
{"str_id": 5, "value": "other.py"}]}),
1730-
jsonl({"type": "frame_def", "v": 1, "run_id": run_id,
1731-
"defs": [{"frame_id": 1, "path_str_id": 2, "func_str_id": 1,
1732-
"line": 10, "end_line": 10},
1733-
{"frame_id": 2, "path_str_id": 2, "func_str_id": 3,
1734-
"line": 20, "end_line": 20},
1735-
{"frame_id": 3, "path_str_id": 5, "func_str_id": 4,
1736-
"line": 5, "end_line": 5}]}),
1737-
jsonl({"type": "agg", "v": 1, "run_id": run_id,
1738-
"kind": "frame", "scope": "final", "samples_total": 3,
1739-
"entries": [{"frame_id": 1, "self": 2, "cumulative": 2},
1740-
{"frame_id": 2, "self": 0, "cumulative": 2},
1741-
{"frame_id": 3, "self": 1, "cumulative": 1}]}),
1742-
jsonl({"type": "end", "v": 1, "run_id": run_id,
1743-
"samples_total": 3}),
1744-
]
1745-
1746-
for exp in expected:
1747-
self.assertIn(exp, lines)
1730+
self.assertEqual(
1731+
content,
1732+
(
1733+
'{"type":"meta","v":1,"run_id":"run-123","sample_interval_usec":1000}\n'
1734+
'{"type":"str_def","v":1,"run_id":"run-123","defs":[{"str_id":1,"value":"func1"},{"str_id":2,"value":"file.py"},{"str_id":3,"value":"func2"},{"str_id":4,"value":"other_func"},{"str_id":5,"value":"other.py"}]}\n'
1735+
'{"type":"frame_def","v":1,"run_id":"run-123","defs":[{"frame_id":1,"path_str_id":2,"func_str_id":1,"line":10,"end_line":10},{"frame_id":2,"path_str_id":2,"func_str_id":3,"line":20,"end_line":20},{"frame_id":3,"path_str_id":5,"func_str_id":4,"line":5,"end_line":5}]}\n'
1736+
'{"type":"agg","v":1,"run_id":"run-123","kind":"frame","scope":"final","samples_total":3,"entries":[{"frame_id":1,"self":2,"cumulative":2},{"frame_id":2,"self":0,"cumulative":2},{"frame_id":3,"self":1,"cumulative":1}]}\n'
1737+
'{"type":"end","v":1,"run_id":"run-123","samples_total":3}\n'
1738+
),
1739+
)
1740+
1741+
def test_jsonl_collector_skip_idle_filters_threads(self):
1742+
jsonl_out = tempfile.NamedTemporaryFile(delete=False)
1743+
self.addCleanup(close_and_unlink, jsonl_out)
1744+
1745+
active_status = THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU
1746+
frames = [
1747+
MockInterpreterInfo(
1748+
0,
1749+
[
1750+
MockThreadInfo(
1751+
1,
1752+
[MockFrameInfo("active1.py", 10, "active_func1")],
1753+
status=active_status,
1754+
),
1755+
MockThreadInfo(
1756+
2,
1757+
[MockFrameInfo("idle.py", 20, "idle_func")],
1758+
status=0,
1759+
),
1760+
MockThreadInfo(
1761+
3,
1762+
[MockFrameInfo("active2.py", 30, "active_func2")],
1763+
status=active_status,
1764+
),
1765+
],
1766+
)
1767+
]
1768+
1769+
def export_summary(skip_idle):
1770+
collector = JsonlCollector(1000, skip_idle=skip_idle)
1771+
collector.collect(frames)
1772+
collector.export(jsonl_out.name)
1773+
1774+
with open(jsonl_out.name, "r", encoding="utf-8") as f:
1775+
records = [json.loads(line) for line in f]
1776+
1777+
_, str_defs, frame_defs, agg_record, _ = _jsonl_tables(records)
1778+
paths = {str_defs[item["path_str_id"]] for item in frame_defs}
1779+
funcs = {str_defs[item["func_str_id"]] for item in frame_defs}
1780+
return paths, funcs, agg_record["samples_total"]
1781+
1782+
paths, funcs, samples_total = export_summary(skip_idle=True)
1783+
self.assertEqual(paths, {"active1.py", "active2.py"})
1784+
self.assertEqual(funcs, {"active_func1", "active_func2"})
1785+
self.assertEqual(samples_total, 2)
1786+
1787+
paths, funcs, samples_total = export_summary(skip_idle=False)
1788+
self.assertEqual(paths, {"active1.py", "idle.py", "active2.py"})
1789+
self.assertEqual(
1790+
funcs, {"active_func1", "idle_func", "active_func2"}
1791+
)
1792+
self.assertEqual(samples_total, 3)
17481793

17491794

17501795
class TestRecursiveFunctionHandling(unittest.TestCase):
@@ -2156,7 +2201,6 @@ def test_jsonl_collector_with_location_info(self):
21562201
self.addCleanup(close_and_unlink, jsonl_out)
21572202

21582203
collector = JsonlCollector(sample_interval_usec=1000)
2159-
run_id = collector.run_id
21602204

21612205
# Frame with LocationInfo
21622206
frame = MockFrameInfo("test.py", 42, "my_function")
@@ -2167,46 +2211,35 @@ def test_jsonl_collector_with_location_info(self):
21672211
]
21682212
collector.collect(frames)
21692213

2170-
# Should extract lineno from location
2171-
with captured_stdout(), captured_stderr():
2172-
collector.export(jsonl_out.name)
2214+
collector.export(jsonl_out.name)
21732215

2174-
# Check file contents
2175-
with open(jsonl_out.name, "r") as f:
2176-
content = f.read()
2216+
with open(jsonl_out.name, "r", encoding="utf-8") as f:
2217+
records = [json.loads(line) for line in f]
21772218

2178-
lines = content.strip().split("\n")
2179-
self.assertEqual(len(lines), 5)
2180-
2181-
def jsonl(obj):
2182-
return json.dumps(obj, separators=(",", ":"))
2183-
2184-
expected = [
2185-
jsonl({"type": "meta", "v": 1, "run_id": run_id,
2186-
"sample_interval_usec": 1000}),
2187-
jsonl({"type": "str_def", "v": 1, "run_id": run_id,
2188-
"defs": [{"str_id": 1, "value": "my_function"},
2189-
{"str_id": 2, "value": "test.py"}]}),
2190-
jsonl({"type": "frame_def", "v": 1, "run_id": run_id,
2191-
"defs": [{"frame_id": 1, "path_str_id": 2, "func_str_id": 1,
2192-
"line": 42, "end_line": 42}]}),
2193-
jsonl({"type": "agg", "v": 1, "run_id": run_id,
2194-
"kind": "frame", "scope": "final", "samples_total": 1,
2195-
"entries": [{"frame_id": 1, "self": 1, "cumulative": 1}]}),
2196-
jsonl({"type": "end", "v": 1, "run_id": run_id,
2197-
"samples_total": 1}),
2198-
]
2199-
2200-
for exp in expected:
2201-
self.assertIn(exp, lines)
2219+
meta, str_defs, frame_defs, agg, end = _jsonl_tables(records)
2220+
self.assertEqual(meta["sample_interval_usec"], 1000)
2221+
self.assertEqual(agg["samples_total"], 1)
2222+
self.assertEqual(end["samples_total"], 1)
2223+
self.assertEqual(len(frame_defs), 1)
2224+
self.assertEqual(str_defs[frame_defs[0]["path_str_id"]], "test.py")
2225+
self.assertEqual(str_defs[frame_defs[0]["func_str_id"]], "my_function")
2226+
self.assertEqual(
2227+
frame_defs[0],
2228+
{
2229+
"frame_id": 1,
2230+
"path_str_id": frame_defs[0]["path_str_id"],
2231+
"func_str_id": frame_defs[0]["func_str_id"],
2232+
"line": 42,
2233+
"end_line": 42,
2234+
},
2235+
)
22022236

22032237
def test_jsonl_collector_with_none_location(self):
22042238
"""Test JsonlCollector handles None location (synthetic frames)."""
22052239
jsonl_out = tempfile.NamedTemporaryFile(delete=False)
22062240
self.addCleanup(close_and_unlink, jsonl_out)
22072241

22082242
collector = JsonlCollector(sample_interval_usec=1000)
2209-
run_id = collector.run_id
22102243

22112244
# Create frame with None location (like GC frame)
22122245
frame = MockFrameInfo("~", 0, "<GC>")
@@ -2219,38 +2252,28 @@ def test_jsonl_collector_with_none_location(self):
22192252
]
22202253
collector.collect(frames)
22212254

2222-
# Should handle None location as synthetic frame
2223-
with captured_stdout(), captured_stderr():
2224-
collector.export(jsonl_out.name)
2225-
2226-
# Check file contents
2227-
with open(jsonl_out.name, "r") as f:
2228-
content = f.read()
2229-
2230-
lines = content.strip().split("\n")
2231-
self.assertEqual(len(lines), 5)
2232-
2233-
def jsonl(obj):
2234-
return json.dumps(obj, separators=(",", ":"))
2255+
collector.export(jsonl_out.name)
22352256

2236-
expected = [
2237-
jsonl({"type": "meta", "v": 1, "run_id": run_id,
2238-
"sample_interval_usec": 1000}),
2239-
jsonl({"type": "str_def", "v": 1, "run_id": run_id,
2240-
"defs": [{"str_id": 1, "value": "<GC>"},
2241-
{"str_id": 2, "value": "~"}]}),
2242-
jsonl({"type": "frame_def", "v": 1, "run_id": run_id,
2243-
"defs": [{"frame_id": 1, "path_str_id": 2, "func_str_id": 1,
2244-
"line": 0, "synthetic": True}]}),
2245-
jsonl({"type": "agg", "v": 1, "run_id": run_id,
2246-
"kind": "frame", "scope": "final", "samples_total": 1,
2247-
"entries": [{"frame_id": 1, "self": 1, "cumulative": 1}]}),
2248-
jsonl({"type": "end", "v": 1, "run_id": run_id,
2249-
"samples_total": 1}),
2250-
]
2257+
with open(jsonl_out.name, "r", encoding="utf-8") as f:
2258+
records = [json.loads(line) for line in f]
22512259

2252-
for exp in expected:
2253-
self.assertIn(exp, lines)
2260+
meta, str_defs, frame_defs, agg, end = _jsonl_tables(records)
2261+
self.assertEqual(meta["sample_interval_usec"], 1000)
2262+
self.assertEqual(agg["samples_total"], 1)
2263+
self.assertEqual(end["samples_total"], 1)
2264+
self.assertEqual(len(frame_defs), 1)
2265+
self.assertEqual(str_defs[frame_defs[0]["path_str_id"]], "~")
2266+
self.assertEqual(str_defs[frame_defs[0]["func_str_id"]], "<GC>")
2267+
self.assertEqual(
2268+
frame_defs[0],
2269+
{
2270+
"frame_id": 1,
2271+
"path_str_id": frame_defs[0]["path_str_id"],
2272+
"func_str_id": frame_defs[0]["func_str_id"],
2273+
"line": 0,
2274+
"synthetic": True,
2275+
},
2276+
)
22542277

22552278

22562279
class TestOpcodeHandling(unittest.TestCase):
@@ -2484,18 +2507,7 @@ def test_jsonl_collector_frame_format(self):
24842507
with open(f.name, "r", encoding="utf-8") as fp:
24852508
records = [json.loads(line) for line in fp]
24862509

2487-
str_defs = {
2488-
item["str_id"]: item["value"]
2489-
for record in records
2490-
if record["type"] == "str_def"
2491-
for item in record["defs"]
2492-
}
2493-
frame_defs = [
2494-
item
2495-
for record in records
2496-
if record["type"] == "frame_def"
2497-
for item in record["defs"]
2498-
]
2510+
_, str_defs, frame_defs, _, _ = _jsonl_tables(records)
24992511

25002512
self.assertEqual(len(frame_defs), 3)
25012513

@@ -2662,18 +2674,7 @@ def test_jsonl_collector_filters_internal_frames(self):
26622674
with open(jsonl_out.name, "r", encoding="utf-8") as f:
26632675
records = [json.loads(line) for line in f]
26642676

2665-
str_defs = {
2666-
item["str_id"]: item["value"]
2667-
for record in records
2668-
if record["type"] == "str_def"
2669-
for item in record["defs"]
2670-
}
2671-
frame_defs = [
2672-
item
2673-
for record in records
2674-
if record["type"] == "frame_def"
2675-
for item in record["defs"]
2676-
]
2677+
_, str_defs, frame_defs, _, _ = _jsonl_tables(records)
26772678

26782679
paths = {str_defs[item["path_str_id"]] for item in frame_defs}
26792680

0 commit comments

Comments
 (0)