From ce99299dc35ba41855be747f86ab3a9ce8948ba7 Mon Sep 17 00:00:00 2001 From: yoann-heitz Date: Fri, 4 Mar 2022 17:28:52 +0100 Subject: [PATCH] OTF2: Add a flows analysis with an initial view Added a new analysis in the OTF2 plugin. One view is provided with this analysis. This view shows the input and output flows of the threads/processes/nodes/clusters when they are exchanging data through MPI blocking operations. The colors of states that are displayed when data is exchanged depends on the mean and standard deviation of all the data that has been exchanged on the row. Test case can be found in tracecompass-test-traces, under the name bandwidth_issue: tracecompass-test-traces/ctf/src/main/resources/bandwidth_issue Signed-off-by: yoann-heitz Change-Id: Ie3ea1d99bb0e1d9e6f5d1646895cd2b477387702 Reviewed-on: https://git.eclipse.org/r/c/tracecompass.incubator/org.eclipse.tracecompass.incubator/+/191486 Tested-by: Marco Miller Tested-by: Trace Compass Bot Reviewed-by: Marco Miller --- .../META-INF/MANIFEST.MF | 1 + .../plugin.properties | 5 +- .../plugin.xml | 20 +- .../analysis/AbstractOtf2StateProvider.java | 31 +- .../otf2/core/analysis/IOtf2Fields.java | 17 +- .../callstack/Otf2CallStackStateProvider.java | 17 -- .../core/analysis/flows/FlowsLocation.java | 157 ++++++++++ .../analysis/flows/FlowsLocationGroup.java | 70 +++++ .../core/analysis/flows/FlowsNodeMap.java | 57 ++++ .../core/analysis/flows/FlowsRowModel.java | 274 +++++++++++++++++ .../analysis/flows/FlowsSystemTreeNode.java | 75 +++++ .../otf2/core/analysis/flows/IFlowsNode.java | 68 +++++ .../analysis/flows/Otf2FlowsAnalysis.java | 49 +++ .../analysis/flows/Otf2FlowsDataProvider.java | 286 ++++++++++++++++++ .../flows/Otf2FlowsDataProviderFactory.java | 38 +++ .../flows/Otf2FlowsStateProvider.java | 254 ++++++++++++++++ .../core/analysis/flows/package-info.java | 12 + .../Otf2SummaryTimelineStateProvider.java | 2 - .../META-INF/MANIFEST.MF | 1 + .../plugin.properties | 3 +- .../plugin.xml | 26 ++ .../otf2/ui/views/flows/Otf2FlowsView.java | 58 ++++ 22 files changed, 1484 insertions(+), 37 deletions(-) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocation.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocationGroup.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsNodeMap.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsRowModel.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsSystemTreeNode.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/IFlowsNode.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsAnalysis.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProviderFactory.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsStateProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/package-info.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/src/org/eclipse/tracecompass/incubator/internal/otf2/ui/views/flows/Otf2FlowsView.java diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/META-INF/MANIFEST.MF b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/META-INF/MANIFEST.MF index 90e8bd46..d21ec294 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/META-INF/MANIFEST.MF +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/META-INF/MANIFEST.MF @@ -24,6 +24,7 @@ Export-Package: org.eclipse.tracecompass.incubator.internal.otf2.core;x-friends: org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.callstack;x-friends:="org.eclipse.tracecompass.incubator.otf2.ui", org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.communicators;x-friends:="org.eclipse.tracecompass.incubator.otf2.ui", org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.summarytimeline;x-friends:="org.eclipse.tracecompass.incubator.otf2.ui", + org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows;x-friends:="org.eclipse.tracecompass.incubator.otf2.ui", org.eclipse.tracecompass.incubator.otf2.core.trace;x-friends:="org.eclipse.tracecompass.incubator.otf2.ui,org.eclipse.tracecompass.incubator.otf2.core.tests" Import-Package: com.google.common.collect, org.apache.commons.io, diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.properties b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.properties index ba7beed2..8a405588 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.properties +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.properties @@ -12,6 +12,7 @@ Bundle-Vendor = Eclipse Trace Compass Incubator Bundle-Name = Trace Compass otf2 Core Plug-in (Incubator) -otf2.analysis.callstack = Callstack analysis +otf2.analysis.callstack = Callstack analysis otf2.analysis.communicators = MPI communicators state analysis -otf2.analysis.summarytimeline = Summary Timeline analysis \ No newline at end of file +otf2.analysis.summarytimeline = Summary Timeline analysis +otf2.analysis.flows = Flows analysis diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.xml index 5787abf1..c090127c 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/plugin.xml @@ -47,6 +47,17 @@ class="org.eclipse.tracecompass.incubator.otf2.core.trace.Otf2Trace"> + + + + @@ -55,11 +66,18 @@ id="org.eclipse.tracecompass.incubator.otf2.core.analysis.communicators.dataprovider"> - + + + + \ No newline at end of file diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/AbstractOtf2StateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/AbstractOtf2StateProvider.java index 245e804f..81a25802 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/AbstractOtf2StateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/AbstractOtf2StateProvider.java @@ -101,18 +101,6 @@ public ITmfStateProvider getNewInstance() { } } - /** - * Basic check to figure out if further processing should be done with an - * event - * - * @param event - * the event - * @return true if the event should be handled, false otherwise - */ - protected static boolean considerEvent(ITmfEvent event) { - return true; - } - @Override protected void eventHandle(ITmfEvent event) { ITmfEventField content = event.getContent(); @@ -363,4 +351,21 @@ protected Map getStringId() { return fStringId; } -} \ No newline at end of file + /** + * Return the location id of a process into an MPI communicator. + * + * @param rank + * The rank of the process in the communicator + * @param communicatorReference + * The communicator id + * @return The rank of the location into the communicator, unknown if the + * rank number is >= to the number of members defined. + */ + protected long getLocationIdFromRank(Integer rank, Integer communicatorReference) { + ArrayList members = getMembersFromCommunicatorReference(communicatorReference); + if (rank >= members.size()) { + return UNKNOWN_RANK; + } + return members.get(rank); + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/IOtf2Fields.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/IOtf2Fields.java index cd8c7305..411ff75f 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/IOtf2Fields.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/IOtf2Fields.java @@ -122,4 +122,19 @@ public interface IOtf2Fields { * Location group field name */ String OTF2_LOCATION_GROUP = "locationGroup"; //$NON-NLS-1$ -} \ No newline at end of file + + /** + * Message length field name + */ + String OTF2_MESSAGE_LENGTH = "msgLength"; //$NON-NLS-1$ + + /** + * Size received field name + */ + String OTF2_SIZE_RECEIVED = "sizeReceived"; //$NON-NLS-1$ + + /** + * Size sent field name + */ + String OTF2_SIZE_SENT = "sizeSent"; //$NON-NLS-1$ +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/callstack/Otf2CallStackStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/callstack/Otf2CallStackStateProvider.java index 326677eb..ecfe0cd0 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/callstack/Otf2CallStackStateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/callstack/Otf2CallStackStateProvider.java @@ -534,23 +534,6 @@ private void initializeQuarks(ITmfStateSystemBuilder ssb) { } } - /** - * Return the location id of a process into an MPI communicator. - * - * @param rank - * The rank of the process in the communicator - * @param communicatorReference - * The communicator id - * @return The rank of the location into the communicator - */ - protected long getLocationIdFromRank(Integer rank, Integer communicatorReference) { - ArrayList members = getMembersFromCommunicatorReference(communicatorReference); - if (rank >= members.size()) { - return UNKNOWN_RANK; - } - return members.get(rank); - } - /* * Calls the corresponding method from the associated location given the * type of event diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocation.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocation.java new file mode 100644 index 00000000..21b25745 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocation.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.Map; + +import org.eclipse.tracecompass.incubator.internal.otf2.core.trace.Location; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; +import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; + +/** + * A class representing a location for the flows analysis + * + * @author Yoann Heitz + */ +class FlowsLocation extends Location implements IFlowsNode { + + private int fInputQuark = UNKNOWN_ID; + private int fOutputQuark = UNKNOWN_ID; + private long fLatestEnteredTimestamp = 0; + private long fInputMessageSize = 0; + private long fOutputMessageSize = 0; + + /** + * Constructor for this class + * + * @param event + * a GlobalDef_Location event + */ + public FlowsLocation(ITmfEvent event) { + super(event); + } + + /** + * Initializes the quarks related to this location. This method should be + * called by the state provider after all definitions have been read + * + * @param ssb + * the state system builder + * @param locationGroupMap + * a map containing the different location groups and their IDs + * @param stringIdMap + * a map containing the different strings and their IDs + */ + public void initializeQuarks(ITmfStateSystemBuilder ssb, FlowsNodeMap locationGroupMap, Map stringIdMap) { + String locationName = getName(stringIdMap); + long parentId = getLocationGroupId(); + int parentInputQuark = locationGroupMap.getNodeQuark(parentId, INPUT); + int parentOutputQuark = locationGroupMap.getNodeQuark(parentId, OUTPUT); + fInputQuark = ssb.getQuarkRelativeAndAdd(parentInputQuark, locationName); + fOutputQuark = ssb.getQuarkRelativeAndAdd(parentOutputQuark, locationName); + } + + @Override + public int getInputQuark() { + return fInputQuark; + } + + @Override + public int getOutputQuark() { + return fOutputQuark; + } + + /** + * Gets the timestamp of the last Enter event for this location + * + * @return the timestamp of the last Enter event for this location + */ + public long getLatestEnteredTimestamp() { + return fLatestEnteredTimestamp; + } + + /** + * Sets the timestamp of the last Enter event for this location + * + * @param latestEnteredTimestamp + * the timestamp when this location entered a region the last + * time + */ + public void setLatestEnteredTimestamp(long latestEnteredTimestamp) { + fLatestEnteredTimestamp = latestEnteredTimestamp; + } + + /** + * Update the flow value of this location for the correct quark, depending + * on the direction of the communication. The flow value is in + * bytes/nanoseconds + * + * @param ssb + * the state system builder + * @param messageLength + * the number of bytes that were exchanged + * @param endTimestamp + * the timestamp of the end of the communication + * @param direction + * the direction of the communication + */ + private void updateFlow(ITmfStateSystemBuilder ssb, Long messageLength, long endTimestamp, String direction) { + int correctQuark = getQuark(direction); + if (correctQuark == UNKNOWN_ID) { + return; + } + long operationDuration = endTimestamp - fLatestEnteredTimestamp; + double flow = (double) messageLength / operationDuration; + StateSystemBuilderUtils.incrementAttributeDouble(ssb, fLatestEnteredTimestamp, correctQuark, flow); + StateSystemBuilderUtils.incrementAttributeDouble(ssb, endTimestamp, correctQuark, -flow); + } + + /** + * Updates the fOutputMessageSize field with a new value + * + * @param messageLength + * the new value for the fOutputMessageSize field + */ + public void updateOutputMessageSize(Long messageLength) { + fOutputMessageSize = messageLength; + } + + /** + * Updates the fInputMessageSize field with a new value + * + * @param messageLength + * the new value for the fInputMessageSize field + */ + public void updateInputMessageSize(Long messageLength) { + fInputMessageSize = messageLength; + } + + /** + * Method to call when an event representing a region exit for this location + * occurs + * + * @param ssb + * the state system builder + * @param leaveTimestamp + * the timestamp when this location exited a region + */ + public void processLeave(ITmfStateSystemBuilder ssb, long leaveTimestamp) { + if (fInputMessageSize != 0) { + updateFlow(ssb, fInputMessageSize, leaveTimestamp, INPUT); + fInputMessageSize = 0; + } + if (fOutputMessageSize != 0) { + updateFlow(ssb, fOutputMessageSize, leaveTimestamp, OUTPUT); + fOutputMessageSize = 0; + } + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocationGroup.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocationGroup.java new file mode 100644 index 00000000..4adf44ec --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsLocationGroup.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.Map; + +import org.eclipse.tracecompass.incubator.internal.otf2.core.trace.LocationGroup; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; + +/** + * A class representing a location group for the flows analysis + * + * @author Yoann Heitz + */ +class FlowsLocationGroup extends LocationGroup implements IFlowsNode { + + private int fInputQuark = UNKNOWN_ID; + private int fOutputQuark = UNKNOWN_ID; + + /** + * Constructor for this class + * + * @param event + * a GlobalDef_LocationGroup event + */ + public FlowsLocationGroup(ITmfEvent event) { + super(event); + } + + /** + * Initializes the quarks related to this location group. This method should + * be called by the state provider after all definitions have been read + * + * @param ssb + * the state system builder + * @param systemTreeNodeMap + * a map containing the different system tree nodes and their IDs + * @param stringIdMap + * a map containing the different strings and their IDs + */ + public void initializeQuarks(ITmfStateSystemBuilder ssb, FlowsNodeMap systemTreeNodeMap, Map stringIdMap) { + long parentId = getParentId(); + String fullName = getFullName(stringIdMap); + int parentInputQuark = systemTreeNodeMap.getNodeQuark(parentId, INPUT); + int parentOutputQuark = systemTreeNodeMap.getNodeQuark(parentId, OUTPUT); + if (parentInputQuark != UNKNOWN_ID && parentOutputQuark != UNKNOWN_ID) { + fInputQuark = ssb.getQuarkRelativeAndAdd(parentInputQuark, fullName); + fOutputQuark = ssb.getQuarkRelativeAndAdd(parentOutputQuark, fullName); + } + } + + @Override + public int getInputQuark() { + return fInputQuark; + } + + @Override + public int getOutputQuark() { + return fOutputQuark; + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsNodeMap.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsNodeMap.java new file mode 100644 index 00000000..7253e06e --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsNodeMap.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.HashMap; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * A generic class representing a map containing all "nodes" for a specific type + * in the physical system. The types may be clusters, physical nodes, processes + * or threads for example and what has been defined as a "node" above, is an + * instance of such a type. + * + * @author Yoann Heitz + */ +final class FlowsNodeMap extends HashMap { + + /** + * Magic number + */ + private static final long serialVersionUID = 1L; + private static final int UNKNOWN_ID = -1; + + /** + * Constructor for this class + */ + FlowsNodeMap() { + super(); + } + + /** + * Gets the correct quark depending on the given node ID and direction + * + * @param id + * the ID of the node + * @param direction + * the direction associated to the quark + * @return the quark associated to the node ID and the direction, -1 if the + * node is not present in the map. + */ + public int getNodeQuark(NodeIdType id, String direction) { + @Nullable NodeType node = get(id); + if (node == null) { + return UNKNOWN_ID; + } + return node.getQuark(direction); + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsRowModel.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsRowModel.java new file mode 100644 index 00000000..77b677c8 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsRowModel.java @@ -0,0 +1,274 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Predicate; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat; +import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; +import org.eclipse.tracecompass.tmf.core.model.StyleProperties; +import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState; +import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel; +import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState; +import org.eclipse.tracecompass.tmf.core.presentation.IPaletteProvider; +import org.eclipse.tracecompass.tmf.core.presentation.RGBAColor; +import org.eclipse.tracecompass.tmf.core.presentation.SequentialPaletteProvider; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; + +/** + * A row model for the timegraph flows views. + * + * @author Yoann Heitz + */ +final class FlowsRowModel extends TimeGraphRowModel { + + /** + * The styles for the row depends on the mean and standard deviation of the + * states. This class allows to store a simple version of the states with + * only the value stored as double, the start and the duration of the state. + * + * @author Yoann Heitz + */ + private static class FlowsState { + private long fStart; + private long fDuration; + private double fValue; + + /** + * Constructor for this class + * + * @param start + * the start timestamp of the state + * @param duration + * the duration of the state + * @param value + * the value of the state + */ + public FlowsState(long start, long duration, double value) { + fStart = start; + fDuration = duration; + fValue = value; + } + + /** + * Gets the start of this state + * + * @return the start of this state + */ + public long getStart() { + return fStart; + } + + /** + * Gets the duration of this state + * + * @return the duration of this state + */ + public long getDuration() { + return fDuration; + } + + /** + * Gets the value of this state + * + * @return the value of this state + */ + public double getValue() { + return fValue; + } + } + + private static final String COLOR = "#ee0000cc"; //$NON-NLS-1$ + private static final int COLORS_NUMBER = 20; + private static final float ZSCORE_FACTOR = (float) (COLORS_NUMBER / 2 * 0.7); + private static final double ZERO_THRESHOLD = 1E-10; + + /** + * This object allows to limit the number of units that are displayed. + * However it needs to be improved. + */ + private DecimalUnitFormat fFormat = new DecimalUnitFormat(1E9); + private List fColors; + private double fMean; + private double fStandardDeviation; + private TreeMap fTimestampFlowChangeMap = new TreeMap<>(); + private @Nullable FlowsRowModel fParentModel; + + /** + * Tests if a double is equal to zero + * + * @param value + * the double to test + * @return true of the double is zero + */ + private static boolean isZero(double value) { + return Math.abs(value) <= ZERO_THRESHOLD; + } + + /** + * Constructor for this class + * + * @param entryID + * the ID of the entry associated to this row + * @param states + * the list of states for this row + * @param parentModel + * the row of the parent of the entry associated with this row. + * It can be null if this row entry is a root + */ + public FlowsRowModel(long entryID, List states, @Nullable FlowsRowModel parentModel) { + super(entryID, states); + fParentModel = parentModel; + RGBAColor baseColor = RGBAColor.fromString(COLOR); + @Nullable IPaletteProvider paletteProvider = baseColor != null ? SequentialPaletteProvider.create(baseColor, COLORS_NUMBER) : null; + List colors = paletteProvider != null ? paletteProvider.get() : new ArrayList<>(); + fColors = colors; + } + + /** + * Clips a value between two values + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the value to clip between min and max + * @return the clipped value + */ + private static double clip(double min, double max, double value) { + return Math.min(max, Math.max(min, value)); + } + + /** + * Gets the style to display depending on the value of the state + * + * @param value + * the value of the state + * @return the style to display + */ + private OutputElementStyle getStateStyle(double value) { + double zScore = fStandardDeviation != 0 ? (value - fMean) / fStandardDeviation : 0; + int index = (int) clip(0, COLORS_NUMBER - 1, COLORS_NUMBER / 2 + ZSCORE_FACTOR * zScore); + RGBAColor color = fColors.get(index); + return new OutputElementStyle(null, ImmutableMap.of(StyleProperties.BORDER_STYLE, StyleProperties.BorderStyle.SOLID, StyleProperties.BACKGROUND_COLOR, color.toString().substring(0, 7))); + } + + /** + * Computes the mean and the standard deviation for the non zero states of + * this row, then produces the styles and states to display on the row + * depending on the previously computed values. + * + * @param dataProvider + * the data provider that called this method + * @param predicates + * the predicates used to filter the timegraph state + * @param monitor + * the progress monitor + */ + public void computeStatisticsAndStates(Otf2FlowsDataProvider dataProvider, Map>> predicates, @Nullable IProgressMonitor monitor) { + if (fTimestampFlowChangeMap.isEmpty()) { + return; + } + long lastTimestamp = fTimestampFlowChangeMap.firstKey(); + ArrayList flowsStates = new ArrayList<>(); + double currentFlowValue = 0; + double cumulativeFlow = 0; + double cumulativeSquaredFlow = 0; + long numberOfStates = 0; + + // The flow changes are parsed to compute the current flow value, + // compute statistics and create associated states + for (Map.Entry entry : fTimestampFlowChangeMap.entrySet()) { + long timestamp = entry.getKey(); + double flowChange = entry.getValue(); + long durationSinceLastTimestamp = timestamp - lastTimestamp; + + // The statistics are only computed on non null flow states + if (!isZero(currentFlowValue)) { + if (!isZero(flowChange)) { + flowsStates.add(new FlowsState(lastTimestamp, durationSinceLastTimestamp, currentFlowValue)); + numberOfStates += 1; + cumulativeFlow += currentFlowValue; + cumulativeSquaredFlow += Math.pow(currentFlowValue, 2); + currentFlowValue += flowChange; + lastTimestamp = timestamp; + } + } else { + flowsStates.add(new FlowsState(lastTimestamp, durationSinceLastTimestamp, 0.0)); + currentFlowValue += flowChange; + lastTimestamp = timestamp; + } + } + fMean = cumulativeFlow / numberOfStates; + double squaredDataMean = cumulativeSquaredFlow / numberOfStates; + double squaredMean = Math.pow(fMean, 2); + + // We must test if squaredDataMean >= squaredMean. Theoretically it + // should not happen, but with inaccuracy when dealing with floats it + // could happen + fStandardDeviation = squaredDataMean >= squaredMean ? Math.sqrt(squaredDataMean - squaredMean) : 0; + + // Once mean and standard deviation have been calculated, we can create + // the states and their styles + List states = getStates(); + for (FlowsState state : flowsStates) { + double value = state.getValue(); + @Nullable OutputElementStyle style = isZero(value) ? null : getStateStyle(value); + @Nullable String label = isZero(value) ? null : fFormat.format(value, new StringBuffer(), null).append("B/s").toString(); //$NON-NLS-1$ + TimeGraphState timeGraphState = new TimeGraphState(state.getStart(), state.getDuration(), label, style); + dataProvider.applyFilterAndAddState(states, timeGraphState, getEntryID(), predicates, monitor); + } + } + + /** + * Recursive function to fill a flow change in the TreeMap containing the + * flow changes. It then calls the same method on the parent row unless this + * row has no parent + * + * @param timestamp + * the timestamp when the flow change occurred + * @param value + * the value of the flow change + */ + public void addFlowChange(long timestamp, double value) { + fTimestampFlowChangeMap.merge(timestamp, value, (oldValue, newValue) -> oldValue + newValue); + if (fParentModel != null) { + fParentModel.addFlowChange(timestamp, value); + } + } + + /** + * Gets the mean of the non null states for this row + * + * @return the mean of the non null states for this row + */ + public double getMean() { + return fMean; + } + + /** + * Get the standard deviation of the non null states for this row + * + * @return the standard deviation of the non null states for this row + */ + public double getStandardDeviation() { + return fStandardDeviation; + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsSystemTreeNode.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsSystemTreeNode.java new file mode 100644 index 00000000..f7b45119 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/FlowsSystemTreeNode.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.incubator.internal.otf2.core.trace.SystemTreeNode; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; + +/** + * A class representing a system tree node for the flows analysis + * + * @author Yoann Heitz + */ +class FlowsSystemTreeNode extends SystemTreeNode implements IFlowsNode { + + private int fInputQuark = UNKNOWN_ID; + private int fOutputQuark = UNKNOWN_ID; + + /** + * Constructor for this class + * + * @param event + * a GlobalDef_SystemTreeNode event + */ + public FlowsSystemTreeNode(ITmfEvent event) { + super(event); + } + + /** + * Initializes the quarks related to this system tree node. This method + * should be called by the state provider after all definitions have been + * read + * + * @param ssb + * the state system builder + * @param systemTreeNodeMap + * a map containing the different system tree nodes and their IDs + * @param stringIdMap + * a map containing the different strings and their IDs + */ + public void initializeQuarks(ITmfStateSystemBuilder ssb, @Nullable FlowsNodeMap systemTreeNodeMap, Map stringIdMap) { + String fullName = getFullName(stringIdMap); + long parentId = getParentId(); + if (isRootNode()) { + fInputQuark = ssb.getQuarkAbsoluteAndAdd(INPUT, fullName); + fOutputQuark = ssb.getQuarkAbsoluteAndAdd(OUTPUT, fullName); + } else if (systemTreeNodeMap != null) { + int parentInputQuark = systemTreeNodeMap.getNodeQuark(parentId, INPUT); + int parentOutputQuark = systemTreeNodeMap.getNodeQuark(parentId, OUTPUT); + fInputQuark = ssb.getQuarkRelativeAndAdd(parentInputQuark, fullName); + fOutputQuark = ssb.getQuarkRelativeAndAdd(parentOutputQuark, fullName); + } + } + + @Override + public int getInputQuark() { + return fInputQuark; + } + + @Override + public int getOutputQuark() { + return fOutputQuark; + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/IFlowsNode.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/IFlowsNode.java new file mode 100644 index 00000000..0575bd1a --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/IFlowsNode.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +/** + * Interface to be implemented by the different "nodes" in the flows analysis. + * These nodes are the locations, location groups and system tree nodes. + * + * @author Yoann Heitz + */ +public interface IFlowsNode { + + /** + * In string for input flows related features + */ + String INPUT = "In"; //$NON-NLS-1$ + + /** + * Out string for output flows related features + */ + String OUTPUT = "Out"; //$NON-NLS-1$ + + /** + * Integer representing an integer ID + */ + int UNKNOWN_ID = -1; + + /** + * Gets the input quark associated to this node + * + * @return the input quark of the node implementing this interface + */ + int getInputQuark(); + + /** + * Gets the output quark associated to this node + * + * @return the output quark of the node implementing this interface + */ + int getOutputQuark(); + + /** + * Return the correct quark depending on a direction + * + * @param direction + * the direction of the communication. It should be the INPUT or + * OUTPUT string + * @return the input or output quark of the node, or UNKNOWN_ID if the + * direction was not OUTPUT or INPUT + */ + default int getQuark(String direction) { + if (direction.equals(INPUT)) { + return getInputQuark(); + } else if (direction.equals(OUTPUT)) { + return getOutputQuark(); + } else { + return UNKNOWN_ID; + } + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsAnalysis.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsAnalysis.java new file mode 100644 index 00000000..0f5d3a59 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsAnalysis.java @@ -0,0 +1,49 @@ +/********************************************************************** + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; + +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.AbstractOtf2Analysis; +import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; + +/** + * MPI messages flow analysis + * + * @author Yoann Heitz + */ +public class Otf2FlowsAnalysis extends AbstractOtf2Analysis { + + /** The ID suffix of this analysis module */ + public static final String ID_SUFFIX = ".flows"; //$NON-NLS-1$ + + /** + * Constructor + */ + public Otf2FlowsAnalysis() { + super(getFullAnalysisId()); + } + + @Override + protected ITmfStateProvider createStateProvider() { + return new Otf2FlowsStateProvider(checkNotNull(getTrace())); + } + + /** + * Returns the ID of this analysis module + * + * @return the full ID of this analysis module + */ + public static String getFullAnalysisId() { + return AbstractOtf2Analysis.getAnalysisIdFromSuffix(ID_SUFFIX); + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProvider.java new file mode 100644 index 00000000..f02dd2eb --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProvider.java @@ -0,0 +1,286 @@ +/********************************************************************** + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; +import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; +import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; +import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; +import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils; +import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage; +import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider; +import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel; +import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter; +import org.eclipse.tracecompass.tmf.core.model.timegraph.AbstractTimeGraphDataProvider; +import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow; +import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel; +import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState; +import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel; +import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel; +import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel; +import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel; +import org.eclipse.tracecompass.tmf.core.response.ITmfResponse; +import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status; +import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.collect.Multimap; + +/** + * Data provider for the OTF2 flows view. + * + * @author Yoann Heitz + */ + +@SuppressWarnings("restriction") +public class Otf2FlowsDataProvider extends AbstractTimeGraphDataProvider implements IOutputStyleProvider { + + /** Data provider suffix ID */ + public static final String SUFFIX = ".dataprovider"; //$NON-NLS-1$ + + /** + * Gets the data provider ID + * + * @return the data provider ID + */ + public static String getFullDataProviderId() { + return Otf2FlowsAnalysis.getFullAnalysisId() + SUFFIX; + } + + /** + * Constructor + * + * @param trace + * the trace for this provider + * @param analysisModule + * the corresponding analysis module + */ + public Otf2FlowsDataProvider(ITmfTrace trace, Otf2FlowsAnalysis analysisModule) { + super(trace, analysisModule); + } + + @Override + public String getId() { + return getAnalysisModule().getId() + SUFFIX; + } + + @Override + public TmfModelResponse fetchStyle(Map fetchParameters, @Nullable IProgressMonitor monitor) { + return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED); + } + + @Override + public TmfModelResponse> fetchArrows(Map fetchParameters, @Nullable IProgressMonitor monitor) { + return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); + } + + private static final String TOTAL_FLOW = "Total flow : "; //$NON-NLS-1$ + private static final String FLOW_UNIT = "MB/s"; //$NON-NLS-1$ + + @Override + public TmfModelResponse> fetchTooltip(Map fetchParameters, @Nullable IProgressMonitor monitor) { + ITmfStateSystem ss = getAnalysisModule().getStateSystem(); + @Nullable List requestedTimes = DataProviderParameterUtils.extractTimeRequested(fetchParameters); + @Nullable List requestedEntries = DataProviderParameterUtils.extractSelectedItems(fetchParameters); + if (ss == null || requestedEntries == null || requestedEntries.size() != 1 || requestedTimes == null || requestedTimes.size() != 1) { + return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); + } + Map entryIdToQuarks = getSelectedEntries(requestedEntries); + + double totalFlow = 0; + Integer quark = entryIdToQuarks.get(requestedEntries.get(0)); + if (quark == null) { + return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); + } + + List quarksToQuery = ss.getSubAttributes(quark, true); + if (quarksToQuery.isEmpty()) { + quarksToQuery.add(quark); + } + try { + for (ITmfStateInterval interval : ss.query2D(quarksToQuery, requestedTimes)) { + totalFlow += interval.getValueDouble(); + } + } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) { + e.printStackTrace(); + } + + Map model = new HashMap<>(); + model.put(TOTAL_FLOW, String.valueOf(totalFlow * 1E3) + FLOW_UNIT); + return new TmfModelResponse<>(model, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); + } + + /** + * Returns the depth of a quark in the state system + * + * @param ss + * the state system + * @param quark + * the quark for which we want to know the depth + * @return the depth of the quark in the state system + */ + private int getQuarkDepth(ITmfStateSystem ss, int quark) { + int parentQuark = ss.getParentAttributeQuark(quark); + if (parentQuark == ITmfStateSystem.ROOT_ATTRIBUTE) { + return 0; + } + return 1 + getQuarkDepth(ss, parentQuark); + } + + private static final int MAX_DEPTH = 4; + + /** + * Return a map associating depths to the list of quarks at this depth in + * the state system + * + * @param ss + * the state system + * @return a map associating depths to the list of quarks at this depth in + * the state system + */ + private Map> getDepthToQuarksMap(ITmfStateSystem ss) { + List allQuarks = ss.getSubAttributes(ITmfStateSystem.ROOT_ATTRIBUTE, true); + Map> depthToQuarks = new HashMap<>(); + for (int depth = 0; depth <= MAX_DEPTH; depth++) { + depthToQuarks.put(depth, new ArrayList<>()); + } + for (int quark : allQuarks) { + int depth = getQuarkDepth(ss, quark); + List quarksAtThisDepth = depthToQuarks.get(depth); + if (quarksAtThisDepth != null) { + quarksAtThisDepth.add(quark); + } + } + return depthToQuarks; + } + + @Override + protected @Nullable TimeGraphModel getRowModel(ITmfStateSystem ss, Map parameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException { + Map>> predicates = new HashMap<>(); + Multimap regexesMap = DataProviderParameterUtils.extractRegexFilter(parameters); + if (regexesMap != null) { + predicates.putAll(computeRegexPredicate(regexesMap)); + } + + SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(parameters); + if (filter == null) { + return null; + } + + Collection times = getTimes(filter, ss.getStartTime(), ss.getCurrentEndTime()); + + Map> depthToQuark = getDepthToQuarksMap(ss); + Map idRowModelMap = new HashMap<>(); + + //Initialize row models + for (int depth = 0; depth <= MAX_DEPTH; depth++) { + List quarksAtThisDepth = depthToQuark.get(depth); + if (quarksAtThisDepth == null) { + return null; + } + for (int quark : quarksAtThisDepth) { + long entryId = getId(quark); + int parentQuark = ss.getParentAttributeQuark(quark); + long parentEntryId = getId(parentQuark); + FlowsRowModel parentRowModel = null; + if (parentQuark != ITmfStateSystem.ROOT_ATTRIBUTE) { + parentRowModel = idRowModelMap.get(parentEntryId); + } + idRowModelMap.put(entryId, new FlowsRowModel(entryId, new ArrayList<>(), parentRowModel)); + } + } + + // Query the intervals for the quarks at maximum depth (representing + // threads) and fill the row models + List threadQuarks = depthToQuark.get(MAX_DEPTH); + if (threadQuarks == null) { + return null; + } + for (ITmfStateInterval interval : ss.query2D(threadQuarks, times)) { + int quark = interval.getAttribute(); + long entryId = getId(quark); + FlowsRowModel rowModel = idRowModelMap.get(entryId); + long startTime = interval.getStartTime(); + long endTime = interval.getEndTime(); + double flowValue = interval.getValueDouble(); + if (rowModel != null) { + rowModel.addFlowChange(startTime, flowValue); + rowModel.addFlowChange(endTime, -flowValue); + } + } + + List rows = new ArrayList<>(); + for (int depth = 0; depth <= MAX_DEPTH; depth++) { + List quarksAtThisDepth = depthToQuark.get(depth); + if (quarksAtThisDepth == null) { + return null; + } + for (int quark : quarksAtThisDepth) { + long entryId = getId(quark); + FlowsRowModel rowModel = idRowModelMap.get(entryId); + if (rowModel != null) { + rowModel.computeStatisticsAndStates(this, predicates, monitor); + List eventList = rowModel.getStates(); + rows.add(new TimeGraphRowModel(entryId, eventList)); + } + } + } + return new TimeGraphModel(rows); + } + + @Override + protected boolean isCacheable() { + return false; + } + + @Override + protected TmfTreeModel getTree(ITmfStateSystem ss, Map parameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException { + Builder builder = new Builder<>(); + long parentId = getId(ITmfStateSystem.ROOT_ATTRIBUTE); + builder.add(new TimeGraphEntryModel(parentId, -1, String.valueOf(getTrace().getName()), ss.getStartTime(), ss.getCurrentEndTime())); + addChildren(ss, builder, ITmfStateSystem.ROOT_ATTRIBUTE, parentId); + return new TmfTreeModel<>(Collections.emptyList(), builder.build()); + } + + /** + * Add children to the TmfTreeModel + * + * @param ss + * the state system + * @param builder + * builder for TimeGraphEntryModel + * @param quark + * the quark for which the children will be added + * @param parentId + * the ID of the parent quark + */ + private void addChildren(ITmfStateSystem ss, Builder builder, int quark, long parentId) { + for (Integer child : ss.getSubAttributes(quark, false)) { + long childId = getId(child); + String name = ss.getAttributeName(child); + builder.add(new TimeGraphEntryModel(childId, parentId, name, ss.getStartTime(), ss.getCurrentEndTime(), true)); + addChildren(ss, builder, child, childId); + } + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProviderFactory.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProviderFactory.java new file mode 100644 index 00000000..c2c26fcc --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsDataProviderFactory.java @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory; +import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel; +import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; + +/** + * Factory for the flows data provider + * + * @author Yoann Heitz + */ +public class Otf2FlowsDataProviderFactory implements IDataProviderFactory { + + @Override + public @Nullable ITmfTreeDataProvider createProvider(ITmfTrace trace) { + Otf2FlowsAnalysis module = TmfTraceUtils.getAnalysisModuleOfClass(trace, Otf2FlowsAnalysis.class, Otf2FlowsAnalysis.getFullAnalysisId()); + if (module != null) { + module.schedule(); + return new Otf2FlowsDataProvider(trace, module); + } + return null; + } + +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsStateProvider.java new file mode 100644 index 00000000..a816f3e6 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/Otf2FlowsStateProvider.java @@ -0,0 +1,254 @@ +/********************************************************************** + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; + +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.AbstractOtf2StateProvider; +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.IOtf2Events; +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.IOtf2Fields; +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.IOtf2GlobalDefinitions; +import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; +import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; + +/** + * State provider for the OTF2 MPI messages flow analysis + * + * @author Yoann Heitz + */ + +public class Otf2FlowsStateProvider extends AbstractOtf2StateProvider { + + private static int VERSION_NUMBER = 1; + private final FlowsNodeMap fMapLocation = new FlowsNodeMap<>(); + private final FlowsNodeMap fMapLocationGroup = new FlowsNodeMap<>(); + private final FlowsNodeMap fMapSystemTreeNode = new FlowsNodeMap<>(); + private boolean initialized = false; + + /** + * Constructor + * + * @param trace + * the trace + */ + public Otf2FlowsStateProvider(ITmfTrace trace) { + super(trace, Otf2FlowsAnalysis.getFullAnalysisId()); + } + + @Override + public int getVersion() { + return VERSION_NUMBER; + } + + @Override + protected void processGlobalDefinition(ITmfEvent event, String name) { + switch (name) { + case IOtf2GlobalDefinitions.OTF2_STRING: { + processStringDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_REGION: { + processRegionDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_LOCATION: { + processLocationDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_LOCATION_GROUP: { + processLocationGroupDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_COMM: { + processCommunicatorDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_GROUP: { + processGroupDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_GROUP_MEMBER: { + processGroupMemberDefinition(event); + break; + } + case IOtf2GlobalDefinitions.OTF2_SYSTEM_TREE_NODE: { + processSystemTreeNodeDefinition(event); + break; + } + default: + return; + } + } + + /** + * Function called when a GlobalDef_Location event is encountered + * + * @param event + * an GlobalDef_Location event + */ + private void processLocationDefinition(ITmfEvent event) { + FlowsLocation location = new FlowsLocation(event); + fMapLocation.put(location.getId(), location); + } + + /** + * Function called when a GlobalDef_LocationGroup event is encountered + * + * @param event + * an GlobalDef_LocationGroup event + */ + private void processLocationGroupDefinition(ITmfEvent event) { + FlowsLocationGroup locationGroup = new FlowsLocationGroup(event); + fMapLocationGroup.put(locationGroup.getId(), locationGroup); + } + + /** + * Function called when a GlobalDef_SystemTreeNode event is encountered + * + * @param event + * an GlobalDef_SystemTreeNode event + */ + private void processSystemTreeNodeDefinition(ITmfEvent event) { + FlowsSystemTreeNode systemTreeNode = new FlowsSystemTreeNode(event); + fMapSystemTreeNode.put(systemTreeNode.getId(), systemTreeNode); + } + + @Override + protected void processOtf2Event(ITmfEvent event, String name, ITmfStateSystemBuilder ssb) { + if (!initialized) { + for (FlowsSystemTreeNode systemTreeNode : fMapSystemTreeNode.values()) { + if (systemTreeNode != null) { + systemTreeNode.initializeQuarks(ssb, fMapSystemTreeNode, getStringId()); + } + } + for (FlowsLocationGroup locationGroup : fMapLocationGroup.values()) { + if (locationGroup != null) { + locationGroup.initializeQuarks(ssb, fMapSystemTreeNode, getStringId()); + } + + } + for (FlowsLocation location : fMapLocation.values()) { + if (location != null) { + location.initializeQuarks(ssb, fMapLocationGroup, getStringId()); + } + } + initialized = true; + } + switch (name) { + case IOtf2Events.OTF2_ENTER: { + processEnter(event); + break; + } + case IOtf2Events.OTF2_LEAVE: { + processLeave(ssb, event); + break; + } + case IOtf2Events.OTF2_MPI_SEND: { + processMpiSend(event); + break; + } + case IOtf2Events.OTF2_MPI_RECV: { + processMpiRecv(event); + break; + } + case IOtf2Events.OTF2_MPI_COLLECTIVE_END: { + processMpiCollectiveCommunication(event); + break; + } + default: + return; + } + } + + /** + * Function called when an Event_Enter event is encountered + * + * @param event + * an Event_Enter event + */ + private void processEnter(ITmfEvent event) { + Long locationId = getLocationId(event); + FlowsLocation location = fMapLocation.get(locationId); + if (location == null) { + return; + } + location.setLatestEnteredTimestamp(event.getTimestamp().toNanos()); + } + + /** + * Function called when an Event_Leave event is encountered + * + * @param event + * an Event_Leave event + */ + private void processLeave(ITmfStateSystemBuilder ssb, ITmfEvent event) { + Long locationId = getLocationId(event); + FlowsLocation location = fMapLocation.get(locationId); + if (location == null) { + return; + } + location.processLeave(ssb, event.getTimestamp().toNanos()); + } + + /** + * Function called when an Event_MpiSend event is encountered + * + * @param event + * an Event_MpiSend event + */ + private void processMpiSend(ITmfEvent event) { + Long locationId = getLocationId(event); + ITmfEventField content = event.getContent(); + Long messageLength = content.getFieldValue(Long.class, IOtf2Fields.OTF2_MESSAGE_LENGTH); + FlowsLocation location = fMapLocation.get(locationId); + if (location == null || messageLength == null) { + return; + } + location.updateOutputMessageSize(messageLength); + } + + /** + * Function called when an Event_MpiRecv event is encountered + * + * @param event + * an Event_MpiRecv event + */ + private void processMpiRecv(ITmfEvent event) { + Long locationId = getLocationId(event); + ITmfEventField content = event.getContent(); + Long messageLength = content.getFieldValue(Long.class, IOtf2Fields.OTF2_MESSAGE_LENGTH); + FlowsLocation location = fMapLocation.get(locationId); + if (location == null || messageLength == null) { + return; + } + location.updateInputMessageSize(messageLength); + } + + /** + * Function called when an Event_MpiCollectiveEnd event is encountered + * + * @param event + * an Event_MpiCollectiveEnd event + */ + private void processMpiCollectiveCommunication(ITmfEvent event) { + Long locationId = getLocationId(event); + ITmfEventField content = event.getContent(); + Long receivedMessageLength = content.getFieldValue(Long.class, IOtf2Fields.OTF2_SIZE_RECEIVED); + Long sentMessageLength = content.getFieldValue(Long.class, IOtf2Fields.OTF2_SIZE_SENT); + FlowsLocation location = fMapLocation.get(locationId); + if (location == null || receivedMessageLength == null || sentMessageLength == null) { + return; + } + location.updateInputMessageSize(receivedMessageLength); + location.updateOutputMessageSize(sentMessageLength); + } +} diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/package-info.java new file mode 100644 index 00000000..18350cc0 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/flows/package-info.java @@ -0,0 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 which accompanies + * this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +@org.eclipse.jdt.annotation.NonNullByDefault +package org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows; diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/summarytimeline/Otf2SummaryTimelineStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/summarytimeline/Otf2SummaryTimelineStateProvider.java index f985a0de..25d844cd 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/summarytimeline/Otf2SummaryTimelineStateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.core/src/org/eclipse/tracecompass/incubator/internal/otf2/core/analysis/summarytimeline/Otf2SummaryTimelineStateProvider.java @@ -218,7 +218,5 @@ protected void processOtf2Event(ITmfEvent event, String name, ITmfStateSystemBui default: break; } - } - } diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/META-INF/MANIFEST.MF b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/META-INF/MANIFEST.MF index fcde3086..368c6d04 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/META-INF/MANIFEST.MF +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/META-INF/MANIFEST.MF @@ -20,6 +20,7 @@ Require-Bundle: org.eclipse.ui, Export-Package: org.eclipse.tracecompass.incubator.internal.otf2.ui;x-internal:=true, org.eclipse.tracecompass.incubator.internal.otf2.ui.views;x-internal:=true, org.eclipse.tracecompass.incubator.internal.otf2.ui.views.communicators;x-internal:=true, + org.eclipse.tracecompass.incubator.internal.otf2.ui.views.flows;x-internal:=true, org.eclipse.tracecompass.incubator.internal.otf2.ui.views.summarytimeline;x-internal:=true Import-Package: com.google.common.cache, com.google.common.collect, diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.properties b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.properties index 3688ad3c..159b3456 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.properties +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.properties @@ -16,4 +16,5 @@ otf2.name = OTF2 otf2.communicators = MPI communicators state otf2.view.communicators = MPI communicators state otf2.view.summarytimeline = Summary Timeline -otf2.perspective = OTF2 \ No newline at end of file +otf2.view.flows = Flows +otf2.perspective = OTF2 diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.xml index 9aa43ceb..daa331f9 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/plugin.xml @@ -33,6 +33,22 @@ restorable="true"> + + + + + + + + + + + + \ No newline at end of file diff --git a/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/src/org/eclipse/tracecompass/incubator/internal/otf2/ui/views/flows/Otf2FlowsView.java b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/src/org/eclipse/tracecompass/incubator/internal/otf2/ui/views/flows/Otf2FlowsView.java new file mode 100644 index 00000000..d17ef4aa --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.otf2.ui/src/org/eclipse/tracecompass/incubator/internal/otf2/ui/views/flows/Otf2FlowsView.java @@ -0,0 +1,58 @@ +/********************************************************************** + * Copyright (c) 2022 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.otf2.ui.views.flows; + +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.tracecompass.incubator.internal.otf2.core.analysis.flows.Otf2FlowsDataProvider; +import org.eclipse.tracecompass.incubator.internal.otf2.ui.views.AbstractOtf2View; +import org.eclipse.tracecompass.internal.provisional.tmf.ui.widgets.timegraph.BaseDataProviderTimeGraphPresentationProvider; +import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer; +import org.eclipse.ui.IWorkbenchActionConstants; + +/** + * View for OTF2 flows + * + * @author Yoann Heitz + */ +@SuppressWarnings("restriction") +public class Otf2FlowsView extends AbstractOtf2View { + + /** View ID suffix */ + public static final String ID_SUFFIX = "flows"; //$NON-NLS-1$ + + /** + * Constructor + */ + public Otf2FlowsView() { + super(ID_SUFFIX, new BaseDataProviderTimeGraphPresentationProvider(), Otf2FlowsDataProvider.getFullDataProviderId()); + setAutoExpandLevel(1); + } + + @Override + protected void fillLocalToolBar(IToolBarManager manager) { + TimeGraphViewer timeGraphViewer = getTimeGraphViewer(); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getResetScaleAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getPreviousEventAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getNextEventAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getToggleBookmarkAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getPreviousMarkerAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getNextMarkerAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getPreviousItemAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getNextItemAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getZoomInAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, timeGraphViewer.getZoomOutAction()); + manager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator()); + } +}