11# Copyright (c) Meta Platforms, Inc. and affiliates.
22# This source code is licensed under the MIT license found in the
33# LICENSE file in the root directory of this source tree.
4- # Unit tests for call_stack.py
54
65import unittest
76
87import pandas as pd
98
10- from hta .common .call_stack import (
11- CallGraph ,
12- CallStackGraph ,
13- CallStackIdentity ,
14- CallStackNode ,
15- )
9+ from hta .common .call_stack import CallStackGraph , CallStackIdentity , CallStackNode
1610from hta .common .trace_filter import ZeroDurationFilter
1711
1812
@@ -87,7 +81,7 @@ def test_construct_call_graph_0_dur(self):
8781 self .assertDictEqual (nodes , self .nodes2 )
8882
8983 def test_sort_events (self ):
90- index = [0 , 1 , 2 , 3 ]
84+ index = [1 , 2 , 3 , 4 ]
9185 start = [0 , 0 , 5 , 5 ]
9286 dur = [10 , 5 , 1 , 5 ]
9387 stream = [- 1 , - 1 , - 1 , - 1 ]
@@ -102,11 +96,11 @@ def test_sort_events(self):
10296 }
10397 )
10498 nodes = {
105- - 1 : CallStackNode (parent = - 1 , depth = - 1 , children = [0 ]),
106- 0 : CallStackNode (parent = - 1 , depth = 0 , children = [1 , 3 ]),
107- 1 : CallStackNode (parent = 0 , depth = 1 , children = []),
108- 2 : CallStackNode (parent = 3 , depth = 2 , children = []),
109- 3 : CallStackNode (parent = 0 , depth = 1 , children = [2 ]),
99+ - 1 : CallStackNode (parent = - 1 , depth = - 1 , children = [1 ]),
100+ 1 : CallStackNode (parent = - 1 , depth = 0 , children = [2 , 4 ]),
101+ 2 : CallStackNode (parent = 1 , depth = 1 , children = []),
102+ 4 : CallStackNode (parent = 1 , depth = 1 , children = [3 ]),
103+ 3 : CallStackNode (parent = 4 , depth = 2 , children = []),
110104 }
111105 csg = CallStackGraph (df , self .csi )
112106 self .assertDictEqual (nodes , csg .get_nodes ())
@@ -136,164 +130,6 @@ def test_node_depth(self):
136130 depth_from_csg = csg .get_depth ().to_dict ()
137131 depth_from_nodes = {idx : node .depth for idx , node in nodes .items () if idx >= 0 }
138132 self .assertDictEqual (depth_from_csg , depth_from_nodes )
139- # Verify df is used
140- self .assertIsNotNone (df )
141-
142-
143- class CallGraphTestCase (unittest .TestCase ):
144- def setUp (self ) -> None :
145- super ().setUp ()
146-
147- # Mock Trace class for testing
148- class MockTrace :
149- def __init__ (self , traces ):
150- self .traces = traces
151-
152- def get_all_traces (self ):
153- return self .traces .keys ()
154-
155- def get_trace (self , rank ):
156- return self .traces [rank ]
157-
158- # Create test data for trim_trace_events
159- self .df_trim = pd .DataFrame (
160- {
161- "index" : [0 , 1 , 2 , 3 ],
162- "ts" : [0 , 2 , 4 , 6 ],
163- "dur" : [10 , 6 , 8 , 2 ], # Child event 2 exceeds parent event 1's duration
164- "pid" : [1 , 1 , 1 , 1 ],
165- "tid" : [1 , 1 , 1 , 1 ],
166- "stream" : [- 1 , - 1 , - 1 , - 1 ],
167- "index_correlation" : [- 1 , - 1 , - 1 , - 1 ],
168- "python_id" : [100 , 101 , 102 , 103 ],
169- "python_parent_id" : [- 1 , 100 , 101 , 102 ],
170- }
171- )
172-
173- self .trace_mock = MockTrace ({0 : self .df_trim })
174-
175- def test_trim_trace_events_basic (self ):
176- # Given & when
177- CallGraph (self .trace_mock , pre_process_trace_data = True )
178-
179- # Then
180- # Event 1: ts=2, dur=6, end=8
181- # Event 2: ts=4, dur=8 (original), should be trimmed to dur=4 to end at 8
182- self .assertEqual (self .df_trim .at [2 , "dur" ], 4 )
183-
184- def test_trim_trace_events_complex_hierarchy (self ):
185- # Given
186- df_complex = pd .DataFrame (
187- {
188- "index" : [0 , 1 , 2 , 3 , 4 ],
189- "ts" : [0 , 2 , 4 , 6 , 7 ],
190- "dur" : [15 , 10 , 10 , 20 , 8 ],
191- "pid" : [1 , 1 , 1 , 1 , 1 ],
192- "tid" : [1 , 1 , 1 , 1 , 1 ],
193- "stream" : [- 1 , - 1 , - 1 , - 1 , - 1 ],
194- "index_correlation" : [- 1 , - 1 , - 1 , - 1 , - 1 ],
195- "python_id" : [100 , 101 , 102 , 103 , 104 ],
196- "python_parent_id" : [- 1 , 100 , 101 , 102 , 102 ],
197- }
198- )
199-
200- trace_complex = type (self .trace_mock )({0 : df_complex })
201-
202- # When
203- CallGraph (trace_complex , pre_process_trace_data = True )
204-
205- # Then
206- # Event 1: ts=2, dur=10, end=12
207- # Event 2: ts=4, dur=10, should be trimmed to dur=8 to end at 12
208- # Event 3: ts=6, dur=20, should be trimmed to dur=6 to end at 12
209- # Event 4: ts=7, dur=8, should be trimmed to dur=5 to end at 12
210- self .assertEqual (df_complex .at [2 , "dur" ], 8 )
211- self .assertEqual (df_complex .at [3 , "dur" ], 6 )
212- self .assertEqual (df_complex .at [4 , "dur" ], 5 )
213-
214- def test_trim_trace_events_different_threads (self ):
215- # Given
216- df_threads = pd .DataFrame (
217- {
218- "index" : [0 , 1 , 2 , 3 ],
219- "ts" : [0 , 2 , 4 , 6 ],
220- "dur" : [10 , 6 , 8 , 2 ],
221- "pid" : [1 , 1 , 1 , 1 ],
222- "tid" : [1 , 1 , 2 , 2 ], # Events 2 and 3 are in a different thread
223- "stream" : [- 1 , - 1 , - 1 , - 1 ],
224- "index_correlation" : [- 1 , - 1 , - 1 , - 1 ],
225- "python_id" : [100 , 101 , 102 , 103 ],
226- "python_parent_id" : [- 1 , 100 , 101 , 102 ],
227- }
228- )
229-
230- trace_threads = type (self .trace_mock )({0 : df_threads })
231-
232- # When
233- CallGraph (trace_threads , pre_process_trace_data = True )
234-
235- # Then
236- # Event 2 should not be trimmed because it's in a different thread than its parent
237- self .assertEqual (df_threads .at [2 , "dur" ], 8 )
238-
239- def test_trim_trace_events_no_trimming_needed (self ):
240- # Given
241- df_no_trim = pd .DataFrame (
242- {
243- "index" : [0 , 1 , 2 , 3 ],
244- "ts" : [0 , 2 , 4 , 6 ],
245- "dur" : [10 , 6 , 3 , 1 ], # All child events end before their parents
246- "pid" : [1 , 1 , 1 , 1 ],
247- "tid" : [1 , 1 , 1 , 1 ],
248- "stream" : [- 1 , - 1 , - 1 , - 1 ],
249- "index_correlation" : [- 1 , - 1 , - 1 , - 1 ],
250- "python_id" : [100 , 101 , 102 , 103 ],
251- "python_parent_id" : [- 1 , 100 , 101 , 102 ],
252- }
253- )
254-
255- trace_no_trim = type (self .trace_mock )({0 : df_no_trim })
256-
257- # When
258- CallGraph (trace_no_trim , pre_process_trace_data = True )
259-
260- # Then
261- # Durations should remain unchanged
262- self .assertEqual (df_no_trim .at [1 , "dur" ], 6 )
263- self .assertEqual (df_no_trim .at [2 , "dur" ], 3 )
264- self .assertEqual (df_no_trim .at [3 , "dur" ], 1 )
265-
266- def test_trim_trace_events_multiple_children (self ):
267- # Given
268- df_multi_children = pd .DataFrame (
269- {
270- "index" : [0 , 1 , 2 , 3 , 4 ],
271- "ts" : [0 , 2 , 3 , 6 , 8 ],
272- "dur" : [
273- 10 ,
274- 8 ,
275- 3 ,
276- 2 ,
277- 5 ,
278- ], # Child event 4 exceeds parent event 1's duration
279- "pid" : [1 , 1 , 1 , 1 , 1 ],
280- "tid" : [1 , 1 , 1 , 1 , 1 ],
281- "stream" : [- 1 , - 1 , - 1 , - 1 , - 1 ],
282- "index_correlation" : [- 1 , - 1 , - 1 , - 1 , - 1 ],
283- "python_id" : [100 , 101 , 102 , 103 , 104 ],
284- "python_parent_id" : [- 1 , 100 , 101 , 101 , 101 ],
285- }
286- )
287-
288- trace_multi = type (self .trace_mock )({0 : df_multi_children })
289-
290- # When
291- CallGraph (trace_multi , pre_process_trace_data = True )
292-
293- # Then
294- # Event 1: ts=2, dur=8, end=10
295- # Event 4: ts=8, dur=5, should be trimmed to dur=2 to end at 10
296- self .assertEqual (df_multi_children .at [4 , "dur" ], 2 )
297133
298134
299135if __name__ == "__main__" : # pragma: no cover
0 commit comments