TraceProcessor: add support for complete JSON events

Will require some follow-up clean ups, but in the meanwhile
brings us into a nice state w.r.t. digesting legacy traces.

This CL:
- Extracts thread and process names from metadata events.
- Creates a full stack of slice using both duration and complete events
  (TRACE_EVENT_BEGIN/END(...) and TRACE_EVENT(...))
- Exposes pids and tid in the process and thread vtables.
- Refactors the ProcessTracker to support identifying threads.
  by the pid+tid tuple.
- Improves the hashing of the string pool using 32-bit FNV-1a.

See https://ghostbin.com/paste/bpvq7 for a sample SQL query.

Change-Id: I567b102e14770e521f60650bcab45cb0f24fb76c
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 6c5cbcc9..47c89f2 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -18,6 +18,8 @@
 
 #include <json/reader.h>
 #include <json/value.h>
+
+#include <limits>
 #include <string>
 
 #include "perfetto/base/build_config.h"
@@ -36,7 +38,7 @@
 namespace trace_processor {
 
 namespace {
-const uint32_t kChunkSize = 65536;
+const uint32_t kChunkSize = 1024 * 512;
 
 // Parses at most one JSON dictionary and returns a pointer to the end of it,
 // or nullptr if no dict could be detected.
@@ -74,6 +76,7 @@
   }
   return nullptr;
 }
+
 }  // namespace
 
 // static
@@ -127,27 +130,104 @@
     StringId cat_id = storage->InternString(cat, strlen(cat));
     StringId name_id = storage->InternString(name, strlen(name));
     UniqueTid utid = procs->UpdateThread(tid, pid);
-    std::vector<Slice>& stack = threads_[utid].stack;
+    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
-        stack.emplace_back(Slice{cat_id, name_id, ts});
+      case 'B': {  // TRACE_EVENT_BEGIN.
+        MaybeCloseStack(ts, stack);
+        stack.emplace_back(Slice{cat_id, name_id, ts, 0});
         break;
-      case 'E':  // TRACE_EVENT_END
-        PERFETTO_CHECK(!stack.empty() && stack.back().cat_id == cat_id &&
-                       stack.back().name_id == name_id);
+      }
+      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();
-        if (stack.size() < 0xff) {
-          slices->AddSlice(slice.start_ts, ts - slice.start_ts, utid, cat_id,
-                           name_id, static_cast<uint8_t>(stack.size()));
-        }
+        slice.end_ts = slice.start_ts;
+        add_slice(slice);
         stack.pop_back();
         break;
+      }
+      case 'X': {  // TRACE_EVENT (scoped event).
+        MaybeCloseStack(ts, stack);
+        uint64_t end_ts = ts + value["dur"].asUInt();
+        stack.emplace_back(Slice{cat_id, name_id, ts, end_ts});
+        Slice& slice = stack.back();
+        add_slice(slice);
+        break;
+      }
+      case 'M': {  // Metadata events (process and thread names).
+        if (strcmp(value["name"].asCString(), "thread_name") == 0) {
+          const char* thread_name = value["args"]["name"].asCString();
+          procs->UpdateThreadName(tid, pid, thread_name, strlen(thread_name));
+          break;
+        }
+        if (strcmp(value["name"].asCString(), "process_name") == 0) {
+          const char* proc_name = value["args"]["name"].asCString();
+          procs->UpdateProcess(pid, proc_name, strlen(proc_name));
+          break;
+        }
+      }
     }
+    // TODO(primiano): auto-close B slices left open at the end.
   }
   offset_ += static_cast<uint64_t>(next - buf);
   return next > buf;
 }
 
+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 98fb6ad..8fc5ef3 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <tuple>
 #include <unordered_map>
 
 #include "src/trace_processor/trace_parser.h"
@@ -51,16 +52,19 @@
     StringId cat_id;
     StringId name_id;
     uint64_t start_ts;
+    uint64_t end_ts;  // Only for complete events (scoped TRACE_EVENT macros).
   };
-  struct ThreadState {
-    std::vector<Slice> stack;
-  };
+  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&);
 
   BlobReader* const reader_;
   TraceProcessorContext* const context_;
   uint64_t offset_ = 0;
   std::unique_ptr<char[]> buffer_;
-  std::unordered_map<UniqueTid, ThreadState> threads_;
+  std::unordered_map<UniqueTid, SlicesStack> threads_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
index dffd4af..471abd0 100644
--- a/src/trace_processor/process_table.cc
+++ b/src/trace_processor/process_table.cc
@@ -36,6 +36,7 @@
                                 "CREATE TABLE process("
                                 "upid UNSIGNED INT, "
                                 "name TEXT, "
+                                "pid UNSIGNED INT, "
                                 "PRIMARY KEY(upid)"
                                 ") WITHOUT ROWID;");
 }
@@ -68,6 +69,11 @@
                           static_cast<int>(name.length()), nullptr);
       break;
     }
+    case Column::kPid: {
+      const auto& process = storage_->GetProcess(upid_filter_.current);
+      sqlite3_result_int64(context, process.pid);
+      break;
+    }
     default:
       PERFETTO_FATAL("Unknown column %d", N);
       break;
diff --git a/src/trace_processor/process_table.h b/src/trace_processor/process_table.h
index 3799e28..64bd1f5 100644
--- a/src/trace_processor/process_table.h
+++ b/src/trace_processor/process_table.h
@@ -30,7 +30,7 @@
 // their details (only name at the moment).
 class ProcessTable : public Table {
  public:
-  enum Column { kUpid = 0, kName = 1 };
+  enum Column { kUpid = 0, kName = 1, kPid = 2 };
 
   static void RegisterTable(sqlite3* db, const TraceStorage* storage);
 
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index 4cdef78..45df50e 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -39,78 +39,95 @@
   }
 
   // If none exist, assign a new utid and store it.
-  UniqueTid new_utid = context_->storage->AddEmptyThread();
+  UniqueTid new_utid = context_->storage->AddEmptyThread(tid);
   TraceStorage::Thread* thread = context_->storage->GetMutableThread(new_utid);
   thread->name_id = thread_name_id;
-  thread->start_ns = timestamp;
+  if (timestamp)
+    thread->start_ns = timestamp;
   tids_.emplace(tid, new_utid);
   return new_utid;
 };
 
-UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t tgid) {
+void ProcessTracker::UpdateThreadName(uint32_t tid,
+                                      uint32_t pid,
+                                      const char* name,
+                                      size_t name_len) {
+  UniqueTid utid = UpdateThread(tid, pid);
+  auto* thread = context_->storage->GetMutableThread(utid);
+  auto name_id = context_->storage->InternString(name, name_len);
+  thread->name_id = name_id;
+}
+
+UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
   auto tids_pair = tids_.equal_range(tid);
 
-  // TODO(b/110409911): Remove once invalidation of threads is implemented.
-  PERFETTO_DCHECK(std::distance(tids_pair.first, tids_pair.second) <= 1);
-
-  UniqueTid utid = 0;
-  // Find matching thread for tid or create new one.
+  // Try looking for a thread that matches both tid and thread group id (pid).
   TraceStorage::Thread* thread = nullptr;
-  if (tids_pair.first == tids_pair.second) {
-    utid = context_->storage->AddEmptyThread();
+  UniqueTid utid = 0;
+  for (auto it = tids_pair.first; it != tids_pair.second; it++) {
+    UniqueTid iter_utid = it->second;
+    auto* iter_thread = context_->storage->GetMutableThread(iter_utid);
+    if (iter_thread->upid == 0) {
+      // We haven't discovered the parent process for the thread. Assign it
+      // now and use this thread.
+      thread = iter_thread;
+      utid = iter_utid;
+      break;
+    }
+    const auto& iter_process = context_->storage->GetProcess(iter_thread->upid);
+    if (iter_process.pid == pid) {
+      // We found a thread that matches both the tid and its parent pid.
+      thread = iter_thread;
+      utid = iter_utid;
+      break;
+    }
+  }  // for(tids).
+
+  // If no matching thread was found, create a new one.
+  if (thread == nullptr) {
+    utid = context_->storage->AddEmptyThread(tid);
     tids_.emplace(tid, utid);
     thread = context_->storage->GetMutableThread(utid);
-  } else {
-    utid = tids_pair.first->second;
-    thread = context_->storage->GetMutableThread(utid);
   }
 
-  // Find matching upid for tgid or create new one.
-  if (thread->upid == 0) {
-    auto pids_pair = pids_.equal_range(tgid);
+  // Find matching process or create new one.
+  if (thread->upid == 0)  // Not set, upid == 0 is invalid.
+    thread->upid = GetOrCreateProcess(pid, thread->start_ns);
 
-    // TODO(b/110409911): Remove once invalidation of threads is implemented.
-    PERFETTO_DCHECK(std::distance(pids_pair.first, pids_pair.second) <= 1);
-
-    TraceStorage::Process* process = nullptr;
-    if (pids_pair.first == pids_pair.second) {
-      UniquePid new_upid = context_->storage->AddEmptyProcess();
-      pids_.emplace(tgid, new_upid);
-      process = context_->storage->GetMutableProcess(new_upid);
-      thread->upid = new_upid;
-    } else {
-      process = context_->storage->GetMutableProcess(pids_pair.first->second);
-      thread->upid = pids_pair.first->second;
-    }
-    if (process->start_ns == 0)
-      process->start_ns = thread->start_ns;
-  }
   return utid;
 }
 
 UniquePid ProcessTracker::UpdateProcess(uint32_t pid,
                                         const char* process_name,
                                         size_t process_name_len) {
-  auto pids_pair = pids_.equal_range(pid);
   auto proc_name_id =
       context_->storage->InternString(process_name, process_name_len);
+  UniquePid upid = GetOrCreateProcess(pid, 0 /* start_ns */);
+  auto* process = context_->storage->GetMutableProcess(upid);
+  process->name_id = proc_name_id;
+  return upid;
+}
 
-  // If a upid exists for the pid, find it and update the name.
-  if (pids_pair.first != pids_pair.second) {
-    auto prev_upid = std::prev(pids_pair.second)->second;
-    TraceStorage::Process* process =
-        context_->storage->GetMutableProcess(prev_upid);
-    process->name_id = proc_name_id;
-    return prev_upid;
+UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid, uint64_t start_ns) {
+  auto pids_pair = pids_.equal_range(pid);
+
+  UniquePid upid = 0;
+  for (auto it = pids_pair.first; it != pids_pair.second; it++) {
+    if (it->first == pid) {
+      upid = it->second;
+      break;
+    }
   }
 
-  // Create a new upid if there isn't one for that pid.
-  UniquePid new_upid = context_->storage->AddEmptyProcess();
-  TraceStorage::Process* process =
-      context_->storage->GetMutableProcess(new_upid);
-  pids_.emplace(pid, new_upid);
-  process->name_id = proc_name_id;
-  return new_upid;
+  if (upid == 0) {
+    upid = context_->storage->AddEmptyProcess(pid);
+    pids_.emplace(pid, upid);
+  }
+
+  auto* process = context_->storage->GetMutableProcess(upid);
+  if (process->start_ns == 0)
+    process->start_ns = start_ns;
+  return upid;
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h
index b087407..651f962 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/process_tracker.h
@@ -56,6 +56,12 @@
   // Virtual for testing.
   virtual UniqueTid UpdateThread(uint32_t tid, uint32_t tgid);
 
+  // Sets the name of the thread identified by the tuple (tid,pid).
+  void UpdateThreadName(uint32_t tid,
+                        uint32_t pid,
+                        const char* name,
+                        size_t name_len);
+
   // Called when a process is seen in a process tree. Retrieves the UniquePid
   // for that pid or assigns a new one.
   // Virtual for testing.
@@ -75,16 +81,18 @@
     return tids_.equal_range(tid);
   }
 
+  UniquePid GetOrCreateProcess(uint32_t pid, uint64_t start_ns);
+
  private:
   TraceProcessorContext* const context_;
 
   // Each tid can have multiple UniqueTid entries, a new UniqueTid is assigned
   // each time a thread is seen in the trace.
-  std::multimap<uint32_t, UniqueTid> tids_;
+  std::multimap<uint32_t /* tid */, UniqueTid> tids_;
 
   // Each pid can have multiple UniquePid entries, a new UniquePid is assigned
   // each time a process is seen in the trace.
-  std::multimap<uint32_t, UniquePid> pids_;
+  std::multimap<uint32_t /* pid (aka tgid) */, UniquePid> pids_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/process_tracker_unittest.cc
index b4b3d6d..2e0b1dd 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/process_tracker_unittest.cc
@@ -74,26 +74,27 @@
 TEST_F(ProcessTrackerTest, UpdateThreadMatch) {
   uint32_t cpu = 3;
   uint64_t timestamp = 100;
-  uint32_t pid_1 = 1;
   uint32_t prev_state = 32;
   static const char kCommProc1[] = "process1";
   static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
 
-  context.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
                                          kCommProc1, sizeof(kCommProc1) - 1,
-                                         pid_2);
-  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
-                                         kCommProc2, sizeof(kCommProc2) - 1,
-                                         pid_1);
+                                         /*tid=*/4);
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+                                         prev_state, kCommProc2,
+                                         sizeof(kCommProc2) - 1,
+                                         /*tid=*/1);
 
-  context.process_tracker->UpdateProcess(2, "test", 4);
-  context.process_tracker->UpdateThread(1, 2);
+  context.process_tracker->UpdateProcess(2, "test", strlen("test"));
+  context.process_tracker->UpdateThread(4, 2);
 
-  TraceStorage::Thread thread = context.storage->GetThread(1);
-  TraceStorage::Process process = context.storage->GetProcess(1);
+  TraceStorage::Thread thread = context.storage->GetThread(/*utid=*/1);
+  TraceStorage::Process process = context.storage->GetProcess(/*utid=*/1);
 
+  ASSERT_EQ(thread.tid, 4);
   ASSERT_EQ(thread.upid, 1);
+  ASSERT_EQ(process.pid, 2);
   ASSERT_EQ(process.start_ns, timestamp);
 }
 
diff --git a/src/trace_processor/sched_tracker.cc b/src/trace_processor/sched_tracker.cc
index 16d1508..1c9984f 100644
--- a/src/trace_processor/sched_tracker.cc
+++ b/src/trace_processor/sched_tracker.cc
@@ -39,9 +39,10 @@
   // slice.
   if (prev->valid() && prev->next_pid != 0 /* Idle process (swapper/N) */) {
     uint64_t duration = timestamp - prev->timestamp;
-
+    StringId prev_thread_name_id =
+        context_->storage->InternString(prev_comm, prev_comm_len);
     UniqueTid utid = context_->process_tracker->UpdateThread(
-        prev->timestamp, prev->prev_pid, prev->prev_thread_name_id);
+        prev->timestamp, prev->next_pid /* == prev_pid */, prev_thread_name_id);
     context_->storage->AddSliceToCpu(cpu, prev->timestamp, duration, utid);
   }
 
@@ -55,8 +56,6 @@
   prev->timestamp = timestamp;
   prev->prev_pid = prev_pid;
   prev->prev_state = prev_state;
-  prev->prev_thread_name_id =
-      context_->storage->InternString(prev_comm, prev_comm_len);
   prev->next_pid = next_pid;
 };
 
diff --git a/src/trace_processor/sched_tracker.h b/src/trace_processor/sched_tracker.h
index 042408d..c0f8f0a 100644
--- a/src/trace_processor/sched_tracker.h
+++ b/src/trace_processor/sched_tracker.h
@@ -40,7 +40,6 @@
 
   struct SchedSwitchEvent {
     uint64_t timestamp = 0;
-    StringId prev_thread_name_id = 0;
     uint32_t prev_pid = 0;
     uint32_t prev_state = 0;
     uint32_t next_pid = 0;
diff --git a/src/trace_processor/sched_tracker_unittest.cc b/src/trace_processor/sched_tracker_unittest.cc
index 96b5bb5..0344446 100644
--- a/src/trace_processor/sched_tracker_unittest.cc
+++ b/src/trace_processor/sched_tracker_unittest.cc
@@ -65,37 +65,44 @@
   ASSERT_EQ(context.storage->GetThread(1).start_ns, timestamp);
   ASSERT_EQ(std::string(context.storage->GetString(
                 context.storage->GetThread(1).name_id)),
-            "process1");
+            kCommProc2);
   ASSERT_EQ(context.storage->SlicesForCpu(cpu).utids().front(), 1);
 }
 
 TEST_F(SchedTrackerTest, InsertThirdSched_SameThread) {
   uint32_t cpu = 3;
   uint64_t timestamp = 100;
-  uint32_t pid_1 = 2;
   uint32_t prev_state = 32;
   static const char kCommProc1[] = "process1";
   static const char kCommProc2[] = "process2";
-  uint32_t pid_2 = 4;
 
   const auto& timestamps = context.storage->SlicesForCpu(cpu).start_ns();
-  context.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/4, prev_state,
                                          kCommProc1, sizeof(kCommProc1) - 1,
-                                         pid_1);
+                                         /*tid=*/2);
   ASSERT_EQ(timestamps.size(), 0);
 
-  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_1, prev_state,
-                                         kCommProc1, sizeof(kCommProc1) - 1,
-                                         pid_2);
-  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, pid_2, prev_state,
-                                         kCommProc2, sizeof(kCommProc2) - 1,
-                                         pid_1);
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/2,
+                                         prev_state, kCommProc1,
+                                         sizeof(kCommProc1) - 1,
+                                         /*tid=*/4);
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 11, /*tid=*/4,
+                                         prev_state, kCommProc2,
+                                         sizeof(kCommProc2) - 1,
+                                         /*tid=*/2);
+  context.sched_tracker->PushSchedSwitch(cpu, timestamp + 31, /*tid=*/4,
+                                         prev_state, kCommProc1,
+                                         sizeof(kCommProc1) - 1,
+                                         /*tid=*/2);
 
-  ASSERT_EQ(timestamps.size(), 2ul);
+  ASSERT_EQ(timestamps.size(), 3ul);
   ASSERT_EQ(timestamps[0], timestamp);
   ASSERT_EQ(context.storage->GetThread(1).start_ns, timestamp);
+  ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(0), 1u);
+  ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(1), 11u - 1u);
+  ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(2), 31u - 11u);
   ASSERT_EQ(context.storage->SlicesForCpu(cpu).utids().at(0),
-            context.storage->SlicesForCpu(cpu).utids().at(1));
+            context.storage->SlicesForCpu(cpu).utids().at(2));
 }
 
 }  // namespace
diff --git a/src/trace_processor/slice_table.cc b/src/trace_processor/slice_table.cc
index a772eac..0a9de2b 100644
--- a/src/trace_processor/slice_table.cc
+++ b/src/trace_processor/slice_table.cc
@@ -39,6 +39,8 @@
                               "cat STRING,"
                               "name STRING,"
                               "depth INT,"
+                              "stack_id UNSIGNED BIG INT,"
+                              "parent_stack_id UNSIGNED BIG INT,"
                               "PRIMARY KEY(utid, ts, depth)"
                               ") WITHOUT ROWID;");
 }
@@ -103,6 +105,14 @@
       sqlite3_result_int64(context,
                            static_cast<sqlite3_int64>(slices.depths()[row_]));
       break;
+    case Column::kStackId:
+      sqlite3_result_int64(
+          context, static_cast<sqlite3_int64>(slices.stack_ids()[row_]));
+      break;
+    case Column::kParentStackId:
+      sqlite3_result_int64(
+          context, static_cast<sqlite3_int64>(slices.parent_stack_ids()[row_]));
+      break;
   }
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/slice_table.h b/src/trace_processor/slice_table.h
index 32e120d..b821ec8 100644
--- a/src/trace_processor/slice_table.h
+++ b/src/trace_processor/slice_table.h
@@ -43,6 +43,8 @@
     kCategory = 3,
     kName = 4,
     kDepth = 5,
+    kStackId = 6,
+    kParentStackId = 7,
   };
 
   SliceTable(const TraceStorage* storage);
diff --git a/src/trace_processor/thread_table.cc b/src/trace_processor/thread_table.cc
index e3a56cd..040f99d 100644
--- a/src/trace_processor/thread_table.cc
+++ b/src/trace_processor/thread_table.cc
@@ -37,6 +37,7 @@
                                "utid UNSIGNED INT, "
                                "upid UNSIGNED INT, "
                                "name TEXT, "
+                               "tid UNSIGNED INT, "
                                "PRIMARY KEY(utid)"
                                ") WITHOUT ROWID;");
 }
@@ -73,6 +74,10 @@
                           static_cast<int>(name.length()), nullptr);
       break;
     }
+    case Column::kTid: {
+      sqlite3_result_int64(context, thread.tid);
+      break;
+    }
     default: {
       PERFETTO_FATAL("Unknown column %d", N);
       break;
diff --git a/src/trace_processor/thread_table.h b/src/trace_processor/thread_table.h
index cbfe908..d5b3609 100644
--- a/src/trace_processor/thread_table.h
+++ b/src/trace_processor/thread_table.h
@@ -30,7 +30,7 @@
 // the metadata for those processes.
 class ThreadTable : public Table {
  public:
-  enum Column { kUtid = 0, kUpid = 1, kName = 2 };
+  enum Column { kUtid = 0, kUpid = 1, kName = 2, kTid = 3 };
 
   static void RegisterTable(sqlite3* db, const TraceStorage* storage);
 
diff --git a/src/trace_processor/thread_table_unittest.cc b/src/trace_processor/thread_table_unittest.cc
index c0b4225..19a4201 100644
--- a/src/trace_processor/thread_table_unittest.cc
+++ b/src/trace_processor/thread_table_unittest.cc
@@ -67,27 +67,27 @@
 TEST_F(ThreadTableUnittest, Select) {
   uint32_t cpu = 3;
   uint64_t timestamp = 100;
-  uint32_t pid_1 = 1;
   uint32_t prev_state = 32;
   static const char kCommProc1[] = "thread1";
   static const char kCommProc2[] = "thread2";
-  uint32_t pid_2 = 4;
 
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
                                           kCommProc1, sizeof(kCommProc1) - 1,
-                                          pid_2);
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
-                                          kCommProc2, sizeof(kCommProc2) - 1,
-                                          pid_1);
+                                          /*tid=*/4);
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+                                          prev_state, kCommProc2,
+                                          sizeof(kCommProc2) - 1,
+                                          /*tid=*/1);
 
-  context_.process_tracker->UpdateProcess(2, "test", 4);
-  context_.process_tracker->UpdateThread(1, 2);
-  PrepareValidStatement("SELECT utid, upid, name FROM thread");
+  context_.process_tracker->UpdateProcess(2, "test", strlen("test"));
+  context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
+  PrepareValidStatement("SELECT utid, upid, tid, name FROM thread");
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
   ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
   ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(2), kCommProc1);
+  ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
+  ASSERT_STREQ(GetColumnAsText(3), kCommProc2);
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
 }
@@ -95,31 +95,33 @@
 TEST_F(ThreadTableUnittest, SelectWhere) {
   uint32_t cpu = 3;
   uint64_t timestamp = 100;
-  uint32_t pid_1 = 1;
   uint32_t prev_state = 32;
   static const char kCommProc1[] = "thread1";
   static const char kCommProc2[] = "thread2";
-  uint32_t pid_2 = 4;
 
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
                                           kCommProc1, sizeof(kCommProc1) - 1,
-                                          pid_2);
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
-                                          kCommProc2, sizeof(kCommProc2) - 1,
-                                          pid_1);
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, pid_1, prev_state,
-                                          kCommProc1, sizeof(kCommProc1) - 1,
-                                          pid_2);
+                                          /*tid=*/4);
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+                                          prev_state, kCommProc2,
+                                          sizeof(kCommProc2) - 1,
+                                          /*tid=*/1);
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, /*tid=*/1,
+                                          prev_state, kCommProc1,
+                                          sizeof(kCommProc1) - 1,
+                                          /*tid=*/4);
 
-  context_.process_tracker->UpdateProcess(2, "test", 4);
-  context_.process_tracker->UpdateThread(1, 2);
-  context_.process_tracker->UpdateThread(2, 2);
-  PrepareValidStatement("SELECT utid, upid, name FROM thread where utid = 1");
+  context_.process_tracker->UpdateProcess(2, "test", strlen("test"));
+  context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
+  context_.process_tracker->UpdateThread(1 /*tid*/, 2 /*pid*/);
+  PrepareValidStatement(
+      "SELECT utid, upid, tid, name FROM thread where tid = 4");
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
   ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
   ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(2), kCommProc1);
+  ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
+  ASSERT_STREQ(GetColumnAsText(3), kCommProc2);
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
 }
@@ -127,32 +129,36 @@
 TEST_F(ThreadTableUnittest, JoinWithProcess) {
   uint32_t cpu = 3;
   uint64_t timestamp = 100;
-  uint32_t pid_1 = 1;
   uint32_t prev_state = 32;
   static const char kCommProc1[] = "thread1";
   static const char kCommProc2[] = "thread2";
-  uint32_t pid_2 = 4;
 
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
                                           kCommProc1, sizeof(kCommProc1) - 1,
-                                          pid_2);
-  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
-                                          kCommProc2, sizeof(kCommProc2) - 1,
-                                          pid_1);
+                                          /*tid=*/4);
+  context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+                                          prev_state, kCommProc2,
+                                          sizeof(kCommProc2) - 1,
+                                          /*tid=*/1);
 
-  context_.process_tracker->UpdateProcess(2, "test", 4);
-  context_.process_tracker->UpdateProcess(3, "test1", 5);
-  context_.process_tracker->UpdateThread(1, 2);
+  // Also create a process for which we haven't seen any thread.
+  context_.process_tracker->UpdateProcess(3, "test2", strlen("test2"));
+
+  context_.process_tracker->UpdateProcess(2, "test1", strlen("test1"));
+  context_.process_tracker->UpdateThread(4, 2);
+
   PrepareValidStatement(
-      "SELECT utid, thread.name, process.upid, process.name FROM thread INNER "
-      "JOIN process USING (upid)");
+      "SELECT utid, thread.tid, thread.name, process.upid, process.pid, "
+      "process.name FROM thread INNER JOIN process USING (upid)");
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
   ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
-  ASSERT_STREQ(GetColumnAsText(1), kCommProc1);
+  ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 4 /* tid */);
+  ASSERT_STREQ(GetColumnAsText(2), kCommProc2);
 
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 1 /* upid */);
-  ASSERT_STREQ(GetColumnAsText(3), "test");
+  ASSERT_EQ(sqlite3_column_int(*stmt_, 3), 2 /* upid */);
+  ASSERT_EQ(sqlite3_column_int(*stmt_, 4), 2 /* pid */);
+  ASSERT_STREQ(GetColumnAsText(5), "test1");
 
   ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
 }
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index eda0116..5b535a0 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -67,14 +67,17 @@
     for (int c = 0; c < res.columns_size(); c++) {
       switch (res.column_descriptors(c).type()) {
         case protos::RawQueryResult_ColumnDesc_Type_STRING:
-          printf("%20s ", res.columns(c).string_values(r).c_str());
+          printf("%-20.20s ", res.columns(c).string_values(r).c_str());
           break;
         case protos::RawQueryResult_ColumnDesc_Type_DOUBLE:
           printf("%20f ", res.columns(c).double_values(r));
           break;
-        case protos::RawQueryResult_ColumnDesc_Type_LONG:
-          printf("%20lld ", res.columns(c).long_values(r));
+        case protos::RawQueryResult_ColumnDesc_Type_LONG: {
+          auto value = res.columns(c).long_values(r);
+          printf((value < 0xffffffll) ? "%20lld " : "%20llx ", value);
+
           break;
+        }
       }
     }
     printf("\n");
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index e6d0f55..38cb414 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -23,8 +23,11 @@
 
 TraceStorage::TraceStorage() {
   // Upid/utid 0 is reserved for invalid processes/threads.
-  unique_processes_.emplace_back();
-  unique_threads_.emplace_back();
+  unique_processes_.emplace_back(0);
+  unique_threads_.emplace_back(0);
+
+  // Reserve string ID 0 for the empty string.
+  InternString("", 0);
 }
 
 TraceStorage::~TraceStorage() {}
@@ -37,9 +40,10 @@
 };
 
 StringId TraceStorage::InternString(const char* data, size_t length) {
-  uint32_t hash = 0;
+  uint32_t hash = 0x811c9dc5;  // FNV-1a-32 offset basis.
   for (size_t i = 0; i < length; ++i) {
-    hash = static_cast<uint32_t>(data[i]) + (hash * 31);
+    hash ^= static_cast<decltype(hash)>(data[i]);
+    hash *= 16777619;  // FNV-1a-32 prime.
   }
   auto id_it = string_index_.find(hash);
   if (id_it != string_index_.end()) {
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 952e2c0..9613ca6 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -53,24 +53,27 @@
 
   constexpr static size_t kMaxCpus = 128;
 
-
   struct Stats {
     uint64_t mismatched_sched_switch_tids_ = 0;
   };
 
   // Information about a unique process seen in a trace.
   struct Process {
+    explicit Process(uint32_t p) : pid(p) {}
     uint64_t start_ns = 0;
     uint64_t end_ns = 0;
     StringId name_id = 0;
+    uint32_t pid = 0;
   };
 
   // Information about a unique thread seen in a trace.
   struct Thread {
+    explicit Thread(uint32_t t) : tid(t) {}
     uint64_t start_ns = 0;
     uint64_t end_ns = 0;
     StringId name_id = 0;
     UniquePid upid = 0;
+    uint32_t tid = 0;
   };
 
   class SlicesPerCpu {
@@ -106,13 +109,17 @@
                          UniqueTid utid,
                          StringId cat,
                          StringId name,
-                         uint8_t depth) {
+                         uint8_t depth,
+                         uint64_t stack_id,
+                         uint64_t parent_stack_id) {
       start_ns_.emplace_back(start_ns);
       durations_.emplace_back(duration_ns);
       utids_.emplace_back(utid);
       cats_.emplace_back(cat);
       names_.emplace_back(name);
       depths_.emplace_back(depth);
+      stack_ids_.emplace_back(stack_id);
+      parent_stack_ids_.emplace_back(parent_stack_id);
     }
 
     size_t slice_count() const { return start_ns_.size(); }
@@ -122,6 +129,10 @@
     const std::deque<StringId>& cats() const { return cats_; }
     const std::deque<StringId>& names() const { return names_; }
     const std::deque<uint8_t>& depths() const { return depths_; }
+    const std::deque<uint64_t>& stack_ids() const { return stack_ids_; }
+    const std::deque<uint64_t>& parent_stack_ids() const {
+      return parent_stack_ids_;
+    }
 
    private:
     std::deque<uint64_t> start_ns_;
@@ -130,6 +141,8 @@
     std::deque<StringId> cats_;
     std::deque<StringId> names_;
     std::deque<uint8_t> depths_;
+    std::deque<uint64_t> stack_ids_;
+    std::deque<uint64_t> parent_stack_ids_;
   };
 
   void ResetStorage();
@@ -139,13 +152,13 @@
                      uint64_t duration_ns,
                      UniqueTid utid);
 
-  UniqueTid AddEmptyThread() {
-    unique_threads_.emplace_back();
+  UniqueTid AddEmptyThread(uint32_t tid) {
+    unique_threads_.emplace_back(tid);
     return static_cast<UniqueTid>(unique_threads_.size() - 1);
   }
 
-  UniquePid AddEmptyProcess() {
-    unique_processes_.emplace_back();
+  UniquePid AddEmptyProcess(uint32_t pid) {
+    unique_processes_.emplace_back(pid);
     return static_cast<UniquePid>(unique_processes_.size() - 1);
   }
 
@@ -156,12 +169,12 @@
   StringId InternString(const char* data, size_t length);
 
   Process* GetMutableProcess(UniquePid upid) {
-    PERFETTO_DCHECK(upid < unique_processes_.size());
+    PERFETTO_DCHECK(upid > 0 && upid < unique_processes_.size());
     return &unique_processes_[upid];
   }
 
   Thread* GetMutableThread(UniqueTid utid) {
-    PERFETTO_DCHECK(utid < unique_threads_.size());
+    PERFETTO_DCHECK(utid > 0 && utid < unique_threads_.size());
     return &unique_threads_[utid];
   }
 
@@ -177,12 +190,12 @@
   }
 
   const Process& GetProcess(UniquePid upid) const {
-    PERFETTO_DCHECK(upid < unique_processes_.size());
+    PERFETTO_DCHECK(upid > 0 && upid < unique_processes_.size());
     return unique_processes_[upid];
   }
 
   const Thread& GetThread(UniqueTid utid) const {
-    PERFETTO_DCHECK(utid < unique_threads_.size());
+    PERFETTO_DCHECK(utid > 0 && utid < unique_threads_.size());
     return unique_threads_[utid];
   }