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