changeset 154:4f77e6262cb3

JMC-4645:Size distribution chart for File/Socket I/O pages Summary:Adds a chart that shows the distribution of I/O events by size Reviewed By: hirt, almac
author kdobson
date Thu, 02 May 2019 11:06:51 -0400
parents 2ecf88bb5f34
children a9bcccbbf05a
files application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/common/DataPageToolkit.java application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/messages/internal/Messages.java application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/FileIOPage.java application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/SocketIOPage.java application/org.openjdk.jmc.flightrecorder.ui/src/main/resources/org/openjdk/jmc/flightrecorder/ui/messages/internal/messages.properties core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java
diffstat 7 files changed, 124 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/common/DataPageToolkit.java	Thu May 02 08:56:53 2019 -0400
+++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/common/DataPageToolkit.java	Thu May 02 11:06:51 2019 -0400
@@ -111,12 +111,14 @@
 import org.openjdk.jmc.common.unit.IQuantity;
 import org.openjdk.jmc.common.unit.IRange;
 import org.openjdk.jmc.common.unit.KindOfQuantity;
+import org.openjdk.jmc.common.unit.QuantitiesToolkit;
 import org.openjdk.jmc.common.unit.QuantityRange;
 import org.openjdk.jmc.common.unit.RangeContentType;
 import org.openjdk.jmc.common.unit.UnitLookup;
 import org.openjdk.jmc.common.util.ColorToolkit;
 import org.openjdk.jmc.common.util.CompositeKey;
 import org.openjdk.jmc.flightrecorder.JfrAttributes;
+import org.openjdk.jmc.flightrecorder.jdk.JdkAggregators;
 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters;
 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
@@ -565,6 +567,29 @@
 		return new ItemRow(title, description, renderer, items);
 	}
 
+	public static ItemRow buildSizeHistogram(
+		String title, String description, IItemCollection items, IAggregator<IQuantity, ?> a, Color color, IAttribute<IQuantity> attribute) {
+		IQuantitySeries<IQuantity[]> allocationSeries = BucketBuilder.aggregatorSeries(items, a,
+				JdkAttributes.IO_SIZE);
+		XYDataRenderer renderer = new XYDataRenderer(getKindOfQuantity(a).getDefaultUnit().quantity(0), title,
+				description);
+		renderer.addBarChart(a.getName(), allocationSeries, color);
+		return new ItemRow(title, description, renderer, items);
+	}
+
+	public static IRange<IQuantity> buildSizeRange(IItemCollection items, boolean isSocket){
+		IQuantity end = null;
+		if(isSocket) {
+			end = QuantitiesToolkit.maxPresent(items.getAggregate(JdkAggregators.SOCKET_READ_LARGEST),
+					items.getAggregate(JdkAggregators.SOCKET_WRITE_LARGEST));
+		} else {
+			end = QuantitiesToolkit.maxPresent(items.getAggregate(JdkAggregators.FILE_READ_LARGEST),
+					items.getAggregate(JdkAggregators.FILE_WRITE_LARGEST));
+		}
+		end = end == null ? UnitLookup.BYTE.quantity(1024) : end;
+		return QuantityRange.createWithEnd(UnitLookup.BYTE.quantity(0), end);
+	}
+
 	// FIXME: Make something that can use something other than time as x-axis?
 	public static IXDataRenderer buildSpanRenderer(IItemCollection pathItems, IColorProvider<IItem> cp) {
 		ISpanSeries<IItem> dataSeries = QuantitySeries.max(pathItems, JfrAttributes.START_TIME, JfrAttributes.END_TIME);
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/messages/internal/Messages.java	Thu May 02 08:56:53 2019 -0400
+++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/messages/internal/Messages.java	Thu May 02 11:06:51 2019 -0400
@@ -152,6 +152,7 @@
 	public static String FLAVOR_SELECTED_RANGE;
 	public static String FOLDER_COULD_NOT_BE_CREATED;
 	public static String FileIOPage_DURATION_SELECTION;
+	public static String FileIOPage_SIZE_SELECTION;
 	public static String FileIOPage_HISTOGRAM_SELECTION;
 	public static String FileIOPage_LOG_SELECTION;
 	public static String FileIOPage_PAGE_NAME;
@@ -210,6 +211,7 @@
 	public static String IO_PAGE_DURATIONS_DESCRIPTION;
 	public static String IO_PAGE_EVENT_LOG_DESCRIPTION;
 	public static String IO_PAGE_TIMELINE_DESCRIPTION;
+	public static String IO_PAGE_SIZE_DESCRIPTION;
 	public static String ITEMHANDLER_CHART_DESCRIPTION;
 	public static String ITEMHANDLER_CHART_TITLE;
 	public static String ITEMHANDLER_LIST_DESCRIPTION;
@@ -346,6 +348,7 @@
 	public static String PAGES_DURATIONS;
 	public static String PAGES_EVENT_LOG;
 	public static String PAGES_TIMELINE;
+	public static String PAGES_SIZE;
 	public static String PAGE_CHANGE_ICON_CHOOSE_IMAGE_FILE;
 	public static String PAGE_CHANGE_ICON_CURRENT_ICON;
 	public static String PAGE_CHANGE_ICON_MENU_ACTION;
@@ -463,6 +466,7 @@
 	public static String SocketIOPage_BY_HOST_AND_PORT_ACTION;
 	public static String SocketIOPage_BY_PORT_ACTION;
 	public static String SocketIOPage_DURATION_SELECTION;
+	public static String SocketIOPage_SIZE_SELECTION;
 	public static String SocketIOPage_HISTOGRAM_SELECTION;
 	public static String SocketIOPage_HOST_AND_PORT;
 	public static String SocketIOPage_LOG_SELECTION;
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/FileIOPage.java	Thu May 02 08:56:53 2019 -0400
+++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/FileIOPage.java	Thu May 02 11:06:51 2019 -0400
@@ -169,6 +169,7 @@
 
 		private final ChartCanvas timelineCanvas;
 		private final ChartCanvas durationCanvas;
+		private final ChartCanvas sizeCanvas;
 		private XYChart timelineChart;
 		private IRange<IQuantity> timeRange;
 		private IItemCollection selectionItems;
@@ -220,7 +221,14 @@
 			DataPageToolkit.createChartTooltip(durationCanvas);
 
 			CTabItem t3 = new CTabItem(tabFolder, SWT.NONE);
-			t3.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
+			t3.setToolTipText(Messages.IO_PAGE_SIZE_DESCRIPTION);
+			sizeCanvas = new ChartCanvas(tabFolder);
+			t3.setText(Messages.PAGES_SIZE);
+			t3.setControl(sizeCanvas);
+			DataPageToolkit.createChartTooltip(sizeCanvas);
+
+			CTabItem t4 = new CTabItem(tabFolder, SWT.NONE);
+			t4.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
 			itemList = LIST.buildWithoutBorder(tabFolder, getTableSettings(state.getChild(LIST_ELEMENT)));
 			MCContextMenuManager itemListMm = MCContextMenuManager
 					.create(itemList.getManager().getViewer().getControl());
@@ -229,13 +237,13 @@
 					Messages.FileIOPage_LOG_SELECTION, itemListMm);
 			itemList.getManager().getViewer().addSelectionChangedListener(
 					e -> pageContainer.showSelection(ItemCollectionToolkit.build(itemList.getSelection().get())));
-			t3.setText(Messages.PAGES_EVENT_LOG);
+			t4.setText(Messages.PAGES_EVENT_LOG);
 			itemListFilter = FilterComponent.createFilterComponent(itemList, FileIOPage.this.itemListFilter,
 					getDataSource().getItems().apply(TABLE_ITEMS), pageContainer.getSelectionStore()::getSelections,
 					this::onListFilterChange);
 			itemListMm.add(itemListFilter.getShowFilterAction());
 			itemListMm.add(itemListFilter.getShowSearchAction());
-			t3.setControl(itemListFilter.getComponent());
+			t4.setControl(itemListFilter.getComponent());
 
 			tableFilter.loadState(state.getChild(FILE_IO_TABLE));
 			itemListFilter.loadState(state.getChild(FILE_IO_LIST));
@@ -323,6 +331,7 @@
 			String pathCount = pathCount(histogramSelection.getRowCount());
 			List<IXDataRenderer> timelineRows = new ArrayList<>();
 			List<IXDataRenderer> durationRows = new ArrayList<>();
+			List<IXDataRenderer> sizeRows = new ArrayList<>();
 			IItemCollection readItems = selectedItems.apply(JdkFilters.FILE_READ);
 			if (readItems.hasItems()) {
 				timelineRows.add(DataPageToolkit.buildSizeRow(Messages.FileIOPage_ROW_FILE_READ + pathCount,
@@ -331,6 +340,9 @@
 				durationRows.add(DataPageToolkit.buildDurationHistogram(Messages.FileIOPage_ROW_FILE_READ + pathCount,
 						JdkAggregators.FILE_READ_COUNT.getDescription(), readItems, JdkAggregators.FILE_READ_COUNT,
 						READ_COLOR));
+				sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.FileIOPage_ROW_FILE_READ + pathCount,
+						JdkAggregators.FILE_READ_COUNT.getDescription(), readItems, JdkAggregators.FILE_READ_COUNT,
+						READ_COLOR, JdkAttributes.IO_FILE_BYTES_READ));
 			}
 			IItemCollection writeItems = selectedItems.apply(JdkFilters.FILE_WRITE);
 			if (writeItems.hasItems()) {
@@ -340,6 +352,9 @@
 				durationRows.add(DataPageToolkit.buildDurationHistogram(Messages.FileIOPage_ROW_FILE_WRITE + pathCount,
 						JdkAggregators.FILE_WRITE_COUNT.getDescription(), writeItems, JdkAggregators.FILE_WRITE_COUNT,
 						WRITE_COLOR));
+				sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.FileIOPage_ROW_FILE_WRITE + pathCount,
+						JdkAggregators.FILE_WRITE_COUNT.getDescription(), writeItems, JdkAggregators.FILE_WRITE_COUNT,
+						WRITE_COLOR, JdkAttributes.IO_FILE_BYTES_WRITTEN));
 			}
 //			ItemRow[] pathRows = selection.getSelectedRows(FileIOPage::buildPathLane).toArray(ItemRow[]::new);
 
@@ -359,6 +374,19 @@
 			SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), durationChart,
 					JfrAttributes.DURATION, Messages.FileIOPage_DURATION_SELECTION, durationCanvas.getContextMenu());
 			itemList.show(selectedItems);
+
+			IXDataRenderer sizeRoot = RendererToolkit.uniformRows(sizeRows);
+			IQuantity sizeMax = selectedItems.getAggregate(JdkAggregators.FILE_READ_LARGEST);
+			// FIXME: Workaround to make max value included
+			sizeMax = sizeMax == null ? UnitLookup.BYTE.quantity(64): sizeMax.add(UnitLookup.BYTE.quantity(64));
+			XYChart sizeChart = new XYChart(UnitLookup.BYTE.quantity(0), sizeMax, sizeRoot, 180);
+			DataPageToolkit.setChart(sizeCanvas, sizeChart, JdkAttributes.IO_SIZE,
+					selection -> pageContainer.showSelection(selection));
+			sizeChart.setVisibleRange(sizeRange.getStart(), sizeRange.getEnd());
+			sizeChart.addVisibleRangeListener(range -> sizeRange = range);
+			sizeCanvas.setChart(sizeChart);
+			SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), sizeChart,
+					JdkAttributes.IO_SIZE, Messages.FileIOPage_SIZE_SELECTION, sizeCanvas.getContextMenu());
 		}
 	}
 
@@ -389,12 +417,14 @@
 	private int tabFolderIndex = 0;
 	private IRange<IQuantity> timelineRange;
 	private IRange<IQuantity> durationRange;
+	private IRange<IQuantity> sizeRange;
 	public FlavorSelectorState flavorSelectorState;
 
 	public FileIOPage(IPageDefinition dpd, StreamModel items, IPageContainer editor) {
 		super(dpd, items, editor);
 		timelineRange = editor.getRecordingRange();
 		durationRange = editor.getRecordingRange();
+		sizeRange = DataPageToolkit.buildSizeRange(items.getItems(), false);
 	}
 
 //	private static ItemRow buildPathLane(Object path, Supplier<Stream<ItemStream>> pathItems) {
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/SocketIOPage.java	Thu May 02 08:56:53 2019 -0400
+++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/SocketIOPage.java	Thu May 02 11:06:51 2019 -0400
@@ -69,6 +69,7 @@
 import org.openjdk.jmc.common.item.ItemFilters;
 import org.openjdk.jmc.common.unit.IQuantity;
 import org.openjdk.jmc.common.unit.IRange;
+import org.openjdk.jmc.common.unit.QuantitiesToolkit;
 import org.openjdk.jmc.common.unit.UnitLookup;
 import org.openjdk.jmc.common.util.ColorToolkit;
 import org.openjdk.jmc.common.util.StateToolkit;
@@ -201,6 +202,7 @@
 
 		private final ChartCanvas timelineCanvas;
 		private final ChartCanvas durationCanvas;
+		private final ChartCanvas sizeCanvas;
 		private final ItemList itemList;
 
 		private final SashForm sash;
@@ -218,6 +220,7 @@
 		private IItemCollection selectionItems;
 		private XYChart timelineChart;
 		private XYChart durationChart;
+		private XYChart sizeChart;
 		private CTabFolder tabFolder;
 		private FlavorSelector flavorSelector;
 
@@ -264,8 +267,27 @@
 			SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), durationChart,
 					JfrAttributes.DURATION, Messages.SocketIOPage_DURATION_SELECTION, durationCanvas.getContextMenu());
 
+			IQuantity sizeMax = QuantitiesToolkit.maxPresent(socketItems.getAggregate(JdkAggregators.SOCKET_READ_LARGEST),
+					socketItems.getAggregate(JdkAggregators.SOCKET_WRITE_LARGEST));
+			// FIXME: Workaround to make max value included
+			sizeMax = sizeMax == null ? UnitLookup.BYTE.quantity(64): sizeMax.add(UnitLookup.BYTE.quantity(64));
+			sizeChart = new XYChart(UnitLookup.BYTE.quantity(0), sizeMax, RendererToolkit.empty(), 180);
+			sizeChart.setVisibleRange(sizeRange.getStart(), sizeMax);
+			sizeChart.addVisibleRangeListener(range -> sizeRange = range);
+
 			CTabItem t3 = new CTabItem(tabFolder, SWT.NONE);
-			t3.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
+			t3.setToolTipText(Messages.IO_PAGE_SIZE_DESCRIPTION);
+			sizeCanvas = new ChartCanvas(tabFolder);
+			t3.setText(Messages.PAGES_SIZE);
+			t3.setControl(sizeCanvas);
+			DataPageToolkit.createChartTooltip(sizeCanvas);
+			DataPageToolkit.setChart(sizeCanvas, sizeChart, JdkAttributes.IO_SIZE,
+					pageContainer::showSelection);
+			SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), sizeChart,
+					JdkAttributes.IO_SIZE, Messages.SocketIOPage_SIZE_SELECTION, sizeCanvas.getContextMenu());
+
+			CTabItem t4 = new CTabItem(tabFolder, SWT.NONE);
+			t4.setToolTipText(Messages.IO_PAGE_EVENT_LOG_DESCRIPTION);
 			itemList = LIST.buildWithoutBorder(tabFolder, getTableSettings(state.getChild(LIST_ELEMENT)));
 			MCContextMenuManager itemListMm = MCContextMenuManager
 					.create(itemList.getManager().getViewer().getControl());
@@ -274,13 +296,13 @@
 					Messages.SocketIOPage_LOG_SELECTION, itemListMm);
 			itemList.getManager().getViewer().addSelectionChangedListener(
 					e -> pageContainer.showSelection(ItemCollectionToolkit.build(itemList.getSelection().get())));
-			t3.setText(Messages.PAGES_EVENT_LOG);
+			t4.setText(Messages.PAGES_EVENT_LOG);
 			eventFilter = FilterComponent.createFilterComponent(itemList, itemListFilter,
 					getDataSource().getItems().apply(TABLE_ITEMS), pageContainer.getSelectionStore()::getSelections,
 					this::onEventFilterChange);
 			itemListMm.add(eventFilter.getShowFilterAction());
 			itemListMm.add(eventFilter.getShowSearchAction());
-			t3.setControl(eventFilter.getComponent());
+			t4.setControl(eventFilter.getComponent());
 			eventFilter.loadState(state.getChild(EVENT_FILTER));
 			onEventFilterChange(itemListFilter);
 			itemList.getManager().setSelectionState(itemListSelection);
@@ -459,6 +481,7 @@
 
 			List<IXDataRenderer> timelineRows = new ArrayList<>();
 			List<IXDataRenderer> durationRows = new ArrayList<>();
+			List<IXDataRenderer> sizeRows = new ArrayList<>();
 			IItemCollection readItems = selectedItems.apply(JdkFilters.SOCKET_READ);
 			if (readItems.hasItems()) {
 				timelineRows.add(DataPageToolkit.buildSizeRow(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
@@ -468,6 +491,9 @@
 						.add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
 								JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems,
 								JdkAggregators.SOCKET_READ_COUNT, READ_COLOR));
+				sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_READ + hostCount,
+						JdkAggregators.SOCKET_READ_COUNT.getDescription(), readItems,
+						JdkAggregators.SOCKET_READ_COUNT, READ_COLOR, JdkAttributes.IO_SOCKET_BYTES_READ));
 			}
 			IItemCollection writeItems = selectedItems.apply(JdkFilters.SOCKET_WRITE);
 			if (writeItems.hasItems()) {
@@ -478,10 +504,14 @@
 						.add(DataPageToolkit.buildDurationHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
 								JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems,
 								JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR));
+				sizeRows.add(DataPageToolkit.buildSizeHistogram(Messages.SocketIOPage_ROW_SOCKET_WRITE + hostCount,
+						JdkAggregators.SOCKET_WRITE_COUNT.getDescription(), writeItems,
+						JdkAggregators.SOCKET_WRITE_COUNT, WRITE_COLOR, JdkAttributes.IO_SOCKET_BYTES_WRITTEN));
 			}
 			if (timelineCanvas != null) {
 				timelineCanvas.replaceRenderer(RendererToolkit.uniformRows(timelineRows));
 				durationCanvas.replaceRenderer(RendererToolkit.uniformRows(durationRows));
+				sizeCanvas.replaceRenderer(RendererToolkit.uniformRows(sizeRows));
 
 				itemList.show(selectedItems);
 				pageContainer.showSelection(selectedItems);
@@ -568,6 +598,7 @@
 	private IItemFilter itemListFilter;
 	private IRange<IQuantity> timelineRange;
 	private IRange<IQuantity> durationRange;
+	private IRange<IQuantity> sizeRange;
 	private int tabFolderIndex = 0;
 	public FlavorSelectorState flavorSelectorState;
 
@@ -579,6 +610,7 @@
 		secondaryTableFilter = new HashMap<>();
 		timelineRange = editor.getRecordingRange();
 		durationRange = editor.getRecordingRange();
+		sizeRange = DataPageToolkit.buildSizeRange(items.getItems(), true);
 	}
 
 	@Override
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/resources/org/openjdk/jmc/flightrecorder/ui/messages/internal/messages.properties	Thu May 02 08:56:53 2019 -0400
+++ b/application/org.openjdk.jmc.flightrecorder.ui/src/main/resources/org/openjdk/jmc/flightrecorder/ui/messages/internal/messages.properties	Thu May 02 11:06:51 2019 -0400
@@ -223,6 +223,7 @@
 FILE_OPENER_WARNING_TITLE=Warning
 FileIOPage_DURATION_SELECTION=File I/O Duration Selection
 FileIOPage_HISTOGRAM_SELECTION=File I/O Histogram Selection
+FileIOPage_SIZE_SELECTION=File I/O Size Selection
 FileIOPage_LOG_SELECTION=File I/O Log Selection
 FileIOPage_PAGE_NAME=File I/O
 FileIOPage_ROW_FILE_READ=File Read
@@ -385,6 +386,7 @@
 SocketIOPage_BY_HOST_AND_PORT_ACTION=By Host and Port
 SocketIOPage_BY_PORT_ACTION=By Port
 SocketIOPage_DURATION_SELECTION=Socket I/O Duration Selection
+SocketIOPage_SIZE_SELECTION=Socket I/O Size Selection
 SocketIOPage_HISTOGRAM_SELECTION=Socket I/O Histogram Selection
 SocketIOPage_HOST_AND_PORT=Host and Port
 SocketIOPage_LOG_SELECTION=Socket I/O Log Selection
@@ -527,6 +529,7 @@
 PAGES_EVENT_LOG=Event Log
 PAGES_TIMELINE=Timeline
 PAGES_DURATIONS=Durations
+PAGES_SIZE=Size
 ProcessesPage_AGGR_CONCURRENT_PROCESSES=Concurrent Processes
 ProcessesPage_AGGR_CONCURRENT_PROCESSES_DESC=Concurrent Processes
 ProcessesPage_AGGR_FIRST_SAMPLE=First Sample
@@ -539,6 +542,7 @@
 ProcessesPage_ROW_CPU_USAGE_DESC=Total CPU usage of the machine, as well as the CPU load of the Java process (divided into user space load and system/kernel space load).
 IO_PAGE_DURATIONS_DESCRIPTION=Distribution of the I/O event durations.
 IO_PAGE_TIMELINE_DESCRIPTION=Timeline displaying the span of the I/O events and the amount of data read/written per time period.
+IO_PAGE_SIZE_DESCRIPTION=Displays the distribution of the I/O events based on the amount of data read/written per event.
 IO_PAGE_EVENT_LOG_DESCRIPTION=Log of the I/O events, displaying all events or a subset depending on the selection in the other tables.
 ItemHandlerPage_DEFAULT_PAGE_NAME=Filtered Events
 # {0} is a number
--- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java	Thu May 02 08:56:53 2019 -0400
+++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java	Thu May 02 11:06:51 2019 -0400
@@ -227,7 +227,9 @@
 			Messages.getString(Messages.AGGR_INSIDE_TLAB_COUNT),
 			Messages.getString(Messages.AGGR_INSIDE_TLAB_COUNT_DESC), ALLOC_INSIDE_TLAB);
 	public static final IAggregator<IQuantity, ?> FILE_WRITE_LONGEST = Aggregators.max(FILE_WRITE, DURATION);
+	public static final IAggregator<IQuantity, ?> FILE_WRITE_LARGEST = Aggregators.max(FILE_WRITE, IO_FILE_BYTES_WRITTEN);
 	public static final IAggregator<IQuantity, ?> FILE_READ_LONGEST = Aggregators.max(FILE_READ, DURATION);
+	public static final IAggregator<IQuantity, ?> FILE_READ_LARGEST = Aggregators.max(FILE_READ, IO_FILE_BYTES_READ);
 	public static final IAggregator<IQuantity, ?> FILE_WRITE_SIZE = Aggregators.sum(
 			Messages.getString(Messages.AGGR_FILE_WRITE_SIZE), Messages.getString(Messages.AGGR_FILE_WRITE_SIZE_DESC),
 			FILE_WRITE, IO_FILE_BYTES_WRITTEN);
@@ -253,7 +255,9 @@
 			Messages.getString(Messages.AGGR_CODE_CACHE_FULL_COUNT),
 			Messages.getString(Messages.AGGR_CODE_CACHE_FULL_COUNT_DESC), CODE_CACHE_FULL);
 	public static final IAggregator<IQuantity, ?> SOCKET_WRITE_LONGEST = Aggregators.max(SOCKET_WRITE, DURATION);
+	public static final IAggregator<IQuantity, ?> SOCKET_WRITE_LARGEST = Aggregators.max(SOCKET_WRITE, IO_SOCKET_BYTES_WRITTEN);
 	public static final IAggregator<IQuantity, ?> SOCKET_READ_LONGEST = Aggregators.max(SOCKET_READ, DURATION);
+	public static final IAggregator<IQuantity, ?> SOCKET_READ_LARGEST = Aggregators.max(SOCKET_READ, IO_SOCKET_BYTES_READ);
 	public static final IAggregator<IQuantity, ?> SOCKET_WRITE_SIZE = Aggregators.sum(
 			Messages.getString(Messages.AGGR_SOCKET_WRITE_SIZE),
 			Messages.getString(Messages.AGGR_SOCKET_WRITE_SIZE_DESC), SOCKET_WRITE, IO_SOCKET_BYTES_WRITTEN);
--- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java	Thu May 02 08:56:53 2019 -0400
+++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java	Thu May 02 11:06:51 2019 -0400
@@ -49,6 +49,7 @@
 import static org.openjdk.jmc.common.unit.UnitLookup.TIMESTAMP;
 import static org.openjdk.jmc.common.unit.UnitLookup.UNKNOWN;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.openjdk.jmc.common.IDisplayable;
@@ -73,6 +74,7 @@
 import org.openjdk.jmc.common.util.FormatToolkit;
 import org.openjdk.jmc.common.util.LabeledIdentifier;
 import org.openjdk.jmc.common.util.MCClassLoader;
+import org.openjdk.jmc.common.util.MemberAccessorToolkit;
 import org.openjdk.jmc.flightrecorder.JfrAttributes;
 import org.openjdk.jmc.flightrecorder.jdk.messages.internal.Messages;
 
@@ -470,6 +472,23 @@
 	public static final IAttribute<IQuantity> IO_SOCKET_BYTES_WRITTEN = attr("bytesWritten", //$NON-NLS-1$
 			Messages.getString(Messages.ATTR_IO_SOCKET_BYTES_WRITTEN),
 			Messages.getString(Messages.ATTR_IO_SOCKET_BYTES_WRITTEN_DESC), MEMORY);
+	public static final IAttribute<IQuantity> IO_SIZE = Attribute.canonicalize(new Attribute<IQuantity>("size",  //#NON-NLS-1$
+			"Size", null, MEMORY) {
+		@Override
+		public <U> IMemberAccessor<IQuantity, U> customAccessor(IType<U> type){
+			List<IMemberAccessor<IQuantity, U>> accessorList = new ArrayList<>();
+			accessorList.add(type.getAccessor(JdkAttributes.IO_SOCKET_BYTES_READ.getKey()));
+			accessorList.add(type.getAccessor(JdkAttributes.IO_SOCKET_BYTES_WRITTEN.getKey()));
+			accessorList.add(type.getAccessor(JdkAttributes.IO_FILE_BYTES_READ.getKey()));
+			accessorList.add(type.getAccessor(JdkAttributes.IO_FILE_BYTES_WRITTEN.getKey()));
+			for (IMemberAccessor<IQuantity, U> accessor : accessorList) {
+				if(accessor != null) {
+					return accessor;
+				}
+			}
+			return MemberAccessorToolkit.constant(UnitLookup.BYTE.quantity(0));
+		}
+	});
 	public static final IAttribute<String> IO_ADDRESS = attr("address", Messages.getString(Messages.ATTR_IO_ADDRESS), //$NON-NLS-1$
 			PLAIN_TEXT);
 	public static final IAttribute<String> IO_HOST = attr("host", Messages.getString(Messages.ATTR_IO_HOST), //$NON-NLS-1$