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.JdkAttributes.IO_ADDRESS;
  36 import static org.openjdk.jmc.flightrecorder.jdk.JdkAttributes.IO_PORT;
  37 
  38 import java.awt.Color;
  39 import java.util.ArrayList;
  40 import java.util.Arrays;
  41 import java.util.HashMap;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Optional;
  45 import java.util.function.Consumer;
  46 import java.util.function.Supplier;
  47 
  48 import org.eclipse.jface.action.IAction;
  49 import org.eclipse.jface.resource.ImageDescriptor;
  50 import org.eclipse.osgi.util.NLS;
  51 import org.eclipse.swt.SWT;
  52 import org.eclipse.swt.custom.CTabFolder;
  53 import org.eclipse.swt.custom.CTabItem;
  54 import org.eclipse.swt.custom.SashForm;
  55 import org.eclipse.swt.layout.FillLayout;
  56 import org.eclipse.swt.widgets.Composite;
  57 import org.eclipse.swt.widgets.Control;
  58 import org.eclipse.ui.forms.widgets.Form;
  59 import org.eclipse.ui.forms.widgets.FormToolkit;
  60 
  61 import org.openjdk.jmc.common.IDisplayable;
  62 import org.openjdk.jmc.common.IState;
  63 import org.openjdk.jmc.common.IWritableState;
  64 import org.openjdk.jmc.common.item.IAccessorFactory;
  65 import org.openjdk.jmc.common.item.IAttribute;
  66 import org.openjdk.jmc.common.item.IItem;
  67 import org.openjdk.jmc.common.item.IItemCollection;
  68 import org.openjdk.jmc.common.item.IItemFilter;
  69 import org.openjdk.jmc.common.item.ItemFilters;
  70 import org.openjdk.jmc.common.unit.IQuantity;
  71 import org.openjdk.jmc.common.unit.IRange;
  72 import org.openjdk.jmc.common.unit.QuantitiesToolkit;
  73 import org.openjdk.jmc.common.unit.UnitLookup;
  74 import org.openjdk.jmc.common.util.ColorToolkit;
  75 import org.openjdk.jmc.common.util.StateToolkit;
  76 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  77 import org.openjdk.jmc.flightrecorder.jdk.JdkAggregators;
  78 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  79 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters;
  80 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  81 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  82 import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
  83 import org.openjdk.jmc.flightrecorder.ui.IDataPageFactory;
  84 import org.openjdk.jmc.flightrecorder.ui.IDisplayablePage;
  85 import org.openjdk.jmc.flightrecorder.ui.IPageContainer;
  86 import org.openjdk.jmc.flightrecorder.ui.IPageDefinition;
  87 import org.openjdk.jmc.flightrecorder.ui.IPageUI;
  88 import org.openjdk.jmc.flightrecorder.ui.ItemCollectionToolkit;
  89 import org.openjdk.jmc.flightrecorder.ui.StreamModel;
  90 import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage;
  91 import org.openjdk.jmc.flightrecorder.ui.common.CompositeKeyAccessorFactory;
  92 import org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit;
  93 import org.openjdk.jmc.flightrecorder.ui.common.FilterComponent;
  94 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector;
  95 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState;
  96 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
  97 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram;
  98 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.HistogramSelection;
  99 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.ItemHistogramBuilder;
 100 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogramWithInput;
 101 import org.openjdk.jmc.flightrecorder.ui.common.ItemList;
 102 import org.openjdk.jmc.flightrecorder.ui.common.ItemList.ItemListBuilder;
 103 import org.openjdk.jmc.flightrecorder.ui.common.TypeLabelProvider;
 104 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
 105 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStoreActionToolkit;
 106 import org.openjdk.jmc.ui.charts.IXDataRenderer;
 107 import org.openjdk.jmc.ui.charts.RendererToolkit;
 108 import org.openjdk.jmc.ui.charts.XYChart;
 109 import org.openjdk.jmc.ui.column.ColumnManager.SelectionState;
 110 import org.openjdk.jmc.ui.column.ColumnMenusFactory;
 111 import org.openjdk.jmc.ui.column.TableSettings;
 112 import org.openjdk.jmc.ui.column.TableSettings.ColumnSettings;
 113 import org.openjdk.jmc.ui.handlers.ActionToolkit;
 114 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
 115 import org.openjdk.jmc.ui.misc.ChartCanvas;
 116 import org.openjdk.jmc.ui.misc.PersistableSashForm;
 117 
 118 public class SocketIOPage extends AbstractDataPage {
 119         public static class SocketIOPageFactory implements IDataPageFactory {
 120                 @Override
 121                 public String getName(IState state) {
 122                         return Messages.SocketIOPage_PAGE_NAME;
 123                 }
 124 
 125                 @Override
 126                 public ImageDescriptor getImageDescriptor(IState state) {
 127                         return FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.PAGE_IO);
 128                 }
 129 
 130                 @Override
 131                 public String[] getTopics(IState state) {
 132                         return new String[] {JfrRuleTopics.SOCKET_IO_TOPIC};
 133                 }
 134 
 135                 @Override
 136                 public IDisplayablePage createPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 137                         return new SocketIOPage(dpd, items, editor);
 138                 }
 139 
 140         }
 141 
 142         private static final Color WRITE_COLOR = TypeLabelProvider.getColor(JdkTypeIDs.SOCKET_WRITE);
 143         private static final Color READ_COLOR = TypeLabelProvider.getColor(JdkTypeIDs.SOCKET_READ);
 144         private static final Color WRITE_ALPHA_COLOR = ColorToolkit.withAlpha(WRITE_COLOR, 80);
 145         private static final Color READ_ALPHA_COLOR = ColorToolkit.withAlpha(READ_COLOR, 80);
 146         private static final IItemFilter TABLE_ITEMS = ItemFilters.type(JdkTypeIDs.SOCKET_READ, JdkTypeIDs.SOCKET_WRITE);
 147         private static final String TOTAL_TIME = "totalTime"; //$NON-NLS-1$
 148         private static final String MAX_TIME = "maxTime"; //$NON-NLS-1$
 149         private static final String AVG_TIME = "avgTime"; //$NON-NLS-1$
 150         private static final String STDDEV_TIME = "stddevTime"; //$NON-NLS-1$
 151         private static final String READ_COUNT = "readCount"; //$NON-NLS-1$
 152         private static final String WRITE_COUNT = "writeCount"; //$NON-NLS-1$
 153         private static final String READ_SIZE = "readSize"; //$NON-NLS-1$
 154         private static final String WRITE_SIZE = "writeSize"; //$NON-NLS-1$
 155         private static final String READ_EOS = "endOfStream"; //$NON-NLS-1$
 156         private static final String IO_TIMEOUT = "timeout"; //$NON-NLS-1$
 157         private static final IAccessorFactory<IDisplayable> HOST_AND_PORT_AF = CompositeKeyAccessorFactory.displayable(
 158                         " : ", JdkAttributes.IO_ADDRESS, //$NON-NLS-1$
 159                         JdkAttributes.IO_PORT);
 160 
 161         private static final ItemHistogramBuilder HISTOGRAM = new ItemHistogramBuilder();
 162         private static final ItemListBuilder LIST = new ItemListBuilder();
 163 
 164         static {
 165                 HISTOGRAM.addCountColumn();
 166                 HISTOGRAM.addColumn(TOTAL_TIME, JdkAggregators.TOTAL_IO_TIME);
 167                 HISTOGRAM.addColumn(MAX_TIME, JdkAggregators.MAX_IO_TIME);
 168                 HISTOGRAM.addColumn(AVG_TIME, JdkAggregators.AVG_IO_TIME);
 169                 HISTOGRAM.addColumn(STDDEV_TIME, JdkAggregators.STDDEV_IO_TIME);
 170                 HISTOGRAM.addColumn(READ_COUNT, JdkAggregators.SOCKET_READ_COUNT);
 171                 HISTOGRAM.addColumn(WRITE_COUNT, JdkAggregators.SOCKET_WRITE_COUNT);
 172                 HISTOGRAM.addColumn(READ_SIZE, JdkAggregators.SOCKET_READ_SIZE);
 173                 HISTOGRAM.addColumn(WRITE_SIZE, JdkAggregators.SOCKET_WRITE_SIZE);
 174                 // FIXME: Would we like to include # of hosts, # of ports and host name in the new histograms?
 175 
 176                 LIST.addColumn(JdkAttributes.IO_ADDRESS);
 177                 LIST.addColumn(JdkAttributes.IO_HOST);
 178                 LIST.addColumn(JdkAttributes.IO_PORT);
 179                 LIST.addColumn(JfrAttributes.START_TIME);
 180                 LIST.addColumn(JfrAttributes.END_TIME);
 181                 LIST.addColumn(JfrAttributes.DURATION);
 182                 LIST.addColumn(JdkAttributes.IO_SOCKET_BYTES_READ);
 183                 LIST.addColumn(JdkAttributes.IO_SOCKET_BYTES_WRITTEN);
 184                 LIST.addColumn(JfrAttributes.EVENT_THREAD);
 185                 LIST.addColumn(JdkAttributes.IO_SOCKET_READ_EOS);
 186                 LIST.addColumn(JdkAttributes.IO_TIMEOUT);
 187         }
 188 
 189         private enum HistogramType {
 190                 HOST, PORT, HOST_AND_PORT
 191         }
 192 
 193         private class IOPageUi implements IPageUI {
 194                 private static final String PRIMARY_FILTER = "primaryFilter"; //$NON-NLS-1$
 195                 private static final String SECONDARY_FILTER = "secondaryFilter"; //$NON-NLS-1$
 196                 private static final String EVENT_FILTER = "eventFilter"; //$NON-NLS-1$
 197                 private static final String SASH_ELEMENT = "sash"; //$NON-NLS-1$
 198                 private static final String LIST_ELEMENT = "eventList"; //$NON-NLS-1$
 199                 private static final String SOCKETIO_TABLE_ELEMENT = "socketTable"; //$NON-NLS-1$
 200                 private static final String SECONDARY_SOCKETIO_TABLE_ELEMENT = "secondarySocketTable"; //$NON-NLS-1$
 201                 private static final String HISTGRAM_TYPE = "histogramType"; //$NON-NLS-1$
 202 
 203                 private final ChartCanvas timelineCanvas;
 204                 private final ChartCanvas durationCanvas;
 205                 private final ChartCanvas sizeCanvas;
 206                 private final ItemList itemList;
 207 
 208                 private final SashForm sash;
 209                 private final IPageContainer pageContainer;
 210                 private final Composite histogramParent;
 211                 private ItemHistogram primaryHistogram;
 212                 private Supplier<TableSettings> secondaryHistogramSettings;
 213                 private Consumer<IItemCollection> itemConsumerRoot;
 214                 private HistogramType histogramType;
 215                 private ItemHistogram secondaryHistogram;
 216                 private FilterComponent primaryFilter;
 217                 private FilterComponent secondaryFilter;
 218                 private FilterComponent eventFilter;
 219                 private IRange<IQuantity> timeRange;
 220                 private IItemCollection selectionItems;
 221                 private XYChart timelineChart;
 222                 private XYChart durationChart;
 223                 private XYChart sizeChart;
 224                 private CTabFolder tabFolder;
 225                 private FlavorSelector flavorSelector;
 226 
 227                 IOPageUi(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) {
 228                         this.pageContainer = pageContainer;
 229                         Form form = DataPageToolkit.createForm(parent, toolkit, getName(), getIcon());
 230                         sash = new SashForm(form.getBody(), SWT.VERTICAL);
 231                         toolkit.adapt(sash);
 232                         histogramParent = toolkit.createComposite(sash);
 233                         histogramParent.setLayout(new FillLayout(SWT.VERTICAL));
 234                         histogramType = StateToolkit.readEnum(state, HISTGRAM_TYPE, HistogramType.HOST, HistogramType.class);
 235                         buildHistograms(TableSettings.forState(state.getChild(SOCKETIO_TABLE_ELEMENT)),
 236                                         TableSettings.forState(state.getChild(SECONDARY_SOCKETIO_TABLE_ELEMENT)));
 237 
 238                         tabFolder = new CTabFolder(sash, SWT.NONE);
 239                         toolkit.adapt(tabFolder);
 240                         CTabItem t1 = new CTabItem(tabFolder, SWT.NONE);
 241                         t1.setToolTipText(Messages.IO_PAGE_TIMELINE_DESCRIPTION);
 242                         timelineCanvas = new ChartCanvas(tabFolder);
 243                         t1.setText(Messages.PAGES_TIMELINE);
 244                         t1.setControl(timelineCanvas);
 245                         DataPageToolkit.createChartTimestampTooltip(timelineCanvas);
 246                         timelineChart = new XYChart(pageContainer.getRecordingRange(), RendererToolkit.empty(), 180);
 247                         timelineChart.setVisibleRange(timelineRange.getStart(), timelineRange.getEnd());
 248                         timelineChart.addVisibleRangeListener(r -> timelineRange = r);
 249                         IItemCollection socketItems = getDataSource().getItems().apply(JdkFilters.SOCKET_READ_OR_WRITE);
 250                         // FIXME: X-auto-range should be done properly
 251                         IQuantity max = socketItems.getAggregate(JdkAggregators.LONGEST_EVENT);
 252                         // FIXME: Workaround to make max value included
 253                         max = max == null ? UnitLookup.MILLISECOND.quantity(20) : max.add(UnitLookup.MILLISECOND.quantity(20));
 254                         durationChart = new XYChart(UnitLookup.MILLISECOND.quantity(0), max, RendererToolkit.empty(), 180);
 255                         durationChart.setVisibleRange(durationRange.getStart(), durationRange.getEnd());
 256                         durationChart.addVisibleRangeListener(r -> durationRange = r);
 257                         buildChart();
 258 
 259                         CTabItem t2 = new CTabItem(tabFolder, SWT.NONE);
 260                         t2.setToolTipText(Messages.IO_PAGE_DURATIONS_DESCRIPTION);
 261                         durationCanvas = new ChartCanvas(tabFolder);
 262                         t2.setText(Messages.PAGES_DURATIONS);
 263                         t2.setControl(durationCanvas);
 264                         DataPageToolkit.createChartTooltip(durationCanvas);
 265                         DataPageToolkit.setChart(durationCanvas, durationChart, JfrAttributes.DURATION,
 266                                         pageContainer::showSelection);
 267                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), durationChart,
 268                                         JfrAttributes.DURATION, Messages.SocketIOPage_DURATION_SELECTION, durationCanvas.getContextMenu());
 269                         
 270                         IQuantity sizeMax = QuantitiesToolkit.maxPresent(socketItems.getAggregate(JdkAggregators.SOCKET_READ_LARGEST),
 271                                         socketItems.getAggregate(JdkAggregators.SOCKET_WRITE_LARGEST)); 
 272                         // FIXME: Workaround to make max value included
 273                         sizeMax = sizeMax == null ? UnitLookup.BYTE.quantity(64): sizeMax.add(UnitLookup.BYTE.quantity(64));
 274                         sizeChart = new XYChart(UnitLookup.BYTE.quantity(0), sizeMax, RendererToolkit.empty(), 180);
 275                         sizeChart.setVisibleRange(sizeRange.getStart(), sizeMax);
 276                         sizeChart.addVisibleRangeListener(range -> sizeRange = range);
 277                         
 278                         CTabItem t3 = new CTabItem(tabFolder, SWT.NONE);
 279                         t3.setToolTipText(Messages.IO_PAGE_SIZE_DESCRIPTION);
 280                         sizeCanvas = new ChartCanvas(tabFolder);
 281                         t3.setText(Messages.PAGES_SIZE);
 282                         t3.setControl(sizeCanvas);
 283                         DataPageToolkit.createChartTooltip(sizeCanvas);
 284                         DataPageToolkit.setChart(sizeCanvas, sizeChart, JdkAttributes.IO_SIZE,
 285                                         pageContainer::showSelection);
 286                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), sizeChart,
 287                                         JdkAttributes.IO_SIZE, Messages.SocketIOPage_SIZE_SELECTION, sizeCanvas.getContextMenu());
 288 
 289                         CTabItem t4 = new CTabItem(tabFolder, SWT.NONE);
 290                         t4.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
 291                         itemList = LIST.buildWithoutBorder(tabFolder, getTableSettings(state.getChild(LIST_ELEMENT)));
 292                         MCContextMenuManager itemListMm = MCContextMenuManager
 293                                         .create(itemList.getManager().getViewer().getControl());
 294                         ColumnMenusFactory.addDefaultMenus(itemList.getManager(), itemListMm);
 295                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), itemList,
 296                                         Messages.SocketIOPage_LOG_SELECTION, itemListMm);
 297                         itemList.getManager().getViewer().addSelectionChangedListener(
 298                                         e -> pageContainer.showSelection(ItemCollectionToolkit.build(itemList.getSelection().get())));
 299                         t4.setText(Messages.PAGES_EVENT_LOG);
 300                         eventFilter = FilterComponent.createFilterComponent(itemList, itemListFilter,
 301                                         getDataSource().getItems().apply(TABLE_ITEMS), pageContainer.getSelectionStore()::getSelections,
 302                                         this::onEventFilterChange);
 303                         itemListMm.add(eventFilter.getShowFilterAction());
 304                         itemListMm.add(eventFilter.getShowSearchAction());
 305                         t4.setControl(eventFilter.getComponent());
 306                         eventFilter.loadState(state.getChild(EVENT_FILTER));
 307                         onEventFilterChange(itemListFilter);
 308                         itemList.getManager().setSelectionState(itemListSelection);
 309                         
 310                         
 311 
 312                         tabFolder.setSelection(tabFolderIndex);
 313 
 314                         PersistableSashForm.loadState(sash, state.getChild(SASH_ELEMENT));
 315 
 316                         flavorSelector = FlavorSelector.itemsWithTimerange(form, TABLE_ITEMS, getDataSource().getItems(),
 317                                         pageContainer, this::onInputSelected, this::onUseRange, flavorSelectorState);
 318 
 319                         form.getToolBarManager()
 320                                         .appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(HistogramType.HOST,
 321                                                         Messages.SocketIOPage_BY_HOST_ACTION,
 322                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_HOST)));
 323                         form.getToolBarManager()
 324                                         .appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(HistogramType.PORT,
 325                                                         Messages.SocketIOPage_BY_PORT_ACTION,
 326                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_PORT)));
 327                         form.getToolBarManager().appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(
 328                                         HistogramType.HOST_AND_PORT, Messages.SocketIOPage_BY_HOST_AND_PORT_ACTION,
 329                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_HOST_AND_PORT)));
 330 
 331                         addResultActions(form);
 332                 }
 333 
 334                 // FIXME: Break out this to a "ConfigurableHistogramUi or something? This is copy-pasted from ExceptionsPage
 335                 private IAction buildHistogramTypeAction(HistogramType histogramType, String text, ImageDescriptor icon) {
 336                         IAction a = ActionToolkit.radioAction(() -> setHistogramType(histogramType), text, icon);
 337                         a.setChecked(histogramType == this.histogramType);
 338                         return a;
 339                 }
 340 
 341                 private void setHistogramType(HistogramType histogramType) {
 342                         if (histogramType != this.histogramType) {
 343                                 primaryTableSelection.put(this.histogramType, primaryHistogram.getManager().getSelectionState());
 344                                 if (secondaryHistogram != null) {
 345                                         secondaryTableSelection.put(this.histogramType,
 346                                                         secondaryHistogram.getManager().getSelectionState());
 347                                 }
 348                                 this.histogramType = histogramType;
 349                                 TableSettings primarySettings = primaryHistogram.getManager().getSettings();
 350                                 TableSettings secondarySettings = secondaryHistogramSettings.get();
 351                                 for (Control c : histogramParent.getChildren()) {
 352                                         c.dispose();
 353                                 }
 354                                 buildHistograms(primarySettings, secondarySettings);
 355                                 refreshPageItems();
 356                         }
 357                 }
 358 
 359                 private void buildHistograms(TableSettings primarySettings, TableSettings secondarySettings) {
 360                         if (histogramType == HistogramType.HOST_AND_PORT) {
 361                                 primaryHistogram = HISTOGRAM.buildWithoutBorder(histogramParent, Messages.SocketIOPage_HOST_AND_PORT,
 362                                                 UnitLookup.UNKNOWN, HOST_AND_PORT_AF, primarySettings);
 363                                 primaryFilter = FilterComponent.createFilterComponent(primaryHistogram,
 364                                                 primaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 365                                                 pageContainer.getSelectionStore()::getSelections, this::onPrimaryFilterChange);
 366                                 secondaryHistogram = null;
 367                                 secondaryHistogramSettings = () -> secondarySettings;
 368                                 secondaryFilter = null;
 369                                 onPrimaryFilterChange(primaryTableFilter.get(histogramType));
 370                                 primaryHistogram.getManager().setSelectionState(primaryTableSelection.get(histogramType));
 371                                 itemConsumerRoot = ItemHistogramWithInput.chain(primaryHistogram, this::updateChartAndListDetails);
 372                         } else {
 373                                 SashForm s2 = new SashForm(histogramParent, SWT.VERTICAL);
 374                                 IAttribute<?> masterAttr = histogramType == HistogramType.HOST ? IO_ADDRESS : IO_PORT;
 375                                 IAttribute<?> slaveAttr = histogramType == HistogramType.PORT ? IO_ADDRESS : IO_PORT;
 376                                 primaryHistogram = HISTOGRAM.buildWithoutBorder(s2, masterAttr, primarySettings);
 377                                 primaryFilter = FilterComponent.createFilterComponent(primaryHistogram,
 378                                                 primaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 379                                                 pageContainer.getSelectionStore()::getSelections, this::onPrimaryFilterChange);
 380 
 381                                 secondaryHistogram = HISTOGRAM.buildWithoutBorder(s2, slaveAttr, secondarySettings);
 382                                 secondaryFilter = FilterComponent.createFilterComponent(secondaryHistogram,
 383                                                 secondaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 384                                                 pageContainer.getSelectionStore()::getSelections, this::onSecondaryFilterChange);
 385                                 secondaryHistogramSettings = secondaryHistogram.getManager()::getSettings;
 386                                 onPrimaryFilterChange(primaryTableFilter.get(histogramType));
 387                                 onSecondaryFilterChange(secondaryTableFilter.get(histogramType));
 388                                 primaryHistogram.getManager().setSelectionState(primaryTableSelection.get(histogramType));
 389                                 secondaryHistogram.getManager().setSelectionState(secondaryTableSelection.get(histogramType));
 390                                 itemConsumerRoot = ItemHistogramWithInput.chain(primaryHistogram, this::updateChartAndListDetails,
 391                                                 secondaryHistogram);
 392                                 addContextMenu(secondaryHistogram, secondaryFilter.getShowFilterAction(),
 393                                                 secondaryFilter.getShowSearchAction());
 394                                 secondaryFilter.loadState(getState().getChild(SECONDARY_FILTER));
 395                         }
 396                         addContextMenu(primaryHistogram, primaryFilter.getShowFilterAction(), primaryFilter.getShowSearchAction());
 397                         primaryFilter.loadState(getState().getChild(PRIMARY_FILTER));
 398                         histogramParent.layout();
 399                 }
 400 
 401                 private void addContextMenu(ItemHistogram h, IAction ... actions) {
 402                         MCContextMenuManager mm = MCContextMenuManager.create(h.getManager().getViewer().getControl());
 403                         ColumnMenusFactory.addDefaultMenus(h.getManager(), mm);
 404                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), h,
 405                                         Messages.SocketIOPage_HISTOGRAM_SELECTION, mm);
 406                         for (IAction action : actions) {
 407                                 mm.add(action);
 408                         }
 409                 }
 410 
 411                 private void onPrimaryFilterChange(IItemFilter filter) {
 412                         primaryFilter.filterChangeHelper(filter, primaryHistogram, getDataSource().getItems().apply(TABLE_ITEMS));
 413                         if (secondaryFilter != null) {
 414                                 secondaryFilter.notifyListener();
 415                         }
 416                         primaryTableFilter.put(histogramType, filter);
 417                 }
 418 
 419                 private void onSecondaryFilterChange(IItemFilter filter) {
 420                         secondaryFilter.filterChangeHelper(filter, secondaryHistogram,
 421                                         getDataSource().getItems().apply(TABLE_ITEMS));
 422                         secondaryTableFilter.put(histogramType, filter);
 423                 }
 424 
 425                 private void onEventFilterChange(IItemFilter filter) {
 426                         eventFilter.filterChangeHelper(filter, itemList, getDataSource().getItems().apply(TABLE_ITEMS));
 427                         itemListFilter = filter;
 428                 }
 429 
 430                 @Override
 431                 public void saveTo(IWritableState writableState) {
 432                         StateToolkit.writeEnum(writableState, HISTGRAM_TYPE, histogramType);
 433                         PersistableSashForm.saveState(sash, writableState.createChild(SASH_ELEMENT));
 434                         primaryHistogram.getManager().getSettings().saveState(writableState.createChild(SOCKETIO_TABLE_ELEMENT));
 435                         primaryFilter.saveState(writableState.createChild(PRIMARY_FILTER));
 436                         Optional.ofNullable(secondaryHistogramSettings.get()).ifPresent(
 437                                         settings -> settings.saveState(writableState.createChild(SECONDARY_SOCKETIO_TABLE_ELEMENT)));
 438                         if (secondaryFilter != null) {
 439                                 secondaryFilter.saveState(writableState.createChild(SECONDARY_FILTER));
 440                         }
 441                         itemList.getManager().getSettings().saveState(writableState.createChild(LIST_ELEMENT));
 442                         eventFilter.saveState(writableState.createChild(EVENT_FILTER));
 443 
 444                         saveToLocal();
 445                 }
 446 
 447                 private void saveToLocal() {
 448                         primaryTableSelection.put(histogramType, primaryHistogram.getManager().getSelectionState());
 449                         if (secondaryHistogram != null) {
 450                                 secondaryTableSelection.put(histogramType, secondaryHistogram.getManager().getSelectionState());
 451                         }
 452                         itemListSelection = itemList.getManager().getSelectionState();
 453                         tabFolderIndex = tabFolder.getSelectionIndex();
 454                         flavorSelectorState = flavorSelector.getFlavorSelectorState();
 455                 }
 456 
 457                 private void onUseRange(Boolean show) {
 458                         IRange<IQuantity> range = show ? timeRange : pageContainer.getRecordingRange();
 459                         timelineChart.setVisibleRange(range.getStart(), range.getEnd());
 460                         buildChart();
 461                 }
 462 
 463                 private void buildChart() {
 464                         DataPageToolkit.setChart(timelineCanvas, timelineChart,
 465                                         selection -> pageContainer.showSelection(selection));
 466                         SelectionStoreActionToolkit.addSelectionStoreRangeActions(pageContainer.getSelectionStore(), timelineChart,
 467                                         JfrAttributes.LIFETIME, Messages.SocketIOPage_TIMELINE_SELECTION, timelineCanvas.getContextMenu());
 468                 }
 469 
 470                 private void onInputSelected(IItemCollection items, IRange<IQuantity> timeRange) {
 471                         this.selectionItems = items;
 472                         this.timeRange = timeRange;
 473                         refreshPageItems();
 474                 }
 475 
 476                 private void refreshPageItems() {
 477                         IItemCollection items = selectionItems != null ? selectionItems : getDataSource().getItems();
 478                         itemConsumerRoot.accept(items.apply(JdkFilters.SOCKET_READ_OR_WRITE));
 479                 }
 480 
 481                 private void updateChartAndListDetails(IItemCollection selectedItems) {
 482                         String hostCount = hostPortCount();
 483 
 484                         List<IXDataRenderer> timelineRows = new ArrayList<>();
 485                         List<IXDataRenderer> durationRows = new ArrayList<>();
 486                         List<IXDataRenderer> sizeRows = new ArrayList<>();
 487 
 488                         IItemCollection readItems = selectedItems.apply(JdkFilters.SOCKET_READ);
 489                         if (readItems.hasItems()) {
 490                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 491                                                 JdkAggregators.SOCKET_READ_SIZE.getDescription(), readItems, JdkAggregators.SOCKET_READ_SIZE,
 492                                                 READ_COLOR, SocketIOPage::getColor));
 493                                 durationRows
 494                                                 .add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 495                                                                 JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems,
 496                                                                 JdkAggregators.SOCKET_READ_COUNT, READ_COLOR));
 497                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 498                                                 JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems, 
 499                                                 JdkAggregators.SOCKET_READ_COUNT, READ_COLOR, JdkAttributes.IO_SOCKET_BYTES_READ));
 500                         }
 501                         IItemCollection writeItems = selectedItems.apply(JdkFilters.SOCKET_WRITE);
 502                         if (writeItems.hasItems()) {
 503                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 504                                                 JdkAggregators.SOCKET_WRITE_SIZE.getDescription(), writeItems, JdkAggregators.SOCKET_WRITE_SIZE,
 505                                                 WRITE_COLOR, SocketIOPage::getColor));
 506                                 durationRows
 507                                                 .add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 508                                                                 JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems,
 509                                                                 JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR));
 510                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount, 
 511                                                 JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems, 
 512                                                 JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR, JdkAttributes.IO_SOCKET_BYTES_WRITTEN));
 513                         }
 514                         if (timelineCanvas != null) {
 515                                 timelineCanvas.replaceRenderer(RendererToolkit.uniformRows(timelineRows));
 516                                 durationCanvas.replaceRenderer(RendererToolkit.uniformRows(durationRows));
 517                                 sizeCanvas.replaceRenderer(RendererToolkit.uniformRows(sizeRows));
 518 
 519 
 520                                 itemList.show(selectedItems);
 521                                 pageContainer.showSelection(selectedItems);
 522                         }
 523                 }
 524 
 525                 public String hostPortCount() {
 526                         HistogramSelection hostSelection = histogramType == HistogramType.HOST ? primaryHistogram.getSelection()
 527                                         : histogramType == HistogramType.PORT ? secondaryHistogram.getSelection() : null;
 528                         HistogramSelection portSelection = histogramType == HistogramType.PORT ? primaryHistogram.getSelection()
 529                                         : histogramType == HistogramType.HOST ? secondaryHistogram.getSelection() : null;
 530                         HistogramSelection hostPortSelection = histogramType == HistogramType.HOST_AND_PORT
 531                                         ? primaryHistogram.getSelection() : null;
 532 
 533                         return hostPortCount(hostSelection != null ? hostSelection.getRowCount() : 0,
 534                                         portSelection != null ? portSelection.getRowCount() : 0,
 535                                         hostPortSelection != null ? hostPortSelection.getRowCount() : 0);
 536                 }
 537 
 538                 public String hostPortCount(int hostCount, int portCount, int hostPortCount) {
 539                         switch (hostPortCount) {
 540                         case 0:
 541                                 switch (hostCount) {
 542                                 case 0:
 543                                         switch (portCount) {
 544                                         case 0:
 545                                                 return ""; //$NON-NLS-1$
 546                                         case 1:
 547                                                 return " (" + Messages.SocketIOPage_SELECTED_PORT + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 548                                         default:
 549                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_PORTS, portCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 550                                         }
 551                                 case 1:
 552                                         switch (portCount) {
 553                                         case 0:
 554                                                 return " (" + Messages.SocketIOPage_SELECTED_HOST + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 555                                         case 1:
 556                                                 return " (" + Messages.SocketIOPage_SELECTED_HOST_AND_PORT + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 557                                         default:
 558                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOST_AND_PORTS, portCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 559                                         }
 560                                 default:
 561                                         switch (portCount) {
 562                                         case 0:
 563                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS, hostCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 564                                         case 1:
 565                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_AND_PORT, hostCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 566                                         default:
 567                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_AND_PORTS, hostCount, portCount) //$NON-NLS-1$
 568                                                                 + ")"; //$NON-NLS-1$
 569                                         }
 570                                 }
 571                         default:
 572                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_PORTS, hostPortCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 573                         }
 574                 }
 575         }
 576 
 577         private static TableSettings getTableSettings(IState state) {
 578                 if (state == null) {
 579                         return new TableSettings(TOTAL_TIME, Arrays.asList(
 580                                         new ColumnSettings(ItemHistogram.KEY_COL_ID, false, 500, null),
 581                                         new ColumnSettings(TOTAL_TIME, true, 120, false), new ColumnSettings(MAX_TIME, false, 120, false),
 582                                         new ColumnSettings(AVG_TIME, false, 120, false), new ColumnSettings(STDDEV_TIME, false, 120, false),
 583                                         new ColumnSettings(READ_COUNT, false, 120, false),
 584                                         new ColumnSettings(WRITE_COUNT, false, 120, false),
 585                                         new ColumnSettings(READ_SIZE, false, 120, false), new ColumnSettings(WRITE_SIZE, false, 120, false),
 586                                         new ColumnSettings(READ_EOS, false, 80, false), new ColumnSettings(IO_TIMEOUT, false, 50, false)));
 587                 } else {
 588                         return new TableSettings(state);
 589                 }
 590         }
 591 
 592         @Override
 593         public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) {
 594                 return new IOPageUi(parent, toolkit, pageContainer, state);
 595         }
 596 
 597         private Map<HistogramType, SelectionState> primaryTableSelection;
 598         private Map<HistogramType, SelectionState> secondaryTableSelection;
 599         private SelectionState itemListSelection;
 600         private Map<HistogramType, IItemFilter> primaryTableFilter;
 601         private Map<HistogramType, IItemFilter> secondaryTableFilter;
 602         private IItemFilter itemListFilter;
 603         private IRange<IQuantity> timelineRange;
 604         private IRange<IQuantity> durationRange;
 605         private IRange<IQuantity> sizeRange;
 606         private int tabFolderIndex = 0;
 607         public FlavorSelectorState flavorSelectorState;
 608 
 609         public SocketIOPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 610                 super(dpd, items, editor);
 611                 primaryTableSelection = new HashMap<>();
 612                 secondaryTableSelection = new HashMap<>();
 613                 primaryTableFilter = new HashMap<>();
 614                 secondaryTableFilter = new HashMap<>();
 615                 timelineRange = editor.getRecordingRange();
 616                 durationRange = editor.getRecordingRange();
 617                 sizeRange = DataPageToolkit.buildSizeRange(items.getItems(), true);
 618 
 619         }
 620 
 621         @Override
 622         public IItemFilter getDefaultSelectionFilter() {
 623                 return TABLE_ITEMS;
 624         }
 625 
 626         private static Color getColor(IItem item) {
 627                 return JdkTypeIDs.SOCKET_READ.equals(item.getType().getIdentifier()) ? READ_ALPHA_COLOR : WRITE_ALPHA_COLOR;
 628         }
 629 
 630 }