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