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