Merge "trace_processor: Parse process and trace packets"
diff --git a/src/trace_processor/trace_parser.cc b/src/trace_processor/trace_parser.cc
index 2847b35..d543630 100644
--- a/src/trace_processor/trace_parser.cc
+++ b/src/trace_processor/trace_parser.cc
@@ -62,22 +62,23 @@
       PERFETTO_ELOG("Non-trace packet field found in root Trace proto");
       continue;
     }
-    ParsePacket(fld.length_limited.data,
-                static_cast<uint32_t>(fld.length_limited.length));
+    ParsePacket(fld.length_limited.data, fld.length_limited.length);
   }
 
   offset_ += decoder.offset();
   return true;
 }
 
-void TraceParser::ParsePacket(const uint8_t* data, uint32_t length) {
+void TraceParser::ParsePacket(const uint8_t* data, size_t length) {
   ProtoDecoder decoder(data, length);
   for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
     switch (fld.id) {
       case protos::TracePacket::kFtraceEventsFieldNumber:
-        ParseFtraceEventBundle(
-            fld.length_limited.data,
-            static_cast<uint32_t>(fld.length_limited.length));
+        ParseFtraceEventBundle(fld.length_limited.data,
+                               fld.length_limited.length);
+        break;
+      case protos::TracePacket::kProcessTreeFieldNumber:
+        ParseProcessTree(fld.length_limited.data, fld.length_limited.length);
         break;
       default:
         break;
@@ -86,7 +87,83 @@
   PERFETTO_DCHECK(decoder.IsEndOfBuffer());
 }
 
-void TraceParser::ParseFtraceEventBundle(const uint8_t* data, uint32_t length) {
+void TraceParser::ParseProcessTree(const uint8_t* data, size_t length) {
+  ProtoDecoder decoder(data, length);
+
+  // TODO(taylori): We currently rely on process packets always coming before
+  // their corresponding threads. This should be true but it is better
+  // not to rely on it.
+  bool parsed_thread_packet = false;
+  for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+    switch (fld.id) {
+      case protos::ProcessTree::kProcessesFieldNumber:
+        PERFETTO_DCHECK(!parsed_thread_packet);
+        ParseProcess(fld.length_limited.data, fld.length_limited.length);
+        break;
+      case protos::ProcessTree::kThreadsFieldNumber:
+        parsed_thread_packet = true;
+        ParseThread(fld.length_limited.data, fld.length_limited.length);
+        break;
+      default:
+        break;
+    }
+  }
+  PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseThread(const uint8_t* data, size_t length) {
+  ProtoDecoder decoder(data, length);
+  uint32_t tid = 0;
+  uint32_t tgid = 0;
+  const char* thread_name = nullptr;
+  size_t thread_name_len = 0;
+  for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+    switch (fld.id) {
+      case protos::ProcessTree::Thread::kTidFieldNumber:
+        tid = fld.as_uint32();
+        break;
+      case protos::ProcessTree::Thread::kTgidFieldNumber:
+        tgid = fld.as_uint32();
+        break;
+      case protos::ProcessTree::Thread::kNameFieldNumber:
+        thread_name = fld.as_char_ptr();
+        thread_name_len = fld.size();
+        break;
+      default:
+        break;
+    }
+  }
+  // TODO(taylori): Store the thread.
+
+  PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseProcess(const uint8_t* data, size_t length) {
+  ProtoDecoder decoder(data, length);
+  uint32_t pid = 0;
+  const char* process_name = nullptr;
+  size_t process_name_len = 0;
+  for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+    switch (fld.id) {
+      case protos::ProcessTree::Process::kPidFieldNumber:
+        pid = fld.as_uint32();
+        break;
+      case protos::ProcessTree::Process::kCmdlineFieldNumber:
+        if (process_name == nullptr) {
+          process_name = fld.as_char_ptr();
+          process_name_len = fld.size();
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  storage_->PushProcess(pid, process_name, process_name_len);
+
+  PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseFtraceEventBundle(const uint8_t* data, size_t length) {
   ProtoDecoder decoder(data, length);
   uint64_t cpu = 0;
   if (!FindIntField(&decoder, protos::FtraceEventBundle::kCpuFieldNumber,
@@ -100,7 +177,7 @@
     switch (fld.id) {
       case protos::FtraceEventBundle::kEventFieldNumber:
         ParseFtraceEvent(static_cast<uint32_t>(cpu), fld.length_limited.data,
-                         static_cast<uint32_t>(fld.length_limited.length));
+                         fld.length_limited.length);
         break;
       default:
         break;
@@ -111,7 +188,7 @@
 
 void TraceParser::ParseFtraceEvent(uint32_t cpu,
                                    const uint8_t* data,
-                                   uint32_t length) {
+                                   size_t length) {
   ProtoDecoder decoder(data, length);
   uint64_t timestamp = 0;
   if (!FindIntField(&decoder, protos::FtraceEvent::kTimestampFieldNumber,
@@ -126,7 +203,7 @@
       case protos::FtraceEvent::kSchedSwitchFieldNumber:
         PERFETTO_DCHECK(timestamp > 0);
         ParseSchedSwitch(cpu, timestamp, fld.length_limited.data,
-                         static_cast<uint32_t>(fld.length_limited.length));
+                         fld.length_limited.length);
         break;
       default:
         break;
@@ -138,7 +215,7 @@
 void TraceParser::ParseSchedSwitch(uint32_t cpu,
                                    uint64_t timestamp,
                                    const uint8_t* data,
-                                   uint32_t length) {
+                                   size_t length) {
   ProtoDecoder decoder(data, length);
 
   uint32_t prev_pid = 0;
diff --git a/src/trace_processor/trace_parser.h b/src/trace_processor/trace_parser.h
index 5934df4..f79d081 100644
--- a/src/trace_processor/trace_parser.h
+++ b/src/trace_processor/trace_parser.h
@@ -40,13 +40,16 @@
   bool ParseNextChunk();
 
  private:
-  void ParsePacket(const uint8_t* data, uint32_t length);
-  void ParseFtraceEventBundle(const uint8_t* data, uint32_t length);
-  void ParseFtraceEvent(uint32_t cpu, const uint8_t* data, uint32_t length);
+  void ParsePacket(const uint8_t* data, size_t length);
+  void ParseFtraceEventBundle(const uint8_t* data, size_t length);
+  void ParseFtraceEvent(uint32_t cpu, const uint8_t* data, size_t length);
   void ParseSchedSwitch(uint32_t cpu,
                         uint64_t timestamp,
                         const uint8_t* data,
-                        uint32_t length);
+                        size_t length);
+  void ParseProcessTree(const uint8_t* data, size_t length);
+  void ParseProcess(const uint8_t* data, size_t length);
+  void ParseThread(const uint8_t* data, size_t length);
 
   BlobReader* const reader_;
   TraceStorage* const storage_;
diff --git a/src/trace_processor/trace_parser_unittest.cc b/src/trace_processor/trace_parser_unittest.cc
index 5c6bdf1..a521590 100644
--- a/src/trace_processor/trace_parser_unittest.cc
+++ b/src/trace_processor/trace_parser_unittest.cc
@@ -60,6 +60,10 @@
                     const char* prev_comm,
                     size_t prev_comm_len,
                     uint32_t next_pid));
+  MOCK_METHOD3(PushProcess,
+               void(uint32_t pid,
+                    const char* process_name,
+                    size_t process_name_len));
 };
 
 TEST(TraceParser, LoadSingleEvent) {
@@ -212,6 +216,46 @@
   parser.ParseNextChunk();
 }
 
+TEST(TraceParse, LoadProcessPacket) {
+  protos::Trace trace;
+
+  auto* tree = trace.add_packet()->mutable_process_tree();
+  auto* process = tree->add_processes();
+  static const char kProcName1[] = "proc1";
+
+  process->add_cmdline(kProcName1);
+  process->set_pid(1);
+  process->set_ppid(2);
+
+  MockTraceStorage storage;
+  EXPECT_CALL(storage, PushProcess(1, _, _))
+      .With(Args<1, 2>(ElementsAreArray(kProcName1, sizeof(kProcName1) - 1)));
+  FakeStringBlobReader reader(trace.SerializeAsString());
+  TraceParser parser(&reader, &storage, 1024);
+  parser.ParseNextChunk();
+}
+
+TEST(TraceParse, LoadProcessPacket_FirstCmdline) {
+  protos::Trace trace;
+
+  auto* tree = trace.add_packet()->mutable_process_tree();
+  auto* process = tree->add_processes();
+  static const char kProcName1[] = "proc1";
+  static const char kProcName2[] = "proc2";
+
+  process->add_cmdline(kProcName1);
+  process->add_cmdline(kProcName2);
+  process->set_pid(1);
+  process->set_ppid(2);
+
+  MockTraceStorage storage;
+  EXPECT_CALL(storage, PushProcess(1, _, _))
+      .With(Args<1, 2>(ElementsAreArray(kProcName1, sizeof(kProcName1) - 1)));
+  FakeStringBlobReader reader(trace.SerializeAsString());
+  TraceParser parser(&reader, &storage, 1024);
+  parser.ParseNextChunk();
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto