Merge "trace_to_text: Output process names and fix json."
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_