main.cc revision 7f37dc899fc5cb56a5f5d5c75790dfa775104fc8
1/*
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>
20#include <algorithm>
21#include <fstream>
22#include <iostream>
23#include <istream>
24#include <map>
25#include <memory>
26#include <ostream>
27#include <sstream>
28#include <string>
29#include <utility>
30
31#include <google/protobuf/compiler/importer.h>
32#include <google/protobuf/dynamic_message.h>
33#include <google/protobuf/io/zero_copy_stream_impl.h>
34#include <google/protobuf/text_format.h>
35#include <google/protobuf/util/field_comparator.h>
36#include <google/protobuf/util/message_differencer.h>
37
38#include "perfetto/base/logging.h"
39#include "perfetto/trace/trace.pb.h"
40#include "perfetto/trace/trace_packet.pb.h"
41
42namespace perfetto {
43namespace {
44
45const char kTraceHeader[] = R"({
46  "traceEvents": [],
47)";
48
49const char kTraceFooter[] = R"(\n",
50  "controllerTraceDataKey": "systraceController"
51})";
52
53const char kFtraceHeader[] =
54    ""
55    "  \"systemTraceEvents\": \""
56    "# tracer: nop\\n"
57    "#\\n"
58    "# entries-in-buffer/entries-written: 30624/30624   #P:4\\n"
59    "#\\n"
60    "#                                      _-----=> irqs-off\\n"
61    "#                                     / _----=> need-resched\\n"
62    "#                                    | / _---=> hardirq/softirq\\n"
63    "#                                    || / _--=> preempt-depth\\n"
64    "#                                    ||| /     delay\\n"
65    "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\\n"
66    "#              | |        |      |   ||||       |         |\\n";
67
68using google::protobuf::Descriptor;
69using google::protobuf::DynamicMessageFactory;
70using google::protobuf::FileDescriptor;
71using google::protobuf::Message;
72using google::protobuf::TextFormat;
73using google::protobuf::compiler::DiskSourceTree;
74using google::protobuf::compiler::Importer;
75using google::protobuf::compiler::MultiFileErrorCollector;
76using google::protobuf::io::OstreamOutputStream;
77using protos::FtraceEvent;
78using protos::FtraceEventBundle;
79using protos::PrintFtraceEvent;
80using protos::SchedSwitchFtraceEvent;
81using protos::CpuFrequencyFtraceEvent;
82using protos::CpuFrequencyLimitsFtraceEvent;
83using protos::CpuIdleFtraceEvent;
84using protos::ClockEnableFtraceEvent;
85using protos::ClockDisableFtraceEvent;
86using protos::ClockSetRateFtraceEvent;
87using protos::Trace;
88using protos::TracePacket;
89
90class MFE : public MultiFileErrorCollector {
91  virtual void AddError(const std::string& filename,
92                        int line,
93                        int column,
94                        const std::string& message) {
95    PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
96                  message.c_str());
97  }
98
99  virtual void AddWarning(const std::string& filename,
100                          int line,
101                          int column,
102                          const std::string& message) {
103    PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
104                  message.c_str());
105  }
106};
107
108const char* GetFlag(int32_t state) {
109  state &= 511;
110  if (state & 1)
111    return "S";
112  if (state & 2)
113    return "D";
114  if (state & 4)
115    return "T";
116  if (state & 8)
117    return "t";
118  if (state & 16)
119    return "Z";
120  if (state & 32)
121    return "X";
122  if (state & 64)
123    return "x";
124  if (state & 128)
125    return "W";
126  return "R";
127}
128
129uint64_t TimestampToSeconds(uint64_t timestamp) {
130  return timestamp / 1000000000ul;
131}
132
133uint64_t TimestampToMicroseconds(uint64_t timestamp) {
134  return (timestamp / 1000) % 1000000ul;
135}
136
137std::string FormatPrefix(uint64_t timestamp, uint64_t cpu) {
138  char line[2048];
139  uint64_t seconds = TimestampToSeconds(timestamp);
140  uint64_t useconds = TimestampToMicroseconds(timestamp);
141  sprintf(line,
142          "<idle>-0     (-----) [%03" PRIu64 "] d..3 %" PRIu64 ".%.6" PRIu64
143          ": ",
144          cpu, seconds, useconds);
145  return std::string(line);
146}
147
148std::string FormatSchedSwitch(const SchedSwitchFtraceEvent& sched_switch) {
149  char line[2048];
150  sprintf(line,
151          "sched_switch: prev_comm=%s "
152          "prev_pid=%d prev_prio=%d prev_state=%s ==> next_comm=%s next_pid=%d "
153          "next_prio=%d\\n",
154          sched_switch.prev_comm().c_str(), sched_switch.prev_pid(),
155          sched_switch.prev_prio(), GetFlag(sched_switch.prev_state()),
156          sched_switch.next_comm().c_str(), sched_switch.next_pid(),
157          sched_switch.next_prio());
158  return std::string(line);
159}
160
161std::string FormatPrint(const PrintFtraceEvent& print) {
162  char line[2048];
163  std::string msg = print.buf();
164  // Remove any newlines in the message. It's not entirely clear what the right
165  // behaviour is here. Maybe we should escape them instead?
166  msg.erase(std::remove(msg.begin(), msg.end(), '\n'), msg.end());
167  sprintf(line, "tracing_mark_write: %s\\n", msg.c_str());
168  return std::string(line);
169}
170
171std::string FormatCpuFrequency(const CpuFrequencyFtraceEvent& event) {
172  char line[2048];
173  sprintf(line, "cpu_frequency: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n",
174          event.state(), event.cpu_id());
175  return std::string(line);
176}
177
178std::string FormatCpuFrequencyLimits(
179    const CpuFrequencyLimitsFtraceEvent& event) {
180  char line[2048];
181  sprintf(line,
182          "cpu_frequency_limits: min_freq=%" PRIu32 "max_freq=%" PRIu32
183          " cpu_id=%" PRIu32 "\\n",
184          event.min_freq(), event.max_freq(), event.cpu_id());
185  return std::string(line);
186}
187
188std::string FormatCpuIdle(const CpuIdleFtraceEvent& event) {
189  char line[2048];
190  sprintf(line, "cpu_idle: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n",
191          event.state(), event.cpu_id());
192  return std::string(line);
193}
194
195std::string FormatClockSetRate(const ClockSetRateFtraceEvent& event) {
196  char line[2048];
197  sprintf(line, "clock_set_rate: %s state=%llu cpu_id=%llu\\n",
198          event.name().empty() ? "todo" : event.name().c_str(), event.state(),
199          event.cpu_id());
200  return std::string(line);
201}
202
203std::string FormatClockEnable(const ClockEnableFtraceEvent& event) {
204  char line[2048];
205  sprintf(line, "clock_enable: %s state=%llu cpu_id=%llu\\n",
206          event.name().empty() ? "todo" : event.name().c_str(), event.state(),
207          event.cpu_id());
208  return std::string(line);
209}
210
211std::string FormatClockDisable(const ClockDisableFtraceEvent& event) {
212  char line[2048];
213  sprintf(line, "clock_disable: %s state=%llu cpu_id=%llu\\n",
214          event.name().empty() ? "todo" : event.name().c_str(), event.state(),
215          event.cpu_id());
216  return std::string(line);
217}
218
219int TraceToText(std::istream* input, std::ostream* output) {
220  DiskSourceTree dst;
221  dst.MapPath("perfetto", "protos/perfetto");
222  MFE mfe;
223  Importer importer(&dst, &mfe);
224  const FileDescriptor* parsed_file =
225      importer.Import("perfetto/trace/trace.proto");
226
227  DynamicMessageFactory dmf;
228  const Descriptor* trace_descriptor = parsed_file->message_type(0);
229  const Message* msg_root = dmf.GetPrototype(trace_descriptor);
230  Message* msg = msg_root->New();
231
232  if (!msg->ParseFromIstream(input)) {
233    PERFETTO_ELOG("Could not parse input.");
234    return 1;
235  }
236  OstreamOutputStream zero_copy_output(output);
237  TextFormat::Print(*msg, &zero_copy_output);
238  return 0;
239}
240
241int TraceToSystrace(std::istream* input, std::ostream* output) {
242  std::multimap<uint64_t, std::string> sorted;
243
244  std::string raw;
245  std::istreambuf_iterator<char> begin(*input), end;
246  raw.assign(begin, end);
247  Trace trace;
248  if (!trace.ParseFromString(raw)) {
249    PERFETTO_ELOG("Could not parse input.");
250    return 1;
251  }
252
253  for (const TracePacket& packet : trace.packet()) {
254    if (!packet.has_ftrace_events())
255      continue;
256
257    const FtraceEventBundle& bundle = packet.ftrace_events();
258    for (const FtraceEvent& event : bundle.event()) {
259      std::string line;
260      if (event.has_sched_switch()) {
261        const auto& inner = event.sched_switch();
262        line = FormatSchedSwitch(inner);
263      } else if (event.has_print()) {
264        const auto& inner = event.print();
265        line = FormatPrint(inner);
266      } else if (event.has_cpu_frequency()) {
267        const auto& inner = event.cpu_frequency();
268        line = FormatCpuFrequency(inner);
269      } else if (event.has_cpu_frequency_limits()) {
270        const auto& inner = event.cpu_frequency_limits();
271        line = FormatCpuFrequencyLimits(inner);
272      } else if (event.has_cpu_idle()) {
273        const auto& inner = event.cpu_idle();
274        line = FormatCpuIdle(inner);
275      } else if (event.has_clock_set_rate()) {
276        const auto& inner = event.clock_set_rate();
277        line = FormatClockSetRate(inner);
278      } else if (event.has_clock_enable()) {
279        const auto& inner = event.clock_enable();
280        line = FormatClockEnable(inner);
281      } else if (event.has_clock_disable()) {
282        const auto& inner = event.clock_disable();
283        line = FormatClockDisable(inner);
284      } else {
285        continue;
286      }
287      sorted.emplace(event.timestamp(),
288                     FormatPrefix(event.timestamp(), bundle.cpu()) + line);
289    }
290  }
291
292  *output << kTraceHeader;
293  *output << kFtraceHeader;
294
295  for (auto it = sorted.begin(); it != sorted.end(); it++)
296    *output << it->second;
297
298  *output << kTraceFooter;
299
300  return 0;
301}
302
303}  // namespace
304}  // namespace perfetto
305
306namespace {
307
308int Usage(int argc, char** argv) {
309  printf("Usage: %s [systrace|text] < trace.proto > trace.txt\n", argv[0]);
310  return 1;
311}
312
313}  // namespace
314
315int main(int argc, char** argv) {
316  if (argc != 2)
317    return Usage(argc, argv);
318
319  std::string format(argv[1]);
320
321  if (format != "systrace" && format != "text")
322    return Usage(argc, argv);
323
324  bool systrace = format == "systrace";
325
326  if (systrace) {
327    return perfetto::TraceToSystrace(&std::cin, &std::cout);
328  } else {
329    return perfetto::TraceToText(&std::cin, &std::cout);
330  }
331}
332