| /******************************************************************************* |
| * Copyright (c) 2015 É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.internal.analysis.graph.core.criticalpath; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.ArrayDeque; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.analysis.graph.core.base.IGraphWorker; |
| import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge; |
| import org.eclipse.tracecompass.analysis.graph.core.base.TmfGraph; |
| import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex; |
| import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex.EdgeDirection; |
| import org.eclipse.tracecompass.analysis.graph.core.criticalpath.CriticalPathAlgorithmException; |
| |
| /** |
| * Critical path bounded algorithm: backward resolution of blocking limited to |
| * the blocking window |
| * |
| * This algorithm is described in |
| * |
| * F. Giraldeau and M.Dagenais, Wait analysis of distributed systems using |
| * kernel tracing, IEEE Transactions on Parallel and Distributed Systems |
| * |
| * @author Francis Giraldeau |
| */ |
| public class CriticalPathAlgorithmBounded extends AbstractCriticalPathAlgorithm { |
| |
| /** |
| * Constructor |
| * |
| * @param graph |
| * The graph on which to calculate the critical path |
| */ |
| public CriticalPathAlgorithmBounded(TmfGraph graph) { |
| super(graph); |
| } |
| |
| @Override |
| public TmfGraph compute(TmfVertex start, @Nullable TmfVertex end) throws CriticalPathAlgorithmException { |
| /* Create new graph for the critical path result */ |
| TmfGraph criticalPath = new TmfGraph(); |
| |
| /* Get the main graph from which to get critical path */ |
| TmfGraph graph = getGraph(); |
| |
| /* |
| * Calculate path starting from the object the start vertex belongs to |
| */ |
| IGraphWorker parent = checkNotNull(graph.getParentOf(start)); |
| criticalPath.add(parent, new TmfVertex(start)); |
| TmfVertex currentVertex = start; |
| TmfEdge nextEdge = currentVertex.getEdge(EdgeDirection.OUTGOING_HORIZONTAL_EDGE); |
| |
| long endTime = Long.MAX_VALUE; |
| if (end != null) { |
| endTime = end.getTs(); |
| } |
| |
| /* |
| * Run through all horizontal edges from this object and resolve each |
| * blocking as they come |
| */ |
| while (nextEdge != null) { |
| TmfVertex nextVertex = nextEdge.getVertexTo(); |
| if (nextVertex.getTs() >= endTime) { |
| break; |
| } |
| switch (nextEdge.getType()) { |
| case IPI: |
| case USER_INPUT: |
| case BLOCK_DEVICE: |
| case TIMER: |
| case INTERRUPTED: |
| case PREEMPTED: |
| case RUNNING: |
| case UNKNOWN: |
| /** |
| * This edge is not blocked, so nothing to resolve, just add the |
| * edge to the critical path |
| */ |
| /** |
| * TODO: Normally, the parent of the link's vertex to should be |
| * the object itself, verify if that is true |
| */ |
| IGraphWorker parentTo = checkNotNull(graph.getParentOf(nextEdge.getVertexTo())); |
| if (parentTo != parent) { |
| throw new CriticalPathAlgorithmException("no, the parents of horizontal edges are not always identical... shouldn't they be?"); //$NON-NLS-1$ |
| } |
| criticalPath.append(parentTo, new TmfVertex(nextEdge.getVertexTo()), nextEdge.getType(), nextEdge.getLinkQualifier()); |
| break; |
| case NETWORK: |
| case BLOCKED: |
| List<TmfEdge> links = resolveBlockingBounded(nextEdge, nextEdge.getVertexFrom()); |
| Collections.reverse(links); |
| appendPathComponent(criticalPath, graph, currentVertex, links); |
| break; |
| case EPS: |
| if (nextEdge.getDuration() != 0) { |
| throw new CriticalPathAlgorithmException("epsilon duration is not zero " + nextEdge); //$NON-NLS-1$ |
| } |
| break; |
| case DEFAULT: |
| throw new CriticalPathAlgorithmException("Illegal link type " + nextEdge.getType()); //$NON-NLS-1$ |
| default: |
| break; |
| } |
| currentVertex = nextVertex; |
| nextEdge = currentVertex.getEdge(EdgeDirection.OUTGOING_HORIZONTAL_EDGE); |
| } |
| return criticalPath; |
| } |
| |
| /** Add the links to the critical path, with currentVertex to glue to */ |
| private void appendPathComponent(TmfGraph criticalPath, TmfGraph graph, TmfVertex currentVertex, List<TmfEdge> links) { |
| IGraphWorker currentActor = checkNotNull(graph.getParentOf(currentVertex)); |
| if (links.isEmpty()) { |
| /* |
| * The next vertex should not be null, since we glue only after |
| * resolve of the blocking of the edge to that vertex |
| */ |
| TmfEdge next = currentVertex.getEdge(EdgeDirection.OUTGOING_HORIZONTAL_EDGE); |
| if (next == null) { |
| return; |
| } |
| criticalPath.append(currentActor, new TmfVertex(next.getVertexTo()), next.getType(), next.getLinkQualifier()); |
| return; |
| } |
| // FIXME: assert last link.to actor == currentActor |
| |
| // attach subpath to b1 |
| TmfVertex b1 = checkNotNull(criticalPath.getTail(currentActor)); |
| |
| // glue head |
| TmfEdge lnk = links.get(0); |
| TmfVertex anchor = null; |
| IGraphWorker objSrc = checkNotNull(graph.getParentOf(lnk.getVertexFrom())); |
| if (objSrc.equals( currentActor)) { |
| anchor = b1; |
| } else { |
| anchor = new TmfVertex(currentVertex); |
| criticalPath.add(objSrc, anchor); |
| b1.linkVertical(anchor); |
| /* fill any gap with UNKNOWN */ |
| if (lnk.getVertexFrom().compareTo(anchor) > 0) { |
| anchor = new TmfVertex(lnk.getVertexFrom()); |
| TmfEdge edge = checkNotNull(criticalPath.append(objSrc, anchor)); |
| edge.setType(TmfEdge.EdgeType.UNKNOWN); |
| } |
| } |
| |
| // glue body |
| TmfEdge prev = null; |
| for (TmfEdge link : links) { |
| // check connectivity |
| if (prev != null && prev.getVertexTo() != link.getVertexFrom()) { |
| anchor = copyLink(criticalPath, graph, anchor, prev.getVertexTo(), link.getVertexFrom(), |
| Math.max(prev.getVertexTo().getTs(), link.getVertexFrom().getTs()), |
| TmfEdge.EdgeType.DEFAULT, link.getLinkQualifier()); |
| } |
| anchor = copyLink(criticalPath, graph, anchor, link.getVertexFrom(), link.getVertexTo(), |
| link.getVertexTo().getTs(), link.getType(), link.getLinkQualifier()); |
| prev = link; |
| } |
| } |
| |
| /** |
| * Resolve a blocking by going through the graph vertically from the |
| * blocking edge |
| * |
| * FIXME: build a tree with partial subpath in order to return the best |
| * path, not the last one traversed |
| * |
| * @param blocking |
| * The blocking edge |
| * @param bound |
| * The vertex that limits the boundary until which to resolve the |
| * blocking |
| * @return The list of non-blocking edges |
| */ |
| private List<TmfEdge> resolveBlockingBounded(TmfEdge blocking, TmfVertex bound) { |
| |
| LinkedList<TmfEdge> subPath = new LinkedList<>(); |
| TmfVertex junction = findIncoming(blocking.getVertexTo(), EdgeDirection.OUTGOING_HORIZONTAL_EDGE); |
| /* if wake-up source is not found, return empty list */ |
| if (junction == null) { |
| return subPath; |
| } |
| |
| TmfEdge down = checkNotNull(junction.getEdge(EdgeDirection.INCOMING_VERTICAL_EDGE)); |
| subPath.add(down); |
| TmfVertex vertexFrom = down.getVertexFrom(); |
| |
| TmfVertex currentBound = bound.compareTo(blocking.getVertexFrom()) < 0 ? blocking.getVertexFrom() : bound; |
| |
| Deque<TmfVertex> stack = new ArrayDeque<>(); |
| while (vertexFrom != null && vertexFrom.compareTo(currentBound) > 0) { |
| /* shortcut for down link that goes beyond the blocking */ |
| TmfEdge inVerticalEdge = vertexFrom.getEdge(EdgeDirection.INCOMING_VERTICAL_EDGE); |
| if (inVerticalEdge != null && inVerticalEdge.getVertexFrom().compareTo(currentBound) <= 0) { |
| subPath.add(inVerticalEdge); |
| break; |
| } |
| |
| /** |
| * Add DOWN links to explore stack in case dead-end occurs. Here is |
| * an example to illustrate the procedure. |
| * |
| * <pre> |
| * ------------------------- |
| * BLOCKED | PREEMPT |
| * ------------------------- |
| * ^ |
| * |WAKE-UP |
| * | |
| * +--------------------+ |
| * +-------+ INTERRUPT +--------+ |
| * +--------------------+ |
| * ^ ^ | ^ |
| * | | | | |
| * + + v + |
| * 1 2 3 4 |
| * </pre> |
| * |
| * The event wake-up is followed backward. The edge 4 will never be |
| * visited (it cannot be the cause of the wake-up, because it occurs |
| * after it). The edge 3 will not be explored, because it is |
| * outgoing. The edges 2 and 1 will be pushed on the stack. When the |
| * beginning of the interrupt is reached, then the edges on the |
| * stack will be explored. |
| * |
| * If a dead-end is reached, while the stack is not empty, the |
| * accumulated path is rewinded, and a different incoming edge is |
| * tried. The backward traversal ends if there is nothing left to |
| * explore, or if the start of the blocking window start is reached. |
| * |
| * Do not add if left is BLOCKED, because this link would be visited |
| * twice |
| */ |
| TmfEdge incomingEdge = vertexFrom.getEdge(EdgeDirection.INCOMING_HORIZONTAL_EDGE); |
| if (inVerticalEdge != null && |
| (incomingEdge == null || |
| (incomingEdge.getType() != TmfEdge.EdgeType.BLOCKED && |
| incomingEdge.getType() != TmfEdge.EdgeType.NETWORK))) { |
| stack.addFirst(vertexFrom); |
| } |
| if (incomingEdge != null) { |
| if (incomingEdge.getType() == TmfEdge.EdgeType.BLOCKED || incomingEdge.getType() == TmfEdge.EdgeType.NETWORK) { |
| List<TmfEdge> blockings = resolveBlockingBounded(incomingEdge, currentBound); |
| if (blockings.isEmpty() && incomingEdge.getType() == TmfEdge.EdgeType.NETWORK) { |
| // There's no explanation for the blocking, keep this |
| // edge if it's network, let the algorithm stitch this |
| // otherwise |
| subPath.add(incomingEdge); |
| } else { |
| subPath.addAll(blockings); |
| } |
| } else { |
| subPath.add(incomingEdge); |
| } |
| vertexFrom = incomingEdge.getVertexFrom(); |
| } else { |
| if (!stack.isEmpty()) { |
| TmfVertex v = stack.removeFirst(); |
| /* rewind subpath */ |
| while (!subPath.isEmpty() && subPath.getLast().getVertexFrom() != v) { |
| subPath.removeLast(); |
| } |
| TmfEdge edge = v.getEdge(EdgeDirection.INCOMING_VERTICAL_EDGE); |
| if (edge != null) { |
| subPath.add(edge); |
| vertexFrom = edge.getVertexFrom(); |
| continue; |
| } |
| } |
| vertexFrom = null; |
| } |
| |
| } |
| return subPath; |
| } |
| |
| } |