diff -r b06ca8658cd4 application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/GarbageCollectionsPage.java --- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/GarbageCollectionsPage.java Fri Aug 17 18:37:47 2018 +0200 +++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/GarbageCollectionsPage.java Mon Aug 20 11:55:23 2018 -0400 @@ -66,9 +66,11 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; - +import org.openjdk.jmc.common.IMCThread; import org.openjdk.jmc.common.IState; import org.openjdk.jmc.common.IWritableState; +import org.openjdk.jmc.common.item.Aggregators; +import org.openjdk.jmc.common.item.IAggregator; import org.openjdk.jmc.common.item.IAttribute; import org.openjdk.jmc.common.item.IItem; import org.openjdk.jmc.common.item.IItemCollection; @@ -107,6 +109,7 @@ import org.openjdk.jmc.flightrecorder.ui.common.ItemList; import org.openjdk.jmc.flightrecorder.ui.common.ItemList.ItemListBuilder; import org.openjdk.jmc.flightrecorder.ui.common.ItemRow; +import org.openjdk.jmc.flightrecorder.ui.common.ThreadGraphLanes; import org.openjdk.jmc.flightrecorder.ui.common.TypeLabelProvider; import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages; import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStoreActionToolkit; @@ -156,6 +159,7 @@ private static final ReferenceStatisticsType[] REF_TYPE = ReferenceStatisticsType.values(); private static final String SASH = "sash"; //$NON-NLS-1$ private static final String TABLE_SASH = "tableSash"; //$NON-NLS-1$ + private static final String THREAD_LANES = "threadLane"; // $NON-NLS-1$ private static final String GCS = "gcs"; //$NON-NLS-1$ private static final String CHART = "chart"; //$NON-NLS-1$ private static final String PHASE_TABLE_FILTER = "phaseTableFilter"; //$NON-NLS-1$ @@ -163,6 +167,7 @@ private static final String METASPACE_TABLE_FILTER = "metaspaceTableFilter"; //$NON-NLS-1$ private static final String PHASE_LIST = "phaseList"; //$NON-NLS-1$ private static final String METASPACE_LIST = "metaspaceList"; //$NON-NLS-1$ + private static final String ACTIVITY_LANES_ID = "threadActivityLanes"; //$NON-NLS-1$ private final static Color LONGEST_PAUSE_COLOR = DataPageToolkit.GC_BASE_COLOR.brighter(); private final static Color SUM_OF_PAUSES_COLOR = DataPageToolkit.GC_BASE_COLOR.brighter().brighter(); @@ -220,6 +225,7 @@ PHASES.addColumn(JdkAttributes.GC_PHASE_NAME); PHASES.addColumn(JfrAttributes.DURATION); PHASES.addColumn(JfrAttributes.START_TIME); + PHASES.addColumn(JfrAttributes.EVENT_THREAD); PHASES.addColumn(JdkAttributes.GC_ID); METASPACE.addColumn(JdkAttributes.GC_METASPACE_USED); @@ -243,6 +249,10 @@ private final ChartCanvas chartCanvas; private final ColumnManager gcList; private IXDataRenderer renderRoot = RendererToolkit.empty(); + private final IAction GCEventThread = DataPageToolkit.createCheckAction( + Messages.JavaApplicationPage_THREAD_ACTIVITY_ACTION, + Messages.JavaApplicationPage_THREAD_ACTIVITY_ACTION_DESC, ACTIVITY_LANES_ID, + FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES), b -> buildChart()); private final IAction enablePhases = ActionToolkit.checkAction(b -> buildChart(), Messages.GarbageCollectionsPage_ROW_PAUSE_PHASES, Messages.GarbageCollectionsPage_ROW_PAUSE_PHASES_DESC, FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_PARTS), "phases"); //$NON-NLS-1$ @@ -253,7 +263,7 @@ private final List allChartSeriesActions = Stream.concat( Stream.concat(HEAP_SUMMARY.getAttributes().stream(), METASPACE_SUMMARY.getAttributes().stream()) .map(a -> createAttributeCheckAction(a, b -> buildChart())), - Stream.of(longestPause, sumOfPauses, enablePhases)).collect(Collectors.toList()); + Stream.of(longestPause, sumOfPauses, enablePhases, GCEventThread)).collect(Collectors.toList()); private final Set excludedAttributeIds; private FilterComponent tableFilter; private XYChart gcChart; @@ -265,6 +275,8 @@ private CTabFolder gcInfoFolder; private IItemCollection selectionItems; private FlavorSelector flavorSelector; + private ThreadGraphLanes lanes; + private MCContextMenuManager mm; GarbageCollectionsUi(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) { this.pageContainer = pageContainer; @@ -310,6 +322,7 @@ updatePhaseList(); updateMetaspaceList(); }); + SelectionStoreActionToolkit.addSelectionStoreActions(gcList.getViewer(), pageContainer.getSelectionStore(), () -> ItemCollectionToolkit.build(gcSelectedGcItems()), Messages.GarbageCollectionsPage_LIST_SELECTION, itemListMm); @@ -363,6 +376,18 @@ GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true); gd.widthHint = 180; chartLegend.setLayoutData(gd); + lanes = new ThreadGraphLanes(() -> getDataSource(), () -> buildChart()); + lanes.initializeChartConfiguration(Stream.of(state.getChildren(THREAD_LANES))); + // Older recordings may not have thread information in pause events. + // In those cases there is no need for the thread activity actions. + if (getDataSource().getItems().apply(ItemFilters.and( + ItemFilters.hasAttribute(JfrAttributes.EVENT_THREAD), + JdkFilters.GC_PAUSE)).hasItems()) { + form.getToolBarManager().add(ActionToolkit.action(() -> lanes.openEditLanesDialog(mm), + Messages.ThreadsPage_EDIT_LANES, FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES_EDIT))); + } else { + GCEventThread.setEnabled(false); + } DataPageToolkit.createChartTimestampTooltip(chartCanvas); gcChart = new XYChart(pageContainer.getRecordingRange(), renderRoot, 180); gcChart.setVisibleRange(timelineRange.getStart(), timelineRange.getEnd()); @@ -383,6 +408,8 @@ gcList.setSelectionState(gcListSelection); phasesList.getManager().setSelectionState(phasesSelection); metaspaceList.getManager().setSelectionState(metaspaceSelection); + mm = (MCContextMenuManager) chartCanvas.getContextMenu(); + lanes.updateContextMenu(mm); } private void updatePhaseList() { @@ -481,6 +508,20 @@ ItemRow l4 = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE_L4); rows.add(RendererToolkit.uniformRows(Arrays.asList(pauses, l1, l2, l3, l4), enablePhases.getText())); } + IItemFilter pauseThreadsFilter = ItemFilters.and(JdkFilters.GC_PAUSE, ItemFilters.hasAttribute(JfrAttributes.EVENT_THREAD)); + // Thread information may not be available in earlier recordings, ensure we actually have items before proceeding + if (GCEventThread.isChecked() && phasesList.getSelection().get().count() > 0 + && allItems.apply(pauseThreadsFilter).hasItems()) { + // Get the event threads from the selected events + IAggregator, ?> distinctThreadsAggregator = Aggregators.distinct(JfrAttributes.EVENT_THREAD); + IItemCollection items = ItemCollectionToolkit.build(phasesList.getSelection().get()); + Set threads = items.getAggregate(distinctThreadsAggregator); + List renderers = threads.stream().map((thread) ->lanes.buildThreadRenderer(thread, + getDataSource().getItems().apply(ItemFilters.equals(JfrAttributes.EVENT_THREAD, thread)))) + .collect(Collectors.toList()); + rows.add(RendererToolkit.uniformRows(renderers)); + } + renderRoot = RendererToolkit.layers(RendererToolkit.uniformRows(rows), buildTableSelectionRenderer()); chartCanvas.replaceRenderer(renderRoot); }