trace-event: Fix 's', 't', 'f' semantics

According to the specification, flow events should not create stack
elements, but should rather be represented as arrows only.

This also adds unit tests on the flow_simple.json trace, to test the
flow events, as well as the complete events.

[fixed] Semantics of trace-event's flow events 's', 't', 'f'

Change-Id: I83f4ede7e9610071f81f7755f7b266c2c2be6aa2
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/139865
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
diff --git a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/EdgeStateValue.java b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/EdgeStateValue.java
index 2a7834c..3c70f34 100644
--- a/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/EdgeStateValue.java
+++ b/callstack/org.eclipse.tracecompass.incubator.callstack.core/src/org/eclipse/tracecompass/incubator/callstack/core/base/EdgeStateValue.java
@@ -11,8 +11,10 @@
 
 import java.nio.charset.Charset;
 import java.util.Comparator;
+import java.util.Objects;
 
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread;
 import org.eclipse.tracecompass.datastore.core.serialization.ISafeByteBufferReader;
 import org.eclipse.tracecompass.datastore.core.serialization.ISafeByteBufferWriter;
@@ -143,4 +145,18 @@
         return "Edge: id=" + fId + ", src=" + fSrc + ", dst=" + fDst; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(fId, fSrc, fDst);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof EdgeStateValue)) {
+            return false;
+        }
+        EdgeStateValue other = (EdgeStateValue) obj;
+        return fId == other.fId && Objects.equals(fSrc, other.fSrc) && Objects.equals(fDst, other.fDst);
+    }
+
 }
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedArrows.csv b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedArrows.csv
index 8a301ff..d140fe5 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedArrows.csv
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedArrows.csv
@@ -1,9 +1,12 @@
-47423153299497000,22182006000,tracecompassLog_small.json,45,tracecompassLog_small.json,51,13
-47423122170646000,260607242000,tracecompassLog_small.json,41,tracecompassLog_small.json,1,10
-47423401876687000,533516000,tracecompassLog_small.json,1,tracecompassLog_small.json,52,15
-47423345114376000,244272754000,tracecompassLog_small.json,28,tracecompassLog_small.json,1,14
-47423590805667000,453954000,tracecompassLog_small.json,1,tracecompassLog_small.json,53,17
-47423423942912000,193832881000,tracecompassLog_small.json,52,tracecompassLog_small.json,1,16
-47423636489198000,426576000,tracecompassLog_small.json,1,tracecompassLog_small.json,54,19
-47423598025237000,39022261000,tracecompassLog_small.json,53,tracecompassLog_small.json,1,18
-47423641550298000,12285314000,tracecompassLog_small.json,54,tracecompassLog_small.json,1,20
\ No newline at end of file
+445191683049142,445191691275706,tracecompassLog_small.json,1,tracecompassLog_small.json,1,1
+445191691275706,445191978125275,tracecompassLog_small.json,1,tracecompassLog_small.json,56,1
+445191978125275,445192914875731,tracecompassLog_small.json,56,tracecompassLog_small.json,1,1
+445192911356651,445192916956912,tracecompassLog_small.json,1,tracecompassLog_small.json,61,2
+445193246795523,445193259486235,tracecompassLog_small.json,1,tracecompassLog_small.json,63,8
+445193285642579,445194650975677,tracecompassLog_small.json,65,tracecompassLog_small.json,1,11
+445194657000113,445194657664985,tracecompassLog_small.json,1,tracecompassLog_small.json,80,26
+445194761580682,445194765420215,tracecompassLog_small.json,1,tracecompassLog_small.json,81,30
+445194768310173,445194828778593,tracecompassLog_small.json,81,tracecompassLog_small.json,1,31
+445196995227272,445196995941296,tracecompassLog_small.json,1,tracecompassLog_small.json,85,32
+445197033644206,445197034601783,tracecompassLog_small.json,85,tracecompassLog_small.json,1,35
+445198031706607,445198267590034,tracecompassLog_small.json,1,tracecompassLog_small.json,1,36
\ No newline at end of file
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedScopeArrows.csv b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedScopeArrows.csv
new file mode 100644
index 0000000..922c4af
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/resources/expectedScopeArrows.csv
@@ -0,0 +1,6 @@
+445191683049143,445191978125275,flow_various.json,1,flow_various.json,56,1
+445191683049142,445191978125276,flow_various.json,1,flow_various.json,56,1
+445191683049144,445191978125277,flow_various.json,1,flow_various.json,56,1
+445191978125277,445192823456607,flow_various.json,56,flow_various.json,1,1
+445191978125275,445192823456608,flow_various.json,56,flow_various.json,1,1
+445191978125276,445192823456609,flow_various.json,56,flow_various.json,1,1
\ No newline at end of file
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/CallStackStateProviderTest.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/CallStackStateProviderTest.java
index a1974c9..7e5cc5e 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/CallStackStateProviderTest.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/CallStackStateProviderTest.java
@@ -12,11 +12,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.File;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread;
+import org.eclipse.tracecompass.incubator.callstack.core.base.EdgeStateValue;
 import org.eclipse.tracecompass.incubator.internal.traceevent.core.analysis.callstack.TraceEventCallstackAnalysis;
 import org.eclipse.tracecompass.incubator.internal.traceevent.core.trace.TraceEventTrace;
 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
@@ -28,6 +31,7 @@
 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
 import org.eclipse.tracecompass.tmf.core.tests.shared.TmfTestHelper;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.junit.After;
 import org.junit.Test;
 
@@ -42,6 +46,14 @@
 
     private ITmfTrace fTrace;
 
+    private static void deleteSuppFiles(@NonNull ITmfTrace trace) {
+        /* Remove supplementary files */
+        File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace));
+        for (File file : suppDir.listFiles()) {
+            file.delete();
+        }
+    }
+
     /**
      * Dispose the trace used in this test
      */
@@ -49,6 +61,7 @@
     public void disposeTrace() {
         ITmfTrace trace = fTrace;
         if (trace != null) {
+            deleteSuppFiles(trace);
             trace.dispose();
         }
     }
@@ -111,6 +124,50 @@
         }
     }
 
+    /**
+     * Test the 's', 'f' and 'X' events
+     *
+     * @throws Exception
+     *             Exception thrown by initialization
+     */
+    @Test
+    public void testEventsXsf() throws Exception {
+        TraceEventCallstackAnalysis csModule = getTraceEventModule("traces/flow_simple.json");
+        try {
+            ITmfStateSystem ss = csModule.getStateSystem();
+            assertNotNull(ss);
+            Set<@NonNull IntervalInfo> intervalInfos = new HashSet<>();
+            // First process, request bar
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 9999, "SenderB"),
+                    new StateIntervalStub(10000, 3331000, (Object) null)),
+                    "Processes", "15903", "15904", "CallStack", "1"));
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 1000999, (Object) null),
+                    new StateIntervalStub(1001000, 1100999, "Blergh"),
+                    new StateIntervalStub(1101000, 3331000, (Object) null)),
+                    "Processes", "15875", "15895", "CallStack", "1"));
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 3001999, (Object) null),
+                    new StateIntervalStub(3002000, 3011999, "SenderA"),
+                    new StateIntervalStub(3012000, 3331000, (Object) null)),
+                    "Processes", "15902", "15903", "CallStack", "1"));
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 3199999, (Object) null),
+                    new StateIntervalStub(3200000, 3331000, "OtherSlice")),
+                    "Processes", "15774", "15794", "CallStack", "1"));
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 3330999, (Object) null),
+                    new StateIntervalStub(3331000, 3331000, "SomeSlice")),
+                    "Processes", "15874", "15894", "CallStack", "1"));
+            intervalInfos.add(new IntervalInfo(ImmutableList.of(new StateIntervalStub(0, 999, (Object) null),
+                    new StateIntervalStub(1000, 1000999, new EdgeStateValue(-1, new HostThread(fTrace.getHostId(), 15904), new HostThread(fTrace.getHostId(), 15895))),
+                    new StateIntervalStub(1001000, 3001999, (Object) null),
+                    new StateIntervalStub(3002000, 3219999, new EdgeStateValue(-1, new HostThread(fTrace.getHostId(), 15903), new HostThread(fTrace.getHostId(), 15794))),
+                    new StateIntervalStub(3220000, 3329999, new EdgeStateValue(-1, new HostThread(fTrace.getHostId(), 15794), new HostThread(fTrace.getHostId(), 15894))),
+                    new StateIntervalStub(3330000, 3331000, (Object) null)),
+                    "EDGES", "0"));
+            StateSystemTestUtils.testIntervals(ss, intervalInfos);
+        } finally {
+            csModule.dispose();
+        }
+    }
+
     private @NonNull TraceEventCallstackAnalysis getTraceEventModule(String path) throws Exception {
         TraceEventTrace trace = new TraceEventTrace();
         TraceEventCallstackAnalysis csModule = null;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/InstrumentedCallStackArrowsTest.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/InstrumentedCallStackArrowsTest.java
index 9f252ef..e57feae 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/InstrumentedCallStackArrowsTest.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/src/org/eclipse/tracecompass/incubator/traceevent/core/tests/InstrumentedCallStackArrowsTest.java
@@ -45,6 +45,11 @@
     private static final String TRACE_PATH = "traces/tracecompassLog_small.json";
 
     /**
+     * Trace compass trace with a small number (9) of arrows.
+     */
+    private static final String TRACE_PATH_SCOPE = "traces/flow_various.json";
+
+    /**
      * Test that the edges returned by the {@link TraceEventCallstackAnalysis} are
      * the expected ones.
      *
@@ -86,6 +91,49 @@
         }
     }
 
+    /**
+     * Test that the edges returned by the {@link TraceEventCallstackAnalysis}
+     * are the expected ones for a trace with events that have scope, cat and
+     * id, or missing flow events
+     *
+     * @throws TmfTraceException
+     *             If we couldn't open the trace
+     * @throws TmfAnalysisException
+     *             This exception should be thrown if the trace is set more than
+     *             once
+     * @throws IOException
+     *             if an I/O error occurs reading from the file or a malformed
+     *             or unmappable byte sequence is read
+     */
+    @Test
+    public void testArrowsWithScopes() throws TmfTraceException, TmfAnalysisException, IOException {
+        TraceEventTrace trace = new TraceEventTrace();
+        TraceEventCallstackAnalysis analysis = new TraceEventCallstackAnalysis();
+        try {
+            trace.initTrace(null, TRACE_PATH_SCOPE, TmfEvent.class);
+
+            /*
+             * Overcome the default start time which is at Long.MIN_VALUE
+             */
+            ITmfContext ctx = trace.seekEvent(0L);
+            trace.getNext(ctx);
+
+            assertTrue(analysis.setTrace(trace));
+            analysis.schedule();
+            assertTrue(analysis.waitForCompletion());
+
+            ITmfStateSystem ss = analysis.getStateSystem();
+            assertNotNull(ss);
+
+            List<@NonNull ITmfStateInterval> actual = analysis.getLinks(ss.getStartTime(), ss.getCurrentEndTime(), new NullProgressMonitor());
+
+            assertEqualsEdges("resources/expectedScopeArrows.csv", actual);
+        } finally {
+            analysis.dispose();
+            trace.dispose();
+        }
+    }
+
     private static void assertEqualsEdges(String path, List<@NonNull ITmfStateInterval> actual) throws IOException {
         List<String> expectedStrings = Files.readAllLines(Paths.get(path));
         /*
@@ -96,26 +144,30 @@
 
         Iterator<String> stringIterator = expectedStrings.iterator();
         Iterator<ITmfStateInterval> intervalIterator = actual.iterator();
+        int i = 0;
         while (stringIterator.hasNext() && intervalIterator.hasNext()) {
             String[] split = stringIterator.next().split(",");
             ITmfStateInterval interval = intervalIterator.next();
 
-            assertEquals("Wrong start time", Long.parseLong(split[0]), interval.getStartTime());
-            assertEquals("Wrong duration", Long.parseLong(split[1]), interval.getEndTime() - interval.getStartTime() + 1);
+            long start = Long.parseLong(split[0]);
+            long end = Long.parseLong(split[1]);
+            assertEquals("Wrong start time for arrow " + i, start, interval.getStartTime());
+            assertEquals("Wrong duration for arrow " + i, end - start, interval.getEndTime() - interval.getStartTime() + 1);
 
             Object value = interval.getValue();
             assertTrue(value instanceof EdgeStateValue);
             EdgeStateValue edge = (EdgeStateValue) value;
 
-            assertEquals("Wrong source host", split[2], edge.getSource().getHost());
+            assertEquals("Wrong source host for arrow " + i, split[2], edge.getSource().getHost());
             Integer srcTid = Integer.parseInt(split[3]);
-            assertEquals("Wrong source TID", srcTid, edge.getSource().getTid());
+            assertEquals("Wrong source TID for arrow " + i, srcTid, edge.getSource().getTid());
 
-            assertEquals("Wrong destination host", Objects.requireNonNull(split[4]), edge.getDestination().getHost());
+            assertEquals("Wrong destination host for arrow " + i, Objects.requireNonNull(split[4]), edge.getDestination().getHost());
             Integer dstTid = Integer.parseInt(split[5]);
-            assertEquals("Wrong destination TID", dstTid, edge.getDestination().getTid());
+            assertEquals("Wrong destination TID for arrow " + i, dstTid, edge.getDestination().getTid());
 
-            assertEquals("Wrong edge id", Integer.parseInt(split[6]), edge.getId());
+            assertEquals("Wrong edge id for arrow " + i, Integer.parseInt(split[6]), edge.getId());
+            i++;
         }
     }
 
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/flow_various.json b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/flow_various.json
new file mode 100644
index 0000000..9dc3194
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/flow_various.json
@@ -0,0 +1,18 @@
+[{"ts":"445191683049.142","ph":"B","tid":1,"pid":1,"name":"reopenTraceFromElement"},
+{"ts":"445191683049.142","ph":"s","tid":1,"pid":1,"name":"reopenTraceFromElement","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445191683049.143","ph":"s","tid":1,"pid":1,"name":"reopenTraceFromElement","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope"},
+{"ts":"445191683049.144","ph":"s","tid":1,"pid":1,"name":"reopenTraceFromElement","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope2"},
+{"ts":"445191977985.942","ph":"E","tid":1,"pid":1},
+{"ts":"445191978125.275","ph":"B","tid":56,"pid":56,"name":"createThread"},
+{"ts":"445191978125.275","ph":"t","tid":56,"pid":56,"name":"createThread","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope"},
+{"ts":"445191978125.276","ph":"t","tid":56,"pid":56,"name":"createThread","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445191978125.277","ph":"t","tid":56,"pid":56,"name":"createThread","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope2"},
+{"ts":"445192819861.607","ph":"E","tid":56,"pid":56},
+{"ts":"445192823456.607","ph":"B","tid":1,"pid":1,"name":"doSomethingElse"},
+{"ts":"445192823456.607","ph":"f","tid":1,"pid":1,"name":"doSomethingElse","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope2"},
+{"ts":"445192823456.608","ph":"f","tid":1,"pid":1,"name":"doSomethingElse","cat":"TmfOpenTraceHelper","id":"0x1","scope":"myScope"},
+{"ts":"445192823456.609","ph":"f","tid":1,"pid":1,"name":"doSomethingElse","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445192823567.609","ph":"E","tid":1,"pid":1},
+{"ts":"445192834567.609","ph":"B","tid":1,"pid":1,"name":"doSomethingElse"},
+{"ts":"445192834567.609","ph":"t","tid":1,"pid":1,"name":"flowIsAlreadyFinished","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445192834568.609","ph":"E","tid":1,"pid":1}]
\ No newline at end of file
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/tracecompassLog_small.json b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/tracecompassLog_small.json
index 53be6da..c4b41c6 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/tracecompassLog_small.json
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core.tests/traces/tracecompassLog_small.json
@@ -1,43 +1,82 @@
-[{"ts":47423094048937,"ph":"s","tid":28,"name":"TimeGraphView:BuildThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x7","args":{"trace":"src/main/resources/bug446190"}},
-{"ts":47423098030507,"ph":"s","tid":28,"name":"TmfAbstractAnalysis:scheduling","cat":"org.eclipse.tracecompass.analysis.os.linux.kernel","id":"0x9","args":{"name":"Linux Kernel"}},
-{"ts":47423098360942,"ph":"f","tid":28,"cat":"org.eclipse.tracecompass.analysis.os.linux.kernel","id":"0x9"},
-{"ts":47423122170646,"ph":"s","tid":41,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xa"},
-{"ts":47423122448445,"ph":"f","tid":41,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xa"},
-{"ts":47423153299497,"ph":"s","tid":45,"name":"RequestExecutor:CreatingThread","cat":"RequestExecutor","id":"0xd"},
-{"ts":47423153918562,"ph":"f","tid":45,"cat":"RequestExecutor","id":"0xd"},
-{"ts":47423156920722,"ph":"f","tid":1,"cat":"TmfOpenTraceHelper","id":"0x1"},
-{"ts":47423175481503,"ph":"s","tid":51,"name":"RequestExecutor:RunningRequest","cat":"RequestExecutor","id":"0xd","args":{"thread":"null","execution type":"FOREGROUND"}},
-{"ts":47423345114376,"ph":"s","tid":28,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xe"},
-{"ts":47423345531559,"ph":"f","tid":28,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xe"},
-{"ts":47423345726920,"ph":"f","tid":28,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x7"},
-{"ts":47423350486368,"ph":"f","tid":51,"cat":"RequestExecutor","id":"0xd"},
-{"ts":47423376541180,"ph":"C","tid":42,"name":"src/main/resources/bug446190","args":{"indexed":0}},
-{"ts":47423382777888,"ph":"s","tid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xa"},
-{"ts":47423401876687,"ph":"s","tid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xf"},
-{"ts":47423402296508,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xf"},
-{"ts":47423402410203,"ph":"s","tid":52,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xf","args":{"start":1412670961211260539,"end":1412670961311260539}},
-{"ts":47423402428566,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xa"},
-{"ts":47423423942912,"ph":"s","tid":52,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x10"},
-{"ts":47423424158544,"ph":"f","tid":52,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x10"},
-{"ts":47423424251654,"ph":"f","tid":52,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xf"},
-{"ts":47423589387130,"ph":"s","tid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xe"},
-{"ts":47423590805667,"ph":"s","tid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x11"},
-{"ts":47423591176414,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x11"},
-{"ts":47423591259621,"ph":"s","tid":53,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x11","args":{"start":1412670961211260539,"end":1412670961311260539}},
-{"ts":47423591310705,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0xe"},
-{"ts":47423598025237,"ph":"s","tid":53,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x12"},
-{"ts":47423598287195,"ph":"f","tid":53,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x12"},
-{"ts":47423598419245,"ph":"f","tid":53,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x11"},
-{"ts":47423617775793,"ph":"s","tid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x10"},
-{"ts":47423619418418,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x10"},
-{"ts":47423627814179,"ph":"C","tid":42,"name":"src/main/resources/bug446190","args":{"indexed":0}},
-{"ts":47423636489198,"ph":"s","tid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x13"},
-{"ts":47423636837566,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x13"},
-{"ts":47423636915774,"ph":"s","tid":54,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x13","args":{"start":1412670961211260539,"end":1412670961311260539}},
-{"ts":47423637047498,"ph":"s","tid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x12"},
-{"ts":47423637684997,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x12"},
-{"ts":47423641550298,"ph":"s","tid":54,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x14"},
-{"ts":47423641879677,"ph":"f","tid":54,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x14"},
-{"ts":47423641972012,"ph":"f","tid":54,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x13"},
-{"ts":47423653835612,"ph":"s","tid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x14"},
-{"ts":47423654550730,"ph":"f","tid":1,"cat":"org.eclipse.tracecompass.analysis.os.linux.views.controlflow","id":"0x14"}]
+[{"ts":"445191683049.142","ph":"B","tid":1,"pid":1,"name":"reopenTraceFromElement"},
+{"ts":"445191683049.142","ph":"s","tid":1,"pid":1,"name":"reopenTraceFromElement","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445191691275.706","ph":"B","tid":1,"pid":1,"name":"createBookmarks"},
+{"ts":"445191691275.706","ph":"t","tid":1,"pid":1,"name":"createBookmarks","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445191975501.045","ph":"E","tid":1,"pid":1},
+{"ts":"445191977985.942","ph":"E","tid":1,"pid":1},
+{"ts":"445191978125.275","ph":"B","tid":56,"pid":56,"name":"createThread"},
+{"ts":"445191978125.275","ph":"t","tid":56,"pid":56,"name":"createThread","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445192819861.607","ph":"E","tid":56,"pid":56},
+{"ts":"445192911356.651","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated"},
+{"ts":"445192911356.651","ph":"s","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x2"},
+{"ts":"445192913778.77","ph":"E","tid":1,"pid":1},
+{"ts":"445192914875.731","ph":"B","tid":1,"pid":1,"name":"OpenEditor"},
+{"ts":"445192914875.731","ph":"t","tid":1,"pid":1,"name":"OpenEditor","cat":"TmfOpenTraceHelper","id":"0x1"},
+{"ts":"445192916956.912","ph":"B","tid":61,"pid":61,"name":"TimeGraphView:ZoomThread","args":{"start":-1,"end":-1}},
+{"ts":"445192916956.912","ph":"t","tid":61,"pid":61,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x2","args":{"start":-1,"end":-1}},
+{"ts":"445192944933.137","ph":"B","tid":61,"pid":61,"name":"RefreshRequested"},
+{"ts":"445192944933.137","ph":"s","tid":61,"pid":61,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x3"},
+{"ts":"445192946468.014","ph":"E","tid":61,"pid":61},
+{"ts":"445192946688.748","ph":"E","tid":61,"pid":61},
+{"ts":"445193245079.232","ph":"i","tid":1,"pid":1,"name":"TimeGraphView:LoadingTrace","args":{"trace":"TraceCompassTutorialTraces/103-compare-package-managers/pacman","viewId":"org.eclipse.tracecompass.analysis.os.linux.views.resources"}},
+{"ts":"445193246795.523","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:Rebuilding"},
+{"ts":"445193246795.523","ph":"s","tid":1,"pid":1,"name":"TimeGraphView:Rebuilding","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x8"},
+{"ts":"445193247050.083","ph":"B","tid":1,"pid":1,"name":"RefreshRequested"},
+{"ts":"445193247050.083","ph":"s","tid":1,"pid":1,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x9"},
+{"ts":"445193247240.118","ph":"E","tid":1,"pid":1},
+{"ts":"445193255221.168","ph":"E","tid":1,"pid":1},
+{"ts":"445193259486.235","ph":"B","tid":63,"pid":63,"name":"TimeGraphView:BuildThread","args":{"trace":"TraceCompassTutorialTraces/103-compare-package-managers/pacman"}},
+{"ts":"445193259486.235","ph":"t","tid":63,"pid":63,"name":"TimeGraphView:BuildThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x8","args":{"trace":"TraceCompassTutorialTraces/103-compare-package-managers/pacman"}},
+{"ts":"445193285642.579","ph":"B","tid":65,"pid":65,"name":"RefreshRequested"},
+{"ts":"445193285642.579","ph":"s","tid":65,"pid":65,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0xb"},
+{"ts":"445193285948.179","ph":"E","tid":65,"pid":65},
+{"ts":"445193487939.169","ph":"E","tid":1,"pid":1},
+{"ts":"445194139753.734","ph":"B","tid":63,"pid":63,"name":"RefreshRequested"},
+{"ts":"445194139753.734","ph":"s","tid":63,"pid":63,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x17"},
+{"ts":"445194140213.705","ph":"E","tid":63,"pid":63},
+{"ts":"445194140393.16","ph":"E","tid":63,"pid":63},
+{"ts":"445194525832.687","ph":"B","tid":78,"pid":78,"name":"RefreshRequested"},
+{"ts":"445194525832.687","ph":"s","tid":78,"pid":78,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x19"},
+{"ts":"445194526225.012","ph":"E","tid":78,"pid":78},
+{"ts":"445194650975.677","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:Refresh"},
+{"ts":"445194650975.677","ph":"t","tid":1,"pid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0xb"},
+{"ts":"445194657000.113","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated"},
+{"ts":"445194657000.113","ph":"s","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1a"},
+{"ts":"445194657541.815","ph":"E","tid":1,"pid":1},
+{"ts":"445194657660.324","ph":"E","tid":1,"pid":1},
+{"ts":"445194657664.985","ph":"B","tid":80,"pid":80,"name":"TimeGraphView:ZoomThread","args":{"start":1539786952342493550,"end":1539786952442493550}},
+{"ts":"445194657664.985","ph":"t","tid":80,"pid":80,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1a","args":{"start":1539786952342493550,"end":1539786952442493550}},
+{"ts":"445194701322.62","ph":"B","tid":80,"pid":80,"name":"RefreshRequested"},
+{"ts":"445194701322.62","ph":"s","tid":80,"pid":80,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1d"},
+{"ts":"445194701614.465","ph":"E","tid":80,"pid":80},
+{"ts":"445194701707.068","ph":"E","tid":80,"pid":80},
+{"ts":"445194761580.682","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated"},
+{"ts":"445194761580.682","ph":"s","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1e"},
+{"ts":"445194765279.868","ph":"E","tid":1,"pid":1},
+{"ts":"445194765420.215","ph":"B","tid":81,"pid":81,"name":"TimeGraphView:ZoomThread","args":{"start":1539786952342493550,"end":1539786952442493550}},
+{"ts":"445194765420.215","ph":"t","tid":81,"pid":81,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1e","args":{"start":1539786952342493550,"end":1539786952442493550}},
+{"ts":"445194768310.173","ph":"B","tid":81,"pid":81,"name":"RefreshRequested"},
+{"ts":"445194768310.173","ph":"s","tid":81,"pid":81,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1f"},
+{"ts":"445194768621.605","ph":"E","tid":81,"pid":81},
+{"ts":"445194768715.691","ph":"E","tid":81,"pid":81},
+{"ts":"445194828778.593","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:Refresh"},
+{"ts":"445194828778.593","ph":"t","tid":1,"pid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x1f"},
+{"ts":"445194830391.092","ph":"E","tid":1,"pid":1},
+{"ts":"445196995227.272","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated"},
+{"ts":"445196995227.272","ph":"s","tid":1,"pid":1,"name":"TimeGraphView:ZoomThreadCreated","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x20"},
+{"ts":"445196995814.512","ph":"E","tid":1,"pid":1},
+{"ts":"445196995941.296","ph":"B","tid":85,"pid":85,"name":"TimeGraphView:ZoomThread","args":{"start":1539786952342493550,"end":1539786952794811137}},
+{"ts":"445196995941.296","ph":"t","tid":85,"pid":85,"name":"TimeGraphView:ZoomThread","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x20","args":{"start":1539786952342493550,"end":1539786952794811137}},
+{"ts":"445197033644.206","ph":"B","tid":85,"pid":85,"name":"RefreshRequested"},
+{"ts":"445197033644.206","ph":"s","tid":85,"pid":85,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x23"},
+{"ts":"445197034063.714","ph":"E","tid":85,"pid":85},
+{"ts":"445197034162.455","ph":"E","tid":85,"pid":85},
+{"ts":"445197034601.783","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:Refresh"},
+{"ts":"445197034601.783","ph":"t","tid":1,"pid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x23"},
+{"ts":"445197035554.368","ph":"E","tid":1,"pid":1},
+{"ts":"445198031706.607","ph":"B","tid":1,"pid":1,"name":"RefreshRequested"},
+{"ts":"445198031706.607","ph":"s","tid":1,"pid":1,"name":"RefreshRequested","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x24"},
+{"ts":"445198032200.912","ph":"E","tid":1,"pid":1},
+{"ts":"445198267590.034","ph":"B","tid":1,"pid":1,"name":"TimeGraphView:Refresh"},
+{"ts":"445198267590.034","ph":"t","tid":1,"pid":1,"name":"TimeGraphView:Refresh","cat":"org.eclipse.tracecompass.analysis.os.linux.views.resources","id":"0x24"},
+{"ts":"445198269104.085","ph":"E","tid":1,"pid":1}]
\ No newline at end of file
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/analysis/callstack/TraceEventCallStackProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/analysis/callstack/TraceEventCallStackProvider.java
index 7537625..edb0ec2 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/analysis/callstack/TraceEventCallStackProvider.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/analysis/callstack/TraceEventCallStackProvider.java
@@ -9,8 +9,6 @@
 
 package org.eclipse.tracecompass.incubator.internal.traceevent.core.analysis.callstack;
 
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
@@ -37,6 +35,10 @@
 import org.eclipse.tracecompass.incubator.internal.traceevent.core.event.TraceEventField;
 import org.eclipse.tracecompass.incubator.internal.traceevent.core.event.TraceEventPhases;
 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
 import org.eclipse.tracecompass.tmf.core.statesystem.TmfAttributePool;
@@ -54,13 +56,13 @@
  */
 public class TraceEventCallStackProvider extends CallStackStateProvider {
 
-    private static final int VERSION_NUMBER = 6;
+    private static final int VERSION_NUMBER = 7;
     private static final int UNSET_ID = -1;
     static final String EDGES = "EDGES"; //$NON-NLS-1$
 
-    private static final Function<String, Integer> FUNCTION = s -> {
+    private static final Function<EventTreeKey, Integer> FUNCTION = s -> {
         try {
-            return Integer.decode(s);
+            return Integer.decode(s.fId);
         } catch (NumberFormatException e) {
             return UNSET_ID;
         }
@@ -75,18 +77,61 @@
 
     private final ITmfEventAspect<?> fIdAspect;
 
+    private final ITmfEventAspect<?> fCatAspect;
+
     /**
      * Map of trace event scope ID string to their start times
      */
-    private final Map<String, Long> fEdgeStartTimes = new HashMap<>();
+    private final Map<EventTreeKey, Long> fEdgeStartTimes = new HashMap<>();
     /**
      * Map of trace event scope ID string to the source {@link HostThread} of the edge.
      */
-    private final Map<String, HostThread> fEdgeSrcHosts = new HashMap<>();
+    private final Map<EventTreeKey, HostThread> fEdgeSrcHosts = new HashMap<>();
     /**
      * Cache of trace event scope ID string to their parsed values
      */
-    private final Map<String, Integer> fIdCache = new HashMap<>();
+    private final Map<EventTreeKey, Integer> fIdCache = new HashMap<>();
+
+    private static class EventTreeKey {
+        private final String fCategory;
+        private final String fId;
+        private final @Nullable String fScope;
+
+        public EventTreeKey(String category, String id) {
+            fCategory = category;
+            fId = id;
+            fScope = null;
+        }
+
+        public EventTreeKey(String category, String id, String scope) {
+            fCategory = category;
+            fId = id;
+            fScope = scope;
+        }
+
+        @Override
+        public String toString() {
+            return fCategory + ':' + fId + (fScope == null ? "" : ':' + fScope); //$NON-NLS-1$
+        }
+
+        @Override
+        public boolean equals(Object arg0) {
+            if (!(arg0 instanceof EventTreeKey)) {
+                return false;
+            }
+            EventTreeKey other = (EventTreeKey) arg0;
+            boolean catAndIdEqual = Objects.equals(fCategory, other.fCategory) && Objects.equals(fId, other.fId);
+            if (catAndIdEqual && fScope != null && other.fScope != null) {
+                return Objects.equals(fScope, other.fScope);
+            }
+            return catAndIdEqual;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(fCategory, fId, fScope);
+        }
+    }
 
     /**
      * Constructor
@@ -103,6 +148,7 @@
         }
         fSafeTime = trace.getStartTime();
         fIdAspect = TraceEventAspects.ID_ASPECT;
+        fCatAspect = TraceEventAspects.CATEGORY_ASPECT;
     }
 
     @Override
@@ -181,7 +227,7 @@
 
     private static boolean isEntry(ITmfEvent event) {
         String phase = event.getContent().getFieldValue(String.class, ITraceEventConstants.PHASE);
-        return TraceEventPhases.NESTABLE_START.equals(phase) || TraceEventPhases.DURATION_START.equals(phase) || TraceEventPhases.FLOW_START.equals(phase);
+        return TraceEventPhases.NESTABLE_START.equals(phase) || TraceEventPhases.DURATION_START.equals(phase);
     }
 
     @Override
@@ -194,7 +240,7 @@
 
     private static boolean isExit(ITmfEvent event) {
         String phase = event.getContent().getFieldValue(String.class, ITraceEventConstants.PHASE);
-        return TraceEventPhases.NESTABLE_END.equals(phase) || TraceEventPhases.DURATION_END.equals(phase) || TraceEventPhases.FLOW_END.equals(phase);
+        return TraceEventPhases.NESTABLE_END.equals(phase) || TraceEventPhases.DURATION_END.equals(phase);
     }
 
     @Override
@@ -218,11 +264,6 @@
             handleStart(event, ss, timestamp, processName);
             break;
 
-        case TraceEventPhases.FLOW_START:
-            handleStart(event, ss, timestamp, processName);
-            updateSLinks(event);
-            break;
-
         case TraceEventPhases.DURATION:
             Number duration = event.getContent().getFieldValue(Number.class, ITraceEventConstants.DURATION);
             if (duration != null) {
@@ -235,9 +276,16 @@
             handleEnd(event, ss, timestamp, processName);
             break;
 
+        case TraceEventPhases.FLOW_START:
+            updateSLinks(event, ss, timestamp, processName);
+            break;
+
+        case TraceEventPhases.FLOW_STEP:
+            updateTLinks(event, ss, timestamp, processName);
+            break;
+
         case TraceEventPhases.FLOW_END:
-            handleEnd(event, ss, timestamp, processName);
-            updateFLinks(event);
+            updateFLinks(event, ss, timestamp, processName);
             break;
         default:
             return;
@@ -260,12 +308,7 @@
         }
     }
 
-    private void updateFLinks(ITmfEvent event) {
-        String id = event.getContent().getFieldValue(String.class, ITraceEventConstants.ID);
-        fEdgeStartTimes.putIfAbsent(id, event.getTimestamp().toNanos());
-    }
-
-    private void updateSLinks(ITmfEvent event) {
+    private EventTreeKey getEventTreeKey(ITmfEvent event) {
         String sId = event.getContent().getFieldValue(String.class, ITraceEventConstants.ID);
         if (sId == null) {
             Object resolve = fIdAspect.resolve(event);
@@ -274,27 +317,126 @@
             }
             sId = String.valueOf(resolve);
         }
+        String sCat = event.getContent().getFieldValue(String.class, ITraceEventConstants.CATEGORY);
+        if (sCat == null) {
+            Object resolve = fCatAspect.resolve(event);
+            if (resolve == null) {
+                resolve = Integer.valueOf(0);
+            }
+            sCat = String.valueOf(resolve);
+        }
+        String sScope = event.getContent().getFieldValue(String.class, ITraceEventConstants.SCOPE);
+
+        return (sScope == null) ? new EventTreeKey(sCat, sId) : new EventTreeKey(sCat, sId, sScope);
+
+    }
+
+    private void updateFLinks(ITmfEvent event, ITmfStateSystemBuilder ss, long ts, String processName) {
+        EventTreeKey key = getEventTreeKey(event);
+
+        String bindingPoint = event.getContent().getFieldValue(String.class, ITraceEventConstants.BINDING_POINT);
+        if (bindingPoint == null) {
+            bindingPoint = "n"; //$NON-NLS-1$
+        }
+
+        int tid = (int) getThreadId(event);
+
+        Long startTime = fEdgeStartTimes.get(key);
+        if (startTime == null) {
+            // We don't have the starting point of the flow, ignore
+            return;
+        }
+
+        HostThread srcHostThread = fEdgeSrcHosts.remove(key);
+        HostThread currHostThread = new HostThread(event.getTrace().getHostId(), tid);
+
+        // If binding point is "enclosing slice", make sure there is such a slice
+        if (bindingPoint == "e" && !validateEnclosingSlice(event, ss, ts, processName, tid)) { //$NON-NLS-1$
+            return;
+        }
+
+        if (srcHostThread != null) {
+            int edgeQuark = getAvailableEdgeQuark(ss, startTime);
+
+            Object edgeStateValue = new EdgeStateValue(fIdCache.computeIfAbsent(key, FUNCTION), srcHostThread, currHostThread);
+            ss.modifyAttribute(startTime, edgeStateValue, edgeQuark);
+            ss.modifyAttribute(ts, (Object) null, edgeQuark);
+        }
+    }
+
+    private void updateTLinks(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long ts, String processName) {
+        EventTreeKey key = getEventTreeKey(event);
+
+        int tid = (int) getThreadId(event);
+
+        Long startTime = fEdgeStartTimes.get(key);
+        if (startTime == null) {
+            // We don't have the starting point of the flow, ignore
+            return;
+        }
+
+        HostThread srcHostThread = fEdgeSrcHosts.remove(key);
+        HostThread currHostThread = new HostThread(event.getTrace().getHostId(), tid);
+
+        // Scope is "enclosing slice", make sure there is such a slice
+        if (!validateEnclosingSlice(event, ss, ts, processName, tid)) {
+            return;
+        }
+
+        if (srcHostThread != null) {
+            int edgeQuark = getAvailableEdgeQuark(ss, startTime);
+
+            Object edgeStateValue = new EdgeStateValue(fIdCache.computeIfAbsent(key, FUNCTION), srcHostThread, currHostThread);
+            ss.modifyAttribute(startTime, edgeStateValue, edgeQuark);
+            ss.modifyAttribute(ts, (Object) null, edgeQuark);
+
+        }
+        // update data for next edge
+        fEdgeStartTimes.put(key, ts);
+        fEdgeSrcHosts.put(key, currHostThread);
+    }
+
+    private boolean validateEnclosingSlice(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long ts, String processName, int tid) {
+        String threadName = getThreadName(event);
+        if (threadName == null) {
+            threadName = Long.toString(tid);
+        }
+        int callstackQuark = ss.optQuarkAbsolute(PROCESSES, processName, threadName, InstrumentedCallStackAnalysis.CALL_STACK);
+        if (callstackQuark < 0) {
+            // No callstack for this thread, no enclosing slice
+            return false;
+        }
+        try {
+            ITmfStateInterval interval = StateSystemUtils.querySingleStackTop(ss, ts, callstackQuark);
+            if (interval == null || interval.getValue() == null) {
+                // Nothing on the top of stack, so no enclosing slice
+                return false;
+            }
+        } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+            // Other problem, no enclosing slice
+            return false;
+        }
+        return true;
+    }
+
+    private void updateSLinks(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long ts, String processName) {
+        EventTreeKey key = getEventTreeKey(event);
+
         int tid = (int) getThreadId(event);
         ITmfStateSystemBuilder ssb = getStateSystemBuilder();
         if (ssb == null) {
             return;
         }
 
-        long ts = event.getTimestamp().toNanos();
-        long startTime = fEdgeStartTimes.getOrDefault(sId, ts);
-
-        HostThread srcHostThread = fEdgeSrcHosts.remove(sId);
         HostThread currHostThread = new HostThread(event.getTrace().getHostId(), tid);
-        if (srcHostThread != null) {
-            int edgeQuark = getAvailableEdgeQuark(ssb, startTime);
 
-            Object edgeStateValue = new EdgeStateValue(fIdCache.computeIfAbsent(sId, FUNCTION), srcHostThread, currHostThread);
-            ssb.modifyAttribute(startTime, edgeStateValue, edgeQuark);
-            ssb.modifyAttribute(ts, (Object) null, edgeQuark);
-
+        // Scope is "enclosing slice", make sure there is such a slice
+        if (!validateEnclosingSlice(event, ss, ts, processName, tid)) {
+            return;
         }
-        fEdgeStartTimes.put(sId, ts);
-        fEdgeSrcHosts.put(sId, currHostThread);
+
+        fEdgeStartTimes.put(key, ts);
+        fEdgeSrcHosts.put(key, currHostThread);
     }
 
     /**
@@ -317,7 +459,7 @@
         for (int quark : subQuarks) {
             long start = ssb.getOngoingStartTime(quark);
             Object value = ssb.queryOngoing(quark);
-            if (value == null && start < startTime) {
+            if (value == null && start <= startTime) {
                 return quark;
             }
         }
@@ -431,7 +573,7 @@
         long end = startTime;
         Number duration = event.getContent().getFieldValue(Number.class, ITraceEventConstants.DURATION);
         if (duration != null) {
-            end += Math.max(duration.longValue() - 1, 0);
+            end += Math.max(duration.longValue(), 0);
         }
         String threadName = getThreadName(event);
         long threadId = getThreadId(event);
@@ -447,16 +589,4 @@
         stack.push(end);
     }
 
-    @Override
-    public void done() {
-        ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
-        for (Entry<Integer, Deque<Long>> stackEntry : fStack.entrySet()) {
-            Deque<Long> stack = stackEntry.getValue();
-            while (!stack.isEmpty()) {
-                ss.popAttribute(stack.pop(), stackEntry.getKey());
-            }
-        }
-        super.done();
-    }
-
 }
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/ITraceEventConstants.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/ITraceEventConstants.java
index 0fbc790..258ea8d 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/ITraceEventConstants.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/ITraceEventConstants.java
@@ -55,5 +55,13 @@
      * Arguments field name
      */
     String ARGS = "args"; //$NON-NLS-1$
+    /**
+     * Binding point field for flow events
+     */
+    String BINDING_POINT = "bp"; //$NON-NLS-1$
+    /**
+     * An optional scope for some event types, to differentiate same ids
+     */
+    String SCOPE = "scope"; //$NON-NLS-1$
 
 }
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventAspects.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventAspects.java
index e3dab7e..e777aeb 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventAspects.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventAspects.java
@@ -40,7 +40,12 @@
     public static final ITmfEventAspect<String> ID_ASPECT = new TraceCompassScopeLogIdAspect();
 
     /**
-     * Apects of a trace
+     * A category aspect, can be used to identify flows.
+     */
+    public static final ITmfEventAspect<String> CATEGORY_ASPECT = new TraceCompassScopeLogCategoryAspect();
+
+    /**
+     * Aspects of a trace
      */
     private static Iterable<@NonNull ITmfEventAspect<?>> aspects;
 
@@ -60,7 +65,7 @@
                     new TraceCompassScopeLogLevel(),
                     new TraceCompassScopeLogTidAspect(),
                     new TraceCompassScopeLogPidAspect(),
-                    new TraceCompassScopeLogCategoryAspect(),
+                    CATEGORY_ASPECT,
                     new TraceCompassScopeLogDurationAspect(),
                     ID_ASPECT,
                     ARGS_ASPECT,
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventField.java b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventField.java
index 558ea1f..a58032c 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventField.java
+++ b/tracetypes/org.eclipse.tracecompass.incubator.traceevent.core/src/org/eclipse/tracecompass/incubator/internal/traceevent/core/event/TraceEventField.java
@@ -101,6 +101,7 @@
         }
         String category = optString(root, ITraceEventConstants.CATEGORY);
         String id = optString(root, ITraceEventConstants.ID);
+        String scope = optString(root, ITraceEventConstants.SCOPE);
         JsonObject args = optJSONObject(root, ITraceEventConstants.ARGS);
         if (args != null) {
             for (Entry<String, JsonElement> entry : args.entrySet()) {
@@ -128,6 +129,9 @@
         if (id != null) {
             argsMap.put(ITraceEventConstants.ID, id);
         }
+        if (scope != null) {
+            argsMap.put(ITraceEventConstants.SCOPE, scope);
+        }
         return new TraceEventField(name, ts, phase, pid, tid, category, id, duration, argsMap);
     }