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 }