private static void ProcessHyperlinkCommand(string command) { GuiApp.MainWindow.StatusBar.LogWriter.WriteLine("Got command " + command); string[] args = command.Split(new char[] { ',' }); StackWindow stackWindow = null; switch (args[0]) { case "ShowBaseStacks": stackWindow = _baselineWindow; break; case "ShowStacks": stackWindow = _sourceWindow; break; } try { stackWindow.SetFocus(args[1]); stackWindow.CallersTab.IsSelected = true; stackWindow.Focus(); } catch (System.NullReferenceException) { GuiApp.MainWindow.StatusBar.LogWriter.WriteLine("Failed to find Stack Window. (Has it been closed?)"); } }
private void OnOpenLargeAllocStacks(object sender, RoutedEventArgs e) { StackSource stacks = m_dataFile.CreateStackSource("GC Heap Alloc Ignore Free (Coarse Sampling)", m_process.ProcessID, m_statusBar.LogWriter, true); StackWindow stackWin = null; m_dataFile.StackWindowTo(null, ref stackWin, stacks, "LOH Heap Alloc"); }
private void OnOpenInducedStacks(object sender, RoutedEventArgs e) { StackSourceBuilder builder = new StackSourceBuilder(m_traceLog); List <TraceGC> events = m_runtime.GC.GCs; for (int i = 0; i < events.Count; i++) { TraceGC ev = events[i]; if (ev.IsInduced()) { GcEventExtra extra = GetGcEventExtra(ev.Number, false); if (extra != null) { builder.AddSample(extra.GCStartThread, ev.PauseDurationMSec, ev.StartRelativeMSec, String.Format("StartGC({0}, {1}, G{2})", ev.Reason, ev.Type, ev.Generation), extra.GCStartIndex); } } } StackSource source = builder.Stacks; if (source.SampleIndexLimit == 0) { MessageBox.Show("No stacks found for induced GC", ".Net Heap Analyzer", MessageBoxButton.OK); } else { StackWindow stackWindow = null; m_dataFile.StackWindowTo(null, ref stackWindow, source, "Induced GC", FirstEventTime, LastEventTime); } }
private static async Task <PerfDataGrid> SelectTabAsync(StackWindow stackWindow, KnownDataGrid grid, CancellationToken cancellationToken) { Assert.Same(stackWindow.Dispatcher.Thread, Thread.CurrentThread); var tabControl = (TabControl)stackWindow.ByNameTab.Parent; PerfDataGrid dataGrid; switch (grid) { case KnownDataGrid.ByName: tabControl.SelectedItem = stackWindow.ByNameTab; dataGrid = stackWindow.ByNameDataGrid; break; case KnownDataGrid.CallerCalleeCallers: case KnownDataGrid.CallerCalleeFocus: case KnownDataGrid.CallerCalleeCallees: tabControl.SelectedItem = stackWindow.CallerCalleeTab; if (grid == KnownDataGrid.CallerCalleeCallers) { dataGrid = stackWindow.CallerCalleeView.CallersGrid; } else if (grid == KnownDataGrid.CallerCalleeFocus) { dataGrid = stackWindow.CallerCalleeView.FocusGrid; } else { dataGrid = stackWindow.CallerCalleeView.CalleesGrid; } break; case KnownDataGrid.CallTree: tabControl.SelectedItem = stackWindow.CallTreeTab; dataGrid = stackWindow.CallTreeDataGrid; break; case KnownDataGrid.Callers: tabControl.SelectedItem = stackWindow.CallersTab; dataGrid = stackWindow.CallersDataGrid; break; case KnownDataGrid.Callees: tabControl.SelectedItem = stackWindow.CalleesTab; dataGrid = stackWindow.CalleesDataGrid; break; default: throw new ArgumentException("Unsupported data grid.", nameof(grid)); } await WaitForUIAsync(stackWindow.Dispatcher, cancellationToken); if (!dataGrid.Grid.HasItems) { PerfDataGrid gridToDoubleClick = null; if (grid == KnownDataGrid.CallerCalleeCallers) { gridToDoubleClick = stackWindow.CallerCalleeView.CalleesGrid; } else if (grid == KnownDataGrid.CallerCalleeCallees) { gridToDoubleClick = stackWindow.CallerCalleeView.CallersGrid; } if (gridToDoubleClick?.Grid.HasItems ?? false) { var itemToDoubleClick = gridToDoubleClick.Grid.Items[0]; var cellToDoubleClick = (DataGridCell)gridToDoubleClick.DisplayNameColumn.GetCellContent(itemToDoubleClick).Parent; var border = (Border)VisualTreeHelper.GetChild(cellToDoubleClick, 0); var textBlock = (TextBlock)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(border, 0), 0), 0); Point controlCenter = new Point(textBlock.ActualWidth / 2, textBlock.ActualHeight / 2); Point controlCenterOnView = textBlock.TranslatePoint(controlCenter, (UIElement)Helpers.RootVisual(textBlock)); Point controlCenterOnDevice = PresentationSource.FromDependencyObject(Helpers.RootVisual(textBlock)).CompositionTarget.TransformToDevice.Transform(controlCenterOnView); RaiseMouseInputReportEvent(textBlock, Environment.TickCount, (int)controlCenterOnDevice.X, (int)controlCenterOnDevice.Y, 0); gridToDoubleClick.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left) { RoutedEvent = Control.MouseDoubleClickEvent, Source = textBlock }); await WaitForUIAsync(stackWindow.Dispatcher, cancellationToken); } } return(dataGrid); }
public static void GenerateOverweightReport(string outputHtmlReport, StackWindow sourceWindow, StackWindow baselineWindow) { _sourceWindow = sourceWindow; _baselineWindow = baselineWindow; StackSource source = sourceWindow.CallTree.StackSource; StackSource baselineSource = baselineWindow.CallTree.StackSource; TextWriter log = GuiApp.MainWindow.StatusBar.LogWriter; var d1 = new Dictionary <string, float>(); var d2 = new Dictionary <string, float>(); var results = new List <Result>(); float total1 = LoadOneTrace(baselineSource, d1); float total2 = LoadOneTrace(source, d2); if (total1 != total2) { ComputeOverweights(d1, d2, results, total1, total2); } using (var w = File.CreateText(outputHtmlReport)) { w.WriteLine("<h1>Overweight report for symbols common between both files</h1>"); w.WriteLine("<br>"); float delta = total2 - total1; float growthPercent = delta / total1 * 100; w.WriteLine("<table style=\"font-size:10pt;\" border=\"1\">"); w.WriteLine("<tr><td>Base (old) Time:</td><td>{0:f1}</td></tr>", total1); w.WriteLine("<tr><td>Test (new) Time:</td><td>{0:f1}</td></tr>", total2); w.WriteLine("<tr><td>Delta:</td><td>{0:f1}</td></tr>", delta); w.WriteLine("<tr><td>Delta %:</td><td>{0:f1}</td></tr>", growthPercent); w.WriteLine("</table>"); w.WriteLine("<br>"); w.WriteLine("In this report, overweight is ratio of actual growth compared to {0:f1}%.<br>", growthPercent); w.WriteLine("Interest level attempts to identify smaller methods which changed a lot. These are likely the most interesting frames to sart investigating<br>"); w.WriteLine("An overweight of greater than 100% indicates the symbol grew in cost more than average.<br>"); w.WriteLine("High overweights are a likely source of regressions and represent good places to investigate.<br>"); w.WriteLine("Only symbols that have at least 2% impact are shown.<br>"); w.WriteLine("<br>"); if (results.Count == 0) { w.WriteLine("There was no growth or no matching symbols, overweight analysis not possible<br>"); } else { w.WriteLine("<table style=\"font-size:10pt;\" border=\"1\">"); w.WriteLine("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td><td>{6}</td></tr>", "<b>Name</b>", "<b>Base</b>", "<b>Test</b>", "<b>Delta</b>", "<b>Responsibility %</b>", "<b>Overweight %</b>", "<b>Interest Level</b>" ); foreach (var r in results) { // filter out the small stuff if (Math.Abs(r.percent) < 2) { continue; } w.WriteLine("<tr><td>{0}</td><td><a href='command:ShowBaseStacks,{0}' title='View Callers of {0} in Base Stacks'>{1:f1}</a></td><td><a href='command:ShowStacks,{0}' title='View Callers of {0} in Test Stacks'>{2:f1}</a></td><td>{3:f1}</td><td>{4:f2}</td><td>{5:f2}</td><td>{6}</td></tr>", r.name, r.before, r.after, r.delta, r.percent, r.overweight, r.interest ); } w.WriteLine("</table>"); } } }