trace_processor: Add slices from ftrace traces

- Refactor slice tracking code from json_trace_parser
  into slice_tracker.
- Introduce Print parsing code in proto_trace_parser.
- Add tests for slice tracking code.

Change-Id: Iec14860f45e1eb36a884ef1ddd91ba35d2e91132
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 910b601..811443f 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -63,6 +63,8 @@
     "scoped_db.h",
     "slice_table.cc",
     "slice_table.h",
+    "slice_tracker.cc",
+    "slice_tracker.h",
     "sqlite_utils.h",
     "string_table.cc",
     "string_table.h",
@@ -136,6 +138,7 @@
     "query_constraints_unittest.cc",
     "sched_slice_table_unittest.cc",
     "sched_tracker_unittest.cc",
+    "slice_tracker_unittest.cc",
     "thread_table_unittest.cc",
     "trace_sorter_unittest.cc",
   ]
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 81e419c..e7a8002 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -26,6 +26,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/utils.h"
 #include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) || \
@@ -106,7 +107,7 @@
 
   ProcessTracker* procs = context_->process_tracker.get();
   TraceStorage* storage = context_->storage.get();
-  TraceStorage::NestableSlices* slices = storage->mutable_nestable_slices();
+  SliceTracker* slice_tracker = context_->slice_tracker.get();
 
   while (next < end) {
     Json::Value value;
@@ -127,42 +128,19 @@
     StringId cat_id = storage->InternString(cat);
     StringId name_id = storage->InternString(name);
     UniqueTid utid = procs->UpdateThread(tid, pid);
-    SlicesStack& stack = threads_[utid];
-
-    auto add_slice = [slices, &stack, utid, cat_id,
-                      name_id](const Slice& slice) {
-      if (stack.size() >= std::numeric_limits<uint8_t>::max())
-        return;
-      const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;
-      uint64_t parent_stack_id, stack_id;
-      std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);
-      slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid,
-                       cat_id, name_id, depth, stack_id, parent_stack_id);
-    };
 
     switch (phase) {
       case 'B': {  // TRACE_EVENT_BEGIN.
-        MaybeCloseStack(ts, stack);
-        stack.emplace_back(Slice{cat_id, name_id, ts, 0});
+        slice_tracker->Begin(ts, utid, cat_id, name_id);
         break;
       }
       case 'E': {  // TRACE_EVENT_END.
-        PERFETTO_CHECK(!stack.empty());
-        MaybeCloseStack(ts, stack);
-        PERFETTO_CHECK(stack.back().cat_id == cat_id);
-        PERFETTO_CHECK(stack.back().name_id == name_id);
-        Slice& slice = stack.back();
-        slice.end_ts = slice.start_ts;
-        add_slice(slice);
-        stack.pop_back();
+        slice_tracker->End(ts, utid, cat_id, name_id);
         break;
       }
       case 'X': {  // TRACE_EVENT (scoped event).
-        MaybeCloseStack(ts, stack);
-        uint64_t end_ts = ts + value["dur"].asUInt() * 1000;
-        stack.emplace_back(Slice{cat_id, name_id, ts, end_ts});
-        Slice& slice = stack.back();
-        add_slice(slice);
+        uint64_t duration = value["dur"].asUInt() * 1000;
+        slice_tracker->Scoped(ts, utid, cat_id, name_id, duration);
         break;
       }
       case 'M': {  // Metadata events (process and thread names).
@@ -178,54 +156,11 @@
         }
       }
     }
-    // TODO(primiano): auto-close B slices left open at the end.
   }
   offset_ += static_cast<uint64_t>(next - buf);
   buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf));
   return true;
 }
 
-void JsonTraceParser::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
-  bool check_only = false;
-  for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
-    const Slice& slice = stack[size_t(i)];
-    if (slice.end_ts == 0) {
-      check_only = true;
-    }
-
-    if (check_only) {
-      PERFETTO_DCHECK(ts >= slice.start_ts);
-      PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
-      continue;
-    }
-
-    if (slice.end_ts <= ts) {
-      stack.pop_back();
-    }
-  }
-}
-
-// Returns <parent_stack_id, stack_id>, where
-// |parent_stack_id| == hash(stack_id - last slice).
-std::tuple<uint64_t, uint64_t> JsonTraceParser::GetStackHashes(
-    const SlicesStack& stack) {
-  PERFETTO_DCHECK(!stack.empty());
-  std::string s;
-  s.reserve(stack.size() * sizeof(uint64_t) * 2);
-  constexpr uint64_t kMask = uint64_t(-1) >> 1;
-  uint64_t parent_stack_id = 0;
-  for (size_t i = 0; i < stack.size(); i++) {
-    if (i == stack.size() - 1)
-      parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
-    const Slice& slice = stack[i];
-    s.append(reinterpret_cast<const char*>(&slice.cat_id),
-             sizeof(slice.cat_id));
-    s.append(reinterpret_cast<const char*>(&slice.name_id),
-             sizeof(slice.name_id));
-  }
-  uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
-  return std::make_tuple(parent_stack_id, stack_id);
-}
-
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h
index c51fbba..b0e39ec 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -44,22 +44,9 @@
   bool Parse(std::unique_ptr<uint8_t[]>, size_t) override;
 
  private:
-  struct Slice {
-    StringId cat_id;
-    StringId name_id;
-    uint64_t start_ts;
-    uint64_t end_ts;  // Only for complete events (scoped TRACE_EVENT macros).
-  };
-  using SlicesStack = std::vector<Slice>;
-
-  static inline void MaybeCloseStack(uint64_t end_ts, SlicesStack&);
-  static inline std::tuple<uint64_t, uint64_t> GetStackHashes(
-      const SlicesStack&);
-
   TraceProcessorContext* const context_;
   uint64_t offset_ = 0;
   std::vector<char> buffer_;
-  std::unordered_map<UniqueTid, SlicesStack> threads_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index fc10f26..731e15c 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -112,6 +112,14 @@
   return upid;
 }
 
+UniquePid ProcessTracker::UpdateProcess(uint32_t pid) {
+  UniquePid upid;
+  TraceStorage::Process* process;
+  std::tie(upid, process) = GetOrCreateProcess(pid, 0 /* start_ns */);
+  UpdateThread(/*tid=*/pid, pid);  // Create an entry for the main thread.
+  return upid;
+}
+
 std::tuple<UniquePid, TraceStorage::Process*>
 ProcessTracker::GetOrCreateProcess(uint32_t pid, uint64_t start_ns) {
   auto pids_pair = pids_.equal_range(pid);
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h
index 5bd4d2b..87d67cf 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/process_tracker.h
@@ -67,6 +67,11 @@
   // Virtual for testing.
   virtual UniquePid UpdateProcess(uint32_t pid, base::StringView name);
 
+  // Called when a process is seen in a process tree. Retrieves the UniquePid
+  // for that pid or assigns a new one.
+  // Virtual for testing.
+  virtual UniquePid UpdateProcess(uint32_t pid);
+
   // Returns the bounds of a range that includes all UniquePids that have the
   // requested pid.
   UniqueProcessBounds UpidsForPid(uint32_t pid) {
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index b3d3dda..8cf1101 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -24,6 +24,7 @@
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/sched_tracker.h"
+#include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 
 #include "perfetto/trace/trace.pb.h"
@@ -32,6 +33,53 @@
 namespace perfetto {
 namespace trace_processor {
 
+// We have to handle trace_marker events of a few different types:
+// 1. some random text
+// 2. B|1636|pokeUserActivity
+// 3. E|1636
+// 4. C|1636|wq:monitor|0
+bool ParseSystraceTracePoint(base::StringView str, SystraceTracePoint* out) {
+  // THIS char* IS NOT NULL TERMINATED.
+  const char* s = str.data();
+  size_t len = str.size();
+
+  // If str matches '[BEC]\|[0-9]+[\|\n]' set pid_length to the length of
+  // the number. Otherwise return false.
+  if (len < 3 || s[1] != '|')
+    return false;
+  if (s[0] != 'B' && s[0] != 'E' && s[0] != 'C')
+    return false;
+  size_t pid_length;
+  for (size_t i = 2;; i++) {
+    if (i >= len)
+      return false;
+    if (s[i] == '|' || s[i] == '\n') {
+      pid_length = i - 2;
+      break;
+    }
+    if (s[i] < '0' || s[i] > '9')
+      return false;
+  }
+
+  std::string pid_str(s + 2, pid_length);
+  out->pid = static_cast<uint32_t>(std::stoi(pid_str.c_str()));
+
+  out->phase = s[0];
+  switch (s[0]) {
+    case 'B': {
+      size_t name_index = 2 + pid_length + 1;
+      out->name = base::StringView(s + name_index, len - name_index);
+      return true;
+    }
+    case 'E':
+      return true;
+    case 'C':
+      return true;
+    default:
+      return false;
+  }
+}
+
 using protozero::ProtoDecoder;
 using protozero::proto_utils::kFieldTypeLengthDelimited;
 
@@ -140,6 +188,12 @@
         ParseCpuFreq(timestamp, ftrace.slice(fld_off, fld.size()));
         break;
       }
+      case protos::FtraceEvent::kPrintFieldNumber: {
+        PERFETTO_DCHECK(timestamp > 0);
+        const size_t fld_off = ftrace.offset_of(fld.data());
+        ParsePrint(cpu, timestamp, ftrace.slice(fld_off, fld.size()));
+        break;
+      }
       default:
         break;
     }
@@ -200,5 +254,39 @@
   PERFETTO_DCHECK(decoder.IsEndOfBuffer());
 }
 
+void ProtoTraceParser::ParsePrint(uint32_t,
+                                  uint64_t timestamp,
+                                  TraceBlobView print) {
+  ProtoDecoder decoder(print.data(), print.length());
+
+  base::StringView buf{};
+  for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+    if (fld.id == protos::PrintFtraceEvent::kBufFieldNumber) {
+      buf = fld.as_string();
+      break;
+    }
+  }
+
+  SystraceTracePoint point{};
+  if (!ParseSystraceTracePoint(buf, &point))
+    return;
+
+  UniquePid upid = context_->process_tracker->UpdateProcess(point.pid);
+
+  switch (point.phase) {
+    case 'B': {
+      StringId name_id = context_->storage->InternString(point.name);
+      context_->slice_tracker->Begin(timestamp, upid, 0 /*cat_id*/, name_id);
+      break;
+    }
+
+    case 'E': {
+      context_->slice_tracker->End(timestamp, upid);
+      break;
+    }
+  }
+  PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/proto_trace_parser.h b/src/trace_processor/proto_trace_parser.h
index 2130beb..be3bc40 100644
--- a/src/trace_processor/proto_trace_parser.h
+++ b/src/trace_processor/proto_trace_parser.h
@@ -20,13 +20,34 @@
 #include <stdint.h>
 #include <memory>
 
+#include "perfetto/base/string_view.h"
 #include "src/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 class TraceProcessorContext;
 
+struct SystraceTracePoint {
+  char phase;
+  uint32_t pid;
+
+  // For phase = 'B' and phase = 'C' only.
+  base::StringView name;
+
+  // For phase = 'C' only.
+  int64_t value;
+};
+
+inline bool operator==(const SystraceTracePoint& x,
+                       const SystraceTracePoint& y) {
+  return std::tie(x.phase, x.pid, x.name, x.value) ==
+         std::tie(y.phase, y.pid, y.name, y.value);
+}
+
+bool ParseSystraceTracePoint(base::StringView, SystraceTracePoint* out);
+
 class ProtoTraceParser {
  public:
   explicit ProtoTraceParser(TraceProcessorContext*);
@@ -40,6 +61,7 @@
   void ParseProcessTree(TraceBlobView);
   void ParseSchedSwitch(uint32_t cpu, uint64_t timestamp, TraceBlobView);
   void ParseCpuFreq(uint64_t timestamp, TraceBlobView);
+  void ParsePrint(uint32_t cpu, uint64_t timestamp, TraceBlobView);
   void ParseThread(TraceBlobView);
   void ParseProcess(TraceBlobView);
 
diff --git a/src/trace_processor/proto_trace_parser_unittest.cc b/src/trace_processor/proto_trace_parser_unittest.cc
index cb1c254..965fc5b 100644
--- a/src/trace_processor/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/proto_trace_parser_unittest.cc
@@ -18,6 +18,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "perfetto/base/string_view.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/proto_trace_parser.h"
 #include "src/trace_processor/sched_tracker.h"
@@ -283,6 +284,15 @@
   Tokenize(trace);
 }
 
+TEST(SystraceParserTest, SystraceEvent) {
+  SystraceTracePoint result{};
+  ASSERT_TRUE(ParseSystraceTracePoint(base::StringView("B|1|foo"), &result));
+  EXPECT_EQ(result, (SystraceTracePoint{'B', 1, base::StringView("foo"), 0}));
+
+  ASSERT_TRUE(ParseSystraceTracePoint(base::StringView("B|42|Bar"), &result));
+  EXPECT_EQ(result, (SystraceTracePoint{'B', 42, base::StringView("Bar"), 0}));
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/slice_tracker.cc b/src/trace_processor/slice_tracker.cc
new file mode 100644
index 0000000..db2a17d
--- /dev/null
+++ b/src/trace_processor/slice_tracker.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include "src/trace_processor/slice_tracker.h"
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+SliceTracker::SliceTracker(TraceProcessorContext* context)
+    : context_(context) {}
+
+SliceTracker::~SliceTracker() = default;
+
+void SliceTracker::Begin(uint64_t timestamp,
+                         UniqueTid utid,
+                         StringId cat,
+                         StringId name) {
+  auto& stack = threads_[utid];
+  MaybeCloseStack(timestamp, stack);
+  stack.emplace_back(Slice{cat, name, timestamp, 0});
+}
+
+void SliceTracker::Scoped(uint64_t timestamp,
+                          UniqueTid utid,
+                          StringId cat,
+                          StringId name,
+                          uint64_t duration) {
+  auto& stack = threads_[utid];
+  MaybeCloseStack(timestamp, stack);
+  stack.emplace_back(Slice{cat, name, timestamp, timestamp + duration});
+  CompleteSlice(utid);
+}
+
+void SliceTracker::End(uint64_t timestamp,
+                       UniqueTid utid,
+                       StringId cat,
+                       StringId name) {
+  auto& stack = threads_[utid];
+  MaybeCloseStack(timestamp, stack);
+  PERFETTO_CHECK(!stack.empty());
+
+  PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat);
+  PERFETTO_CHECK(name == 0 || stack.back().name_id == name);
+
+  Slice& slice = stack.back();
+  slice.end_ts = timestamp;
+
+  CompleteSlice(utid);
+  // TODO(primiano): auto-close B slices left open at the end.
+}
+
+void SliceTracker::CompleteSlice(UniqueTid utid) {
+  auto& stack = threads_[utid];
+  if (stack.size() >= std::numeric_limits<uint8_t>::max()) {
+    stack.pop_back();
+    return;
+  }
+  const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;
+
+  uint64_t parent_stack_id, stack_id;
+  std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);
+
+  Slice& slice = stack.back();
+  auto* slices = context_->storage->mutable_nestable_slices();
+  slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, 0,
+                   slice.name_id, depth, stack_id, parent_stack_id);
+
+  stack.pop_back();
+}
+
+void SliceTracker::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
+  bool check_only = false;
+  for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
+    const Slice& slice = stack[size_t(i)];
+    if (slice.end_ts == 0) {
+      check_only = true;
+    }
+
+    if (check_only) {
+      PERFETTO_DCHECK(ts >= slice.start_ts);
+      PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
+      continue;
+    }
+
+    if (slice.end_ts <= ts) {
+      stack.pop_back();
+    }
+  }
+}
+
+// Returns <parent_stack_id, stack_id>, where
+// |parent_stack_id| == hash(stack_id - last slice).
+std::tuple<uint64_t, uint64_t> SliceTracker::GetStackHashes(
+    const SlicesStack& stack) {
+  PERFETTO_DCHECK(!stack.empty());
+  std::string s;
+  s.reserve(stack.size() * sizeof(uint64_t) * 2);
+  constexpr uint64_t kMask = uint64_t(-1) >> 1;
+  uint64_t parent_stack_id = 0;
+  for (size_t i = 0; i < stack.size(); i++) {
+    if (i == stack.size() - 1)
+      parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
+    const Slice& slice = stack[i];
+    s.append(reinterpret_cast<const char*>(&slice.cat_id),
+             sizeof(slice.cat_id));
+    s.append(reinterpret_cast<const char*>(&slice.name_id),
+             sizeof(slice.name_id));
+  }
+  uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
+  return std::make_tuple(parent_stack_id, stack_id);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/slice_tracker.h b/src/trace_processor/slice_tracker.h
new file mode 100644
index 0000000..5197527
--- /dev/null
+++ b/src/trace_processor/slice_tracker.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
+
+#include <stdint.h>
+
+#include "src/trace_processor/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class SliceTracker {
+ public:
+  explicit SliceTracker(TraceProcessorContext*);
+  ~SliceTracker();
+
+  void Begin(uint64_t timestamp, UniqueTid utid, StringId cat, StringId name);
+
+  void Scoped(uint64_t timestamp,
+              UniqueTid utid,
+              StringId cat,
+              StringId name,
+              uint64_t duration);
+
+  void End(uint64_t timestamp,
+           UniqueTid utid,
+           StringId opt_cat = {},
+           StringId opt_name = {});
+
+ private:
+  struct Slice {
+    StringId cat_id;
+    StringId name_id;
+    uint64_t start_ts;
+    uint64_t end_ts;  // Only for complete events (scoped TRACE_EVENT macros).
+  };
+  using SlicesStack = std::vector<Slice>;
+
+  static inline void MaybeCloseStack(uint64_t end_ts, SlicesStack&);
+  static inline std::tuple<uint64_t, uint64_t> GetStackHashes(
+      const SlicesStack&);
+  void CompleteSlice(UniqueTid tid);
+
+  TraceProcessorContext* const context_;
+  std::unordered_map<UniqueTid, SlicesStack> threads_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
diff --git a/src/trace_processor/slice_tracker_unittest.cc b/src/trace_processor/slice_tracker_unittest.cc
new file mode 100644
index 0000000..8eb72b9
--- /dev/null
+++ b/src/trace_processor/slice_tracker_unittest.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "src/trace_processor/slice_tracker.h"
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+
+struct SliceInfo {
+  uint64_t start;
+  uint64_t duration;
+
+  bool operator==(const SliceInfo& other) const {
+    return std::tie(start, duration) == std::tie(other.start, other.duration);
+  }
+};
+
+inline void PrintTo(const SliceInfo& info, ::std::ostream* os) {
+  *os << "SliceInfo{" << info.start << ", " << info.duration << "}";
+}
+
+std::vector<SliceInfo> ToSliceInfo(const TraceStorage::NestableSlices& slices) {
+  std::vector<SliceInfo> infos;
+  for (size_t i = 0; i < slices.slice_count(); i++) {
+    infos.emplace_back(SliceInfo{slices.start_ns()[i], slices.durations()[i]});
+  }
+  return infos;
+}
+
+TEST(SliceTrackerTest, OneSliceDetailed) {
+  TraceProcessorContext context;
+  context.storage.reset(new TraceStorage());
+  SliceTracker tracker(&context);
+
+  tracker.Begin(2 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/);
+  tracker.End(10 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/);
+
+  auto slices = context.storage->nestable_slices();
+  EXPECT_EQ(slices.slice_count(), 1);
+  EXPECT_EQ(slices.start_ns()[0], 2);
+  EXPECT_EQ(slices.durations()[0], 8);
+  EXPECT_EQ(slices.cats()[0], 0);
+  EXPECT_EQ(slices.names()[0], 1);
+  EXPECT_EQ(slices.utids()[0], 42);
+  EXPECT_EQ(slices.depths()[0], 0);
+}
+
+TEST(SliceTrackerTest, TwoSliceDetailed) {
+  TraceProcessorContext context;
+  context.storage.reset(new TraceStorage());
+  SliceTracker tracker(&context);
+
+  tracker.Begin(2 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/);
+  tracker.Begin(3 /*ts*/, 42 /*tid*/, 0 /*cat*/, 2 /*name*/);
+  tracker.End(5 /*ts*/, 42 /*tid*/);
+  tracker.End(10 /*ts*/, 42 /*tid*/);
+
+  auto slices = context.storage->nestable_slices();
+
+  EXPECT_EQ(slices.slice_count(), 2);
+
+  EXPECT_EQ(slices.start_ns()[0], 3);
+  EXPECT_EQ(slices.durations()[0], 2);
+  EXPECT_EQ(slices.cats()[0], 0);
+  EXPECT_EQ(slices.names()[0], 2);
+  EXPECT_EQ(slices.utids()[0], 42);
+  EXPECT_EQ(slices.depths()[0], 1);
+
+  EXPECT_EQ(slices.start_ns()[1], 2);
+  EXPECT_EQ(slices.durations()[1], 8);
+  EXPECT_EQ(slices.cats()[1], 0);
+  EXPECT_EQ(slices.names()[1], 1);
+  EXPECT_EQ(slices.utids()[1], 42);
+  EXPECT_EQ(slices.depths()[1], 0);
+
+  EXPECT_EQ(slices.parent_stack_ids()[1], 0);
+  EXPECT_EQ(slices.stack_ids()[1], slices.parent_stack_ids()[0]);
+  EXPECT_NE(slices.stack_ids()[0], 0);
+}
+
+TEST(SliceTrackerTest, Scoped) {
+  TraceProcessorContext context;
+  context.storage.reset(new TraceStorage());
+  SliceTracker tracker(&context);
+
+  tracker.Begin(0 /*ts*/, 42 /*tid*/, 0, 0);
+  tracker.Begin(1 /*ts*/, 42 /*tid*/, 0, 0);
+  tracker.Scoped(2 /*ts*/, 42 /*tid*/, 0, 0, 6);
+  tracker.End(9 /*ts*/, 42 /*tid*/);
+  tracker.End(10 /*ts*/, 42 /*tid*/);
+
+  auto slices = ToSliceInfo(context.storage->nestable_slices());
+  EXPECT_THAT(slices,
+              ElementsAre(SliceInfo{2, 6}, SliceInfo{1, 8}, SliceInfo{0, 10}));
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/trace_processor.cc b/src/trace_processor/trace_processor.cc
index 62b58a1..ddda225 100644
--- a/src/trace_processor/trace_processor.cc
+++ b/src/trace_processor/trace_processor.cc
@@ -28,6 +28,7 @@
 #include "src/trace_processor/sched_slice_table.h"
 #include "src/trace_processor/sched_tracker.h"
 #include "src/trace_processor/slice_table.h"
+#include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/string_table.h"
 #include "src/trace_processor/table.h"
 #include "src/trace_processor/thread_table.h"
@@ -43,6 +44,7 @@
   PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
   db_.reset(std::move(db));
 
+  context_.slice_tracker.reset(new SliceTracker(&context_));
   context_.sched_tracker.reset(new SchedTracker(&context_));
   context_.proto_parser.reset(new ProtoTraceParser(&context_));
   context_.process_tracker.reset(new ProcessTracker(&context_));
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index a26e30c..477ec09 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -20,6 +20,7 @@
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/proto_trace_parser.h"
 #include "src/trace_processor/sched_tracker.h"
+#include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/trace_sorter.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
index 493c39a..f8432c6 100644
--- a/src/trace_processor/trace_processor_context.h
+++ b/src/trace_processor/trace_processor_context.h
@@ -22,6 +22,7 @@
 namespace perfetto {
 namespace trace_processor {
 
+class SliceTracker;
 class ProcessTracker;
 class TraceStorage;
 class SchedTracker;
@@ -35,6 +36,7 @@
   TraceProcessorContext();
   ~TraceProcessorContext();
 
+  std::unique_ptr<SliceTracker> slice_tracker;
   std::unique_ptr<ProcessTracker> process_tracker;
   std::unique_ptr<SchedTracker> sched_tracker;
   std::unique_ptr<TraceStorage> storage;
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 925f307..2fe305d 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -103,7 +103,7 @@
     const std::deque<uint64_t>& cycles() const { return cycles_; }
 
    private:
-    // Each vector below has the same number of entries (the number of slices
+    // Each deque below has the same number of entries (the number of slices
     // in the trace for the CPU).
     std::deque<uint64_t> start_ns_;
     std::deque<uint64_t> durations_;