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_