main.cc revision d404ea1f6a7f4fa896229ea9da8000dd717eb499
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::BinderLockedFtraceEvent; 78using protos::BinderLockFtraceEvent; 79using protos::BinderSetPriorityFtraceEvent; 80using protos::BinderTransactionFtraceEvent; 81using protos::BinderTransactionReceivedFtraceEvent; 82using protos::BinderUnlockFtraceEvent; 83using protos::BlockRqIssueFtraceEvent; 84using protos::CgroupAttachTaskFtraceEvent; 85using protos::CgroupDestroyRootFtraceEvent; 86using protos::CgroupMkdirFtraceEvent; 87using protos::CgroupReleaseFtraceEvent; 88using protos::CgroupRemountFtraceEvent; 89using protos::CgroupRenameFtraceEvent; 90using protos::CgroupRmdirFtraceEvent; 91using protos::CgroupSetupRootFtraceEvent; 92using protos::CgroupTransferTasksFtraceEvent; 93using protos::ClockDisableFtraceEvent; 94using protos::ClockEnableFtraceEvent; 95using protos::ClockSetRateFtraceEvent; 96using protos::CpuFrequencyFtraceEvent; 97using protos::CpuFrequencyLimitsFtraceEvent; 98using protos::CpuIdleFtraceEvent; 99using protos::Ext4DaWriteBeginFtraceEvent; 100using protos::Ext4DaWriteEndFtraceEvent; 101using protos::Ext4SyncFileEnterFtraceEvent; 102using protos::Ext4SyncFileExitFtraceEvent; 103using protos::FtraceEvent; 104using protos::FtraceEventBundle; 105using protos::I2cReadFtraceEvent; 106using protos::I2cReplyFtraceEvent; 107using protos::I2cResultFtraceEvent; 108using protos::I2cWriteFtraceEvent; 109using protos::IpiEntryFtraceEvent; 110using protos::IpiExitFtraceEvent; 111using protos::IpiRaiseFtraceEvent; 112using protos::IrqHandlerEntryFtraceEvent; 113using protos::IrqHandlerExitFtraceEvent; 114using protos::LowmemoryKillFtraceEvent; 115using protos::MdpCmdKickoffFtraceEvent; 116using protos::MdpCmdPingpongDoneFtraceEvent; 117using protos::MdpCmdReadptrDoneFtraceEvent; 118using protos::MdpCmdReleaseBwFtraceEvent; 119using protos::MdpCmdWaitPingpongFtraceEvent; 120using protos::MdpCommitFtraceEvent; 121using protos::MdpCompareBwFtraceEvent; 122using protos::MdpMisrCrcFtraceEvent; 123using protos::MdpMixerUpdateFtraceEvent; 124using protos::MdpPerfPrefillCalcFtraceEvent; 125using protos::MdpPerfSetOtFtraceEvent; 126using protos::MdpPerfSetPanicLutsFtraceEvent; 127using protos::MdpPerfSetQosLutsFtraceEvent; 128using protos::MdpPerfSetWmLevelsFtraceEvent; 129using protos::MdpPerfUpdateBusFtraceEvent; 130using protos::MdpSsppChangeFtraceEvent; 131using protos::MdpSsppSetFtraceEvent; 132using protos::MdpTraceCounterFtraceEvent; 133using protos::MdpVideoUnderrunDoneFtraceEvent; 134using protos::MmCompactionBeginFtraceEvent; 135using protos::MmCompactionDeferCompactionFtraceEvent; 136using protos::MmCompactionDeferredFtraceEvent; 137using protos::MmCompactionDeferResetFtraceEvent; 138using protos::MmCompactionEndFtraceEvent; 139using protos::MmCompactionFinishedFtraceEvent; 140using protos::MmCompactionIsolateFreepagesFtraceEvent; 141using protos::MmCompactionIsolateMigratepagesFtraceEvent; 142using protos::MmCompactionKcompactdSleepFtraceEvent; 143using protos::MmCompactionKcompactdWakeFtraceEvent; 144using protos::MmCompactionMigratepagesFtraceEvent; 145using protos::MmCompactionSuitableFtraceEvent; 146using protos::MmCompactionTryToCompactPagesFtraceEvent; 147using protos::MmCompactionWakeupKcompactdFtraceEvent; 148using protos::MmFilemapAddToPageCacheFtraceEvent; 149using protos::MmFilemapDeleteFromPageCacheFtraceEvent; 150using protos::MmVmscanDirectReclaimBeginFtraceEvent; 151using protos::MmVmscanDirectReclaimEndFtraceEvent; 152using protos::MmVmscanKswapdSleepFtraceEvent; 153using protos::MmVmscanKswapdWakeFtraceEvent; 154using protos::PrintFtraceEvent; 155using protos::ProcessTree; 156using protos::RegulatorDisableCompleteFtraceEvent; 157using protos::RegulatorDisableFtraceEvent; 158using protos::RegulatorEnableCompleteFtraceEvent; 159using protos::RegulatorEnableDelayFtraceEvent; 160using protos::RegulatorEnableFtraceEvent; 161using protos::RegulatorSetVoltageCompleteFtraceEvent; 162using protos::RegulatorSetVoltageFtraceEvent; 163using protos::RotatorBwAoAsContextFtraceEvent; 164using protos::SchedBlockedReasonFtraceEvent; 165using protos::SchedCpuHotplugFtraceEvent; 166using protos::SchedSwitchFtraceEvent; 167using protos::SchedWakeupFtraceEvent; 168using protos::SchedWakeupNewFtraceEvent; 169using protos::SchedWakingFtraceEvent; 170using protos::SmbusReadFtraceEvent; 171using protos::SmbusReplyFtraceEvent; 172using protos::SmbusResultFtraceEvent; 173using protos::SmbusWriteFtraceEvent; 174using protos::SoftirqEntryFtraceEvent; 175using protos::SoftirqExitFtraceEvent; 176using protos::SoftirqRaiseFtraceEvent; 177using protos::SuspendResumeFtraceEvent; 178using protos::SyncPtFtraceEvent; 179using protos::SyncTimelineFtraceEvent; 180using protos::SyncWaitFtraceEvent; 181using protos::Trace; 182using protos::TracePacket; 183using protos::TracingMarkWriteFtraceEvent; 184using protos::WorkqueueActivateWorkFtraceEvent; 185using protos::WorkqueueExecuteEndFtraceEvent; 186using protos::WorkqueueExecuteStartFtraceEvent; 187using protos::WorkqueueQueueWorkFtraceEvent; 188using Process = protos::ProcessTree::Process; 189 190// TODO(hjd): Add tests. 191 192class MFE : public MultiFileErrorCollector { 193 virtual void AddError(const std::string& filename, 194 int line, 195 int column, 196 const std::string& message) { 197 PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column, 198 message.c_str()); 199 } 200 201 virtual void AddWarning(const std::string& filename, 202 int line, 203 int column, 204 const std::string& message) { 205 PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column, 206 message.c_str()); 207 } 208}; 209 210const char* GetFlag(int32_t state) { 211 state &= 511; 212 if (state & 1) 213 return "S"; 214 if (state & 2) 215 return "D"; 216 if (state & 4) 217 return "T"; 218 if (state & 8) 219 return "t"; 220 if (state & 16) 221 return "Z"; 222 if (state & 32) 223 return "X"; 224 if (state & 64) 225 return "x"; 226 if (state & 128) 227 return "W"; 228 return "R"; 229} 230 231const char* MmCompactionRetArray[] = { 232 "deferred", "skipped", "continue", "partial", 233 "complete", "no_suitable_page", "not_suitable_zone", "contended"}; 234 235const char* MmCompactionSuitableArray[] = {"DMA", "Normal", "Movable"}; 236 237const char* SoftirqArray[] = {"HI", "TIMER", "NET_TX", "NET_RX", 238 "BLOCK", "BLOCK_IOPOLL", "TASKLET", "SCHED", 239 "HRTIMER", "RCU"}; 240 241uint64_t TimestampToSeconds(uint64_t timestamp) { 242 return timestamp / 1000000000ul; 243} 244 245uint64_t TimestampToMicroseconds(uint64_t timestamp) { 246 return (timestamp / 1000) % 1000000ul; 247} 248 249std::string FormatPrefix(uint64_t timestamp, uint64_t cpu) { 250 char line[2048]; 251 uint64_t seconds = TimestampToSeconds(timestamp); 252 uint64_t useconds = TimestampToMicroseconds(timestamp); 253 sprintf(line, 254 "<idle>-0 (-----) [%03" PRIu64 "] d..3 %" PRIu64 ".%.6" PRIu64 255 ": ", 256 cpu, seconds, useconds); 257 return std::string(line); 258} 259 260std::string FormatSchedSwitch(const SchedSwitchFtraceEvent& sched_switch) { 261 char line[2048]; 262 sprintf(line, 263 "sched_switch: prev_comm=%s " 264 "prev_pid=%d prev_prio=%d prev_state=%s ==> next_comm=%s next_pid=%d " 265 "next_prio=%d\\n", 266 sched_switch.prev_comm().c_str(), sched_switch.prev_pid(), 267 sched_switch.prev_prio(), GetFlag(sched_switch.prev_state()), 268 sched_switch.next_comm().c_str(), sched_switch.next_pid(), 269 sched_switch.next_prio()); 270 return std::string(line); 271} 272 273std::string FormatSchedWakeup(const SchedWakeupFtraceEvent& sched_wakeup) { 274 char line[2048]; 275 sprintf(line, 276 "sched_wakeup: comm=%s " 277 "pid=%d prio=%d success=%d target_cpu=%03d\\n", 278 sched_wakeup.comm().c_str(), sched_wakeup.pid(), sched_wakeup.prio(), 279 sched_wakeup.success(), sched_wakeup.target_cpu()); 280 return std::string(line); 281} 282 283std::string FormatSchedBlockedReason( 284 const SchedBlockedReasonFtraceEvent& event) { 285 char line[2048]; 286 sprintf(line, "sched_blocked_reason: pid=%d iowait=%d caller=%llxS\\n", 287 event.pid(), event.io_wait(), event.caller()); 288 return std::string(line); 289} 290 291std::string FormatPrint(const PrintFtraceEvent& print) { 292 char line[2048]; 293 std::string msg = print.buf(); 294 // Remove any newlines in the message. It's not entirely clear what the right 295 // behaviour is here. Maybe we should escape them instead? 296 msg.erase(std::remove(msg.begin(), msg.end(), '\n'), msg.end()); 297 sprintf(line, "tracing_mark_write: %s\\n", msg.c_str()); 298 return std::string(line); 299} 300 301std::string FormatCpuFrequency(const CpuFrequencyFtraceEvent& event) { 302 char line[2048]; 303 sprintf(line, "cpu_frequency: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n", 304 event.state(), event.cpu_id()); 305 return std::string(line); 306} 307 308std::string FormatCpuFrequencyLimits( 309 const CpuFrequencyLimitsFtraceEvent& event) { 310 char line[2048]; 311 sprintf(line, 312 "cpu_frequency_limits: min_freq=%" PRIu32 "max_freq=%" PRIu32 313 " cpu_id=%" PRIu32 "\\n", 314 event.min_freq(), event.max_freq(), event.cpu_id()); 315 return std::string(line); 316} 317 318std::string FormatCpuIdle(const CpuIdleFtraceEvent& event) { 319 char line[2048]; 320 sprintf(line, "cpu_idle: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n", 321 event.state(), event.cpu_id()); 322 return std::string(line); 323} 324 325std::string FormatClockSetRate(const ClockSetRateFtraceEvent& event) { 326 char line[2048]; 327 sprintf(line, "clock_set_rate: %s state=%llu cpu_id=%llu\\n", 328 event.name().empty() ? "todo" : event.name().c_str(), event.state(), 329 event.cpu_id()); 330 return std::string(line); 331} 332 333std::string FormatClockEnable(const ClockEnableFtraceEvent& event) { 334 char line[2048]; 335 sprintf(line, "clock_enable: %s state=%llu cpu_id=%llu\\n", 336 event.name().empty() ? "todo" : event.name().c_str(), event.state(), 337 event.cpu_id()); 338 return std::string(line); 339} 340 341std::string FormatClockDisable(const ClockDisableFtraceEvent& event) { 342 char line[2048]; 343 sprintf(line, "clock_disable: %s state=%llu cpu_id=%llu\\n", 344 event.name().empty() ? "todo" : event.name().c_str(), event.state(), 345 event.cpu_id()); 346 return std::string(line); 347} 348 349std::string FormatTracingMarkWrite(const TracingMarkWriteFtraceEvent& event) { 350 char line[2048]; 351 sprintf(line, "tracing_mark_write: %s|%d|%s\\n", 352 event.trace_begin() ? "B" : "E", event.pid(), 353 event.trace_name().c_str()); 354 return std::string(line); 355} 356 357std::string FormatBinderLocked(const BinderLockedFtraceEvent& event) { 358 char line[2048]; 359 sprintf(line, "binder_locked: tag=%s\\n", event.tag().c_str()); 360 return std::string(line); 361} 362 363std::string FormatBinderUnlock(const BinderUnlockFtraceEvent& event) { 364 char line[2048]; 365 sprintf(line, "binder_unlock: tag=%s\\n", event.tag().c_str()); 366 return std::string(line); 367} 368 369std::string FormatBinderLock(const BinderLockFtraceEvent& event) { 370 char line[2048]; 371 sprintf(line, "binder_lock: tag=%s\\n", event.tag().c_str()); 372 return std::string(line); 373} 374 375std::string FormatBinderTransaction(const BinderTransactionFtraceEvent& event) { 376 char line[2048]; 377 sprintf(line, 378 "binder_transaction: transaction=%d dest_node=%d dest_proc=%d " 379 "dest_thread=%d reply=%d flags=0x%x code=0x%x\\n", 380 event.debug_id(), event.target_node(), event.to_proc(), 381 event.to_thread(), event.reply(), event.flags(), event.code()); 382 return std::string(line); 383} 384 385std::string FormatBinderTransactionReceived( 386 const BinderTransactionReceivedFtraceEvent& event) { 387 char line[2048]; 388 sprintf(line, "binder_transaction_received: transaction=%d\\n", 389 event.debug_id()); 390 return std::string(line); 391} 392 393std::string FormatExt4SyncFileEnter(const Ext4SyncFileEnterFtraceEvent& event) { 394 char line[2048]; 395 sprintf(line, 396 "ext4_sync_file_enter: dev %d,%d ino %lu parent %lu datasync %d \\n", 397 (unsigned int)(event.dev() >> 20), 398 (unsigned int)(event.dev() & ((1U << 20) - 1)), 399 (unsigned long)event.ino(), (unsigned long)event.parent(), 400 event.datasync()); 401 return std::string(line); 402} 403 404std::string FormatExt4SyncFileExit(const Ext4SyncFileExitFtraceEvent& event) { 405 char line[2048]; 406 sprintf(line, "ext4_sync_file_exit: dev %d,%d ino %lu ret %d\\n", 407 (unsigned int)(event.dev() >> 20), 408 (unsigned int)(event.dev() & ((1U << 20) - 1)), 409 (unsigned long)event.ino(), event.ret()); 410 return std::string(line); 411} 412 413std::string FormatExt4DaWriteBegin(const Ext4DaWriteBeginFtraceEvent& event) { 414 char line[2048]; 415 sprintf(line, 416 "ext4_da_write_begin: dev %d,%d ino %lu pos %lld len %u flags %u\\n", 417 (unsigned int)(event.dev() >> 20), 418 (unsigned int)(event.dev() & ((1U << 20) - 1)), 419 (unsigned long)event.ino(), event.pos(), event.len(), event.flags()); 420 return std::string(line); 421} 422 423std::string FormatExt4DaWriteEnd(const Ext4DaWriteEndFtraceEvent& event) { 424 char line[2048]; 425 sprintf(line, 426 "ext4_da_write_end: dev %d,%d ino %lu pos %lld len %u copied %u\\n", 427 (unsigned int)(event.dev() >> 20), 428 (unsigned int)(event.dev() & ((1U << 20) - 1)), 429 (unsigned long)event.ino(), event.pos(), event.len(), event.copied()); 430 return std::string(line); 431} 432 433std::string FormatBlockRqIssue(const BlockRqIssueFtraceEvent& event) { 434 char line[2048]; 435 sprintf(line, "block_rq_issue: %d,%d %s %u (%s) %llu + %u [%s]\\n", 436 (unsigned int)(event.dev() >> 20), 437 (unsigned int)(event.dev() & ((1U << 20) - 1)), event.rwbs().c_str(), 438 event.bytes(), event.cmd().c_str(), 439 (unsigned long long)event.sector(), event.nr_sector(), 440 event.comm().c_str()); 441 return std::string(line); 442} 443 444std::string FormatI2cRead(const I2cReadFtraceEvent& event) { 445 char line[2048]; 446 sprintf(line, "i2c_read: i2c-%d #%u a=%03x f=%04x l=%u\\n", 447 event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(), 448 event.len()); 449 return std::string(line); 450} 451 452std::string FormatI2cResult(const I2cResultFtraceEvent& event) { 453 char line[2048]; 454 sprintf(line, "i2c_result: i2c-%d n=%u ret=%d\\n", event.adapter_nr(), 455 event.nr_msgs(), event.ret()); 456 return std::string(line); 457} 458 459std::string FormatIrqHandlerEntry(const IrqHandlerEntryFtraceEvent& event) { 460 char line[2048]; 461 sprintf(line, "irq_handler_entry: irq=%d name=%s\\n", event.irq(), 462 event.name().c_str()); 463 return std::string(line); 464} 465 466std::string FormatIrqHandlerExit(const IrqHandlerExitFtraceEvent& event) { 467 char line[2048]; 468 sprintf(line, "irq_handler_exit: irq=%d ret=%s\\n", event.irq(), 469 event.ret() ? "handled" : "unhandled"); 470 return std::string(line); 471} 472 473std::string FormatMmVmscanKswapdWake( 474 const MmVmscanKswapdWakeFtraceEvent& event) { 475 char line[2048]; 476 sprintf(line, "mm_vmscan_kswapd_wake: nid=%d order=%d\\n", event.nid(), 477 event.order()); 478 return std::string(line); 479} 480 481std::string FormatMmVmscanKswapdSleep( 482 const MmVmscanKswapdSleepFtraceEvent& event) { 483 char line[2048]; 484 sprintf(line, "mm_vmscan_kswapd_sleep: nid=%d\\n", event.nid()); 485 return std::string(line); 486} 487 488std::string FormatRegulatorEnable(const RegulatorEnableFtraceEvent& event) { 489 char line[2048]; 490 sprintf(line, "regulator_enable: name=%s\\n", event.name().c_str()); 491 return std::string(line); 492} 493 494std::string FormatRegulatorEnableDelay( 495 const RegulatorEnableDelayFtraceEvent& event) { 496 char line[2048]; 497 sprintf(line, "regulator_enable_delay: name=%s\\n", event.name().c_str()); 498 return std::string(line); 499} 500 501std::string FormatRegulatorEnableComplete( 502 const RegulatorEnableCompleteFtraceEvent& event) { 503 char line[2048]; 504 sprintf(line, "regulator_enable_complete: name=%s\\n", event.name().c_str()); 505 return std::string(line); 506} 507 508std::string FormatRegulatorDisable(const RegulatorDisableFtraceEvent& event) { 509 char line[2048]; 510 sprintf(line, "regulator_disable: name=%s\\n", event.name().c_str()); 511 return std::string(line); 512} 513 514std::string FormatRegulatorDisableComplete( 515 const RegulatorDisableCompleteFtraceEvent& event) { 516 char line[2048]; 517 sprintf(line, "regulator_disable_complete: name=%s\\n", event.name().c_str()); 518 return std::string(line); 519} 520 521std::string FormatRegulatorSetVoltage( 522 const RegulatorSetVoltageFtraceEvent& event) { 523 char line[2048]; 524 sprintf(line, "regulator_set_voltage: name=%s (%d-%d)\\n", 525 event.name().c_str(), event.min(), event.max()); 526 return std::string(line); 527} 528 529std::string FormatRegulatorSetVoltageComplete( 530 const RegulatorSetVoltageCompleteFtraceEvent& event) { 531 char line[2048]; 532 sprintf(line, "regulator_set_voltage_complete: name=%s, val=%u\\n", 533 event.name().c_str(), event.val()); 534 return std::string(line); 535} 536 537std::string FormatSchedCpuHotplug(const SchedCpuHotplugFtraceEvent& event) { 538 char line[2048]; 539 sprintf(line, "sched_cpu_hotplug: cpu %d %s error=%d\\n", 540 event.affected_cpu(), event.status() ? "online" : "offline", 541 event.error()); 542 return std::string(line); 543} 544 545std::string FormatSyncTimeline(const SyncTimelineFtraceEvent& event) { 546 char line[2048]; 547 sprintf(line, "sync_timeline: name=%s value=%s\\n", event.name().c_str(), 548 event.value().c_str()); 549 return std::string(line); 550} 551 552std::string FormatSyncWait(const SyncWaitFtraceEvent& event) { 553 char line[2048]; 554 sprintf(line, "sync_wait: %s name=%s state=%d\\n", 555 event.begin() ? "begin" : "end", event.name().c_str(), 556 event.status()); 557 return std::string(line); 558} 559 560std::string FormatSyncPt(const SyncPtFtraceEvent& event) { 561 char line[2048]; 562 sprintf(line, "sync_pt: name=%s value=%s\\n", event.timeline().c_str(), 563 event.value().c_str()); 564 return std::string(line); 565} 566 567int TraceToText(std::istream* input, std::ostream* output) { 568 DiskSourceTree dst; 569 dst.MapPath("perfetto", "protos/perfetto"); 570 MFE mfe; 571 Importer importer(&dst, &mfe); 572 const FileDescriptor* parsed_file = 573 importer.Import("perfetto/trace/trace.proto"); 574 575 DynamicMessageFactory dmf; 576 const Descriptor* trace_descriptor = parsed_file->message_type(0); 577 const Message* msg_root = dmf.GetPrototype(trace_descriptor); 578 Message* msg = msg_root->New(); 579 580 if (!msg->ParseFromIstream(input)) { 581 PERFETTO_ELOG("Could not parse input."); 582 return 1; 583 } 584 OstreamOutputStream zero_copy_output(output); 585 TextFormat::Print(*msg, &zero_copy_output); 586 return 0; 587} 588 589std::string FormatSoftirqRaise(const SoftirqRaiseFtraceEvent& event) { 590 char line[2048]; 591 sprintf(line, "softirq_raise: vec=%u [action=%s]\\n", event.vec(), 592 SoftirqArray[event.vec()]); 593 return std::string(line); 594} 595 596std::string FormatSoftirqEntry(const SoftirqEntryFtraceEvent& event) { 597 char line[2048]; 598 sprintf(line, "softirq_entry: vec=%u [action=%s]\\n", event.vec(), 599 SoftirqArray[event.vec()]); 600 return std::string(line); 601} 602 603std::string FormatSoftirqExit(const SoftirqExitFtraceEvent& event) { 604 char line[2048]; 605 sprintf(line, "softirq_exit: vec=%u [action=%s]\\n", event.vec(), 606 SoftirqArray[event.vec()]); 607 return std::string(line); 608} 609 610std::string FormatI2cWrite(const I2cWriteFtraceEvent& event) { 611 char line[2048]; 612 // TODO(hjd): Check event.buf(). 613 sprintf(line, "i2c_write: i2c-%d #%u a=%03x f=%04x l=%u\\n", 614 event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(), 615 event.len()); 616 return std::string(line); 617} 618 619std::string FormatI2cReply(const I2cReplyFtraceEvent& event) { 620 char line[2048]; 621 // TODO(hjd): Check event.buf(). 622 sprintf(line, "i2c_reply: i2c-%d #%u a=%03x f=%04x l=%u\\n", 623 event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(), 624 event.len()); 625 return std::string(line); 626} 627 628// TODO(hjd): Check gfp_flags 629std::string FormatMmVmscanDirectReclaimBegin( 630 const MmVmscanDirectReclaimBeginFtraceEvent& event) { 631 char line[2048]; 632 sprintf(line, "mm_vmscan_direct_reclaim_begin: order=%d may_writepage=%d\\n", 633 event.order(), event.may_writepage()); 634 return std::string(line); 635} 636 637std::string FormatMmVmscanDirectReclaimEnd( 638 const MmVmscanDirectReclaimEndFtraceEvent& event) { 639 char line[2048]; 640 sprintf(line, "mm_vmscan_direct_reclaim_end: nr_reclaimed=%llu\\n", 641 event.nr_reclaimed()); 642 return std::string(line); 643} 644 645std::string FormatLowmemoryKill(const LowmemoryKillFtraceEvent& event) { 646 char line[2048]; 647 sprintf(line, 648 "lowmemory_kill: %s (%d), page cache %lldkB (limit %lldkB), free " 649 "%lldKb\\n", 650 event.comm().c_str(), event.pid(), event.pagecache_size(), 651 event.pagecache_limit(), event.free()); 652 return std::string(line); 653} 654 655std::string FormatWorkqueueExecuteStart( 656 const WorkqueueExecuteStartFtraceEvent& event) { 657 char line[2048]; 658 sprintf(line, "workqueue_execute_start: work struct %llx: function %llxf\\n", 659 event.work(), event.function()); 660 return std::string(line); 661} 662 663std::string FormatWorkqueueExecuteEnd( 664 const WorkqueueExecuteEndFtraceEvent& event) { 665 char line[2048]; 666 sprintf(line, "workqueue_execute_end: work struct %llx\\n", event.work()); 667 return std::string(line); 668} 669 670std::string FormatWorkqueueQueueWork( 671 const WorkqueueQueueWorkFtraceEvent& event) { 672 char line[2048]; 673 sprintf( 674 line, 675 "workqueue_queue_work: work struct=%llx function=%llxf workqueue=%llx " 676 "req_cpu=%u cpu=%u\\n", 677 event.work(), event.function(), event.workqueue(), event.req_cpu(), 678 event.cpu()); 679 return std::string(line); 680} 681 682std::string FormatWorkqueueActivateWork( 683 const WorkqueueActivateWorkFtraceEvent& event) { 684 char line[2048]; 685 sprintf(line, "workqueue_activate_work: work struct %llx\\n", event.work()); 686 return std::string(line); 687} 688 689std::string FormatMmCompactionBegin(const MmCompactionBeginFtraceEvent& event) { 690 char line[2048]; 691 sprintf(line, 692 "mm_compaction_begin: zone_start=0x%llx migrate_pfn=0x%llx " 693 "free_pfn=0x%llx zone_end=0x%llx, mode=%s\\n", 694 event.zone_start(), event.migrate_pfn(), event.free_pfn(), 695 event.zone_end(), event.sync() ? "sync" : "async"); 696 return std::string(line); 697} 698 699std::string FormatMmCompactionDeferCompaction( 700 const MmCompactionDeferCompactionFtraceEvent& event) { 701 char line[2048]; 702 sprintf(line, 703 "mm_compaction_defer_compaction: node=%d zone=%-8s order=%d " 704 "order_failed=%d consider=%u limit=%lu\\n", 705 event.nid(), MmCompactionSuitableArray[event.idx()], event.order(), 706 event.order_failed(), event.considered(), 1UL << event.defer_shift()); 707 return std::string(line); 708} 709 710std::string FormatMmCompactionDeferred( 711 const MmCompactionDeferredFtraceEvent& event) { 712 char line[2048]; 713 sprintf(line, 714 "mm_compaction_deferred: node=%d zone=%-8s order=%d order_failed=%d " 715 "consider=%u limit=%lu\\n", 716 event.nid(), MmCompactionSuitableArray[event.idx()], event.order(), 717 event.order_failed(), event.considered(), 1UL << event.defer_shift()); 718 return std::string(line); 719} 720 721std::string FormatMmCompactionDeferReset( 722 const MmCompactionDeferResetFtraceEvent& event) { 723 char line[2048]; 724 sprintf(line, 725 "mm_compaction_defer_reset: node=%d zone=%-8s order=%d " 726 "order_failed=%d consider=%u limit=%lu\\n", 727 event.nid(), MmCompactionSuitableArray[event.idx()], event.order(), 728 event.order_failed(), event.considered(), 1UL << event.defer_shift()); 729 return std::string(line); 730} 731 732std::string FormatMmCompactionEnd(const MmCompactionEndFtraceEvent& event) { 733 char line[2048]; 734 sprintf(line, 735 "mm_compaction_end: zone_start=0x%llx migrate_pfn=0x%llx " 736 "free_pfn=0x%llx zone_end=0x%llx, mode=%s status=%s\\n", 737 event.zone_start(), event.migrate_pfn(), event.free_pfn(), 738 event.zone_end(), event.sync() ? "sync" : "aysnc", 739 MmCompactionRetArray[event.status()]); 740 return std::string(line); 741} 742 743std::string FormatMmCompactionFinished( 744 const MmCompactionFinishedFtraceEvent& event) { 745 char line[2048]; 746 sprintf(line, "mm_compaction_finished: node=%d zone=%-8s order=%d ret=%s\\n", 747 event.nid(), MmCompactionSuitableArray[event.idx()], event.order(), 748 MmCompactionRetArray[event.ret()]); 749 return std::string(line); 750} 751 752std::string FormatMmCompactionIsolateFreepages( 753 const MmCompactionIsolateFreepagesFtraceEvent& event) { 754 char line[2048]; 755 sprintf(line, 756 "mm_compaction_isolate_freepages: range=(0x%llx ~ 0x%llx) " 757 "nr_scanned=%llu nr_taken=%llu\\n", 758 event.start_pfn(), event.end_pfn(), event.nr_scanned(), 759 event.nr_taken()); 760 return std::string(line); 761} 762 763std::string FormatMmCompactionIsolateMigratepages( 764 const MmCompactionIsolateMigratepagesFtraceEvent& event) { 765 char line[2048]; 766 sprintf(line, 767 "mm_compaction_isolate_migratepages: range=(0x%llx ~ 0x%llx) " 768 "nr_scanned=%llu nr_taken=%llu\\n", 769 event.start_pfn(), event.end_pfn(), event.nr_scanned(), 770 event.nr_taken()); 771 return std::string(line); 772} 773 774std::string FormatMmCompactionKcompactdSleep( 775 const MmCompactionKcompactdSleepFtraceEvent& event) { 776 char line[2048]; 777 sprintf(line, "mm_compaction_kcompactd_sleep: nid=%d\\n", event.nid()); 778 return std::string(line); 779} 780 781std::string FormatMmCompactionKcompactdWake( 782 const MmCompactionKcompactdWakeFtraceEvent& event) { 783 char line[2048]; 784 sprintf(line, 785 "mm_compaction_kcompactd_wake: nid=%d order=%d classzone_idx=%-8s\\n", 786 event.nid(), event.order(), 787 MmCompactionSuitableArray[event.classzone_idx()]); 788 return std::string(line); 789} 790 791std::string FormatMmCompactionMigratepages( 792 const MmCompactionMigratepagesFtraceEvent& event) { 793 char line[2048]; 794 sprintf(line, 795 "mm_compaction_migratepages: nr_migrated=%llu nr_failed=%llu\\n", 796 event.nr_migrated(), event.nr_failed()); 797 return std::string(line); 798} 799 800std::string FormatMmCompactionSuitable( 801 const MmCompactionSuitableFtraceEvent& event) { 802 char line[2048]; 803 sprintf(line, "mm_compaction_suitable: node=%d zone=%-8s order=%d ret=%s\\n", 804 event.nid(), MmCompactionSuitableArray[event.idx()], event.order(), 805 MmCompactionRetArray[event.ret()]); 806 return std::string(line); 807} 808 809std::string FormatMmCompactionTryToCompactPages( 810 const MmCompactionTryToCompactPagesFtraceEvent& event) { 811 char line[2048]; 812 sprintf( 813 line, 814 "mm_compaction_try_to_compact_pages: order=%d gfp_mask=0x%x mode=%d\\n", 815 event.order(), event.gfp_mask(), 816 event.mode()); // convert to int? 817 return std::string(line); 818} 819 820std::string FormatMmCompactionWakeupKcompactd( 821 const MmCompactionWakeupKcompactdFtraceEvent& event) { 822 char line[2048]; 823 sprintf( 824 line, 825 "mm_compaction_wakeup_kcompactd: nid=%d order=%d classzone_idx=%-8s\\n", 826 event.nid(), event.order(), 827 MmCompactionSuitableArray[event.classzone_idx()]); 828 return std::string(line); 829} 830 831std::string FormatSuspendResume(const SuspendResumeFtraceEvent& event) { 832 char line[2048]; 833 sprintf(line, "suspend_resume: %s[%u] %s\\n", event.action().c_str(), 834 event.val(), event.start() ? "begin" : "end"); 835 return std::string(line); 836} 837 838std::string FormatSchedWakeupNew(const SchedWakeupNewFtraceEvent& event) { 839 char line[2048]; 840 sprintf(line, "sched_wakeup_new: comm=%s pid=%d prio=%d target_cpu=%03d\\n", 841 event.comm().c_str(), event.pid(), event.prio(), event.target_cpu()); 842 return std::string(line); 843} 844 845// TODO(taylori): Confirm correct format for this. 846std::string FormatProcess(const Process& process) { 847 char line[2048]; 848 sprintf(line, "process: pid=%d ppid=%d cmdline=%s\\n", process.pid(), 849 process.ppid(), process.cmdline(0).c_str()); 850 std::string output = std::string(line); 851 for (auto thread : process.threads()) { 852 char thread_line[2048]; 853 sprintf(thread_line, "thread: tid=%d name=%s\\n", thread.tid(), 854 thread.name().c_str()); 855 output += thread_line; 856 } 857 return output; 858} 859 860int TraceToSystrace(std::istream* input, std::ostream* output) { 861 std::multimap<uint64_t, std::string> sorted; 862 863 std::string raw; 864 std::istreambuf_iterator<char> begin(*input), end; 865 raw.assign(begin, end); 866 Trace trace; 867 if (!trace.ParseFromString(raw)) { 868 PERFETTO_ELOG("Could not parse input."); 869 return 1; 870 } 871 872 for (const TracePacket& packet : trace.packet()) { 873 if (packet.has_process_tree()) { 874 const ProcessTree& process_tree = packet.process_tree(); 875 for (const auto& process : process_tree.processes()) { 876 std::string line = FormatProcess(process); 877 sorted.emplace(0, line); 878 } 879 } 880 881 if (!packet.has_ftrace_events()) 882 continue; 883 884 const FtraceEventBundle& bundle = packet.ftrace_events(); 885 for (const FtraceEvent& event : bundle.event()) { 886 std::string line; 887 if (event.has_sched_switch()) { 888 const auto& inner = event.sched_switch(); 889 line = FormatSchedSwitch(inner); 890 } else if (event.has_sched_wakeup()) { 891 const auto& inner = event.sched_wakeup(); 892 line = FormatSchedWakeup(inner); 893 } else if (event.has_sched_blocked_reason()) { 894 const auto& inner = event.sched_blocked_reason(); 895 line = FormatSchedBlockedReason(inner); 896 } else if (event.has_tracing_mark_write()) { 897 const auto& inner = event.tracing_mark_write(); 898 line = FormatTracingMarkWrite(inner); 899 } else if (event.has_binder_locked()) { 900 const auto& inner = event.binder_locked(); 901 line = FormatBinderLocked(inner); 902 } else if (event.has_binder_unlock()) { 903 const auto& inner = event.binder_unlock(); 904 line = FormatBinderUnlock(inner); 905 } else if (event.has_binder_lock()) { 906 const auto& inner = event.binder_lock(); 907 line = FormatBinderLock(inner); 908 } else if (event.has_binder_transaction()) { 909 const auto& inner = event.binder_transaction(); 910 line = FormatBinderTransaction(inner); 911 } else if (event.has_binder_transaction_received()) { 912 const auto& inner = event.binder_transaction_received(); 913 line = FormatBinderTransactionReceived(inner); 914 } else if (event.has_ext4_sync_file_enter()) { 915 const auto& inner = event.ext4_sync_file_enter(); 916 line = FormatExt4SyncFileEnter(inner); 917 } else if (event.has_ext4_sync_file_exit()) { 918 const auto& inner = event.ext4_sync_file_exit(); 919 line = FormatExt4SyncFileExit(inner); 920 } else if (event.has_ext4_da_write_begin()) { 921 const auto& inner = event.ext4_da_write_begin(); 922 line = FormatExt4DaWriteBegin(inner); 923 } else if (event.has_ext4_da_write_end()) { 924 const auto& inner = event.ext4_da_write_end(); 925 line = FormatExt4DaWriteEnd(inner); 926 } else if (event.has_block_rq_issue()) { 927 const auto& inner = event.block_rq_issue(); 928 line = FormatBlockRqIssue(inner); 929 } else if (event.has_i2c_read()) { 930 const auto& inner = event.i2c_read(); 931 line = FormatI2cRead(inner); 932 } else if (event.has_i2c_result()) { 933 const auto& inner = event.i2c_result(); 934 line = FormatI2cResult(inner); 935 } else if (event.has_irq_handler_entry()) { 936 const auto& inner = event.irq_handler_entry(); 937 line = FormatIrqHandlerEntry(inner); 938 } else if (event.has_irq_handler_exit()) { 939 const auto& inner = event.irq_handler_exit(); 940 line = FormatIrqHandlerExit(inner); 941 } else if (event.has_mm_vmscan_kswapd_wake()) { 942 const auto& inner = event.mm_vmscan_kswapd_wake(); 943 line = FormatMmVmscanKswapdWake(inner); 944 } else if (event.has_mm_vmscan_kswapd_sleep()) { 945 const auto& inner = event.mm_vmscan_kswapd_sleep(); 946 line = FormatMmVmscanKswapdSleep(inner); 947 } else if (event.has_regulator_enable()) { 948 const auto& inner = event.regulator_enable(); 949 line = FormatRegulatorEnable(inner); 950 } else if (event.has_regulator_enable_delay()) { 951 const auto& inner = event.regulator_enable_delay(); 952 line = FormatRegulatorEnableDelay(inner); 953 } else if (event.has_regulator_enable_complete()) { 954 const auto& inner = event.regulator_enable_complete(); 955 line = FormatRegulatorEnableComplete(inner); 956 } else if (event.has_regulator_disable()) { 957 const auto& inner = event.regulator_disable(); 958 line = FormatRegulatorDisable(inner); 959 } else if (event.has_regulator_disable_complete()) { 960 const auto& inner = event.regulator_disable_complete(); 961 line = FormatRegulatorDisableComplete(inner); 962 } else if (event.has_regulator_set_voltage()) { 963 const auto& inner = event.regulator_set_voltage(); 964 line = FormatRegulatorSetVoltage(inner); 965 } else if (event.has_regulator_set_voltage_complete()) { 966 const auto& inner = event.regulator_set_voltage_complete(); 967 line = FormatRegulatorSetVoltageComplete(inner); 968 } else if (event.has_sched_cpu_hotplug()) { 969 const auto& inner = event.sched_cpu_hotplug(); 970 line = FormatSchedCpuHotplug(inner); 971 } else if (event.has_sync_timeline()) { 972 const auto& inner = event.sync_timeline(); 973 line = FormatSyncTimeline(inner); 974 } else if (event.has_sync_wait()) { 975 const auto& inner = event.sync_wait(); 976 line = FormatSyncWait(inner); 977 } else if (event.has_sync_pt()) { 978 const auto& inner = event.sync_pt(); 979 line = FormatSyncPt(inner); 980 } else if (event.has_print()) { 981 const auto& inner = event.print(); 982 line = FormatPrint(inner); 983 } else if (event.has_cpu_frequency()) { 984 const auto& inner = event.cpu_frequency(); 985 line = FormatCpuFrequency(inner); 986 } else if (event.has_cpu_frequency_limits()) { 987 const auto& inner = event.cpu_frequency_limits(); 988 line = FormatCpuFrequencyLimits(inner); 989 } else if (event.has_cpu_idle()) { 990 const auto& inner = event.cpu_idle(); 991 line = FormatCpuIdle(inner); 992 } else if (event.has_clock_set_rate()) { 993 const auto& inner = event.clock_set_rate(); 994 line = FormatClockSetRate(inner); 995 } else if (event.has_clock_enable()) { 996 const auto& inner = event.clock_enable(); 997 line = FormatClockEnable(inner); 998 } else if (event.has_clock_disable()) { 999 const auto& inner = event.clock_disable(); 1000 line = FormatClockDisable(inner); 1001 } else if (event.has_i2c_write()) { 1002 const auto& inner = event.i2c_write(); 1003 line = FormatI2cWrite(inner); 1004 } else if (event.has_i2c_reply()) { 1005 const auto& inner = event.i2c_reply(); 1006 line = FormatI2cReply(inner); 1007 } else if (event.has_softirq_raise()) { 1008 const auto& inner = event.softirq_raise(); 1009 line = FormatSoftirqRaise(inner); 1010 } else if (event.has_softirq_entry()) { 1011 const auto& inner = event.softirq_entry(); 1012 line = FormatSoftirqEntry(inner); 1013 } else if (event.has_softirq_exit()) { 1014 const auto& inner = event.softirq_exit(); 1015 line = FormatSoftirqExit(inner); 1016 } else if (event.has_mm_vmscan_direct_reclaim_begin()) { 1017 const auto& inner = event.mm_vmscan_direct_reclaim_begin(); 1018 line = FormatMmVmscanDirectReclaimBegin(inner); 1019 } else if (event.has_mm_vmscan_direct_reclaim_end()) { 1020 const auto& inner = event.mm_vmscan_direct_reclaim_end(); 1021 line = FormatMmVmscanDirectReclaimEnd(inner); 1022 } else if (event.has_lowmemory_kill()) { 1023 const auto& inner = event.lowmemory_kill(); 1024 line = FormatLowmemoryKill(inner); 1025 } else if (event.has_workqueue_execute_start()) { 1026 const auto& inner = event.workqueue_execute_start(); 1027 line = FormatWorkqueueExecuteStart(inner); 1028 } else if (event.has_workqueue_execute_end()) { 1029 const auto& inner = event.workqueue_execute_end(); 1030 line = FormatWorkqueueExecuteEnd(inner); 1031 } else if (event.has_workqueue_queue_work()) { 1032 const auto& inner = event.workqueue_queue_work(); 1033 line = FormatWorkqueueQueueWork(inner); 1034 } else if (event.has_workqueue_activate_work()) { 1035 const auto& inner = event.workqueue_activate_work(); 1036 line = FormatWorkqueueActivateWork(inner); 1037 } else if (event.has_mm_compaction_begin()) { 1038 const auto& inner = event.mm_compaction_begin(); 1039 line = FormatMmCompactionBegin(inner); 1040 } else if (event.has_mm_compaction_deferred()) { 1041 const auto& inner = event.mm_compaction_deferred(); 1042 line = FormatMmCompactionDeferred(inner); 1043 } else if (event.has_mm_compaction_defer_reset()) { 1044 const auto& inner = event.mm_compaction_defer_reset(); 1045 line = FormatMmCompactionDeferReset(inner); 1046 } else if (event.has_mm_compaction_end()) { 1047 const auto& inner = event.mm_compaction_end(); 1048 line = FormatMmCompactionEnd(inner); 1049 } else if (event.has_mm_compaction_finished()) { 1050 const auto& inner = event.mm_compaction_finished(); 1051 line = FormatMmCompactionFinished(inner); 1052 } else if (event.has_mm_compaction_isolate_freepages()) { 1053 const auto& inner = event.mm_compaction_isolate_freepages(); 1054 line = FormatMmCompactionIsolateFreepages(inner); 1055 } else if (event.has_mm_compaction_isolate_migratepages()) { 1056 const auto& inner = event.mm_compaction_isolate_migratepages(); 1057 line = FormatMmCompactionIsolateMigratepages(inner); 1058 } else if (event.has_mm_compaction_kcompactd_sleep()) { 1059 const auto& inner = event.mm_compaction_kcompactd_sleep(); 1060 line = FormatMmCompactionKcompactdSleep(inner); 1061 } else if (event.has_mm_compaction_kcompactd_wake()) { 1062 const auto& inner = event.mm_compaction_kcompactd_wake(); 1063 line = FormatMmCompactionKcompactdWake(inner); 1064 } else if (event.has_mm_compaction_migratepages()) { 1065 const auto& inner = event.mm_compaction_migratepages(); 1066 line = FormatMmCompactionMigratepages(inner); 1067 } else if (event.has_mm_compaction_suitable()) { 1068 const auto& inner = event.mm_compaction_suitable(); 1069 line = FormatMmCompactionSuitable(inner); 1070 } else if (event.has_mm_compaction_try_to_compact_pages()) { 1071 const auto& inner = event.mm_compaction_try_to_compact_pages(); 1072 line = FormatMmCompactionTryToCompactPages(inner); 1073 } else if (event.has_mm_compaction_wakeup_kcompactd()) { 1074 const auto& inner = event.mm_compaction_wakeup_kcompactd(); 1075 line = FormatMmCompactionWakeupKcompactd(inner); 1076 } else if (event.has_suspend_resume()) { 1077 const auto& inner = event.suspend_resume(); 1078 line = FormatSuspendResume(inner); 1079 } else if (event.has_sched_wakeup_new()) { 1080 const auto& inner = event.sched_wakeup_new(); 1081 line = FormatSchedWakeupNew(inner); 1082 } else { 1083 continue; 1084 } 1085 sorted.emplace(event.timestamp(), 1086 FormatPrefix(event.timestamp(), bundle.cpu()) + line); 1087 } 1088 } 1089 1090 *output << kTraceHeader; 1091 *output << kFtraceHeader; 1092 1093 for (auto it = sorted.begin(); it != sorted.end(); it++) 1094 *output << it->second; 1095 1096 *output << kTraceFooter; 1097 1098 return 0; 1099} 1100 1101} // namespace 1102} // namespace perfetto 1103 1104namespace { 1105 1106int Usage(int argc, char** argv) { 1107 printf("Usage: %s [systrace|text] < trace.proto > trace.txt\n", argv[0]); 1108 return 1; 1109} 1110 1111} // namespace 1112 1113int main(int argc, char** argv) { 1114 if (argc != 2) 1115 return Usage(argc, argv); 1116 1117 std::string format(argv[1]); 1118 1119 if (format != "systrace" && format != "text") 1120 return Usage(argc, argv); 1121 1122 bool systrace = format == "systrace"; 1123 1124 if (systrace) { 1125 return perfetto::TraceToSystrace(&std::cin, &std::cout); 1126 } else { 1127 return perfetto::TraceToText(&std::cin, &std::cout); 1128 } 1129} 1130