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