package com.jetbrains.actionscript.profiler.ui; import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; import com.intellij.ide.util.scopeChooser.ScopeDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.scope.ProjectFilesScope; import com.intellij.ui.PopupHandler; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeTableSpeedSearch; import com.intellij.util.Alarm; import com.intellij.util.Function; import com.intellij.util.ui.tree.TreeUtil; import com.jetbrains.actionscript.profiler.calltree.CallTree; import com.jetbrains.actionscript.profiler.calltree.CallTreeUtil; import com.jetbrains.actionscript.profiler.calltreetable.CallTreeTable; import com.jetbrains.actionscript.profiler.calltreetable.MergedCallNode; import com.jetbrains.actionscript.profiler.model.ProfileData; import com.jetbrains.actionscript.profiler.render.FrameInfoCellRenderer; import com.jetbrains.actionscript.profiler.sampler.FrameInfo; import com.jetbrains.actionscript.profiler.sampler.Sample; import com.jetbrains.actionscript.profiler.util.AllSearchScope; import com.jetbrains.actionscript.profiler.util.JTreeUtil; import com.jetbrains.actionscript.profiler.util.ResolveUtil; import com.jetbrains.actionscript.profiler.vo.CallInfo; import com.jetbrains.profiler.ProfileView; import icons.FlexProfilerIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableColumn; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author: Fedor.Korotkov */ public class CPUSnapshotView extends ProfileView implements Disposable { private static final int MS_COLUMN_WIDTH = 140; private JCheckBox myFilterSystemStuff; private ScopeChooserCombo filterScope; private JLabel scopeLabel; private CallTreeTable myHotSpotsTreeTable; private CallTreeTable myTracesTreeTable; private JPanel mainPanel; private JLabel invokedFunctionsLabel; private JLabel mergedCalleesLabel; private JPanel topPanel; private JPanel bottomPanel; private final CallTree rawCallTree; private Alarm myAlarm; private final GlobalSearchScope projectScope; private final Function<List<FrameInfo>, List<FrameInfo>> scopeMatcher = traces -> { final GlobalSearchScope scope = getCurrentScope(); return ResolveUtil.filterByScope(traces, scope); }; public CPUSnapshotView(VirtualFile file, Project project) { super(file, project); projectScope = GlobalSearchScope.projectScope(project); rawCallTree = file.getUserData(ProfileData.CALL_TREE_KEY); setupUI(); buildPerformanceSamples(myHotSpotsTreeTable.getSortableTreeTableModel()); } @Nullable private GlobalSearchScope getCurrentScope() { final SearchScope _selectedScope = filterScope.getSelectedScope(); return _selectedScope instanceof GlobalSearchScope ? (GlobalSearchScope)_selectedScope : GlobalSearchScope.allScope(getProject()); } @Override protected void uiSettingsChange() { super.uiSettingsChange(); myHotSpotsTreeTable.clearColorCaches(); myTracesTreeTable.clearColorCaches(); } private void setupUI() { myHotSpotsTreeTable.setRootVisible(false); myTracesTreeTable.setRootVisible(false); setColumnWidth(myHotSpotsTreeTable.getColumnModel().getColumn(1), MS_COLUMN_WIDTH); setColumnWidth(myHotSpotsTreeTable.getColumnModel().getColumn(2), MS_COLUMN_WIDTH); setColumnWidth(myTracesTreeTable.getColumnModel().getColumn(1), MS_COLUMN_WIDTH); setColumnWidth(myTracesTreeTable.getColumnModel().getColumn(2), MS_COLUMN_WIDTH); scopeLabel.setLabelFor(filterScope.getComboBox()); invokedFunctionsLabel.setLabelFor(myHotSpotsTreeTable); mergedCalleesLabel.setLabelFor(myTracesTreeTable); new TreeTableSpeedSearch(myHotSpotsTreeTable).setComparator(new SpeedSearchComparator(false)); new TreeTableSpeedSearch(myTracesTreeTable).setComparator(new SpeedSearchComparator(false)); PopupHandler.installPopupHandler(myHotSpotsTreeTable, PROFILER_VIEW_GROUP_ID, ActionPlaces.UNKNOWN); PopupHandler.installPopupHandler(myTracesTreeTable, PROFILER_VIEW_GROUP_ID, ActionPlaces.UNKNOWN); final ComboBoxModel model = filterScope.getComboBox().getModel(); if (model instanceof DefaultComboBoxModel) { ((DefaultComboBoxModel)model).insertElementAt(new ScopeDescriptor(new AllSearchScope(getProject())), 0); } myHotSpotsTreeTable.getTree().setCellRenderer(new FrameInfoCellRenderer(projectScope) { @Override public void customizeCellRenderer(Object value, boolean selected) { setPaintFocusBorder(false); setScopeIcon(FlexProfilerIcons.CallerArrow); setNonScopeIcon(FlexProfilerIcons.CallerLeafArrow); super.customizeCellRenderer(value, selected); } }); myTracesTreeTable.getTree().setCellRenderer(new FrameInfoCellRenderer(projectScope) { @Override public void customizeCellRenderer(Object value, boolean selected) { setPaintFocusBorder(false); setScopeIcon(FlexProfilerIcons.CalleeArrow); setNonScopeIcon(FlexProfilerIcons.CalleeLeafArrow); super.customizeCellRenderer(value, selected); } }); myFilterSystemStuff.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { buildPerformanceSamples(myHotSpotsTreeTable.getSortableTreeTableModel()); TreeUtil.expand(myHotSpotsTreeTable.getTree(), 1); } }); filterScope.getComboBox().addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { myAlarm.cancelAllRequests(); myAlarm.addRequest(() -> { buildPerformanceSamples(myHotSpotsTreeTable.getSortableTreeTableModel()); TreeUtil.expand(myHotSpotsTreeTable.getTree(), 1); }, 100); } }); } private void createUIComponents() { myTracesTreeTable = new CallTreeTable(getProject()); createHotSpotsTreeTable(); myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); filterScope = new ScopeChooserCombo(getProject(), true, false, ProjectFilesScope.NAME); } private void createHotSpotsTreeTable() { myHotSpotsTreeTable = new CallTreeTable(getProject()); myHotSpotsTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { final Object node = myHotSpotsTreeTable.getSelectedValue(); if (!(node instanceof MergedCallNode)) return; myAlarm.cancelAllRequests(); final MergedCallNode mergedCallNode = (MergedCallNode)node; myAlarm.addRequest(() -> { FrameInfo[] frames = new FrameInfo[]{mergedCallNode.getFrameInfo()}; final Pair<Map<FrameInfo, Long>, Map<FrameInfo, Long>> countMaps = mergedCallNode.getCallTree().getCalleesTimeMaps(frames); final Map<FrameInfo, Long> countMap = countMaps.getFirst(); final Map<FrameInfo, Long> selfCountMap = countMaps.getSecond(); DefaultMutableTreeNode tracesRoot = (DefaultMutableTreeNode)myTracesTreeTable.getSortableTreeTableModel().getRoot(); JTreeUtil.removeChildren(tracesRoot, myTracesTreeTable.getSortableTreeTableModel()); fillTreeModelRoot(tracesRoot, mergedCallNode.getCallTree(), countMap, selfCountMap, false, frames); myTracesTreeTable.reload(); TreeUtil.expand(myTracesTreeTable.getTree(), 1); }, 500); } }); } private void buildPerformanceSamples(final DefaultTreeModel treeModel) { final boolean skipSystemStuff = myFilterSystemStuff.isSelected(); CallTree filteredCallTree = rawCallTree; if (skipSystemStuff) { filteredCallTree = CallTreeUtil.filterSystemStuff(filteredCallTree); } final Pair<Map<FrameInfo, Long>, Map<FrameInfo, Long>> countMaps = filteredCallTree.getTimeMaps(); final Map<FrameInfo, Long> countMap = countMaps.getFirst(); final Map<FrameInfo, Long> selfCountMap = countMaps.getSecond(); DefaultMutableTreeNode tracesRoot = (DefaultMutableTreeNode)treeModel.getRoot(); JTreeUtil.removeChildren(tracesRoot, treeModel); fillTreeModelRoot(tracesRoot, filteredCallTree, countMap, selfCountMap, true, FrameInfo.EMPTY_FRAME_INFO_ARRAY); treeModel.reload(); } private <T extends Sample> void fillTreeModelRoot(TreeNode node, CallTree callTree, final Map<FrameInfo, Long> countMap, final Map<FrameInfo, Long> selfCountMap, boolean backTrace, FrameInfo[] frames) { final MutableTreeNode root = (MutableTreeNode)node; List<FrameInfo> traces = scopeMatcher.fun(new ArrayList<>(countMap.keySet())); GlobalSearchScope scope = getCurrentScope(); int index = 0; for (final FrameInfo s : traces) { root .insert(new MergedCallNode<T>(new CallInfo(s, countMap.get(s), selfCountMap.get(s)), callTree, frames, backTrace, scope), index++); } } private static void setColumnWidth(TableColumn column, int newSize) { column.setMinWidth(newSize); column.setWidth(newSize); column.setMaxWidth(newSize); } @Override public void dispose() { } @NotNull @Override public JComponent getComponent() { return mainPanel; } @Override public JComponent getPreferredFocusedComponent() { return null; } }