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 java.awt.Color;
  36 import java.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.List;
  39 
  40 import org.eclipse.jface.resource.ImageDescriptor;
  41 import org.eclipse.osgi.util.NLS;
  42 import org.eclipse.swt.SWT;
  43 import org.eclipse.swt.custom.CTabFolder;
  44 import org.eclipse.swt.custom.CTabItem;
  45 import org.eclipse.swt.custom.SashForm;
  46 import org.eclipse.swt.events.SelectionAdapter;
  47 import org.eclipse.swt.events.SelectionEvent;
  48 import org.eclipse.swt.widgets.Composite;
  49 import org.eclipse.ui.forms.widgets.Form;
  50 import org.eclipse.ui.forms.widgets.FormToolkit;
  51 import org.openjdk.jmc.common.IState;
  52 import org.openjdk.jmc.common.IWritableState;
  53 import org.openjdk.jmc.common.item.IItem;
  54 import org.openjdk.jmc.common.item.IItemCollection;
  55 import org.openjdk.jmc.common.item.IItemFilter;
  56 import org.openjdk.jmc.common.item.ItemFilters;
  57 import org.openjdk.jmc.common.unit.IQuantity;
  58 import org.openjdk.jmc.common.unit.IRange;
  59 import org.openjdk.jmc.common.unit.UnitLookup;
  60 import org.openjdk.jmc.common.util.ColorToolkit;
  61 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  62 import org.openjdk.jmc.flightrecorder.jdk.JdkAggregators;
  63 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  64 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters;
  65 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  66 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  67 import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
  68 import org.openjdk.jmc.flightrecorder.ui.IDataPageFactory;
  69 import org.openjdk.jmc.flightrecorder.ui.IDisplayablePage;
  70 import org.openjdk.jmc.flightrecorder.ui.IPageContainer;
  71 import org.openjdk.jmc.flightrecorder.ui.IPageDefinition;
  72 import org.openjdk.jmc.flightrecorder.ui.IPageUI;
  73 import org.openjdk.jmc.flightrecorder.ui.ItemCollectionToolkit;
  74 import org.openjdk.jmc.flightrecorder.ui.StreamModel;
  75 import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage;
  76 import org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit;
  77 import org.openjdk.jmc.flightrecorder.ui.common.DurationPercentileTable;
  78 import org.openjdk.jmc.flightrecorder.ui.common.DurationPercentileTable.DurationPercentileTableBuilder;
  79 import org.openjdk.jmc.flightrecorder.ui.common.FilterComponent;
  80 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector;
  81 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState;
  82 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
  83 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram;
  84 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.HistogramSelection;
  85 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.ItemHistogramBuilder;
  86 import org.openjdk.jmc.flightrecorder.ui.common.ItemList;
  87 import org.openjdk.jmc.flightrecorder.ui.common.ItemList.ItemListBuilder;
  88 import org.openjdk.jmc.flightrecorder.ui.common.TypeLabelProvider;
  89 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
  90 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStoreActionToolkit;
  91 import org.openjdk.jmc.ui.charts.IXDataRenderer;
  92 import org.openjdk.jmc.ui.charts.RendererToolkit;
  93 import org.openjdk.jmc.ui.charts.XYChart;
  94 import org.openjdk.jmc.ui.column.ColumnManager.SelectionState;
  95 import org.openjdk.jmc.ui.column.ColumnMenusFactory;
  96 import org.openjdk.jmc.ui.column.TableSettings;
  97 import org.openjdk.jmc.ui.column.TableSettings.ColumnSettings;
  98 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
  99 import org.openjdk.jmc.ui.layout.SimpleLayout;
 100 import org.openjdk.jmc.ui.layout.SimpleLayoutData;
 101 import org.openjdk.jmc.ui.misc.ChartCanvas;
 102 import org.openjdk.jmc.ui.misc.PersistableSashForm;
 103 
 104 public class FileIOPage extends AbstractDataPage {
 105         public static class FileIOPageFactory implements IDataPageFactory {
 106 
 107                 @Override
 108                 public String getName(IState state) {
 109                         return Messages.FileIOPage_PAGE_NAME;
 110                 }
 111 
 112                 @Override
 113                 public ImageDescriptor getImageDescriptor(IState state) {
 114                         return FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.PAGE_IO);
 115                 }
 116 
 117                 @Override
 118                 public String[] getTopics(IState state) {
 119                         return new String[] {JfrRuleTopics.FILE_IO_TOPIC};
 120                 }
 121 
 122                 @Override
 123                 public IDisplayablePage createPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 124                         return new FileIOPage(dpd, items, editor);
 125                 }
 126 
 127         }
 128 
 129         private static final Color WRITE_COLOR = TypeLabelProvider.getColor(JdkTypeIDs.FILE_WRITE);
 130         private static final Color READ_COLOR = TypeLabelProvider.getColor(JdkTypeIDs.FILE_READ);
 131         private static final Color WRITE_ALPHA_COLOR = ColorToolkit.withAlpha(WRITE_COLOR, 80);
 132         private static final Color READ_ALPHA_COLOR = ColorToolkit.withAlpha(READ_COLOR, 80);
 133         private static final IItemFilter TABLE_ITEMS = ItemFilters.type(JdkTypeIDs.FILE_READ, JdkTypeIDs.FILE_WRITE);
 134         private static final String TOTAL_TIME = "totalTime"; //$NON-NLS-1$
 135         private static final String MAX_TIME = "maxTime"; //$NON-NLS-1$
 136         private static final String AVG_TIME = "avgTime"; //$NON-NLS-1$
 137         private static final String STDDEV_TIME = "stddevTime"; //$NON-NLS-1$
 138         private static final String READ_COUNT = "readCount"; //$NON-NLS-1$
 139         private static final String WRITE_COUNT = "writeCount"; //$NON-NLS-1$
 140         private static final String READ_SIZE = "readSize"; //$NON-NLS-1$
 141         private static final String WRITE_SIZE = "writeSize"; //$NON-NLS-1$
 142         private static final String READ_EOF = "endOfFile"; //$NON-NLS-1$
 143         private static final String PERCENTILE_READ_TIME = "percentileReadTime"; //$NON-NLS-1$
 144         private static final String PERCENTILE_READ_COUNT = "percentileReadCount"; //$NON-NLS-1$
 145         private static final String PERCENTILE_WRITE_TIME = "percentileWriteTime"; //$NON-NLS-1$
 146         private static final String PERCENTILE_WRITE_COUNT = "percentileWriteCount"; //$NON-NLS-1$
 147 
 148         private static final ItemHistogramBuilder HISTOGRAM = new ItemHistogramBuilder();
 149         private static final ItemListBuilder LIST = new ItemListBuilder();
 150         private static final DurationPercentileTableBuilder PERCENTILES = new DurationPercentileTableBuilder();
 151 
 152         static {
 153                 HISTOGRAM.addCountColumn();
 154                 HISTOGRAM.addColumn(TOTAL_TIME, JdkAggregators.TOTAL_IO_TIME);
 155                 HISTOGRAM.addColumn(MAX_TIME, JdkAggregators.MAX_IO_TIME);
 156                 HISTOGRAM.addColumn(AVG_TIME, JdkAggregators.AVG_IO_TIME);
 157                 HISTOGRAM.addColumn(STDDEV_TIME, JdkAggregators.STDDEV_IO_TIME);
 158                 HISTOGRAM.addColumn(READ_COUNT, JdkAggregators.FILE_READ_COUNT);
 159                 HISTOGRAM.addColumn(WRITE_COUNT, JdkAggregators.FILE_WRITE_COUNT);
 160                 HISTOGRAM.addColumn(READ_SIZE, JdkAggregators.FILE_READ_SIZE);
 161                 HISTOGRAM.addColumn(WRITE_SIZE, JdkAggregators.FILE_WRITE_SIZE);
 162                 LIST.addColumn(JdkAttributes.IO_PATH);
 163                 LIST.addColumn(JfrAttributes.START_TIME);
 164                 LIST.addColumn(JfrAttributes.END_TIME);
 165                 LIST.addColumn(JfrAttributes.DURATION);
 166                 LIST.addColumn(JdkAttributes.IO_FILE_BYTES_READ);
 167                 LIST.addColumn(JdkAttributes.IO_FILE_BYTES_WRITTEN);
 168                 LIST.addColumn(JfrAttributes.EVENT_THREAD);
 169                 LIST.addColumn(JdkAttributes.IO_FILE_READ_EOF);
 170 
 171                 PERCENTILES.addSeries(PERCENTILE_READ_TIME, Messages.FileIOPage_ROW_FILE_READ,
 172                                 PERCENTILE_READ_COUNT, JdkAggregators.FILE_READ_COUNT.getName(), JdkTypeIDs.FILE_READ);
 173                 PERCENTILES.addSeries(PERCENTILE_WRITE_TIME, Messages.FileIOPage_ROW_FILE_WRITE,
 174                                 PERCENTILE_WRITE_COUNT, JdkAggregators.FILE_WRITE_COUNT.getName(), JdkTypeIDs.FILE_WRITE);
 175         }
 176 
 177         private class IOPageUi implements IPageUI {
 178                 private static final String FILE_IO_TABLE = "fileIoTable"; //$NON-NLS-1$
 179                 private static final String FILE_IO_LIST = "fileIoList"; //$NON-NLS-1$
 180                 private static final String SASH_ELEMENT = "sash"; //$NON-NLS-1$
 181                 private static final String LIST_ELEMENT = "eventList"; //$NON-NLS-1$
 182                 private static final String TABLE_ELEMENT = "table"; //$NON-NLS-1$
 183                 private static final String PERCENTILE_TABLE_ELEMENT = "percentileTable"; //$NON-NLS-1$
 184 
 185                 private final ChartCanvas timelineCanvas;
 186                 private final ChartCanvas durationCanvas;
 187                 private final ChartCanvas sizeCanvas;
 188                 private XYChart timelineChart;
 189                 private IRange<IQuantity> timeRange;
 190                 private IItemCollection selectionItems;
 191                 private final ItemList itemList;
 192                 private final ItemHistogram table;
 193                 private final SashForm sash;
 194                 private final IPageContainer pageContainer;
 195                 private FilterComponent tableFilter;
 196                 private FilterComponent itemListFilter;
 197                 private FlavorSelector flavorSelector;
 198                 private DurationPercentileTable percentileTable;
 199                 private Composite durationParent;
 200 
 201                 IOPageUi(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) {
 202                         this.pageContainer = pageContainer;
 203                         Form form = DataPageToolkit.createForm(parent, toolkit, getName(), getIcon());
 204                         sash = new SashForm(form.getBody(), SWT.VERTICAL);
 205                         toolkit.adapt(sash);
 206 
 207                         addResultActions(form);
 208 
 209                         table = HISTOGRAM.buildWithoutBorder(sash, JdkAttributes.IO_PATH,
 210                                         getTableSettings(state.getChild(TABLE_ELEMENT)));
 211                         MCContextMenuManager mm = MCContextMenuManager.create(table.getManager().getViewer().getControl());
 212                         ColumnMenusFactory.addDefaultMenus(table.getManager(), mm);
 213                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), table,
 214                                         Messages.FileIOPage_HISTOGRAM_SELECTION, mm);
 215                         table.getManager().getViewer().addSelectionChangedListener(e -> updateDetails());
 216                         table.getManager().getViewer()
 217                                         .addSelectionChangedListener(e -> pageContainer.showSelection(table.getSelection().getItems()));
 218                         tableFilter = FilterComponent.createFilterComponent(table, FileIOPage.this.tableFilter,
 219                                         getDataSource().getItems().apply(TABLE_ITEMS), pageContainer.getSelectionStore()::getSelections,
 220                                         this::onTableFilterChange);
 221                         mm.add(tableFilter.getShowFilterAction());
 222                         mm.add(tableFilter.getShowSearchAction());
 223 
 224                         CTabFolder tabFolder = new CTabFolder(sash, SWT.NONE);
 225                         toolkit.adapt(tabFolder);
 226                         CTabItem t1 = new CTabItem(tabFolder, SWT.NONE);
 227                         t1.setToolTipText(Messages.IO_PAGE_TIMELINE_DESCRIPTION);
 228                         timelineCanvas = new ChartCanvas(tabFolder);
 229                         t1.setText(Messages.PAGES_TIMELINE);
 230                         t1.setControl(timelineCanvas);
 231                         DataPageToolkit.createChartTimestampTooltip(timelineCanvas);
 232 
 233                         CTabItem t2 = new CTabItem(tabFolder, SWT.NONE);
 234                         durationParent = toolkit.createComposite(tabFolder);
 235                         durationParent.setLayout(new SimpleLayout());
 236                         t2.setToolTipText(Messages.IO_PAGE_DURATIONS_DESCRIPTION);
 237                         durationCanvas = new ChartCanvas(durationParent);
 238                         durationCanvas.setLayoutData(new SimpleLayoutData(3.5f));
 239                         DataPageToolkit.createChartTooltip(durationCanvas);
 240 
 241                         percentileTable = PERCENTILES.build(durationParent,
 242                                         TableSettings.forState(state.getChild(PERCENTILE_TABLE_ELEMENT)));
 243                         percentileTable.getManager().getViewer().getControl().setLayoutData(new SimpleLayoutData(6.5f));
 244                         MCContextMenuManager percentileTableMm = MCContextMenuManager
 245                                         .create(percentileTable.getManager().getViewer().getControl());
 246                         ColumnMenusFactory.addDefaultMenus(percentileTable.getManager(), percentileTableMm);
 247                         SelectionStoreActionToolkit.addSelectionStoreActions(percentileTable.getManager().getViewer(),
 248                                         pageContainer.getSelectionStore(), percentileTable::getSelectedItems,
 249                                         Messages.FileIOPage_PERCENTILE_SELECTION, percentileTableMm);
 250                         t2.setText(Messages.PAGES_DURATIONS);
 251                         t2.setControl(durationParent);
 252 
 253                         CTabItem t3 = new CTabItem(tabFolder, SWT.NONE);
 254                         t3.setToolTipText(Messages.IO_PAGE_SIZE_DESCRIPTION);
 255                         sizeCanvas = new ChartCanvas(tabFolder);
 256                         t3.setText(Messages.PAGES_SIZE);
 257                         t3.setControl(sizeCanvas);
 258                         DataPageToolkit.createChartTooltip(sizeCanvas);
 259 
 260                         CTabItem t4 = new CTabItem(tabFolder, SWT.NONE);
 261                         t4.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
 262                         itemList = LIST.buildWithoutBorder(tabFolder, getTableSettings(state.getChild(LIST_ELEMENT)));
 263                         MCContextMenuManager itemListMm = MCContextMenuManager
 264                                         .create(itemList.getManager().getViewer().getControl());
 265                         ColumnMenusFactory.addDefaultMenus(itemList.getManager(), itemListMm);
 266                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), itemList,
 267                                         Messages.FileIOPage_LOG_SELECTION, itemListMm);
 268                         itemList.getManager().getViewer().addSelectionChangedListener(
 269                                         e -> pageContainer.showSelection(ItemCollectionToolkit.build(itemList.getSelection().get())));
 270                         t4.setText(Messages.PAGES_EVENT_LOG);
 271                         itemListFilter = FilterComponent.createFilterComponent(itemList, FileIOPage.this.itemListFilter,
 272                                         getDataSource().getItems().apply(TABLE_ITEMS), pageContainer.getSelectionStore()::getSelections,
 273                                         this::onListFilterChange);
 274                         itemListMm.add(itemListFilter.getShowFilterAction());
 275                         itemListMm.add(itemListFilter.getShowSearchAction());
 276                         t4.setControl(itemListFilter.getComponent());
 277 
 278                         tableFilter.loadState(state.getChild(FILE_IO_TABLE));
 279                         itemListFilter.loadState(state.getChild(FILE_IO_LIST));
 280 
 281                         tabFolder.setSelection(tabFolderIndex);
 282                         tabFolder.addSelectionListener(new SelectionAdapter() {
 283                                 @Override
 284                                 public void widgetSelected(SelectionEvent e) {
 285                                         tabFolderIndex = ((CTabFolder) e.getSource()).getSelectionIndex();
 286                                 }
 287                         });
 288 
 289                         timelineChart = createTimelineChart(pageContainer);
 290                         hookUpTimeLineChart();
 291                         PersistableSashForm.loadState(sash, state.getChild(SASH_ELEMENT));
 292 
 293                         flavorSelector = FlavorSelector.itemsWithTimerange(form, TABLE_ITEMS, getDataSource().getItems(),
 294                                         pageContainer, this::onInputSelected, this::onShowFlavor, flavorSelectorState);
 295 
 296                         table.getManager().setSelectionState(tableSelection);
 297                         percentileTable.getManager().setSelectionState(percentileSelection);
 298                         itemList.getManager().setSelectionState(itemListSelection);
 299                 }
 300 
 301                 private XYChart createTimelineChart(IPageContainer pageContainer) {
 302                         XYChart timelineChart = new XYChart(pageContainer.getRecordingRange(), RendererToolkit.empty(), 180);
 303                         timelineChart.setVisibleRange(timelineRange.getStart(), timelineRange.getEnd());
 304                         timelineChart.addVisibleRangeListener(range -> timelineRange = range);
 305                         return timelineChart;
 306                 }
 307 
 308                 private void onTableFilterChange(IItemFilter filter) {
 309                         tableFilter.filterChangeHelper(filter, table, getDataSource().getItems().apply(TABLE_ITEMS));
 310                         itemListFilter.notifyListener();
 311                         FileIOPage.this.tableFilter = filter;
 312                 }
 313 
 314                 private void onListFilterChange(IItemFilter filter) {
 315                         itemListFilter.filterChangeHelper(filter, itemList, getDataSource().getItems().apply(TABLE_ITEMS));
 316                         FileIOPage.this.itemListFilter = filter;
 317                 }
 318 
 319                 @Override
 320                 public void saveTo(IWritableState writableState) {
 321                         PersistableSashForm.saveState(sash, writableState.createChild(SASH_ELEMENT));
 322                         table.getManager().getSettings().saveState(writableState.createChild(TABLE_ELEMENT));
 323                         tableFilter.saveState(writableState.createChild(FILE_IO_TABLE));
 324                         itemList.getManager().getSettings().saveState(writableState.createChild(LIST_ELEMENT));
 325                         itemListFilter.saveState(writableState.createChild(FILE_IO_LIST));
 326                         percentileTable.getManager().getSettings().saveState(writableState.createChild(PERCENTILE_TABLE_ELEMENT));
 327 
 328                         saveToLocal();
 329                 }
 330 
 331                 private void saveToLocal() {
 332                         tableSelection = table.getManager().getSelectionState();
 333                         itemListSelection = itemList.getManager().getSelectionState();
 334                         flavorSelectorState = flavorSelector.getFlavorSelectorState();
 335                         percentileSelection = percentileTable.getManager().getSelectionState();
 336                 }
 337 
 338                 private void onShowFlavor(Boolean show) {
 339                         IRange<IQuantity> range = show ? timeRange : pageContainer.getRecordingRange();
 340                         timelineChart.setVisibleRange(range.getStart(), range.getEnd());
 341                         hookUpTimeLineChart();
 342                 }
 343 
 344                 private void hookUpTimeLineChart() {
 345                         DataPageToolkit.setChart(timelineCanvas, timelineChart, pageContainer::showSelection);
 346                         SelectionStoreActionToolkit.addSelectionStoreRangeActions(pageContainer.getSelectionStore(), timelineChart,
 347                                         JfrAttributes.LIFETIME, Messages.FileIOPage_TIMELINE_SELECTION, timelineCanvas.getContextMenu());
 348                 }
 349 
 350                 private void onInputSelected(IItemCollection items, IRange<IQuantity> timeRange) {
 351                         this.selectionItems = items;
 352                         this.timeRange = timeRange;
 353                         updateDetails();
 354                 }
 355 
 356                 private void updateDetails() {
 357                         IItemCollection items = selectionItems != null ? selectionItems.apply(TABLE_ITEMS)
 358                                         : getDataSource().getItems().apply(TABLE_ITEMS);
 359                         table.show(items);
 360                         HistogramSelection histogramSelection = table.getSelection();
 361                         IItemCollection selectedItems = histogramSelection.getRowCount() == 0 ? items
 362                                         : histogramSelection.getItems();
 363 
 364                         String pathCount = pathCount(histogramSelection.getRowCount());
 365                         List<IXDataRenderer> timelineRows = new ArrayList<>();
 366                         List<IXDataRenderer> durationRows = new ArrayList<>();
 367                         List<IXDataRenderer> sizeRows = new ArrayList<>();
 368                         IItemCollection readItems = selectedItems.apply(JdkFilters.FILE_READ);
 369                         if (readItems.hasItems()) {
 370                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.FileIOPage_ROW_FILE_READ + pathCount,
 371                                                 JdkAggregators.FILE_READ_SIZE.getDescription(), readItems, JdkAggregators.FILE_READ_SIZE,
 372                                                 READ_COLOR, FileIOPage::getColor));
 373                                 durationRows.add(DataPageToolkit.buildDurationHistogram(Messages.FileIOPage_ROW_FILE_READ + pathCount,
 374                                                 JdkAggregators.FILE_READ_COUNT.getDescription(), readItems, JdkAggregators.FILE_READ_COUNT,
 375                                                 READ_COLOR));
 376                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.FileIOPage_ROW_FILE_READ + pathCount,
 377                                                 JdkAggregators.FILE_READ_COUNT.getDescription(), readItems, JdkAggregators.FILE_READ_COUNT,
 378                                                 READ_COLOR, JdkAttributes.IO_FILE_BYTES_READ));
 379                         }
 380                         IItemCollection writeItems = selectedItems.apply(JdkFilters.FILE_WRITE);
 381                         if (writeItems.hasItems()) {
 382                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.FileIOPage_ROW_FILE_WRITE + pathCount,
 383                                                 JdkAggregators.FILE_WRITE_SIZE.getDescription(), writeItems, JdkAggregators.FILE_WRITE_SIZE,
 384                                                 WRITE_COLOR, FileIOPage::getColor));
 385                                 durationRows.add(DataPageToolkit.buildDurationHistogram(Messages.FileIOPage_ROW_FILE_WRITE + pathCount,
 386                                                 JdkAggregators.FILE_WRITE_COUNT.getDescription(), writeItems, JdkAggregators.FILE_WRITE_COUNT,
 387                                                 WRITE_COLOR));
 388                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.FileIOPage_ROW_FILE_WRITE + pathCount,
 389                                                 JdkAggregators.FILE_WRITE_COUNT.getDescription(), writeItems, JdkAggregators.FILE_WRITE_COUNT,
 390                                                 WRITE_COLOR, JdkAttributes.IO_FILE_BYTES_WRITTEN));
 391                         }
 392 //                      ItemRow[] pathRows = selection.getSelectedRows(FileIOPage::buildPathLane).toArray(ItemRow[]::new);
 393 
 394                         timelineCanvas.replaceRenderer(RendererToolkit.uniformRows(timelineRows));
 395 
 396                         IXDataRenderer durationRoot = RendererToolkit.uniformRows(durationRows);
 397                         // FIXME: X-auto-range should be done properly
 398                         IQuantity max = selectedItems.getAggregate(JdkAggregators.LONGEST_EVENT);
 399                         // FIXME: Workaround to make max value included
 400                         max = max == null ? UnitLookup.MILLISECOND.quantity(20) : max.add(UnitLookup.MILLISECOND.quantity(20));
 401                         XYChart durationChart = new XYChart(UnitLookup.MILLISECOND.quantity(0), max, durationRoot, 180);
 402                         DataPageToolkit.setChart(durationCanvas, durationChart, JfrAttributes.DURATION,
 403                                         selection -> pageContainer.showSelection(selection));
 404                         durationChart.setVisibleRange(durationRange.getStart(), durationRange.getEnd());
 405                         durationChart.addVisibleRangeListener(range -> durationRange = range);
 406                         durationCanvas.setChart(durationChart);
 407                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), durationChart,
 408                                         JfrAttributes.DURATION, Messages.FileIOPage_DURATION_SELECTION, durationCanvas.getContextMenu());
 409                         itemList.show(selectedItems);
 410                         percentileTable.update(selectedItems);
 411 
 412                         IXDataRenderer sizeRoot = RendererToolkit.uniformRows(sizeRows);
 413                         IQuantity sizeMax = selectedItems.getAggregate(JdkAggregators.FILE_READ_LARGEST);
 414                         // FIXME: Workaround to make max value included
 415                         sizeMax = sizeMax == null ? UnitLookup.BYTE.quantity(64): sizeMax.add(UnitLookup.BYTE.quantity(64));
 416                         XYChart sizeChart = new XYChart(UnitLookup.BYTE.quantity(0), sizeMax, sizeRoot, 180);
 417                         DataPageToolkit.setChart(sizeCanvas, sizeChart, JdkAttributes.IO_SIZE,
 418                                         selection -> pageContainer.showSelection(selection));
 419                         sizeChart.setVisibleRange(sizeRange.getStart(), sizeRange.getEnd());
 420                         sizeChart.addVisibleRangeListener(range -> sizeRange = range);
 421                         sizeCanvas.setChart(sizeChart);
 422                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), sizeChart,
 423                                         JdkAttributes.IO_SIZE, Messages.FileIOPage_SIZE_SELECTION, sizeCanvas.getContextMenu());
 424                 }
 425         }
 426 
 427         private static TableSettings getTableSettings(IState state) {
 428                 if (state == null) {
 429                         return new TableSettings(TOTAL_TIME, Arrays.asList(
 430                                         new ColumnSettings(ItemHistogram.KEY_COL_ID, false, 500, null),
 431                                         new ColumnSettings(TOTAL_TIME, true, 120, false), new ColumnSettings(MAX_TIME, false, 120, false),
 432                                         new ColumnSettings(AVG_TIME, false, 120, false), new ColumnSettings(STDDEV_TIME, false, 120, false),
 433                                         new ColumnSettings(READ_COUNT, false, 120, false),
 434                                         new ColumnSettings(WRITE_COUNT, false, 120, false),
 435                                         new ColumnSettings(READ_SIZE, false, 120, false), new ColumnSettings(WRITE_SIZE, false, 120, false),
 436                                         new ColumnSettings(READ_EOF, false, 80, false)));
 437                 } else {
 438                         return new TableSettings(state);
 439                 }
 440         }
 441 
 442         @Override
 443         public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) {
 444                 return new IOPageUi(parent, toolkit, pageContainer, state);
 445         }
 446 
 447         private SelectionState tableSelection;
 448         private SelectionState itemListSelection;
 449         private SelectionState percentileSelection;
 450         private IItemFilter tableFilter = null;
 451         private IItemFilter itemListFilter = null;
 452         private int tabFolderIndex = 0;
 453         private IRange<IQuantity> timelineRange;
 454         private IRange<IQuantity> durationRange;
 455         private IRange<IQuantity> sizeRange;
 456         public FlavorSelectorState flavorSelectorState;
 457 
 458         public FileIOPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 459                 super(dpd, items, editor);
 460                 timelineRange = editor.getRecordingRange();
 461                 durationRange = editor.getRecordingRange();
 462                 sizeRange = DataPageToolkit.buildSizeRange(items.getItems(), false);
 463         }
 464 
 465 //      private static ItemRow buildPathLane(Object path, Supplier<Stream<ItemStream>> pathItems) {
 466 //              String pathName = String.valueOf(path);
 467 //              pathName = pathName.length() > 26 ? pathName.substring(0, 23) + "..." : pathName; //$NON-NLS-1$
 468 //              return new ItemRow(pathName, buildSpanRenderer(pathItems), pathItems);
 469 //      }
 470 
 471         private static Color getColor(IItem item) {
 472                 return JdkTypeIDs.FILE_READ.equals(item.getType().getIdentifier()) ? READ_ALPHA_COLOR : WRITE_ALPHA_COLOR;
 473         }
 474 
 475         @Override
 476         public IItemFilter getDefaultSelectionFilter() {
 477                 return TABLE_ITEMS;
 478         }
 479 
 480         private static String pathCount(int count) {
 481                 switch (count) {
 482                 case 0:
 483                         return ""; //$NON-NLS-1$
 484                 case 1:
 485                         return " (" + Messages.FileIOPage_SELECTED_PATH + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 486                 default:
 487                         return " (" + NLS.bind(Messages.FileIOPage_SELECTED_PATHS, count) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 488                 }
 489         }
 490 }