1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.flightrecorder.ui.pages; 34 35 import static org.openjdk.jmc.flightrecorder.jdk.JdkAggregators.LONGEST_GC_PAUSE; 36 import static org.openjdk.jmc.flightrecorder.jdk.JdkAggregators.TOTAL_GC_PAUSE; 37 import static org.openjdk.jmc.flightrecorder.jdk.JdkQueries.HEAP_SUMMARY; 38 import static org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit.createAggregatorCheckAction; 39 import static org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit.createAttributeCheckAction; 40 41 import java.awt.Color; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.Iterator; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Set; 49 import java.util.function.Predicate; 50 import java.util.function.Supplier; 51 import java.util.stream.Collectors; 52 import java.util.stream.Stream; 53 54 import org.eclipse.jface.action.IAction; 55 import org.eclipse.jface.resource.ImageDescriptor; 56 import org.eclipse.jface.viewers.ArrayContentProvider; 57 import org.eclipse.jface.viewers.CheckboxTableViewer; 58 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; 59 import org.eclipse.jface.viewers.IStructuredSelection; 60 import org.eclipse.jface.viewers.TableViewer; 61 import org.eclipse.swt.SWT; 62 import org.eclipse.swt.custom.CTabFolder; 63 import org.eclipse.swt.custom.SashForm; 64 import org.eclipse.swt.layout.GridData; 65 import org.eclipse.swt.layout.GridLayout; 66 import org.eclipse.swt.widgets.Composite; 67 import org.eclipse.ui.forms.widgets.Form; 68 import org.eclipse.ui.forms.widgets.FormToolkit; 69 70 import org.openjdk.jmc.common.IMCThread; 71 import org.openjdk.jmc.common.IState; 72 import org.openjdk.jmc.common.IWritableState; 73 import org.openjdk.jmc.common.item.Aggregators; 74 import org.openjdk.jmc.common.item.IAggregator; 75 import org.openjdk.jmc.common.item.IAttribute; 76 import org.openjdk.jmc.common.item.IItem; 77 import org.openjdk.jmc.common.item.IItemCollection; 78 import org.openjdk.jmc.common.item.IItemFilter; 79 import org.openjdk.jmc.common.item.IItemIterable; 80 import org.openjdk.jmc.common.item.IItemQuery; 81 import org.openjdk.jmc.common.item.IMemberAccessor; 82 import org.openjdk.jmc.common.item.IType; 83 import org.openjdk.jmc.common.item.ItemFilters; 84 import org.openjdk.jmc.common.item.ItemQueryBuilder; 85 import org.openjdk.jmc.common.item.ItemToolkit; 86 import org.openjdk.jmc.common.unit.IQuantity; 87 import org.openjdk.jmc.common.unit.IRange; 88 import org.openjdk.jmc.common.unit.LinearKindOfQuantity; 89 import org.openjdk.jmc.common.unit.UnitLookup; 90 import org.openjdk.jmc.flightrecorder.JfrAttributes; 91 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes; 92 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters; 93 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs; 94 import org.openjdk.jmc.flightrecorder.rules.jdk.memory.ReferenceStatisticsType; 95 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics; 96 import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI; 97 import org.openjdk.jmc.flightrecorder.ui.IDataPageFactory; 98 import org.openjdk.jmc.flightrecorder.ui.IDisplayablePage; 99 import org.openjdk.jmc.flightrecorder.ui.IPageContainer; 100 import org.openjdk.jmc.flightrecorder.ui.IPageDefinition; 101 import org.openjdk.jmc.flightrecorder.ui.IPageUI; 102 import org.openjdk.jmc.flightrecorder.ui.ItemCollectionToolkit; 103 import org.openjdk.jmc.flightrecorder.ui.StreamModel; 104 import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage; 105 import org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit; 106 import org.openjdk.jmc.flightrecorder.ui.common.FilterComponent; 107 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector; 108 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState; 109 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants; 110 import org.openjdk.jmc.flightrecorder.ui.common.ItemList; 111 import org.openjdk.jmc.flightrecorder.ui.common.ItemList.ItemListBuilder; 112 import org.openjdk.jmc.flightrecorder.ui.common.ItemRow; 113 import org.openjdk.jmc.flightrecorder.ui.common.ThreadGraphLanes; 114 import org.openjdk.jmc.flightrecorder.ui.common.TypeLabelProvider; 115 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages; 116 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStoreActionToolkit; 117 import org.openjdk.jmc.ui.charts.AWTChartToolkit; 118 import org.openjdk.jmc.ui.charts.ISpanSeries; 119 import org.openjdk.jmc.ui.charts.IXDataRenderer; 120 import org.openjdk.jmc.ui.charts.QuantitySeries; 121 import org.openjdk.jmc.ui.charts.RendererToolkit; 122 import org.openjdk.jmc.ui.charts.SpanRenderer; 123 import org.openjdk.jmc.ui.charts.XYChart; 124 import org.openjdk.jmc.ui.column.ColumnBuilder; 125 import org.openjdk.jmc.ui.column.ColumnManager; 126 import org.openjdk.jmc.ui.column.ColumnManager.SelectionState; 127 import org.openjdk.jmc.ui.column.ColumnMenusFactory; 128 import org.openjdk.jmc.ui.column.IColumn; 129 import org.openjdk.jmc.ui.column.TableSettings; 130 import org.openjdk.jmc.ui.handlers.ActionToolkit; 131 import org.openjdk.jmc.ui.handlers.MCContextMenuManager; 132 import org.openjdk.jmc.ui.misc.ActionUiToolkit; 133 import org.openjdk.jmc.ui.misc.ChartCanvas; 134 import org.openjdk.jmc.ui.misc.PersistableSashForm; 135 136 public class GarbageCollectionsPage extends AbstractDataPage { 137 public static class GarbageCollectionPageFactory implements IDataPageFactory { 138 139 @Override 140 public String getName(IState state) { 141 return Messages.GarbageCollectionsPage_PAGE_NAME; 142 } 143 144 @Override 145 public ImageDescriptor getImageDescriptor(IState state) { 146 return FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.PAGE_GC); 147 } 148 149 @Override 150 public String[] getTopics(IState state) { 151 return new String[] {JfrRuleTopics.GARBAGE_COLLECTION_TOPIC}; 152 } 153 154 @Override 155 public IDisplayablePage createPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) { 156 return new GarbageCollectionsPage(dpd, items, editor); 157 } 158 } 159 160 private static final ReferenceStatisticsType[] REF_TYPE = ReferenceStatisticsType.values(); 161 private static final String SASH = "sash"; //$NON-NLS-1$ 162 private static final String TABLE_SASH = "tableSash"; //$NON-NLS-1$ 163 private static final String THREAD_LANES = "threadLane"; //$NON-NLS-1$ 164 private static final String GCS = "gcs"; //$NON-NLS-1$ 165 private static final String CHART = "chart"; //$NON-NLS-1$ 166 private static final String PHASE_TABLE_FILTER = "phaseTableFilter"; //$NON-NLS-1$ 167 private static final String GC_TABLE_FILTER = "gcTableFilter"; //$NON-NLS-1$ 168 private static final String METASPACE_TABLE_FILTER = "metaspaceTableFilter"; //$NON-NLS-1$ 169 private static final String PHASE_LIST = "phaseList"; //$NON-NLS-1$ 170 private static final String METASPACE_LIST = "metaspaceList"; //$NON-NLS-1$ 171 private static final String ACTIVITY_LANES_ID = "threadActivityLanes"; //$NON-NLS-1$ 172 173 private final static Color LONGEST_PAUSE_COLOR = DataPageToolkit.GC_BASE_COLOR.brighter(); 174 private final static Color SUM_OF_PAUSES_COLOR = DataPageToolkit.GC_BASE_COLOR.brighter().brighter(); 175 176 private final static IItemQuery METASPACE_SUMMARY = ItemQueryBuilder.fromWhere(JdkFilters.METASPACE_SUMMARY) 177 .select(JdkAttributes.GC_METASPACE_USED, JdkAttributes.GC_METASPACE_CAPACITY, 178 JdkAttributes.GC_METASPACE_COMMITTED, JdkAttributes.GC_METASPACE_RESERVED) 179 .build(); 180 181 private static class GC { 182 final IType<IItem> type; 183 final IItem gcItem; 184 final Object[] referenceStatisticsData; 185 IQuantity gcId; 186 IQuantity duration; 187 String gcCause; 188 String gcName; 189 IQuantity longestPause; 190 IQuantity sumOfPauses; 191 IQuantity startTime; 192 IQuantity endTime; 193 IQuantity usedDelta; 194 IQuantity committedDelta; 195 IQuantity usedMetaspaceDelta; 196 IQuantity committedMetaspaceDelta; 197 198 GC(IItem gcItem, IType<IItem> type) { 199 this.type = type; 200 this.gcItem = gcItem; 201 referenceStatisticsData = new Object[REF_TYPE.length]; 202 usedDelta = UnitLookup.BYTE.quantity(0); 203 committedDelta = UnitLookup.BYTE.quantity(0); 204 usedMetaspaceDelta = UnitLookup.BYTE.quantity(0); 205 committedMetaspaceDelta = UnitLookup.BYTE.quantity(0); 206 } 207 208 Object getRefCount(ReferenceStatisticsType type) { 209 return referenceStatisticsData[type.ordinal()]; 210 } 211 212 void setRefCount(Object type, Object count) { 213 for (int i = 0; i < REF_TYPE.length; i++) { 214 if (REF_TYPE[i].typeValue.equals(type)) { 215 referenceStatisticsData[i] = count; 216 break; 217 } 218 } 219 } 220 } 221 222 private static final ItemListBuilder PHASES = new ItemListBuilder(); 223 private static final ItemListBuilder METASPACE = new ItemListBuilder(); 224 static { 225 PHASES.addColumn(JfrAttributes.EVENT_TYPE); 226 PHASES.addColumn(JdkAttributes.GC_PHASE_NAME); 227 PHASES.addColumn(JfrAttributes.DURATION); 228 PHASES.addColumn(JfrAttributes.START_TIME); 229 PHASES.addColumn(JfrAttributes.EVENT_THREAD); 230 PHASES.addColumn(JdkAttributes.GC_ID); 231 232 METASPACE.addColumn(JdkAttributes.GC_METASPACE_USED); 233 METASPACE.addColumn(JdkAttributes.GC_DATASPACE_COMMITTED); 234 METASPACE.addColumn(JdkAttributes.GC_DATASPACE_RESERVED); 235 METASPACE.addColumn(JdkAttributes.GC_DATASPACE_USED); 236 METASPACE.addColumn(JdkAttributes.GC_CLASSSPACE_COMMITTED); 237 METASPACE.addColumn(JdkAttributes.GC_CLASSSPACE_RESERVED); 238 METASPACE.addColumn(JdkAttributes.GC_CLASSSPACE_USED); 239 METASPACE.addColumn(JdkAttributes.GC_THRESHOLD); 240 METASPACE.addColumn(JdkAttributes.GC_WHEN); 241 METASPACE.addColumn(JdkAttributes.GC_ID); 242 METASPACE.addColumn(JfrAttributes.START_TIME); 243 } 244 245 private class GarbageCollectionsUi implements IPageUI { 246 247 private final SashForm sash; 248 private final SashForm tableSash; 249 private final IPageContainer pageContainer; 250 private final ChartCanvas chartCanvas; 251 private final ColumnManager gcList; 252 private IXDataRenderer renderRoot = RendererToolkit.empty(); 253 private IAction GCEventThread = DataPageToolkit.createCheckAction( 254 Messages.JavaApplicationPage_THREAD_ACTIVITY_ACTION, 255 Messages.JavaApplicationPage_THREAD_ACTIVITY_ACTION_DESC, ACTIVITY_LANES_ID, 256 FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES), b -> buildChart()); 257 private final IAction enablePhases = ActionToolkit.checkAction(b -> buildChart(), 258 Messages.GarbageCollectionsPage_ROW_PAUSE_PHASES, Messages.GarbageCollectionsPage_ROW_PAUSE_PHASES_DESC, 259 FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_PARTS), "phases"); //$NON-NLS-1$ 260 private final IAction longestPause = createAggregatorCheckAction(LONGEST_GC_PAUSE, "longestPause", //$NON-NLS-1$ 261 LONGEST_PAUSE_COLOR, b -> buildChart()); 262 private final IAction sumOfPauses = createAggregatorCheckAction(TOTAL_GC_PAUSE, "sumOfPauses", //$NON-NLS-1$ 263 SUM_OF_PAUSES_COLOR, b -> buildChart()); 264 private final List<IAction> allChartSeriesActions = Stream.concat( 265 Stream.concat(HEAP_SUMMARY.getAttributes().stream(), METASPACE_SUMMARY.getAttributes().stream()) 266 .map(a -> createAttributeCheckAction(a, b -> buildChart())), 267 Stream.of(longestPause, sumOfPauses, enablePhases, GCEventThread)).collect(Collectors.toList()); 268 private final Set<String> excludedAttributeIds; 269 private FilterComponent tableFilter; 270 private XYChart gcChart; 271 private IRange<IQuantity> currentRange; 272 private ItemList phasesList; 273 private FilterComponent phasesFilter; 274 private ItemList metaspaceList; 275 private FilterComponent metaspaceFilter; 276 private CTabFolder gcInfoFolder; 277 private IItemCollection selectionItems; 278 private FlavorSelector flavorSelector; 279 private ThreadGraphLanes lanes; 280 private MCContextMenuManager mm; 281 282 GarbageCollectionsUi(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) { 283 this.pageContainer = pageContainer; 284 excludedAttributeIds = calculateExcludedAttributeIds(getDataSource().getItems()); 285 Form form = DataPageToolkit.createForm(parent, toolkit, getName(), getIcon()); 286 sash = new SashForm(form.getBody(), SWT.VERTICAL); 287 toolkit.adapt(sash); 288 tableSash = new SashForm(sash, SWT.HORIZONTAL); 289 toolkit.adapt(tableSash); 290 291 TableViewer tableViewer = new TableViewer(tableSash, 292 SWT.MULTI | SWT.VIRTUAL | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); 293 tableViewer.setContentProvider(ArrayContentProvider.getInstance()); 294 ColumnViewerToolTipSupport.enableFor(tableViewer); 295 List<IColumn> columns = new ArrayList<>(); 296 columns.add(buildGCItemAttributeColumn(JdkAttributes.GC_ID, o -> ((GC) o).gcId)); 297 columns.add(buildGCItemAttributeColumn(JfrAttributes.DURATION, o -> ((GC) o).duration)); 298 columns.add(buildGCItemAttributeColumn(JdkAttributes.GC_CAUSE, o -> ((GC) o).gcCause)); 299 columns.add(buildGCItemAttributeColumn(JdkAttributes.GC_NAME, o -> ((GC) o).gcName)); 300 columns.add(buildGCItemAttributeColumn(JdkAttributes.GC_LONGEST_PAUSE, o -> ((GC) o).longestPause)); 301 columns.add(buildGCItemAttributeColumn(JdkAttributes.GC_SUM_OF_PAUSES, o -> ((GC) o).sumOfPauses)); 302 columns.add(buildGCItemAttributeColumn(JfrAttributes.START_TIME, o -> ((GC) o).startTime)); 303 columns.add(buildGCItemAttributeColumn(JfrAttributes.END_TIME, o -> ((GC) o).endTime)); 304 for (ReferenceStatisticsType t : REF_TYPE) { 305 columns.add(new ColumnBuilder(t.localizedName, "ReferenceStatisticsType-" + t.name(), //$NON-NLS-1$ 306 o -> ((GC) o).getRefCount(t)).style(SWT.RIGHT).build()); 307 } 308 columns.add(new ColumnBuilder(Messages.GarbageCollectionsPage_USED_HEAP_DELTA, "usedHeapDelta", //$NON-NLS-1$ 309 o -> ((GC) o).usedDelta).style(SWT.RIGHT).build()); 310 columns.add(new ColumnBuilder(Messages.GarbageCollectionsPage_COMMITTED_HEAP_DELTA, "committedHeapDelta", //$NON-NLS-1$ 311 o -> ((GC) o).committedDelta).style(SWT.RIGHT).build()); 312 columns.add(new ColumnBuilder(Messages.GarbageCollectionsPage_USED_METASPACE_DELTA, "usedMetaspaceDelta", //$NON-NLS-1$ 313 o -> ((GC) o).usedMetaspaceDelta).style(SWT.RIGHT).build()); 314 columns.add(new ColumnBuilder(Messages.GarbageCollectionsPage_COMMITTED_METASPACE_DELTA, 315 "committedMetaspaceDelta", o -> ((GC) o).committedMetaspaceDelta).style(SWT.RIGHT).build()); //$NON-NLS-1$ 316 317 gcList = ColumnManager.build(tableViewer, columns, TableSettings.forState(state.getChild(GCS))); 318 MCContextMenuManager itemListMm = MCContextMenuManager.create(gcList.getViewer().getControl()); 319 ColumnMenusFactory.addDefaultMenus(gcList, itemListMm); 320 gcList.getViewer().addSelectionChangedListener(e -> { 321 buildChart(); 322 pageContainer.showSelection(ItemCollectionToolkit.build(gcSelectedGcItems())); 323 updatePhaseList(); 324 updateMetaspaceList(); 325 }); 326 327 SelectionStoreActionToolkit.addSelectionStoreActions(gcList.getViewer(), pageContainer.getSelectionStore(), 328 () -> ItemCollectionToolkit.build(gcSelectedGcItems()), 329 Messages.GarbageCollectionsPage_LIST_SELECTION, itemListMm); 330 tableFilter = FilterComponent.createFilterComponent(tableViewer.getControl(), gcList, tableFilterState, 331 getDataSource().getItems().apply(JdkFilters.GARBAGE_COLLECTION), 332 pageContainer.getSelectionStore()::getSelections, this::onFilterChange); 333 itemListMm.add(tableFilter.getShowFilterAction()); 334 itemListMm.add(tableFilter.getShowSearchAction()); 335 336 gcInfoFolder = new CTabFolder(tableSash, SWT.NONE); 337 phasesList = PHASES.buildWithoutBorder(gcInfoFolder, TableSettings.forState(state.getChild(PHASE_LIST))); 338 phasesList.getManager().getViewer().addSelectionChangedListener(e -> { 339 buildChart(); 340 pageContainer.showSelection(ItemCollectionToolkit.build(phasesList.getSelection().get())); 341 }); 342 phasesFilter = FilterComponent.createFilterComponent(phasesList, phasesFilterState, 343 getDataSource().getItems().apply(JdkFilters.GC_PAUSE_PHASE), 344 pageContainer.getSelectionStore()::getSelections, this::onPhasesFilterChange); 345 MCContextMenuManager phasesMm = MCContextMenuManager 346 .create(phasesList.getManager().getViewer().getControl()); 347 ColumnMenusFactory.addDefaultMenus(phasesList.getManager(), phasesMm); 348 SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), phasesList, 349 Messages.GarbageCollectionsPage_PAUSE_PHASE_SELECTION, phasesMm); 350 phasesMm.add(phasesFilter.getShowFilterAction()); 351 phasesMm.add(phasesFilter.getShowSearchAction()); 352 DataPageToolkit.addTabItem(gcInfoFolder, phasesFilter.getComponent(), 353 Messages.GarbageCollectionsPage_PAUSE_PHASES_TITLE); 354 355 metaspaceList = METASPACE.buildWithoutBorder(gcInfoFolder, 356 TableSettings.forState(state.getChild(METASPACE_LIST))); 357 metaspaceList.getManager().getViewer().addSelectionChangedListener( 358 e -> pageContainer.showSelection(ItemCollectionToolkit.build(metaspaceList.getSelection().get()))); 359 metaspaceFilter = FilterComponent.createFilterComponent(metaspaceList, metaspaceFilterState, 360 getDataSource().getItems().apply(JdkFilters.METASPACE_SUMMARY), 361 pageContainer.getSelectionStore()::getSelections, this::onMetaspaceFilterChange); 362 MCContextMenuManager metaspaceMm = MCContextMenuManager 363 .create(metaspaceList.getManager().getViewer().getControl()); 364 ColumnMenusFactory.addDefaultMenus(metaspaceList.getManager(), metaspaceMm); 365 SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), metaspaceList, 366 Messages.GarbageCollectionsPage_METASPACE_SELECTION, metaspaceMm); 367 metaspaceMm.add(metaspaceFilter.getShowFilterAction()); 368 metaspaceMm.add(metaspaceFilter.getShowSearchAction()); 369 DataPageToolkit.addTabItem(gcInfoFolder, metaspaceFilter.getComponent(), 370 Messages.GarbageCollectionsPage_METASPACE_TITLE); 371 372 Composite chartContainer = toolkit.createComposite(sash); 373 chartContainer.setLayout(new GridLayout(2, false)); 374 chartCanvas = new ChartCanvas(chartContainer); 375 chartCanvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 376 ActionToolkit.loadCheckState(state.getChild(CHART), allChartSeriesActions.stream()); 377 CheckboxTableViewer chartLegend = ActionUiToolkit.buildCheckboxViewer(chartContainer, 378 allChartSeriesActions.stream().filter(a -> includeAttribute(a.getId()))); 379 GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true); 380 gd.widthHint = 180; 381 chartLegend.getControl().setLayoutData(gd); 382 lanes = new ThreadGraphLanes(() -> getDataSource(), () -> buildChart()); 383 lanes.initializeChartConfiguration(Stream.of(state.getChildren(THREAD_LANES))); 384 IAction editLanesAction = ActionToolkit.action(() -> lanes.openEditLanesDialog(mm, false), 385 Messages.ThreadsPage_EDIT_LANES, FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES_EDIT)); 386 form.getToolBarManager().add(editLanesAction); 387 388 DataPageToolkit.createChartTimestampTooltip(chartCanvas); 389 gcChart = new XYChart(pageContainer.getRecordingRange(), renderRoot, 180); 390 gcChart.setVisibleRange(timelineRange.getStart(), timelineRange.getEnd()); 391 gcChart.addVisibleRangeListener(r -> timelineRange = r); 392 393 PersistableSashForm.loadState(sash, state.getChild(SASH)); 394 PersistableSashForm.loadState(tableSash, state.getChild(TABLE_SASH)); 395 396 flavorSelector = FlavorSelector.itemsWithTimerange(form, JdkFilters.GARBAGE_COLLECTION, 397 getDataSource().getItems(), pageContainer, this::onInputSelected, this::onShow, 398 flavorSelectorState); 399 400 gcInfoFolder.setSelection(gcInfoTabSelection); 401 addResultActions(form); 402 tableFilter.loadState(state.getChild(GC_TABLE_FILTER)); 403 phasesFilter.loadState(state.getChild(PHASE_TABLE_FILTER)); 404 metaspaceFilter.loadState(state.getChild(METASPACE_TABLE_FILTER)); 405 gcList.setSelectionState(gcListSelection); 406 phasesList.getManager().setSelectionState(phasesSelection); 407 metaspaceList.getManager().setSelectionState(metaspaceSelection); 408 mm = (MCContextMenuManager) chartCanvas.getContextMenu(); 409 lanes.updateContextMenu(mm, false); 410 lanes.updateContextMenu(MCContextMenuManager.create(chartLegend.getControl()), true); 411 412 // Older recordings may not have thread information in pause events. 413 // In those cases there is no need for the thread activity actions. 414 if (!getDataSource().getItems().apply(ItemFilters.and(ItemFilters.hasAttribute(JfrAttributes.EVENT_THREAD), 415 JdkFilters.GC_PAUSE)).hasItems()) { 416 editLanesAction.setEnabled(false); 417 editLanesAction.setToolTipText(Messages.GarbageCollectionsPage_DISABLED_TOOLTIP); 418 GCEventThread.setEnabled(false); 419 GCEventThread.setDescription(Messages.GarbageCollectionsPage_DISABLED_TOOLTIP); 420 for (IAction action : lanes.getContextMenuActions()) { 421 action.setEnabled(false); 422 } 423 } 424 } 425 426 private void updatePhaseList() { 427 phasesList.show(ItemCollectionToolkit.filterIfNotNull(getPhaseItems(), phasesFilterState)); 428 } 429 430 private void updateMetaspaceList() { 431 metaspaceList.show(ItemCollectionToolkit.filterIfNotNull(getMetaspaceItems(), metaspaceFilterState)); 432 } 433 434 private IItemCollection getMetaspaceItems() { 435 Set<IQuantity> selectedGcIds = getSelectedGcIds(); 436 IItemCollection metaspaceItems = getDataSource().getItems().apply(JdkFilters.METASPACE_SUMMARY) 437 .apply(ItemFilters.memberOf(JdkAttributes.GC_ID, selectedGcIds)); 438 return metaspaceItems; 439 } 440 441 private IItemCollection getPhaseItems() { 442 Set<IQuantity> gcIds = getSelectedGcIds(); 443 IItemCollection gcIdPausePhases = getDataSource().getItems().apply(JdkFilters.GC_PAUSE_PHASE) 444 .apply(ItemFilters.memberOf(JdkAttributes.GC_ID, gcIds)); 445 return gcIdPausePhases; 446 } 447 448 private Set<IQuantity> getSelectedGcIds() { 449 @SuppressWarnings("unchecked") 450 List<GC> selected = ((IStructuredSelection) gcList.getViewer().getSelection()).toList(); 451 Set<IQuantity> gcIds = selected.stream() 452 .map(gc -> gc.type.getAccessor(JdkAttributes.GC_ID.getKey()).getMember(gc.gcItem)) 453 .collect(Collectors.toSet()); 454 return gcIds; 455 } 456 457 private void onFilterChange(IItemFilter newFilter) { 458 IItemCollection items = selectionItems != null ? selectionItems : getDataSource().getItems(); 459 items = items.apply(JdkFilters.GARBAGE_COLLECTION); 460 if (tableFilter.isVisible()) { 461 updateTable(ItemCollectionToolkit.filterIfNotNull(items, newFilter)); 462 Object input = gcList.getViewer().getInput(); 463 tableFilter.setColor(input instanceof Object[] && ((Object[]) input).length > 0 ? 1 : 0); 464 } else { 465 updateTable(items); 466 } 467 tableFilterState = newFilter; 468 } 469 470 private void onPhasesFilterChange(IItemFilter filter) { 471 phasesFilter.filterChangeHelper(filter, phasesList, 472 getDataSource().getItems().apply(JdkFilters.GC_PAUSE_PHASE)); 473 phasesFilterState = filter; 474 } 475 476 private void onMetaspaceFilterChange(IItemFilter filter) { 477 metaspaceFilter.filterChangeHelper(filter, metaspaceList, 478 getDataSource().getItems().apply(JdkFilters.METASPACE_SUMMARY)); 479 metaspaceFilterState = filter; 480 } 481 482 private ItemRow buildSpanRow(IItemCollection items, String typeId) { 483 IItemCollection filtered = items.apply(ItemFilters.type(typeId)); 484 return new ItemRow(DataPageToolkit.buildSpanRenderer(filtered, 485 AWTChartToolkit.staticColor(TypeLabelProvider.getColorOrDefault(typeId))), filtered); 486 } 487 488 private void buildChart() { 489 IItemCollection allItems = getDataSource().getItems(); 490 List<IXDataRenderer> rows = new ArrayList<>(); 491 Predicate<IAttribute<IQuantity>> legendFilter = this::isAttributeEnabled; 492 DataPageToolkit.buildLinesRow(Messages.GarbageCollectionsPage_ROW_HEAP, 493 Messages.GarbageCollectionsPage_ROW_HEAP_DESC, allItems, false, HEAP_SUMMARY, legendFilter, 494 UnitLookup.BYTE.quantity(0), null).ifPresent(rows::add); 495 DataPageToolkit.buildLinesRow(Messages.GarbageCollectionsPage_ROW_METASPACE, 496 Messages.GarbageCollectionsPage_ROW_METASPACE_DESC, allItems, false, METASPACE_SUMMARY, 497 legendFilter, UnitLookup.BYTE.quantity(0), null).ifPresent(rows::add); 498 // Pauses 499 List<IXDataRenderer> gcPauseRows = new ArrayList<>(); 500 IItemCollection pauseEvents = allItems.apply(JdkFilters.GC_PAUSE); 501 if (longestPause.isChecked()) { 502 gcPauseRows.add(DataPageToolkit.buildTimestampHistogramRenderer(pauseEvents, LONGEST_GC_PAUSE, 503 LONGEST_PAUSE_COLOR)); 504 } 505 if (sumOfPauses.isChecked()) { 506 gcPauseRows.add(DataPageToolkit.buildTimestampHistogramRenderer(pauseEvents, TOTAL_GC_PAUSE, 507 SUM_OF_PAUSES_COLOR)); 508 } 509 if (!gcPauseRows.isEmpty()) { 510 rows.add(RendererToolkit.layers(DataPageToolkit.buildGcPauseRow(allItems), 511 RendererToolkit.uniformRows(gcPauseRows))); 512 } 513 // Phases 514 if (enablePhases.isChecked()) { 515 ItemRow pauses = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE); 516 ItemRow l1 = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE_L1); 517 ItemRow l2 = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE_L2); 518 ItemRow l3 = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE_L3); 519 ItemRow l4 = buildSpanRow(allItems, JdkTypeIDs.GC_PAUSE_L4); 520 rows.add(RendererToolkit.uniformRows(Arrays.asList(pauses, l1, l2, l3, l4), enablePhases.getText())); 521 } 522 IItemFilter pauseThreadsFilter = ItemFilters.and(JdkFilters.GC_PAUSE, ItemFilters.hasAttribute(JfrAttributes.EVENT_THREAD)); 523 // Thread information may not be available in earlier recordings, ensure we actually have items before proceeding 524 if (GCEventThread.isChecked() && phasesList.getSelection().get().count() > 0 525 && allItems.apply(pauseThreadsFilter).hasItems()) { 526 // Get the event threads from the selected events 527 IAggregator<Set<IMCThread>, ?> distinctThreadsAggregator = Aggregators.distinct(JfrAttributes.EVENT_THREAD); 528 IItemCollection items = ItemCollectionToolkit.build(phasesList.getSelection().get()); 529 Set<IMCThread> threads = items.getAggregate(distinctThreadsAggregator); 530 List<IXDataRenderer> renderers = threads.stream().map((thread) ->lanes.buildThreadRenderer(thread, 531 getDataSource().getItems().apply(ItemFilters.equals(JfrAttributes.EVENT_THREAD, thread)))) 532 .collect(Collectors.toList()); 533 rows.add(RendererToolkit.uniformRows(renderers)); 534 } 535 536 renderRoot = RendererToolkit.layers(RendererToolkit.uniformRows(rows), buildTableSelectionRenderer()); 537 chartCanvas.replaceRenderer(renderRoot); 538 } 539 540 private boolean isAttributeEnabled(IAttribute<IQuantity> attr) { 541 String id = attr.getIdentifier(); 542 return includeAttribute(id) 543 && allChartSeriesActions.stream().filter(a -> id.equals(a.getId())).findAny().get().isChecked(); 544 } 545 546 private boolean includeAttribute(String attrId) { 547 return !excludedAttributeIds.contains(attrId); 548 } 549 550 private IXDataRenderer buildTableSelectionRenderer() { 551 Supplier<Stream<? extends IItem>> phaseSelection = phasesList.getSelection(); 552 Stream<? extends IItem> gcItems = phaseSelection.get().count() > 0 ? phaseSelection.get() 553 : gcSelectedGcItems(); 554 ISpanSeries<IItem> gcBackdrop = QuantitySeries.max(ItemCollectionToolkit.build(gcItems), 555 JfrAttributes.START_TIME, JfrAttributes.END_TIME); 556 return SpanRenderer.build(gcBackdrop, AWTChartToolkit.staticColor(new Color(100, 180, 220, 150))); 557 } 558 559 @Override 560 public void saveTo(IWritableState memento) { 561 PersistableSashForm.saveState(sash, memento.createChild(SASH)); 562 PersistableSashForm.saveState(tableSash, memento.createChild(TABLE_SASH)); 563 gcList.getSettings().saveState(memento.createChild(GCS)); 564 phasesList.getManager().getSettings().saveState(memento.createChild(PHASE_LIST)); 565 metaspaceList.getManager().getSettings().saveState(memento.createChild(METASPACE_LIST)); 566 ActionToolkit.saveCheckState(memento.createChild(CHART), allChartSeriesActions.stream()); 567 tableFilter.saveState(memento.createChild(GC_TABLE_FILTER)); 568 phasesFilter.saveState(memento.createChild(PHASE_TABLE_FILTER)); 569 metaspaceFilter.saveState(memento.createChild(METASPACE_TABLE_FILTER)); 570 571 saveToLocal(); 572 } 573 574 private void saveToLocal() { 575 gcListSelection = gcList.getSelectionState(); 576 phasesSelection = phasesList.getManager().getSelectionState(); 577 metaspaceSelection = metaspaceList.getManager().getSelectionState(); 578 gcInfoTabSelection = gcInfoFolder.getSelectionIndex(); 579 flavorSelectorState = flavorSelector.getFlavorSelectorState(); 580 } 581 582 private void updateTable(IItemCollection gcs) { 583 Map<Object, GC> gcMap = new HashMap<>(); 584 gcs.forEach(is -> { 585 IMemberAccessor<IQuantity, IItem> gcIdAccessor = JdkAttributes.GC_ID.getAccessor(is.getType()); 586 IMemberAccessor<IQuantity, IItem> durationAccessor = JfrAttributes.DURATION.getAccessor(is.getType()); 587 IMemberAccessor<String, IItem> causeAccessor = JdkAttributes.GC_CAUSE.getAccessor(is.getType()); 588 IMemberAccessor<String, IItem> nameAccessor = JdkAttributes.GC_NAME.getAccessor(is.getType()); 589 IMemberAccessor<IQuantity, IItem> longestPauseAccessor = JdkAttributes.GC_LONGEST_PAUSE 590 .getAccessor(is.getType()); 591 IMemberAccessor<IQuantity, IItem> sumPauseAccessor = JdkAttributes.GC_SUM_OF_PAUSES 592 .getAccessor(is.getType()); 593 IMemberAccessor<IQuantity, IItem> startTimeAccessor = JfrAttributes.START_TIME 594 .getAccessor(is.getType()); 595 IMemberAccessor<IQuantity, IItem> endTimeAccessor = JfrAttributes.END_TIME.getAccessor(is.getType()); 596 597 is.forEach(item -> { 598 GC value = new GC(item, is.getType()); 599 value.gcId = gcIdAccessor.getMember(item); 600 value.duration = durationAccessor.getMember(item); 601 value.gcCause = causeAccessor.getMember(item); 602 value.gcName = nameAccessor.getMember(item); 603 value.longestPause = longestPauseAccessor.getMember(item); 604 value.sumOfPauses = sumPauseAccessor.getMember(item); 605 value.startTime = startTimeAccessor.getMember(item); 606 value.endTime = endTimeAccessor.getMember(item); 607 logDuplicateGcId(gcMap.put(gcIdAccessor.getMember(item), value)); 608 }); 609 }); 610 IItemCollection refItems = getDataSource().getItems().apply(JdkFilters.REFERENCE_STATISTICS); 611 refItems.forEach(is -> { 612 IMemberAccessor<IQuantity, IItem> gdIdAccessor = JdkAttributes.GC_ID.getAccessor(is.getType()); 613 IMemberAccessor<String, IItem> typeAccessor = JdkAttributes.REFERENCE_STATISTICS_TYPE 614 .getAccessor(is.getType()); 615 IMemberAccessor<IQuantity, IItem> countAccessor = JdkAttributes.REFERENCE_STATISTICS_COUNT 616 .getAccessor(is.getType()); 617 is.forEach(item -> { 618 GC gc = gcMap.get(gdIdAccessor.getMember(item)); 619 if (gc != null) { 620 gc.setRefCount(typeAccessor.getMember(item), countAccessor.getMember(item)); 621 } 622 }); 623 }); 624 IItemCollection heapItems = getDataSource().getItems().apply(JdkFilters.HEAP_SUMMARY); 625 heapItems.forEach(is -> { 626 IMemberAccessor<IQuantity, IItem> gcIdAccessor = JdkAttributes.GC_ID.getAccessor(is.getType()); 627 IMemberAccessor<String, IItem> gcWhenAccessor = JdkAttributes.GC_WHEN.getAccessor(is.getType()); 628 IMemberAccessor<IQuantity, IItem> usedHeapAccessor = JdkAttributes.HEAP_USED.getAccessor(is.getType()); 629 IMemberAccessor<IQuantity, IItem> committedHeapAccessor = JdkAttributes.GC_HEAPSPACE_COMMITTED 630 .getAccessor(is.getType()); 631 632 is.forEach(item -> { 633 GC gc = gcMap.get(gcIdAccessor.getMember(item)); 634 if (gc != null) { 635 String when = gcWhenAccessor.getMember(item); 636 if ("Before GC".equals(when)) { //$NON-NLS-1$ 637 gc.usedDelta = gc.usedDelta.subtract(usedHeapAccessor.getMember(item)); 638 gc.committedDelta = gc.committedDelta.subtract(committedHeapAccessor.getMember(item)); 639 } else { 640 gc.usedDelta = gc.usedDelta.add(usedHeapAccessor.getMember(item)); 641 gc.committedDelta = gc.committedDelta.add(committedHeapAccessor.getMember(item)); 642 } 643 } 644 }); 645 }); 646 647 IItemCollection metaspaceItems = getDataSource().getItems().apply(JdkFilters.METASPACE_SUMMARY); 648 metaspaceItems.forEach(is -> { 649 IMemberAccessor<IQuantity, IItem> gcIdAccessor = JdkAttributes.GC_ID.getAccessor(is.getType()); 650 IMemberAccessor<String, IItem> gcWhenAccessor = JdkAttributes.GC_WHEN.getAccessor(is.getType()); 651 IMemberAccessor<IQuantity, IItem> usedMetaspaceAccessor = JdkAttributes.GC_METASPACE_USED 652 .getAccessor(is.getType()); 653 IMemberAccessor<IQuantity, IItem> committedMetaspaceAccessor = JdkAttributes.GC_METASPACE_COMMITTED 654 .getAccessor(is.getType()); 655 656 is.forEach(item -> { 657 GC gc = gcMap.get(gcIdAccessor.getMember(item)); 658 if (gc != null && usedMetaspaceAccessor != null && committedMetaspaceAccessor != null 659 && gcWhenAccessor != null) { 660 String when = gcWhenAccessor.getMember(item); 661 if ("Before GC".equals(when)) { //$NON-NLS-1$ 662 gc.usedMetaspaceDelta = gc.usedMetaspaceDelta 663 .subtract(usedMetaspaceAccessor.getMember(item)); 664 gc.committedMetaspaceDelta = gc.committedMetaspaceDelta 665 .subtract(committedMetaspaceAccessor.getMember(item)); 666 } else { 667 gc.usedMetaspaceDelta = gc.usedMetaspaceDelta.add(usedMetaspaceAccessor.getMember(item)); 668 gc.committedMetaspaceDelta = gc.committedMetaspaceDelta 669 .add(committedMetaspaceAccessor.getMember(item)); 670 } 671 } 672 }); 673 }); 674 gcList.getViewer().setInput(gcMap.values().toArray()); 675 } 676 677 private void onShow(Boolean show) { 678 IRange<IQuantity> range = show ? currentRange : pageContainer.getRecordingRange(); 679 gcChart.setVisibleRange(range.getStart(), range.getEnd()); 680 buildChart(); 681 } 682 683 private void updateChart() { 684 DataPageToolkit.setChart(chartCanvas, gcChart, pageContainer::showSelection); 685 SelectionStoreActionToolkit.addSelectionStoreRangeActions(pageContainer.getSelectionStore(), gcChart, 686 JfrAttributes.LIFETIME, Messages.GarbageCollectionsPage_TIMELINE_SELECTION, 687 chartCanvas.getContextMenu()); 688 buildChart(); 689 } 690 691 private void onInputSelected(IItemCollection items, IRange<IQuantity> timeRange) { 692 this.currentRange = timeRange; 693 selectionItems = items; 694 IItemCollection gcs = items != null ? items : getDataSource().getItems(); 695 updateTable(gcs.apply(JdkFilters.GARBAGE_COLLECTION)); 696 updateChart(); 697 } 698 699 private Stream<? extends IItem> gcSelectedGcItems() { 700 @SuppressWarnings("unchecked") 701 List<GC> sel = ((IStructuredSelection) gcList.getViewer().getSelection()).toList(); 702 return sel.stream().map(gc -> gc.gcItem); 703 } 704 } 705 706 private static void logDuplicateGcId(GC duplicateGC) { 707 if (duplicateGC != null) { 708 IQuantity gcID = JdkAttributes.GC_ID.getAccessor(ItemToolkit.getItemType(duplicateGC.gcItem)) 709 .getMember(duplicateGC.gcItem); 710 FlightRecorderUI.getDefault().getLogger().severe("GC with id " + gcID + " is duplicated"); //$NON-NLS-1$ //$NON-NLS-2$ 711 } 712 } 713 714 private static IColumn buildGCItemAttributeColumn(IAttribute<?> a, IMemberAccessor<?, Object> cellAccessor) { 715 int style = a.getContentType() instanceof LinearKindOfQuantity ? SWT.RIGHT : SWT.NONE; 716 return new ColumnBuilder(a.getName(), a.getIdentifier(), cellAccessor).description(a.getDescription()) 717 .style(style).build(); 718 } 719 720 @Override 721 public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) { 722 return new GarbageCollectionsUi(parent, toolkit, pageContainer, state); 723 } 724 725 private IItemFilter tableFilterState; 726 private IItemFilter phasesFilterState; 727 private IItemFilter metaspaceFilterState; 728 private IRange<IQuantity> timelineRange; 729 private SelectionState gcListSelection; 730 private SelectionState phasesSelection; 731 private SelectionState metaspaceSelection; 732 private int gcInfoTabSelection = 0; 733 public FlavorSelectorState flavorSelectorState; 734 735 public GarbageCollectionsPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) { 736 super(dpd, items, editor); 737 timelineRange = editor.getRecordingRange(); 738 } 739 740 @Override 741 public IItemFilter getDefaultSelectionFilter() { 742 return ItemFilters.or(JdkFilters.GC_PAUSE, JdkFilters.GC_PAUSE_PHASE, JdkFilters.HEAP_SUMMARY, 743 JdkFilters.METASPACE_SUMMARY); 744 } 745 746 private static Set<String> calculateExcludedAttributeIds(IItemCollection items) { 747 // In JDK7 there are no metaspace events. In early JDK8 748 // metaspace:committed is missing. In later JDK8 metaspace:capacity is 749 // missing. 750 Stream<IAttribute<?>> exclude = METASPACE_SUMMARY.getAttributes().stream(); 751 Iterator<IItemIterable> iterator = items.apply(METASPACE_SUMMARY.getFilter()).iterator(); 752 if (iterator.hasNext()) { 753 IType<IItem> type = iterator.next().getType(); 754 exclude = exclude.filter(a -> a.getAccessor(type) == null); 755 } 756 return exclude.map(IAttribute::getIdentifier).collect(Collectors.toSet()); 757 } 758 }