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 }