blob: 79bc012f11c15fc3547c4b0952a004ef43051831 [file] [log] [blame]
Hector Dearman20b3c1c2018-01-15 15:34:03 +00001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <inttypes.h>
18
19#include <stdio.h>
Hector Dearman85ef5362018-03-27 14:48:47 +010020#include <sys/ioctl.h>
21#include <unistd.h>
22
Hector Dearman20b3c1c2018-01-15 15:34:03 +000023#include <algorithm>
24#include <fstream>
Lalit Maganti910852d2018-03-28 13:16:45 +010025#include <functional>
Hector Dearman20b3c1c2018-01-15 15:34:03 +000026#include <iostream>
27#include <istream>
Hector Dearman85ef5362018-03-27 14:48:47 +010028#include <limits>
Hector Dearman20b3c1c2018-01-15 15:34:03 +000029#include <map>
30#include <memory>
31#include <ostream>
32#include <sstream>
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +010033#include <unordered_map>
Hector Dearman20b3c1c2018-01-15 15:34:03 +000034#include <utility>
35
36#include <google/protobuf/compiler/importer.h>
37#include <google/protobuf/dynamic_message.h>
38#include <google/protobuf/io/zero_copy_stream_impl.h>
39#include <google/protobuf/text_format.h>
40#include <google/protobuf/util/field_comparator.h>
41#include <google/protobuf/util/message_differencer.h>
42
Primiano Tucci1c752c12018-10-23 09:27:19 +010043#include "perfetto/base/build_config.h"
Hector Dearman20b3c1c2018-01-15 15:34:03 +000044#include "perfetto/base/logging.h"
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +010045#include "perfetto/trace/ftrace/ftrace_stats.pb.h"
Primiano Tucci20b760c2018-01-19 12:36:12 +000046#include "perfetto/trace/trace.pb.h"
47#include "perfetto/trace/trace_packet.pb.h"
Primiano Tucci82a8bfd2018-09-19 11:33:04 +010048#include "perfetto/traced/sys_stats_counters.h"
Hector Dearman85ef5362018-03-27 14:48:47 +010049#include "tools/trace_to_text/ftrace_event_formatter.h"
Anna Zappone7c104622018-03-28 16:35:18 +010050#include "tools/trace_to_text/ftrace_inode_handler.h"
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +010051#include "tools/trace_to_text/process_formatter.h"
Hector Dearman20b3c1c2018-01-15 15:34:03 +000052
Primiano Tucci1c752c12018-10-23 09:27:19 +010053// When running in Web Assembly, fflush() is a no-op and the stdio buffering
54// sends progress updates to JS only when a write ends with \n.
55#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
56#define PROGRESS_CHAR "\n"
57#else
58#define PROGRESS_CHAR "\r"
59#endif
60
Hector Dearman20b3c1c2018-01-15 15:34:03 +000061namespace perfetto {
62namespace {
63
Primiano Tucci1c752c12018-10-23 09:27:19 +010064using google::protobuf::Descriptor;
65using google::protobuf::DynamicMessageFactory;
66using google::protobuf::FileDescriptor;
67using google::protobuf::Message;
68using google::protobuf::TextFormat;
69using google::protobuf::compiler::DiskSourceTree;
70using google::protobuf::compiler::Importer;
71using google::protobuf::compiler::MultiFileErrorCollector;
72using google::protobuf::io::OstreamOutputStream;
73
74using protos::FtraceEvent;
75using protos::FtraceEventBundle;
76using protos::InodeFileMap;
77using protos::PrintFtraceEvent;
78using protos::ProcessTree;
79using protos::Trace;
80using protos::TracePacket;
81using protos::FtraceStats;
82using protos::FtraceStats_Phase_START_OF_TRACE;
83using protos::FtraceStats_Phase_END_OF_TRACE;
84using protos::SysStats;
85using Entry = protos::InodeFileMap::Entry;
86using Process = protos::ProcessTree::Process;
87
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +010088// Having an empty traceEvents object is necessary for trace viewer to
89// load the json properly.
Hector Dearman20b3c1c2018-01-15 15:34:03 +000090const char kTraceHeader[] = R"({
91 "traceEvents": [],
92)";
93
94const char kTraceFooter[] = R"(\n",
95 "controllerTraceDataKey": "systraceController"
96})";
97
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +010098const char kProcessDumpHeader[] =
99 ""
100 "\"androidProcessDump\": "
101 "\"PROCESS DUMP\\nUSER PID PPID VSZ RSS WCHAN "
102 "PC S NAME COMM \\n";
103
104const char kThreadHeader[] = "USER PID TID CMD \\n";
105
Isabelle Taylor1ebefdc2018-10-18 11:02:53 +0100106const char kSystemTraceEvents[] =
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000107 ""
Isabelle Taylor1ebefdc2018-10-18 11:02:53 +0100108 " \"systemTraceEvents\": \"";
109
110const char kFtraceHeader[] =
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000111 "# tracer: nop\\n"
112 "#\\n"
113 "# entries-in-buffer/entries-written: 30624/30624 #P:4\\n"
114 "#\\n"
115 "# _-----=> irqs-off\\n"
116 "# / _----=> need-resched\\n"
117 "# | / _---=> hardirq/softirq\\n"
118 "# || / _--=> preempt-depth\\n"
119 "# ||| / delay\\n"
120 "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\\n"
121 "# | | | | |||| | |\\n";
122
Primiano Tucci1c752c12018-10-23 09:27:19 +0100123bool g_output_is_tty = false;
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000124
Hector Dearman85ef5362018-03-27 14:48:47 +0100125size_t GetWidth() {
126 if (!isatty(STDOUT_FILENO))
127 return 80;
128 struct winsize win_size;
129 ioctl(STDOUT_FILENO, TIOCGWINSZ, &win_size);
130 return win_size.ws_col;
131}
132
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000133class MFE : public MultiFileErrorCollector {
134 virtual void AddError(const std::string& filename,
135 int line,
136 int column,
137 const std::string& message) {
138 PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
139 message.c_str());
140 }
141
142 virtual void AddWarning(const std::string& filename,
143 int line,
144 int column,
145 const std::string& message) {
146 PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
147 message.c_str());
148 }
149};
150
Hector Dearman85ef5362018-03-27 14:48:47 +0100151void ForEachPacketInTrace(
152 std::istream* input,
153 const std::function<void(const protos::TracePacket&)>& f) {
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100154 size_t bytes_processed = 0;
155 // The trace stream can be very large. We cannot just pass it in one go to
156 // libprotobuf as that will refuse to parse messages > 64MB. However we know
157 // that a trace is merely a sequence of TracePackets. Here we just manually
158 // tokenize the repeated TracePacket messages and parse them individually
159 // using libprotobuf.
Primiano Tucci1c752c12018-10-23 09:27:19 +0100160 for (uint32_t i = 0;; i++) {
161 if ((i & 0x3f) == 0) {
162 fprintf(stderr, "Processing trace: %8zu KB" PROGRESS_CHAR,
163 bytes_processed / 1024);
164 fflush(stderr);
165 }
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100166 // A TracePacket consists in one byte stating its field id and type ...
167 char preamble;
168 input->get(preamble);
169 if (!input->good())
170 break;
171 bytes_processed++;
172 PERFETTO_DCHECK(preamble == 0x0a); // Field ID:1, type:length delimited.
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000173
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100174 // ... a varint stating its size ...
175 uint32_t field_size = 0;
176 uint32_t shift = 0;
177 for (;;) {
178 char c = 0;
179 input->get(c);
180 field_size |= static_cast<uint32_t>(c & 0x7f) << shift;
181 shift += 7;
182 bytes_processed++;
183 if (!(c & 0x80))
184 break;
185 }
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000186
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100187 // ... and the actual TracePacket itself.
188 std::unique_ptr<char[]> buf(new char[field_size]);
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100189 input->read(buf.get(), static_cast<std::streamsize>(field_size));
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100190 bytes_processed += field_size;
191
192 protos::TracePacket packet;
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100193 auto res = packet.ParseFromArray(buf.get(), static_cast<int>(field_size));
Primiano Tuccide2476b2018-08-23 00:53:20 +0200194 if (!res) {
195 PERFETTO_ELOG("Skipping invalid packet");
196 continue;
197 }
Hector Dearman85ef5362018-03-27 14:48:47 +0100198 f(packet);
199 }
200}
201
Primiano Tucci45c9b182018-03-29 14:10:51 +0100202int TraceToSystrace(std::istream* input,
203 std::ostream* output,
204 bool wrap_in_json) {
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100205 std::multimap<uint64_t, std::string> ftrace_sorted;
206 std::vector<std::string> proc_dump;
207 std::vector<std::string> thread_dump;
208 std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/> thread_map;
Hector Dearman85ef5362018-03-27 14:48:47 +0100209
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100210 std::vector<const char*> meminfo_strs = BuildMeminfoCounterNames();
211 std::vector<const char*> vmstat_strs = BuildVmstatCounterNames();
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000212
Isabelle Taylor543a7e62018-10-16 16:17:30 +0100213 std::vector<protos::TracePacket> packets_to_process;
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100214
215 ForEachPacketInTrace(
216 input, [&thread_map, &packets_to_process, &proc_dump,
217 &thread_dump](const protos::TracePacket& packet) {
218 if (!packet.has_process_tree()) {
219 packets_to_process.emplace_back(std::move(packet));
220 return;
221 }
222 const ProcessTree& process_tree = packet.process_tree();
223 for (const auto& process : process_tree.processes()) {
224 // Main threads will have the same pid as tgid.
225 thread_map[static_cast<uint32_t>(process.pid())] =
226 static_cast<uint32_t>(process.pid());
227 std::string p = FormatProcess(process);
228 proc_dump.emplace_back(p);
229 }
230 for (const auto& thread : process_tree.threads()) {
231 // Populate thread map for matching tids to tgids.
232 thread_map[static_cast<uint32_t>(thread.tid())] =
233 static_cast<uint32_t>(thread.tgid());
234 std::string t = FormatThread(thread);
235 thread_dump.emplace_back(t);
236 }
237 });
238
239 for (const auto& packet : packets_to_process) {
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100240 if (packet.has_ftrace_events()) {
241 const FtraceEventBundle& bundle = packet.ftrace_events();
242 for (const FtraceEvent& event : bundle.event()) {
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100243 std::string line = FormatFtraceEvent(event.timestamp(), bundle.cpu(),
244 event, thread_map);
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100245 if (line == "")
246 continue;
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100247 ftrace_sorted.emplace(event.timestamp(), line);
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100248 }
249 } // packet.has_ftrace_events
250
251 if (packet.has_sys_stats()) {
252 const SysStats& sys_stats = packet.sys_stats();
253 for (const auto& meminfo : sys_stats.meminfo()) {
254 FtraceEvent event;
255 uint64_t ts = static_cast<uint64_t>(packet.timestamp());
256 char str[256];
257 event.set_timestamp(ts);
258 event.set_pid(1);
259 sprintf(str, "C|1|%s|%" PRIu64, meminfo_strs[meminfo.key()],
260 static_cast<uint64_t>(meminfo.value()));
261 event.mutable_print()->set_buf(str);
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100262 ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100263 }
264 for (const auto& vmstat : sys_stats.vmstat()) {
265 FtraceEvent event;
266 uint64_t ts = static_cast<uint64_t>(packet.timestamp());
267 char str[256];
268 event.set_timestamp(ts);
269 event.set_pid(1);
270 sprintf(str, "C|1|%s|%" PRIu64, vmstat_strs[vmstat.key()],
271 static_cast<uint64_t>(vmstat.value()));
272 event.mutable_print()->set_buf(str);
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100273 ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
Primiano Tucci82a8bfd2018-09-19 11:33:04 +0100274 }
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000275 }
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100276 }
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000277
Primiano Tucci45c9b182018-03-29 14:10:51 +0100278 if (wrap_in_json) {
279 *output << kTraceHeader;
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100280 *output << kProcessDumpHeader;
281 for (const auto& process : proc_dump) {
282 *output << process << "\\n";
283 }
284 *output << kThreadHeader;
285 for (const auto& thread : thread_dump) {
286 *output << thread << "\\n";
287 }
288 *output << "\",";
Isabelle Taylor1ebefdc2018-10-18 11:02:53 +0100289 *output << kSystemTraceEvents;
290 *output << kFtraceHeader;
291 } else {
292 *output << "TRACE:\n";
Primiano Tucci45c9b182018-03-29 14:10:51 +0100293 *output << kFtraceHeader;
294 }
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000295
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100296 fprintf(stderr, "\n");
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100297 size_t total_events = ftrace_sorted.size();
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100298 size_t written_events = 0;
Isabelle Taylor0a6b4c72018-10-15 11:30:52 +0100299 for (auto it = ftrace_sorted.begin(); it != ftrace_sorted.end(); it++) {
Primiano Tucci1c752c12018-10-23 09:27:19 +0100300 *output << it->second;
301 *output << (wrap_in_json ? "\\n" : "\n");
302 if (!g_output_is_tty && (written_events++ % 1000 == 0 ||
303 written_events == ftrace_sorted.size())) {
304 fprintf(stderr, "Writing trace: %.2f %%" PROGRESS_CHAR,
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100305 written_events * 100.0 / total_events);
306 fflush(stderr);
Primiano Tucci1c752c12018-10-23 09:27:19 +0100307 output->flush();
Primiano Tucci2ffd1a52018-03-27 01:01:30 +0100308 }
309 }
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000310
Primiano Tucci45c9b182018-03-29 14:10:51 +0100311 if (wrap_in_json)
312 *output << kTraceFooter;
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000313
314 return 0;
315}
316
Hector Dearman85ef5362018-03-27 14:48:47 +0100317int TraceToText(std::istream* input, std::ostream* output) {
318 DiskSourceTree dst;
319 dst.MapPath("perfetto", "protos/perfetto");
320 MFE mfe;
321 Importer importer(&dst, &mfe);
322 const FileDescriptor* parsed_file =
323 importer.Import("perfetto/trace/trace.proto");
324
325 DynamicMessageFactory dmf;
326 const Descriptor* trace_descriptor = parsed_file->message_type(0);
327 const Message* msg_root = dmf.GetPrototype(trace_descriptor);
328 Message* msg = msg_root->New();
329
330 if (!msg->ParseFromIstream(input)) {
331 PERFETTO_ELOG("Could not parse input.");
332 return 1;
333 }
334 OstreamOutputStream zero_copy_output(output);
335 TextFormat::Print(*msg, &zero_copy_output);
336 return 0;
337}
338
Anna Zappone7c104622018-03-28 16:35:18 +0100339void PrintFtraceTrack(std::ostream* output,
340 const uint64_t& start,
341 const uint64_t& end,
342 const std::multiset<uint64_t>& ftrace_timestamps) {
Hector Dearman85ef5362018-03-27 14:48:47 +0100343 constexpr char kFtraceTrackName[] = "ftrace ";
344 size_t width = GetWidth();
345 size_t bucket_count = width - strlen(kFtraceTrackName);
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100346 size_t bucket_size = static_cast<size_t>(end - start) / bucket_count;
Hector Dearman85ef5362018-03-27 14:48:47 +0100347 size_t max = 0;
348 std::vector<size_t> buckets(bucket_count);
349 for (size_t i = 0; i < bucket_count; i++) {
350 auto low = ftrace_timestamps.lower_bound(i * bucket_size + start);
351 auto high = ftrace_timestamps.upper_bound((i + 1) * bucket_size + start);
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100352 buckets[i] = static_cast<size_t>(std::distance(low, high));
Hector Dearman85ef5362018-03-27 14:48:47 +0100353 max = std::max(max, buckets[i]);
354 }
355
356 std::vector<std::string> out =
357 std::vector<std::string>({" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇"});
Anna Zappone7c104622018-03-28 16:35:18 +0100358 *output << "-------------------- " << kFtraceTrackName
359 << "--------------------\n";
360 char line[2048];
Hector Dearman85ef5362018-03-27 14:48:47 +0100361 for (size_t i = 0; i < bucket_count; i++) {
362 sprintf(
363 line, "%s",
364 out[std::min(buckets[i] / (max / out.size()), out.size() - 1)].c_str());
365 *output << std::string(line);
366 }
Isabelle Taylord496ea52018-03-28 11:59:59 +0100367 *output << "\n\n";
Anna Zappone7c104622018-03-28 16:35:18 +0100368}
Isabelle Taylord496ea52018-03-28 11:59:59 +0100369
Lalit Magantic35fcd52018-04-20 15:52:35 +0100370void PrintFtraceStats(std::ostream* output,
371 uint64_t overwrite_count,
Lalit Maganti13c61772018-04-23 12:51:48 +0100372 std::map<FtraceEvent::EventCase, uint64_t> event_counts,
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +0100373 const FtraceStats& before_stats,
374 const FtraceStats& after_stats,
Lalit Magantic35fcd52018-04-20 15:52:35 +0100375 bool compact_output) {
376 if (!compact_output)
377 *output << "--------------------Ftrace Stats-------------------\n";
378
379 char line[2048];
380 if (compact_output) {
381 sprintf(line, "ftrace_overwrite_count,%" PRIu64 "\n", overwrite_count);
382 } else {
383 sprintf(line, "Events overwritten: %" PRIu64 "\n", overwrite_count);
384 }
385 *output << std::string(line);
386
Lalit Maganti13c61772018-04-23 12:51:48 +0100387 DiskSourceTree dst;
388 dst.MapPath("perfetto", "protos/perfetto");
389 MFE mfe;
390 Importer importer(&dst, &mfe);
391 const FileDescriptor* parsed_file =
392 importer.Import("perfetto/trace/ftrace/ftrace_event.proto");
393
394 DynamicMessageFactory dmf;
395 const Descriptor* ftrace_descriptor = parsed_file->message_type(0);
396 for (const auto& event_to_count : event_counts) {
397 const std::string& event_name =
398 ftrace_descriptor->FindFieldByNumber(event_to_count.first)->name();
399 uint64_t count = event_to_count.second;
400 if (compact_output) {
401 sprintf(line, "%s,%" PRIu64 "\n", event_name.c_str(), count);
402 } else {
403 sprintf(line, "%s count: %" PRIu64 "\n", event_name.c_str(), count);
404 }
405 *output << std::string(line);
406 }
407
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +0100408 uint64_t before_total_overrun = 0;
409 uint64_t after_total_overrun = 0;
410 for (const auto& cpu_stats : before_stats.cpu_stats()) {
411 before_total_overrun += cpu_stats.overrun();
412 }
413 for (const auto& cpu_stats : after_stats.cpu_stats()) {
414 after_total_overrun += cpu_stats.overrun();
415 }
416
417 if (compact_output) {
418 sprintf(line, "total_overrun,%" PRIu64 "\n",
419 after_total_overrun - before_total_overrun);
420 } else {
421 sprintf(line, "total_overrun: %" PRIu64 " (= %" PRIu64 " - %" PRIu64 ")\n",
422 after_total_overrun - before_total_overrun, after_total_overrun,
423 before_total_overrun);
424 }
425 *output << std::string(line);
426
Lalit Magantic35fcd52018-04-20 15:52:35 +0100427 if (!compact_output)
428 *output << "\n";
429}
430
Anna Zappone7c104622018-03-28 16:35:18 +0100431void PrintInodeStats(std::ostream* output,
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100432 const std::set<uint64_t>& ftrace_inodes,
433 const uint64_t& ftrace_inode_count,
434 const std::set<uint64_t>& resolved_map_inodes,
Lalit Magantic27975c2018-04-10 21:42:18 +0100435 const std::set<uint64_t>& resolved_scan_inodes,
436 bool compact_output) {
437 if (!compact_output)
438 *output << "--------------------Inode Stats-------------------\n";
439
Anna Zappone7c104622018-03-28 16:35:18 +0100440 char line[2048];
Lalit Magantic27975c2018-04-10 21:42:18 +0100441 if (compact_output) {
442 sprintf(line, "events_inodes,%" PRIu64 "\n", ftrace_inode_count);
443 } else {
444 sprintf(line, "Events with inodes: %" PRIu64 "\n", ftrace_inode_count);
445 }
Anna Zappone7c104622018-03-28 16:35:18 +0100446 *output << std::string(line);
447
Lalit Magantic27975c2018-04-10 21:42:18 +0100448 if (compact_output) {
449 sprintf(line, "events_unique_inodes,%zu\n", ftrace_inodes.size());
450 } else {
451 sprintf(line, "Unique inodes from events: %zu\n", ftrace_inodes.size());
452 }
Anna Zappone7c104622018-03-28 16:35:18 +0100453 *output << std::string(line);
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100454
Lalit Magantic27975c2018-04-10 21:42:18 +0100455 if (compact_output) {
456 sprintf(line, "resolved_inodes_static,%zu\n", resolved_map_inodes.size());
457 } else {
458 sprintf(line, "Resolved inodes from static map: %zu\n",
459 resolved_map_inodes.size());
460 }
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100461 *output << std::string(line);
462
Lalit Magantic27975c2018-04-10 21:42:18 +0100463 if (compact_output) {
464 sprintf(line, "resolved_inodes_scan_cache,%zu\n",
465 resolved_scan_inodes.size());
466 } else {
467 sprintf(line, "Resolved inodes from scan and cache: %zu\n",
468 resolved_scan_inodes.size());
469 }
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100470 *output << std::string(line);
471
472 std::set<uint64_t> resolved_inodes;
473 set_union(resolved_map_inodes.begin(), resolved_map_inodes.end(),
474 resolved_scan_inodes.begin(), resolved_scan_inodes.end(),
475 std::inserter(resolved_inodes, resolved_inodes.begin()));
476
Lalit Magantic27975c2018-04-10 21:42:18 +0100477 if (compact_output) {
478 sprintf(line, "total_resolved_inodes,%zu\n", resolved_inodes.size());
479 } else {
480 sprintf(line, "Total resolved inodes: %zu\n", resolved_inodes.size());
481 }
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100482 *output << std::string(line);
483
484 std::set<uint64_t> intersect;
485 set_intersection(resolved_inodes.begin(), resolved_inodes.end(),
486 ftrace_inodes.begin(), ftrace_inodes.end(),
487 std::inserter(intersect, intersect.begin()));
488
Lalit Magantic27975c2018-04-10 21:42:18 +0100489 size_t unresolved_inodes = ftrace_inodes.size() - intersect.size();
490 if (compact_output) {
491 sprintf(line, "unresolved_inodes,%zu\n", unresolved_inodes);
492 } else {
493 sprintf(line, "Unresolved inodes: %zu\n", unresolved_inodes);
494 }
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100495 *output << std::string(line);
496
Lalit Magantic27975c2018-04-10 21:42:18 +0100497 size_t unexpected_inodes = resolved_inodes.size() - intersect.size();
498 if (compact_output) {
499 sprintf(line, "unexpected_inodes_fs,%zu\n", unexpected_inodes);
500 } else {
501 sprintf(line, "Unexpected inodes from filesystem: %zu\n",
502 unexpected_inodes);
503 }
504 *output << std::string(line);
505
506 if (!compact_output)
507 *output << "\n";
Anna Zappone7c104622018-03-28 16:35:18 +0100508}
509
510void PrintProcessStats(std::ostream* output,
511 const std::set<pid_t>& tids_in_tree,
Lalit Magantic27975c2018-04-10 21:42:18 +0100512 const std::set<pid_t>& tids_in_events,
513 bool compact_output) {
514 if (!compact_output)
515 *output << "----------------Process Tree Stats----------------\n";
Isabelle Taylord496ea52018-03-28 11:59:59 +0100516
517 char tid[2048];
Lalit Magantic27975c2018-04-10 21:42:18 +0100518 if (compact_output) {
519 sprintf(tid, "unique_thread_process,%zu\n", tids_in_tree.size());
520 } else {
521 sprintf(tid, "Unique thread ids in process tree: %zu\n",
522 tids_in_tree.size());
523 }
Isabelle Taylord496ea52018-03-28 11:59:59 +0100524 *output << std::string(tid);
525
526 char tid_event[2048];
Lalit Magantic27975c2018-04-10 21:42:18 +0100527 if (compact_output) {
528 sprintf(tid_event, "unique_thread_ftrace,%zu\n", tids_in_events.size());
529 } else {
530 sprintf(tid_event, "Unique thread ids in ftrace events: %zu\n",
531 tids_in_events.size());
532 }
Isabelle Taylord496ea52018-03-28 11:59:59 +0100533 *output << std::string(tid_event);
534
535 std::set<pid_t> intersect;
536 set_intersection(tids_in_tree.begin(), tids_in_tree.end(),
537 tids_in_events.begin(), tids_in_events.end(),
538 std::inserter(intersect, intersect.begin()));
539
540 char matching[2048];
Lalit Magantic27975c2018-04-10 21:42:18 +0100541 size_t thread_id_process_info =
542 (intersect.size() * 100) / tids_in_events.size();
543 if (compact_output) {
544 sprintf(matching,
545 "tids_with_pinfo,%zu\ntids,%zu\ntids_with_pinfo_percentage,%zu\n",
546 intersect.size(), tids_in_events.size(), thread_id_process_info);
547 } else {
Lalit Maganti13c61772018-04-23 12:51:48 +0100548 sprintf(matching, "Thread ids with process info: %zu/%zu -> %zu %%\n",
Lalit Magantic27975c2018-04-10 21:42:18 +0100549 intersect.size(), tids_in_events.size(), thread_id_process_info);
550 }
Isabelle Taylord496ea52018-03-28 11:59:59 +0100551 *output << std::string(matching);
Lalit Magantic27975c2018-04-10 21:42:18 +0100552
553 if (!compact_output)
554 *output << "\n";
Anna Zappone7c104622018-03-28 16:35:18 +0100555}
556
Primiano Tucci5e33cad2018-04-30 14:41:25 +0100557void PrintTraceStats(std::ostream* output,
558 const protos::TraceStats& stats,
559 bool compact_output) {
560 if (compact_output)
561 return;
562 *output << "--------------------Trace Stats-------------------\n";
563 size_t buf_num = 0;
564 for (const auto& buf : stats.buffer_stats()) {
565 *output << "Buffer " << buf_num++ << "\n"
566 << " bytes_written: " << buf.bytes_written() << "\n"
567 << " chunks_written: " << buf.chunks_written() << "\n"
568 << " chunks_overwritten: " << buf.chunks_overwritten() << "\n"
569 << " write_wrap_count: " << buf.write_wrap_count() << "\n"
570 << " patches_succeeded: " << buf.patches_succeeded() << "\n"
571 << " patches_failed: " << buf.patches_failed() << "\n"
572 << " readaheads_succeeded: " << buf.readaheads_succeeded() << "\n"
573 << " readaheads_failed: " << buf.readaheads_failed() << "\n"
574 << " abi_violations: " << buf.abi_violations() << "\n";
575 }
576 *output << "producers_connected: " << stats.producers_connected() << "\n"
577 << "producers_seen: " << stats.producers_seen() << "\n"
578 << "data_sources_reg: " << stats.data_sources_registered() << "\n"
579 << "data_sources_seen: " << stats.data_sources_seen() << "\n"
580 << "tracing_sessions: " << stats.tracing_sessions() << "\n"
581 << "total_buffers: " << stats.total_buffers() << "\n";
582}
583
Lalit Magantic27975c2018-04-10 21:42:18 +0100584int TraceToSummary(std::istream* input,
585 std::ostream* output,
586 bool compact_output) {
Florian Mayere932d182018-05-17 18:26:37 +0100587 uint64_t ftrace_start = std::numeric_limits<uint64_t>::max();
588 uint64_t ftrace_end = 0;
589 uint64_t boottime_start = std::numeric_limits<uint64_t>::max();
590 uint64_t boottime_end = 0;
Lalit Magantic35fcd52018-04-20 15:52:35 +0100591 uint64_t ftrace_overwrites = 0;
Lalit Maganti13c61772018-04-23 12:51:48 +0100592 std::map<FtraceEvent::EventCase, uint64_t> ftrace_event_counts;
Anna Zappone7c104622018-03-28 16:35:18 +0100593 std::multiset<uint64_t> ftrace_timestamps;
594 std::set<pid_t> tids_in_tree;
595 std::set<pid_t> tids_in_events;
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100596 std::set<uint64_t> ftrace_inodes;
597 uint64_t ftrace_inode_count = 0;
598 std::set<uint64_t> resolved_map_inodes;
599 std::set<uint64_t> resolved_scan_inodes;
Primiano Tucci5e33cad2018-04-30 14:41:25 +0100600 protos::TraceStats last_stats;
Anna Zappone7c104622018-03-28 16:35:18 +0100601
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +0100602 FtraceStats before_stats;
603 FtraceStats after_stats;
604
Anna Zappone7c104622018-03-28 16:35:18 +0100605 ForEachPacketInTrace(
Primiano Tucci5e33cad2018-04-30 14:41:25 +0100606 input,
Florian Mayere932d182018-05-17 18:26:37 +0100607 [&ftrace_start, &ftrace_end, &ftrace_overwrites, &ftrace_event_counts,
608 &before_stats, &after_stats, &ftrace_timestamps, &tids_in_tree,
609 &tids_in_events, &ftrace_inodes, &ftrace_inode_count,
610 &resolved_map_inodes, &resolved_scan_inodes, &last_stats,
611 &boottime_start, &boottime_end](const protos::TracePacket& packet) {
Anna Zappone7c104622018-03-28 16:35:18 +0100612 if (packet.has_process_tree()) {
613 const ProcessTree& tree = packet.process_tree();
614 for (Process process : tree.processes()) {
Isabelle Taylorb2499472018-04-03 15:21:04 +0100615 tids_in_tree.insert(process.pid());
Primiano Tuccibd0c02f2018-04-25 14:00:59 +0100616 for (ProcessTree::Thread thread : process.threads_deprecated())
Anna Zappone7c104622018-03-28 16:35:18 +0100617 tids_in_tree.insert(thread.tid());
Anna Zappone7c104622018-03-28 16:35:18 +0100618 }
Primiano Tuccibd0c02f2018-04-25 14:00:59 +0100619 for (ProcessTree::Thread thread : tree.threads())
620 tids_in_tree.insert(thread.tid());
Anna Zappone7c104622018-03-28 16:35:18 +0100621 }
622
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100623 if (packet.has_inode_file_map()) {
624 const InodeFileMap& inode_file_map = packet.inode_file_map();
625 const auto& mount_points = inode_file_map.mount_points();
626 bool from_scan = std::find(mount_points.begin(), mount_points.end(),
627 "/data") != mount_points.end();
628 for (const auto& entry : inode_file_map.entries())
629 if (from_scan)
630 resolved_scan_inodes.insert(entry.inode_number());
631 else
632 resolved_map_inodes.insert(entry.inode_number());
633 }
634
Primiano Tucci5e33cad2018-04-30 14:41:25 +0100635 if (packet.has_trace_stats())
636 last_stats = packet.trace_stats();
637
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +0100638 if (packet.has_ftrace_stats()) {
639 const auto& ftrace_stats = packet.ftrace_stats();
640 if (ftrace_stats.phase() == FtraceStats_Phase_START_OF_TRACE) {
641 before_stats = ftrace_stats;
642 // TODO(hjd): Check not yet set.
643 } else if (ftrace_stats.phase() == FtraceStats_Phase_END_OF_TRACE) {
644 after_stats = ftrace_stats;
645 // TODO(hjd): Check not yet set.
646 } else {
647 // TODO(hjd): Error here.
648 }
649 }
650
Florian Mayere932d182018-05-17 18:26:37 +0100651 if (packet.has_clock_snapshot()) {
652 for (const auto& clock : packet.clock_snapshot().clocks()) {
653 if (clock.type() == protos::ClockSnapshot_Clock_Type_MONOTONIC) {
654 boottime_start =
655 std::min<uint64_t>(boottime_start, clock.timestamp());
656 boottime_end =
657 std::max<uint64_t>(boottime_end, clock.timestamp());
658 }
659 }
660 }
661
Anna Zappone7c104622018-03-28 16:35:18 +0100662 if (!packet.has_ftrace_events())
663 return;
664
665 const FtraceEventBundle& bundle = packet.ftrace_events();
Lalit Magantic35fcd52018-04-20 15:52:35 +0100666 ftrace_overwrites += bundle.overwrite_count();
667
Anna Zappone7c104622018-03-28 16:35:18 +0100668 uint64_t inode_number = 0;
669 for (const FtraceEvent& event : bundle.event()) {
Lalit Maganti13c61772018-04-23 12:51:48 +0100670 ftrace_event_counts[event.event_case()] += 1;
671
Anna Zappone7c104622018-03-28 16:35:18 +0100672 if (ParseInode(event, &inode_number)) {
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100673 ftrace_inodes.insert(inode_number);
674 ftrace_inode_count++;
Anna Zappone7c104622018-03-28 16:35:18 +0100675 }
676 if (event.pid()) {
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100677 tids_in_events.insert(static_cast<int>(event.pid()));
Anna Zappone7c104622018-03-28 16:35:18 +0100678 }
679 if (event.timestamp()) {
Florian Mayere932d182018-05-17 18:26:37 +0100680 ftrace_start = std::min<uint64_t>(ftrace_start, event.timestamp());
681 ftrace_end = std::max<uint64_t>(ftrace_end, event.timestamp());
Anna Zappone7c104622018-03-28 16:35:18 +0100682 ftrace_timestamps.insert(event.timestamp());
683 }
684 }
685 });
686
687 fprintf(stderr, "\n");
688
689 char line[2048];
Florian Mayere932d182018-05-17 18:26:37 +0100690 uint64_t ftrace_duration = (ftrace_end - ftrace_start) / (1000 * 1000);
Lalit Magantic27975c2018-04-10 21:42:18 +0100691 if (compact_output) {
Florian Mayere932d182018-05-17 18:26:37 +0100692 sprintf(line, "ftrace duration,%" PRIu64 "\n", ftrace_duration);
Lalit Magantic27975c2018-04-10 21:42:18 +0100693 } else {
Florian Mayerf6d1bfa2018-05-18 10:47:18 +0100694 sprintf(line, "Ftrace duration: %" PRIu64 "ms\n", ftrace_duration);
Florian Mayere932d182018-05-17 18:26:37 +0100695 }
696 *output << std::string(line);
697
698 uint64_t boottime_duration = (boottime_end - boottime_start) / (1000 * 1000);
699 if (compact_output) {
Florian Mayerf6d1bfa2018-05-18 10:47:18 +0100700 sprintf(line, "boottime duration,%" PRIu64 "\n", boottime_duration);
Florian Mayere932d182018-05-17 18:26:37 +0100701 } else {
Florian Mayerf6d1bfa2018-05-18 10:47:18 +0100702 sprintf(line, "Boottime duration: %" PRIu64 "ms\n", boottime_duration);
Lalit Magantic27975c2018-04-10 21:42:18 +0100703 }
Anna Zappone7c104622018-03-28 16:35:18 +0100704 *output << std::string(line);
705
Lalit Magantic27975c2018-04-10 21:42:18 +0100706 if (!compact_output)
Florian Mayere932d182018-05-17 18:26:37 +0100707 PrintFtraceTrack(output, ftrace_start, ftrace_end, ftrace_timestamps);
Hector Dearmanf3f8b8f2018-04-30 15:00:51 +0100708 PrintFtraceStats(output, ftrace_overwrites, ftrace_event_counts, before_stats,
709 after_stats, compact_output);
Lalit Magantic27975c2018-04-10 21:42:18 +0100710 PrintProcessStats(output, tids_in_tree, tids_in_events, compact_output);
Anna Zappone5e8fa4b2018-03-29 12:56:22 +0100711 PrintInodeStats(output, ftrace_inodes, ftrace_inode_count,
Lalit Magantic27975c2018-04-10 21:42:18 +0100712 resolved_map_inodes, resolved_scan_inodes, compact_output);
Primiano Tucci5e33cad2018-04-30 14:41:25 +0100713 PrintTraceStats(output, last_stats, compact_output);
Isabelle Taylord496ea52018-03-28 11:59:59 +0100714
Hector Dearman85ef5362018-03-27 14:48:47 +0100715 return 0;
716}
717
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000718} // namespace
719} // namespace perfetto
720
721namespace {
722
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100723int Usage(const char* argv0) {
Lalit Magantic27975c2018-04-10 21:42:18 +0100724 printf(
Primiano Tucci1c752c12018-10-23 09:27:19 +0100725 "Usage: %s systrace|json|text|summary|short_summary [trace.proto] "
726 "[trace.txt]\n",
Lalit Magantic27975c2018-04-10 21:42:18 +0100727 argv0);
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000728 return 1;
729}
730
731} // namespace
732
733int main(int argc, char** argv) {
Primiano Tucci1c752c12018-10-23 09:27:19 +0100734 if (argc < 2)
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100735 return Usage(argv[0]);
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000736
Primiano Tucci1c752c12018-10-23 09:27:19 +0100737 std::istream* input_stream;
738 std::ifstream file_istream;
739 if (argc > 2) {
740 const char* file_path = argv[2];
741 file_istream.open(file_path, std::ios_base::in | std::ios_base::binary);
742 if (!file_istream.is_open())
743 PERFETTO_FATAL("Could not open %s", file_path);
744 input_stream = &file_istream;
745 } else {
746 if (isatty(STDIN_FILENO)) {
747 PERFETTO_ELOG("Reading from stdin but it's connected to a TTY");
748 PERFETTO_LOG("It is unlikely that you want to type in some binary.");
749 PERFETTO_LOG("Either pass a file path to the cmdline or pipe stdin");
750 return Usage(argv[0]);
751 }
752 input_stream = &std::cin;
753 }
754
755 std::ostream* output_stream;
756 std::ofstream file_ostream;
757 if (argc > 3) {
758 const char* file_path = argv[3];
759 file_ostream.open(file_path, std::ios_base::out | std::ios_base::trunc);
760 if (!file_ostream.is_open())
761 PERFETTO_FATAL("Could not open %s", file_path);
762 output_stream = &file_ostream;
763 } else {
764 output_stream = &std::cout;
765 perfetto::g_output_is_tty = isatty(STDOUT_FILENO);
766 }
767
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000768 std::string format(argv[1]);
769
Primiano Tucci45c9b182018-03-29 14:10:51 +0100770 if (format == "json")
Primiano Tucci1c752c12018-10-23 09:27:19 +0100771 return perfetto::TraceToSystrace(input_stream, output_stream,
Primiano Tucci45c9b182018-03-29 14:10:51 +0100772 /*wrap_in_json=*/true);
773 if (format == "systrace")
Primiano Tucci1c752c12018-10-23 09:27:19 +0100774 return perfetto::TraceToSystrace(input_stream, output_stream,
Primiano Tucci45c9b182018-03-29 14:10:51 +0100775 /*wrap_in_json=*/false);
776 if (format == "text")
Primiano Tucci1c752c12018-10-23 09:27:19 +0100777 return perfetto::TraceToText(input_stream, output_stream);
Primiano Tuccif0269902018-04-13 11:13:19 +0100778
Primiano Tucci45c9b182018-03-29 14:10:51 +0100779 if (format == "summary")
Primiano Tucci1c752c12018-10-23 09:27:19 +0100780 return perfetto::TraceToSummary(input_stream, output_stream,
Primiano Tuccif0269902018-04-13 11:13:19 +0100781 /* compact_output */ false);
Lalit Magantic27975c2018-04-10 21:42:18 +0100782 if (format == "short_summary")
Primiano Tucci1c752c12018-10-23 09:27:19 +0100783 return perfetto::TraceToSummary(input_stream, output_stream,
Lalit Magantic27975c2018-04-10 21:42:18 +0100784 /* compact_output */ true);
785
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100786 return Usage(argv[0]);
Hector Dearman20b3c1c2018-01-15 15:34:03 +0000787}