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                         tabFolder.setSelection(tabFolderIndex);
 311 
 312                         PersistableSashForm.loadState(sash, state.getChild(SASH_ELEMENT));
 313 
 314                         flavorSelector = FlavorSelector.itemsWithTimerange(form, TABLE_ITEMS, getDataSource().getItems(),
 315                                         pageContainer, this::onInputSelected, this::onUseRange, flavorSelectorState);
 316 
 317                         form.getToolBarManager()
 318                                         .appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(HistogramType.HOST,
 319                                                         Messages.SocketIOPage_BY_HOST_ACTION,
 320                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_HOST)));
 321                         form.getToolBarManager()
 322                                         .appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(HistogramType.PORT,
 323                                                         Messages.SocketIOPage_BY_PORT_ACTION,
 324                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_PORT)));
 325                         form.getToolBarManager().appendToGroup(DataPageToolkit.FORM_TOOLBAR_PAGE_SETUP, buildHistogramTypeAction(
 326                                         HistogramType.HOST_AND_PORT, Messages.SocketIOPage_BY_HOST_AND_PORT_ACTION,
 327                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_IO_BY_HOST_AND_PORT)));
 328 
 329                         addResultActions(form);
 330                 }
 331 
 332                 // FIXME: Break out this to a "ConfigurableHistogramUi or something? This is copy-pasted from ExceptionsPage
 333                 private IAction buildHistogramTypeAction(HistogramType histogramType, String text, ImageDescriptor icon) {
 334                         IAction a = ActionToolkit.radioAction(() -> setHistogramType(histogramType), text, icon);
 335                         a.setChecked(histogramType == this.histogramType);
 336                         return a;
 337                 }
 338 
 339                 private void setHistogramType(HistogramType histogramType) {
 340                         if (histogramType != this.histogramType) {
 341                                 primaryTableSelection.put(this.histogramType, primaryHistogram.getManager().getSelectionState());
 342                                 if (secondaryHistogram != null) {
 343                                         secondaryTableSelection.put(this.histogramType,
 344                                                         secondaryHistogram.getManager().getSelectionState());
 345                                 }
 346                                 this.histogramType = histogramType;
 347                                 TableSettings primarySettings = primaryHistogram.getManager().getSettings();
 348                                 TableSettings secondarySettings = secondaryHistogramSettings.get();
 349                                 for (Control c : histogramParent.getChildren()) {
 350                                         c.dispose();
 351                                 }
 352                                 buildHistograms(primarySettings, secondarySettings);
 353                                 refreshPageItems();
 354                         }
 355                 }
 356 
 357                 private void buildHistograms(TableSettings primarySettings, TableSettings secondarySettings) {
 358                         if (histogramType == HistogramType.HOST_AND_PORT) {
 359                                 primaryHistogram = HISTOGRAM.buildWithoutBorder(histogramParent, Messages.SocketIOPage_HOST_AND_PORT,
 360                                                 UnitLookup.UNKNOWN, HOST_AND_PORT_AF, primarySettings);
 361                                 primaryFilter = FilterComponent.createFilterComponent(primaryHistogram,
 362                                                 primaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 363                                                 pageContainer.getSelectionStore()::getSelections, this::onPrimaryFilterChange);
 364                                 secondaryHistogram = null;
 365                                 secondaryHistogramSettings = () -> secondarySettings;
 366                                 secondaryFilter = null;
 367                                 onPrimaryFilterChange(primaryTableFilter.get(histogramType));
 368                                 primaryHistogram.getManager().setSelectionState(primaryTableSelection.get(histogramType));
 369                                 itemConsumerRoot = ItemHistogramWithInput.chain(primaryHistogram, this::updateChartAndListDetails);
 370                         } else {
 371                                 SashForm s2 = new SashForm(histogramParent, SWT.VERTICAL);
 372                                 IAttribute<?> masterAttr = histogramType == HistogramType.HOST ? IO_ADDRESS : IO_PORT;
 373                                 IAttribute<?> slaveAttr = histogramType == HistogramType.PORT ? IO_ADDRESS : IO_PORT;
 374                                 primaryHistogram = HISTOGRAM.buildWithoutBorder(s2, masterAttr, primarySettings);
 375                                 primaryFilter = FilterComponent.createFilterComponent(primaryHistogram,
 376                                                 primaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 377                                                 pageContainer.getSelectionStore()::getSelections, this::onPrimaryFilterChange);
 378 
 379                                 secondaryHistogram = HISTOGRAM.buildWithoutBorder(s2, slaveAttr, secondarySettings);
 380                                 secondaryFilter = FilterComponent.createFilterComponent(secondaryHistogram,
 381                                                 secondaryTableFilter.get(histogramType), getDataSource().getItems().apply(TABLE_ITEMS),
 382                                                 pageContainer.getSelectionStore()::getSelections, this::onSecondaryFilterChange);
 383                                 secondaryHistogramSettings = secondaryHistogram.getManager()::getSettings;
 384                                 onPrimaryFilterChange(primaryTableFilter.get(histogramType));
 385                                 onSecondaryFilterChange(secondaryTableFilter.get(histogramType));
 386                                 primaryHistogram.getManager().setSelectionState(primaryTableSelection.get(histogramType));
 387                                 secondaryHistogram.getManager().setSelectionState(secondaryTableSelection.get(histogramType));
 388                                 itemConsumerRoot = ItemHistogramWithInput.chain(primaryHistogram, this::updateChartAndListDetails,
 389                                                 secondaryHistogram);
 390                                 addContextMenu(secondaryHistogram, secondaryFilter.getShowFilterAction(),
 391                                                 secondaryFilter.getShowSearchAction());
 392                                 secondaryFilter.loadState(getState().getChild(SECONDARY_FILTER));
 393                         }
 394                         addContextMenu(primaryHistogram, primaryFilter.getShowFilterAction(), primaryFilter.getShowSearchAction());
 395                         primaryFilter.loadState(getState().getChild(PRIMARY_FILTER));
 396                         histogramParent.layout();
 397                 }
 398 
 399                 private void addContextMenu(ItemHistogram h, IAction ... actions) {
 400                         MCContextMenuManager mm = MCContextMenuManager.create(h.getManager().getViewer().getControl());
 401                         ColumnMenusFactory.addDefaultMenus(h.getManager(), mm);
 402                         SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), h,
 403                                         Messages.SocketIOPage_HISTOGRAM_SELECTION, mm);
 404                         for (IAction action : actions) {
 405                                 mm.add(action);
 406                         }
 407                 }
 408 
 409                 private void onPrimaryFilterChange(IItemFilter filter) {
 410                         primaryFilter.filterChangeHelper(filter, primaryHistogram, getDataSource().getItems().apply(TABLE_ITEMS));
 411                         if (secondaryFilter != null) {
 412                                 secondaryFilter.notifyListener();
 413                         }
 414                         primaryTableFilter.put(histogramType, filter);
 415                 }
 416 
 417                 private void onSecondaryFilterChange(IItemFilter filter) {
 418                         secondaryFilter.filterChangeHelper(filter, secondaryHistogram,
 419                                         getDataSource().getItems().apply(TABLE_ITEMS));
 420                         secondaryTableFilter.put(histogramType, filter);
 421                 }
 422 
 423                 private void onEventFilterChange(IItemFilter filter) {
 424                         eventFilter.filterChangeHelper(filter, itemList, getDataSource().getItems().apply(TABLE_ITEMS));
 425                         itemListFilter = filter;
 426                 }
 427 
 428                 @Override
 429                 public void saveTo(IWritableState writableState) {
 430                         StateToolkit.writeEnum(writableState, HISTGRAM_TYPE, histogramType);
 431                         PersistableSashForm.saveState(sash, writableState.createChild(SASH_ELEMENT));
 432                         primaryHistogram.getManager().getSettings().saveState(writableState.createChild(SOCKETIO_TABLE_ELEMENT));
 433                         primaryFilter.saveState(writableState.createChild(PRIMARY_FILTER));
 434                         Optional.ofNullable(secondaryHistogramSettings.get()).ifPresent(
 435                                         settings -> settings.saveState(writableState.createChild(SECONDARY_SOCKETIO_TABLE_ELEMENT)));
 436                         if (secondaryFilter != null) {
 437                                 secondaryFilter.saveState(writableState.createChild(SECONDARY_FILTER));
 438                         }
 439                         itemList.getManager().getSettings().saveState(writableState.createChild(LIST_ELEMENT));
 440                         eventFilter.saveState(writableState.createChild(EVENT_FILTER));
 441 
 442                         saveToLocal();
 443                 }
 444 
 445                 private void saveToLocal() {
 446                         primaryTableSelection.put(histogramType, primaryHistogram.getManager().getSelectionState());
 447                         if (secondaryHistogram != null) {
 448                                 secondaryTableSelection.put(histogramType, secondaryHistogram.getManager().getSelectionState());
 449                         }
 450                         itemListSelection = itemList.getManager().getSelectionState();
 451                         tabFolderIndex = tabFolder.getSelectionIndex();
 452                         flavorSelectorState = flavorSelector.getFlavorSelectorState();
 453                 }
 454 
 455                 private void onUseRange(Boolean show) {
 456                         IRange<IQuantity> range = show ? timeRange : pageContainer.getRecordingRange();
 457                         timelineChart.setVisibleRange(range.getStart(), range.getEnd());
 458                         buildChart();
 459                 }
 460 
 461                 private void buildChart() {
 462                         DataPageToolkit.setChart(timelineCanvas, timelineChart,
 463                                         selection -> pageContainer.showSelection(selection));
 464                         SelectionStoreActionToolkit.addSelectionStoreRangeActions(pageContainer.getSelectionStore(), timelineChart,
 465                                         JfrAttributes.LIFETIME, Messages.SocketIOPage_TIMELINE_SELECTION, timelineCanvas.getContextMenu());
 466                 }
 467 
 468                 private void onInputSelected(IItemCollection items, IRange<IQuantity> timeRange) {
 469                         this.selectionItems = items;
 470                         this.timeRange = timeRange;
 471                         refreshPageItems();
 472                 }
 473 
 474                 private void refreshPageItems() {
 475                         IItemCollection items = selectionItems != null ? selectionItems : getDataSource().getItems();
 476                         itemConsumerRoot.accept(items.apply(JdkFilters.SOCKET_READ_OR_WRITE));
 477                 }
 478 
 479                 private void updateChartAndListDetails(IItemCollection selectedItems) {
 480                         String hostCount = hostPortCount();
 481 
 482                         List<IXDataRenderer> timelineRows = new ArrayList<>();
 483                         List<IXDataRenderer> durationRows = new ArrayList<>();
 484                         List<IXDataRenderer> sizeRows = new ArrayList<>();
 485                         IItemCollection readItems = selectedItems.apply(JdkFilters.SOCKET_READ);
 486                         if (readItems.hasItems()) {
 487                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 488                                                 JdkAggregators.SOCKET_READ_SIZE.getDescription(), readItems, JdkAggregators.SOCKET_READ_SIZE,
 489                                                 READ_COLOR, SocketIOPage::getColor));
 490                                 durationRows
 491                                                 .add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 492                                                                 JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems,
 493                                                                 JdkAggregators.SOCKET_READ_COUNT, READ_COLOR));
 494                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 495                                                 JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems,
 496                                                 JdkAggregators.SOCKET_READ_COUNT, READ_COLOR, JdkAttributes.IO_SOCKET_BYTES_READ));
 497                         }
 498                         IItemCollection writeItems = selectedItems.apply(JdkFilters.SOCKET_WRITE);
 499                         if (writeItems.hasItems()) {
 500                                 timelineRows.add(DataPageToolkit.buildSizeRow(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 501                                                 JdkAggregators.SOCKET_WRITE_SIZE.getDescription(), writeItems, JdkAggregators.SOCKET_WRITE_SIZE,
 502                                                 WRITE_COLOR, SocketIOPage::getColor));
 503                                 durationRows
 504                                                 .add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 505                                                                 JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems,
 506                                                                 JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR));
 507                                 sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 508                                                 JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems,
 509                                                 JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR, JdkAttributes.IO_SOCKET_BYTES_WRITTEN));
 510                         }
 511                         if (timelineCanvas != null) {
 512                                 timelineCanvas.replaceRenderer(RendererToolkit.uniformRows(timelineRows));
 513                                 durationCanvas.replaceRenderer(RendererToolkit.uniformRows(durationRows));
 514                                 sizeCanvas.replaceRenderer(RendererToolkit.uniformRows(sizeRows));
 515 
 516                                 itemList.show(selectedItems);
 517                                 pageContainer.showSelection(selectedItems);
 518                         }
 519                 }
 520 
 521                 public String hostPortCount() {
 522                         HistogramSelection hostSelection = histogramType == HistogramType.HOST ? primaryHistogram.getSelection()
 523                                         : histogramType == HistogramType.PORT ? secondaryHistogram.getSelection() : null;
 524                         HistogramSelection portSelection = histogramType == HistogramType.PORT ? primaryHistogram.getSelection()
 525                                         : histogramType == HistogramType.HOST ? secondaryHistogram.getSelection() : null;
 526                         HistogramSelection hostPortSelection = histogramType == HistogramType.HOST_AND_PORT
 527                                         ? primaryHistogram.getSelection() : null;
 528 
 529                         return hostPortCount(hostSelection != null ? hostSelection.getRowCount() : 0,
 530                                         portSelection != null ? portSelection.getRowCount() : 0,
 531                                         hostPortSelection != null ? hostPortSelection.getRowCount() : 0);
 532                 }
 533 
 534                 public String hostPortCount(int hostCount, int portCount, int hostPortCount) {
 535                         switch (hostPortCount) {
 536                         case 0:
 537                                 switch (hostCount) {
 538                                 case 0:
 539                                         switch (portCount) {
 540                                         case 0:
 541                                                 return ""; //$NON-NLS-1$
 542                                         case 1:
 543                                                 return " (" + Messages.SocketIOPage_SELECTED_PORT + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 544                                         default:
 545                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_PORTS, portCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 546                                         }
 547                                 case 1:
 548                                         switch (portCount) {
 549                                         case 0:
 550                                                 return " (" + Messages.SocketIOPage_SELECTED_HOST + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 551                                         case 1:
 552                                                 return " (" + Messages.SocketIOPage_SELECTED_HOST_AND_PORT + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 553                                         default:
 554                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOST_AND_PORTS, portCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 555                                         }
 556                                 default:
 557                                         switch (portCount) {
 558                                         case 0:
 559                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS, hostCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 560                                         case 1:
 561                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_AND_PORT, hostCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 562                                         default:
 563                                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_AND_PORTS, hostCount, portCount) //$NON-NLS-1$
 564                                                                 + ")"; //$NON-NLS-1$
 565                                         }
 566                                 }
 567                         default:
 568                                 return " (" + NLS.bind(Messages.SocketIOPage_SELECTED_HOSTS_PORTS, hostPortCount) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 569                         }
 570                 }
 571         }
 572 
 573         private static TableSettings getTableSettings(IState state) {
 574                 if (state == null) {
 575                         return new TableSettings(TOTAL_TIME, Arrays.asList(
 576                                         new ColumnSettings(ItemHistogram.KEY_COL_ID, false, 500, null),
 577                                         new ColumnSettings(TOTAL_TIME, true, 120, false), new ColumnSettings(MAX_TIME, false, 120, false),
 578                                         new ColumnSettings(AVG_TIME, false, 120, false), new ColumnSettings(STDDEV_TIME, false, 120, false),
 579                                         new ColumnSettings(READ_COUNT, false, 120, false),
 580                                         new ColumnSettings(WRITE_COUNT, false, 120, false),
 581                                         new ColumnSettings(READ_SIZE, false, 120, false), new ColumnSettings(WRITE_SIZE, false, 120, false),
 582                                         new ColumnSettings(READ_EOS, false, 80, false), new ColumnSettings(IO_TIMEOUT, false, 50, false)));
 583                 } else {
 584                         return new TableSettings(state);
 585                 }
 586         }
 587 
 588         @Override
 589         public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer pageContainer, IState state) {
 590                 return new IOPageUi(parent, toolkit, pageContainer, state);
 591         }
 592 
 593         private Map<HistogramType, SelectionState> primaryTableSelection;
 594         private Map<HistogramType, SelectionState> secondaryTableSelection;
 595         private SelectionState itemListSelection;
 596         private Map<HistogramType, IItemFilter> primaryTableFilter;
 597         private Map<HistogramType, IItemFilter> secondaryTableFilter;
 598         private IItemFilter itemListFilter;
 599         private IRange<IQuantity> timelineRange;
 600         private IRange<IQuantity> durationRange;
 601         private IRange<IQuantity> sizeRange;
 602         private int tabFolderIndex = 0;
 603         public FlavorSelectorState flavorSelectorState;
 604 
 605         public SocketIOPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 606                 super(dpd, items, editor);
 607                 primaryTableSelection = new HashMap<>();
 608                 secondaryTableSelection = new HashMap<>();
 609                 primaryTableFilter = new HashMap<>();
 610                 secondaryTableFilter = new HashMap<>();
 611                 timelineRange = editor.getRecordingRange();
 612                 durationRange = editor.getRecordingRange();
 613                 sizeRange = DataPageToolkit.buildSizeRange(items.getItems(), true);
 614         }
 615 
 616         @Override
 617         public IItemFilter getDefaultSelectionFilter() {
 618                 return TABLE_ITEMS;
 619         }
 620 
 621         private static Color getColor(IItem item) {
 622                 return JdkTypeIDs.SOCKET_READ.equals(item.getType().getIdentifier()) ? READ_ALPHA_COLOR : WRITE_ALPHA_COLOR;
 623         }
 624 
 625 }