trace_to_text: Output process names and fix json.

The json output can now be uploaded to catapult
and will contain process and thread names.

In order to have the tgid available when processing
the ftrace events we do need to process the trace twice.

Bug:117588397
Change-Id: I8013c93e2627896ed7b91b965efce74f176e1c40
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index ba7cd9f..e606369 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -30,6 +30,7 @@
     "ftrace_inode_handler.cc",
     "ftrace_inode_handler.h",
     "main.cc",
+    "process_formatter.h",
   ]
 }
 
diff --git a/tools/trace_to_text/ftrace_event_formatter.cc b/tools/trace_to_text/ftrace_event_formatter.cc
index f6f8825..4f312e6 100644
--- a/tools/trace_to_text/ftrace_event_formatter.cc
+++ b/tools/trace_to_text/ftrace_event_formatter.cc
@@ -2859,26 +2859,58 @@
   return (timestamp / 1000) % 1000000ul;
 }
 
-std::string FormatPrefix(uint64_t timestamp, uint64_t cpu) {
+std::string FormatPrefix(uint64_t timestamp,
+                         uint64_t cpu,
+                         uint32_t pid,
+                         uint32_t tgid,
+                         std::string name) {
   char line[2048];
   uint64_t seconds = TimestampToSeconds(timestamp);
   uint64_t useconds = TimestampToMicroseconds(timestamp);
-  sprintf(line,
-          "<idle>-0     (-----) [%03" PRIu64 "] d..3 %" PRIu64 ".%.6" PRIu64
-          ": ",
-          cpu, seconds, useconds);
+  if (pid == 0) {
+    name = "<idle>";
+  }
+  if (tgid == 0) {
+    sprintf(line,
+            "%s-%" PRIu32 "     (-----) [%03" PRIu32 "] d..3 %" PRIu64
+            ".%.6" PRIu64 ": ",
+            name.c_str(), pid, cpu, seconds, useconds);
+  } else {
+    sprintf(line,
+            "%s-%" PRIu32 "     (%5" PRIu32 ") [%03" PRIu32 "] d..3 %" PRIu64
+            ".%.6" PRIu64 ": ",
+            name.c_str(), pid, tgid, cpu, seconds, useconds);
+  }
   return std::string(line);
 }
 
 }  // namespace
 
-std::string FormatFtraceEvent(uint64_t timestamp,
-                              size_t cpu,
-                              const protos::FtraceEvent& event) {
+std::string FormatFtraceEvent(
+    uint64_t timestamp,
+    size_t cpu,
+    const protos::FtraceEvent& event,
+    const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map) {
+  // Sched_switch events contain the thread name so use that in the prefix.
+  std::string name;
+  if (event.has_sched_switch()) {
+    name = event.sched_switch().prev_comm();
+  } else {
+    name = "<...>";
+  }
+
   std::string line = FormatEventText(event);
   if (line == "")
     return "";
-  return FormatPrefix(timestamp, cpu) + line;
+
+  // Retrieve the tgid if it exists for the current event pid.
+  uint32_t pid = event.pid();
+  uint32_t tgid = 0;
+  auto it = thread_map.find(pid);
+  if (it != thread_map.end()) {
+    tgid = it->second;
+  }
+  return FormatPrefix(timestamp, cpu, pid, tgid, name) + line;
 }
 
 }  // namespace perfetto
diff --git a/tools/trace_to_text/ftrace_event_formatter.h b/tools/trace_to_text/ftrace_event_formatter.h
index 7b76e26..27b0142 100644
--- a/tools/trace_to_text/ftrace_event_formatter.h
+++ b/tools/trace_to_text/ftrace_event_formatter.h
@@ -20,14 +20,17 @@
 #include "tools/trace_to_text/ftrace_event_formatter.h"
 
 #include <string>
+#include <unordered_map>
 
 #include "perfetto/trace/trace_packet.pb.h"
 
 namespace perfetto {
 
-std::string FormatFtraceEvent(uint64_t timestamp,
-                              size_t cpu,
-                              const protos::FtraceEvent&);
+std::string FormatFtraceEvent(
+    uint64_t timestamp,
+    size_t cpu,
+    const protos::FtraceEvent&,
+    const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map);
 
 }  // namespace perfetto
 
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 709d139..2950bb2 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -30,6 +30,7 @@
 #include <memory>
 #include <ostream>
 #include <sstream>
+#include <unordered_map>
 #include <utility>
 
 #include <google/protobuf/compiler/importer.h>
@@ -46,10 +47,13 @@
 #include "perfetto/traced/sys_stats_counters.h"
 #include "tools/trace_to_text/ftrace_event_formatter.h"
 #include "tools/trace_to_text/ftrace_inode_handler.h"
+#include "tools/trace_to_text/process_formatter.h"
 
 namespace perfetto {
 namespace {
 
+// Having an empty traceEvents object is necessary for trace viewer to
+// load the json properly.
 const char kTraceHeader[] = R"({
   "traceEvents": [],
 )";
@@ -58,6 +62,14 @@
   "controllerTraceDataKey": "systraceController"
 })";
 
+const char kProcessDumpHeader[] =
+    ""
+    "\"androidProcessDump\": "
+    "\"PROCESS DUMP\\nUSER           PID  PPID     VSZ    RSS WCHAN  "
+    "PC S NAME                        COMM                       \\n";
+
+const char kThreadHeader[] = "USER           PID   TID CMD \\n";
+
 const char kFtraceHeader[] =
     ""
     "  \"systemTraceEvents\": \""
@@ -176,21 +188,49 @@
 int TraceToSystrace(std::istream* input,
                     std::ostream* output,
                     bool wrap_in_json) {
-  std::multimap<uint64_t, std::string> sorted;
+  std::multimap<uint64_t, std::string> ftrace_sorted;
+  std::vector<std::string> proc_dump;
+  std::vector<std::string> thread_dump;
+  std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/> thread_map;
 
   std::vector<const char*> meminfo_strs = BuildMeminfoCounterNames();
   std::vector<const char*> vmstat_strs = BuildVmstatCounterNames();
 
-  ForEachPacketInTrace(input, [&sorted, &meminfo_strs, &vmstat_strs](
-                                  const protos::TracePacket& packet) {
+  std::vector<const protos::TracePacket> packets_to_process;
+
+  ForEachPacketInTrace(
+      input, [&thread_map, &packets_to_process, &proc_dump,
+              &thread_dump](const protos::TracePacket& packet) {
+        if (!packet.has_process_tree()) {
+          packets_to_process.emplace_back(std::move(packet));
+          return;
+        }
+        const ProcessTree& process_tree = packet.process_tree();
+        for (const auto& process : process_tree.processes()) {
+          // Main threads will have the same pid as tgid.
+          thread_map[static_cast<uint32_t>(process.pid())] =
+              static_cast<uint32_t>(process.pid());
+          std::string p = FormatProcess(process);
+          proc_dump.emplace_back(p);
+        }
+        for (const auto& thread : process_tree.threads()) {
+          // Populate thread map for matching tids to tgids.
+          thread_map[static_cast<uint32_t>(thread.tid())] =
+              static_cast<uint32_t>(thread.tgid());
+          std::string t = FormatThread(thread);
+          thread_dump.emplace_back(t);
+        }
+      });
+
+  for (const auto& packet : packets_to_process) {
     if (packet.has_ftrace_events()) {
       const FtraceEventBundle& bundle = packet.ftrace_events();
       for (const FtraceEvent& event : bundle.event()) {
-        std::string line =
-            FormatFtraceEvent(event.timestamp(), bundle.cpu(), event);
+        std::string line = FormatFtraceEvent(event.timestamp(), bundle.cpu(),
+                                             event, thread_map);
         if (line == "")
           continue;
-        sorted.emplace(event.timestamp(), line);
+        ftrace_sorted.emplace(event.timestamp(), line);
       }
     }  // packet.has_ftrace_events
 
@@ -205,7 +245,7 @@
         sprintf(str, "C|1|%s|%" PRIu64, meminfo_strs[meminfo.key()],
                 static_cast<uint64_t>(meminfo.value()));
         event.mutable_print()->set_buf(str);
-        sorted.emplace(ts, FormatFtraceEvent(ts, 0, event));
+        ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
       }
       for (const auto& vmstat : sys_stats.vmstat()) {
         FtraceEvent event;
@@ -216,20 +256,29 @@
         sprintf(str, "C|1|%s|%" PRIu64, vmstat_strs[vmstat.key()],
                 static_cast<uint64_t>(vmstat.value()));
         event.mutable_print()->set_buf(str);
-        sorted.emplace(ts, FormatFtraceEvent(ts, 0, event));
+        ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
       }
     }
-  });
+  }
 
   if (wrap_in_json) {
     *output << kTraceHeader;
+    *output << kProcessDumpHeader;
+    for (const auto& process : proc_dump) {
+      *output << process << "\\n";
+    }
+    *output << kThreadHeader;
+    for (const auto& thread : thread_dump) {
+      *output << thread << "\\n";
+    }
+    *output << "\",";
     *output << kFtraceHeader;
   }
 
   fprintf(stderr, "\n");
-  size_t total_events = sorted.size();
+  size_t total_events = ftrace_sorted.size();
   size_t written_events = 0;
-  for (auto it = sorted.begin(); it != sorted.end(); it++) {
+  for (auto it = ftrace_sorted.begin(); it != ftrace_sorted.end(); it++) {
     *output << it->second << (wrap_in_json ? "\\n" : "\n");
     if (written_events++ % 100 == 0 && !isatty(STDOUT_FILENO)) {
       fprintf(stderr, "Writing trace: %.2f %%\r",
diff --git a/tools/trace_to_text/process_formatter.h b/tools/trace_to_text/process_formatter.h
new file mode 100644
index 0000000..62daf72
--- /dev/null
+++ b/tools/trace_to_text/process_formatter.h
@@ -0,0 +1,49 @@
+/*
+ * 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 TOOLS_TRACE_TO_TEXT_PROCESS_FORMATTER_H_
+#define TOOLS_TRACE_TO_TEXT_PROCESS_FORMATTER_H_
+
+#include <string>
+
+#include "perfetto/trace/trace_packet.pb.h"
+
+namespace perfetto {
+
+inline std::string FormatProcess(const protos::ProcessTree::Process& p) {
+  char line[2048];
+  sprintf(line,
+          "root             %d     %d   00000   000 null 0000000000 S %s       "
+          "  null",
+          p.pid(), p.ppid(), p.cmdline(0).c_str());
+  return line;
+};
+
+inline std::string FormatThread(const protos::ProcessTree::Thread& t) {
+  char line[2048];
+  std::string name;
+  if (t.has_name()) {
+    name = t.name();
+  } else {
+    name = "<...>";
+  }
+  sprintf(line, "root         %d %d %s", t.tgid(), t.tid(), name.c_str());
+  return line;
+};
+
+}  // namespace perfetto
+
+#endif  // TOOLS_TRACE_TO_TEXT_PROCESS_FORMATTER_H_