@@ -349,89 +349,6 @@ public Task TestSetTimeRangeAfterGotoCalleesAsync()
349349 return RunUITestAsync ( setupAsync , testDriverAsync , cleanupAsync ) ;
350350 }
351351
352- [ WpfFact ]
353- [ WorkItem ( 2280 , "https://github.com/Microsoft/perfview/issues/2280" ) ]
354- [ UseCulture ( "en-US" ) ]
355- public Task TestGotoCallersAccumulatesAcrossAllThreadsAsync ( )
356- {
357- Func < Task < StackWindow > > setupAsync = async ( ) =>
358- {
359- await JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
360-
361- var file = new MultiThreadFile ( ) ;
362- await OpenAsync ( JoinableTaskFactory , file , GuiApp . MainWindow , GuiApp . MainWindow . StatusBar ) . ConfigureAwait ( true ) ;
363- var stackSource = file . GetStackSource ( ) ;
364- return stackSource . Viewer ;
365- } ;
366-
367- Func < StackWindow , Task > cleanupAsync = async stackWindow =>
368- {
369- await JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
370- stackWindow . Close ( ) ;
371- } ;
372-
373- Func < StackWindow , Task > testDriverAsync = async stackWindow =>
374- {
375- await JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
376-
377- // Start from the call tree tab (where user typically starts)
378- stackWindow . CallTreeTab . IsSelected = true ;
379- await WaitForUIAsync ( stackWindow . Dispatcher , CancellationToken . None ) ;
380-
381- // Find a specific node in the call tree (simulating selecting a thread-specific node)
382- var callTreeRoot = stackWindow . m_callTreeView . Root ;
383- Assert . NotNull ( callTreeRoot ) ;
384- Assert . True ( callTreeRoot . Callees . Count > 0 , "Should have callees" ) ;
385-
386- // Find a callee that exists across multiple threads
387- var targetNodeName = "MultiThreadMethod" ;
388- var callTreeNode = callTreeRoot . Callees . FirstOrDefault ( c => c . Name == targetNodeName ) ;
389- Assert . NotNull ( callTreeNode , $ "Should find { targetNodeName } in call tree") ;
390-
391- // Simulate selecting this node in CallTree view
392- stackWindow . CallTreeDataGrid . Grid . SelectedIndex = 0 ; // Select first item
393- await WaitForUIAsync ( stackWindow . Dispatcher , CancellationToken . None ) ;
394-
395- // Get the inclusive metric from ByName view (this aggregates across all threads)
396- stackWindow . ByNameTab . IsSelected = true ;
397- await WaitForUIAsync ( stackWindow . Dispatcher , CancellationToken . None ) ;
398-
399- var byNameNode = stackWindow . m_byNameView . FirstOrDefault ( n => n . Name == targetNodeName ) ;
400- Assert . NotNull ( byNameNode , $ "Should find { targetNodeName } in ByName view") ;
401- var expectedInclusiveMetric = byNameNode . InclusiveMetric ;
402-
403- // Go back to CallTree and use F10 (Goto Item in Callers)
404- stackWindow . CallTreeTab . IsSelected = true ;
405- await WaitForUIAsync ( stackWindow . Dispatcher , CancellationToken . None ) ;
406-
407- // Execute the "Goto Item in Callers" command (this calls DoViewInCallers)
408- stackWindow . CallersTab . IsSelected = true ;
409- await WaitForUIAsync ( stackWindow . Dispatcher , CancellationToken . None ) ;
410-
411- // Verify that the callers view shows accumulated data from all threads
412- var focusName = stackWindow . FocusName ;
413- Assert . Equal ( targetNodeName , focusName ) ;
414-
415- // The key test: verify that the focus node in callers view has the same
416- // inclusive metric as the ByName view (indicating it's aggregated across all threads)
417- var callersView = stackWindow . m_callersView ;
418- Assert . True ( callersView . Count > 0 , "Should have callers" ) ;
419-
420- // Find the root of callers view which should represent all callers across all threads
421- var callersRoot = callersView . FirstOrDefault ( ) ;
422- Assert . NotNull ( callersRoot ) ;
423-
424- // This is the critical assertion: the callers view should show aggregated data
425- // If the regression existed, this would only show one thread's worth of callers
426- Assert . True ( callersRoot . InclusiveMetric > 0 , "Callers should have positive inclusive metric" ) ;
427-
428- // For a multi-thread scenario, we should see multiple samples
429- Assert . True ( callersRoot . InclusiveCount >= 2 , "Should accumulate samples from multiple threads" ) ;
430- } ;
431-
432- return RunUITestAsync ( setupAsync , testDriverAsync , cleanupAsync ) ;
433- }
434-
435352 private Task TestSetTimeRangeWithSpaceImplAsync ( CultureInfo culture )
436353 {
437354 Func < Task < StackWindow > > setupAsync = async ( ) =>
@@ -942,167 +859,5 @@ public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verbo
942859 return frameIndex . ToString ( ) ;
943860 }
944861 }
945-
946- /// <summary>
947- /// A file containing multiple threads with the same method to test caller/callee aggregation
948- /// </summary>
949- private class MultiThreadFile : PerfViewFile
950- {
951- public MultiThreadFile ( ) : this ( new MultiThreadStackSource ( ) )
952- {
953- }
954-
955- public MultiThreadFile ( MultiThreadStackSource stackSource )
956- {
957- Title = FormatName = nameof ( MultiThreadFile ) ;
958- StackSource = stackSource ;
959- }
960-
961- public override string Title { get ; }
962-
963- public override string FormatName { get ; }
964-
965- public override string [ ] FileExtensions { get ; } = new [ ] { "Multi Thread Test" } ;
966-
967- public MultiThreadStackSource StackSource { get ; }
968-
969- protected override Action < Action > OpenImpl ( Window parentWindow , StatusBar worker )
970- {
971- return doAfter =>
972- {
973- m_singletonStackSource = new PerfViewStackSource ( this , string . Empty ) ;
974- m_singletonStackSource . Open ( parentWindow , worker , doAfter ) ;
975- } ;
976- }
977-
978- protected internal override StackSource OpenStackSourceImpl ( TextWriter log )
979- {
980- return StackSource ;
981- }
982- }
983-
984- private class MultiThreadStackSource : StackSource
985- {
986- // Define call stacks for multi-thread scenario
987- private const StackSourceCallStackIndex StackRoot = StackSourceCallStackIndex . Invalid ;
988- private const StackSourceCallStackIndex StackThread1Entry = StackSourceCallStackIndex . Start + 0 ;
989- private const StackSourceCallStackIndex StackThread1Multi = StackSourceCallStackIndex . Start + 1 ;
990- private const StackSourceCallStackIndex StackThread2Entry = StackSourceCallStackIndex . Start + 2 ;
991- private const StackSourceCallStackIndex StackThread2Multi = StackSourceCallStackIndex . Start + 3 ;
992-
993- private const StackSourceFrameIndex FrameThread1Entry = StackSourceFrameIndex . Start + 0 ;
994- private const StackSourceFrameIndex FrameMultiThreadMethod = StackSourceFrameIndex . Start + 1 ;
995- private const StackSourceFrameIndex FrameThread2Entry = StackSourceFrameIndex . Start + 2 ;
996-
997- public MultiThreadStackSource ( )
998- {
999- Samples = new List < StackSourceSample > ( 4 )
1000- {
1001- new StackSourceSample ( this )
1002- {
1003- SampleIndex = 0 ,
1004- StackIndex = StackThread1Multi ,
1005- Metric = 1 ,
1006- TimeRelativeMSec = 0
1007- } ,
1008- new StackSourceSample ( this )
1009- {
1010- SampleIndex = ( StackSourceSampleIndex ) 1 ,
1011- StackIndex = StackThread2Multi ,
1012- Metric = 1 ,
1013- TimeRelativeMSec = 100
1014- } ,
1015- new StackSourceSample ( this )
1016- {
1017- SampleIndex = ( StackSourceSampleIndex ) 2 ,
1018- StackIndex = StackThread1Multi ,
1019- Metric = 1 ,
1020- TimeRelativeMSec = 200
1021- } ,
1022- new StackSourceSample ( this )
1023- {
1024- SampleIndex = ( StackSourceSampleIndex ) 3 ,
1025- StackIndex = StackThread2Multi ,
1026- Metric = 1 ,
1027- TimeRelativeMSec = 300
1028- } ,
1029- } ;
1030- }
1031-
1032- public List < StackSourceSample > Samples { get ; }
1033-
1034- public override int CallStackIndexLimit
1035- => ( int ) StackThread2Multi + 1 ;
1036-
1037- public override int CallFrameIndexLimit
1038- => ( int ) FrameThread2Entry + 1 ;
1039-
1040- public override int SampleIndexLimit
1041- => Samples . Count ;
1042-
1043- public override double SampleTimeRelativeMSecLimit
1044- => 400 ;
1045-
1046- public override void ForEach ( Func < StackSourceSample , bool > callback )
1047- {
1048- foreach ( var sample in Samples )
1049- {
1050- if ( ! callback ( sample ) )
1051- {
1052- break ;
1053- }
1054- }
1055- }
1056-
1057- public override StackSourceSample GetSampleByIndex ( StackSourceSampleIndex sampleIndex )
1058- => Samples [ ( int ) sampleIndex ] ;
1059-
1060- public override StackSourceCallStackIndex GetCallerIndex ( StackSourceCallStackIndex callStackIndex )
1061- {
1062- switch ( callStackIndex )
1063- {
1064- case StackThread1Multi :
1065- return StackThread1Entry ;
1066- case StackThread2Multi :
1067- return StackThread2Entry ;
1068- case StackThread1Entry :
1069- case StackThread2Entry :
1070- return StackSourceCallStackIndex . Invalid ;
1071- default :
1072- return StackSourceCallStackIndex . Invalid ;
1073- }
1074- }
1075-
1076- public override StackSourceFrameIndex GetFrameIndex ( StackSourceCallStackIndex callStackIndex )
1077- {
1078- switch ( callStackIndex )
1079- {
1080- case StackThread1Entry :
1081- return FrameThread1Entry ;
1082- case StackThread1Multi :
1083- case StackThread2Multi :
1084- return FrameMultiThreadMethod ;
1085- case StackThread2Entry :
1086- return FrameThread2Entry ;
1087- default :
1088- return StackSourceFrameIndex . Root ;
1089- }
1090- }
1091-
1092- public override string GetFrameName ( StackSourceFrameIndex frameIndex , bool verboseName )
1093- {
1094- switch ( frameIndex )
1095- {
1096- case FrameThread1Entry :
1097- return "Thread1Entry" ;
1098- case FrameMultiThreadMethod :
1099- return "MultiThreadMethod" ;
1100- case FrameThread2Entry :
1101- return "Thread2Entry" ;
1102- default :
1103- return frameIndex . ToString ( ) ;
1104- }
1105- }
1106- }
1107862 }
1108863}
0 commit comments