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