Home | History | Annotate | Download | only in perfetto_cmd
      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 "src/perfetto_cmd/perfetto_cmd.h"
     18 
     19 #include <fcntl.h>
     20 #include <getopt.h>
     21 #include <signal.h>
     22 #include <stdio.h>
     23 #include <sys/stat.h>
     24 #include <time.h>
     25 #include <unistd.h>
     26 
     27 #include <fstream>
     28 #include <iostream>
     29 #include <iterator>
     30 #include <sstream>
     31 
     32 #include "perfetto/base/file_utils.h"
     33 #include "perfetto/base/logging.h"
     34 #include "perfetto/base/time.h"
     35 #include "perfetto/base/utils.h"
     36 #include "perfetto/protozero/proto_utils.h"
     37 #include "perfetto/traced/traced.h"
     38 #include "perfetto/tracing/core/data_source_config.h"
     39 #include "perfetto/tracing/core/data_source_descriptor.h"
     40 #include "perfetto/tracing/core/trace_config.h"
     41 #include "perfetto/tracing/core/trace_packet.h"
     42 
     43 #include "perfetto/config/trace_config.pb.h"
     44 
     45 #include "src/tracing/ipc/default_socket.h"
     46 
     47 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
     48 
     49 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
     50 #include <android/os/DropBoxManager.h>
     51 #include <utils/Looper.h>
     52 #include <utils/StrongPointer.h>
     53 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
     54 
     55 // TODO(primiano): add the ability to pass the file descriptor directly to the
     56 // traced service instead of receiving a copy of the slices and writing them
     57 // from this process.
     58 namespace perfetto {
     59 namespace {
     60 
     61 constexpr char kDefaultDropBoxTag[] = "perfetto";
     62 
     63 perfetto::PerfettoCmd* g_consumer_cmd;
     64 
     65 }  // namespace
     66 
     67 // Temporary directory for DropBox traces. Note that this is automatically
     68 // created by the system by setting setprop persist.traced.enable=1.
     69 const char* kTempDropBoxTraceDir = "/data/misc/perfetto-traces";
     70 
     71 using protozero::proto_utils::WriteVarInt;
     72 using protozero::proto_utils::MakeTagLengthDelimited;
     73 
     74 int PerfettoCmd::PrintUsage(const char* argv0) {
     75   PERFETTO_ELOG(R"(
     76 Usage: %s
     77   --background     -b     : Exits immediately and continues tracing in background
     78   --config         -c     : /path/to/trace/config/file or - for stdin
     79   --out            -o     : /path/to/out/trace/file
     80   --dropbox        -d TAG : Upload trace into DropBox using tag TAG (default: %s)
     81   --no-guardrails  -n     : Ignore guardrails triggered when using --dropbox (for testing).
     82   --help           -h
     83 
     84 statsd-specific flags:
     85   --alert-id           : ID of the alert that triggered this trace.
     86   --config-id          : ID of the triggering config.
     87   --config-uid         : UID of app which registered the config.
     88 )",
     89                 argv0, kDefaultDropBoxTag);
     90   return 1;
     91 }
     92 
     93 int PerfettoCmd::Main(int argc, char** argv) {
     94   enum LongOption {
     95     OPT_ALERT_ID = 1000,
     96     OPT_CONFIG_ID,
     97     OPT_CONFIG_UID,
     98   };
     99   static const struct option long_options[] = {
    100       // |option_index| relies on the order of options, don't reshuffle them.
    101       {"help", required_argument, nullptr, 'h'},
    102       {"config", required_argument, nullptr, 'c'},
    103       {"out", required_argument, nullptr, 'o'},
    104       {"background", no_argument, nullptr, 'b'},
    105       {"dropbox", optional_argument, nullptr, 'd'},
    106       {"no-guardrails", optional_argument, nullptr, 'n'},
    107       {"alert-id", required_argument, nullptr, OPT_ALERT_ID},
    108       {"config-id", required_argument, nullptr, OPT_CONFIG_ID},
    109       {"config-uid", required_argument, nullptr, OPT_CONFIG_UID},
    110       {nullptr, 0, nullptr, 0}};
    111 
    112   int option_index = 0;
    113   std::string trace_config_raw;
    114   bool background = false;
    115   bool ignore_guardrails = false;
    116   perfetto::protos::TraceConfig::StatsdMetadata statsd_metadata;
    117   for (;;) {
    118     int option =
    119         getopt_long(argc, argv, "c:o:bd::n", long_options, &option_index);
    120 
    121     if (option == -1)
    122       break;  // EOF.
    123 
    124     if (option == 'c') {
    125       if (strcmp(optarg, "-") == 0) {
    126         std::istreambuf_iterator<char> begin(std::cin), end;
    127         trace_config_raw.assign(begin, end);
    128       } else if (strcmp(optarg, ":test") == 0) {
    129         // TODO(primiano): temporary for testing only.
    130         perfetto::protos::TraceConfig test_config;
    131         test_config.add_buffers()->set_size_kb(4096);
    132         test_config.set_duration_ms(2000);
    133         auto* ds_config = test_config.add_data_sources()->mutable_config();
    134         ds_config->set_name("linux.ftrace");
    135         ds_config->mutable_ftrace_config()->add_ftrace_events("sched_switch");
    136         ds_config->mutable_ftrace_config()->add_ftrace_events("cpu_idle");
    137         ds_config->mutable_ftrace_config()->add_ftrace_events("cpu_frequency");
    138         ds_config->set_target_buffer(0);
    139         test_config.SerializeToString(&trace_config_raw);
    140       } else {
    141         if (!base::ReadFile(optarg, &trace_config_raw)) {
    142           PERFETTO_PLOG("Could not open %s", optarg);
    143           return 1;
    144         }
    145       }
    146       continue;
    147     }
    148 
    149     if (option == 'o') {
    150       trace_out_path_ = optarg;
    151       continue;
    152     }
    153 
    154     if (option == 'd') {
    155 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
    156       dropbox_tag_ = optarg ? optarg : kDefaultDropBoxTag;
    157       continue;
    158 #else
    159       PERFETTO_ELOG("DropBox is only supported with Android tree builds");
    160       return 1;
    161 #endif
    162     }
    163 
    164     if (option == 'b') {
    165       background = true;
    166       continue;
    167     }
    168 
    169     if (option == 'n') {
    170       ignore_guardrails = true;
    171       continue;
    172     }
    173 
    174     if (option == OPT_ALERT_ID) {
    175       statsd_metadata.set_triggering_alert_id(atoll(optarg));
    176       continue;
    177     }
    178 
    179     if (option == OPT_CONFIG_ID) {
    180       statsd_metadata.set_triggering_config_id(atoll(optarg));
    181       continue;
    182     }
    183 
    184     if (option == OPT_CONFIG_UID) {
    185       statsd_metadata.set_triggering_config_uid(atoi(optarg));
    186       continue;
    187     }
    188 
    189     return PrintUsage(argv[0]);
    190   }
    191 
    192   if (!trace_out_path_.empty() && !dropbox_tag_.empty()) {
    193     PERFETTO_ELOG(
    194         "Can't log to a file (--out) and DropBox (--dropbox) at the same "
    195         "time");
    196     return 1;
    197   }
    198 
    199   if (trace_out_path_.empty() && dropbox_tag_.empty()) {
    200     return PrintUsage(argv[0]);
    201   }
    202 
    203   if (trace_config_raw.empty()) {
    204     PERFETTO_ELOG("The TraceConfig is empty");
    205     return 1;
    206   }
    207 
    208   perfetto::protos::TraceConfig trace_config_proto;
    209   PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
    210   bool parsed = trace_config_proto.ParseFromString(trace_config_raw);
    211   if (!parsed) {
    212     PERFETTO_ELOG("Could not parse TraceConfig proto from stdin");
    213     return 1;
    214   }
    215   *trace_config_proto.mutable_statsd_metadata() = std::move(statsd_metadata);
    216   trace_config_.reset(new TraceConfig());
    217   trace_config_->FromProto(trace_config_proto);
    218   trace_config_raw.clear();
    219 
    220   if (!OpenOutputFile())
    221     return 1;
    222 
    223   if (background) {
    224     PERFETTO_CHECK(daemon(0 /*nochdir*/, 0 /*noclose*/) == 0);
    225     PERFETTO_DLOG("Continuing in background");
    226   }
    227 
    228   RateLimiter limiter;
    229   RateLimiter::Args args{};
    230   args.is_dropbox = !dropbox_tag_.empty();
    231   args.current_time = base::GetWallTimeS();
    232   args.ignore_guardrails = ignore_guardrails;
    233 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_USERDEBUG_BUILD)
    234   args.max_upload_bytes_override =
    235       trace_config_->guardrail_overrides().max_upload_per_day_bytes();
    236 #endif
    237   if (!limiter.ShouldTrace(args))
    238     return 1;
    239 
    240   consumer_endpoint_ =
    241       ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
    242   SetupCtrlCSignalHandler();
    243   task_runner_.Run();
    244 
    245   return limiter.OnTraceDone(args, did_process_full_trace_,
    246                              bytes_uploaded_to_dropbox_)
    247              ? 0
    248              : 1;
    249 }
    250 
    251 void PerfettoCmd::OnConnect() {
    252   PERFETTO_LOG(
    253       "Connected to the Perfetto traced service, starting tracing for %d ms",
    254       trace_config_->duration_ms());
    255   PERFETTO_DCHECK(trace_config_);
    256   trace_config_->set_enable_extra_guardrails(!dropbox_tag_.empty());
    257 
    258   base::ScopedFile optional_fd;
    259   if (trace_config_->write_into_file())
    260     optional_fd.reset(dup(fileno(*trace_out_stream_)));
    261 
    262   consumer_endpoint_->EnableTracing(*trace_config_, std::move(optional_fd));
    263 
    264   // Failsafe mechanism to avoid waiting indefinitely if the service hangs.
    265   if (trace_config_->duration_ms()) {
    266     task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
    267                                  trace_config_->duration_ms() + 10000);
    268   }
    269 }
    270 
    271 void PerfettoCmd::OnDisconnect() {
    272   PERFETTO_LOG("Disconnected from the Perfetto traced service");
    273   task_runner_.Quit();
    274 }
    275 
    276 void PerfettoCmd::OnTimeout() {
    277   PERFETTO_ELOG("Timed out while waiting for trace from the service, aborting");
    278   task_runner_.Quit();
    279 }
    280 
    281 void PerfettoCmd::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
    282   for (TracePacket& packet : packets) {
    283     uint8_t preamble[16];
    284     uint8_t* pos = preamble;
    285     // ID of the |packet| field in trace.proto. Hardcoded as this we not depend
    286     // on protos/trace:lite for binary size saving reasons.
    287     static constexpr uint32_t kPacketFieldNumber = 1;
    288     pos = WriteVarInt(MakeTagLengthDelimited(kPacketFieldNumber), pos);
    289     pos = WriteVarInt(static_cast<uint32_t>(packet.size()), pos);
    290     fwrite(reinterpret_cast<const char*>(preamble),
    291            static_cast<size_t>(pos - preamble), 1, trace_out_stream_.get());
    292     for (const Slice& slice : packet.slices()) {
    293       fwrite(reinterpret_cast<const char*>(slice.start), slice.size, 1,
    294              trace_out_stream_.get());
    295     }
    296   }
    297 
    298   if (!has_more)
    299     FinalizeTraceAndExit();  // Reached end of trace.
    300 }
    301 
    302 void PerfettoCmd::OnTracingDisabled() {
    303   if (trace_config_->write_into_file()) {
    304     // If write_into_file == true, at this point the passed file contains
    305     // already all the packets.
    306     return FinalizeTraceAndExit();
    307   }
    308   // This will cause a bunch of OnTraceData callbacks. The last one will
    309   // save the file and exit.
    310   consumer_endpoint_->ReadBuffers();
    311 }
    312 
    313 void PerfettoCmd::FinalizeTraceAndExit() {
    314   fflush(*trace_out_stream_);
    315   fseek(*trace_out_stream_, 0, SEEK_END);
    316   long bytes_written = ftell(*trace_out_stream_);
    317   if (dropbox_tag_.empty()) {
    318     trace_out_stream_.reset();
    319     did_process_full_trace_ = true;
    320     PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
    321                   trace_out_path_.c_str());
    322   } else {
    323 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
    324     android::sp<android::os::DropBoxManager> dropbox =
    325         new android::os::DropBoxManager();
    326     fseek(*trace_out_stream_, 0, SEEK_SET);
    327     // DropBox takes ownership of the file descriptor, so give it a duplicate.
    328     // Also we need to give it a read-only copy of the fd or will hit a SELinux
    329     // violation (about system_server ending up with a writable FD to our dir).
    330     char fdpath[64];
    331     sprintf(fdpath, "/proc/self/fd/%d", fileno(*trace_out_stream_));
    332     base::ScopedFile read_only_fd(open(fdpath, O_RDONLY));
    333     PERFETTO_CHECK(read_only_fd);
    334     trace_out_stream_.reset();
    335     android::binder::Status status =
    336         dropbox->addFile(android::String16(dropbox_tag_.c_str()),
    337                          read_only_fd.release(), 0 /* flags */);
    338     if (status.isOk()) {
    339       // TODO(hjd): Account for compression.
    340       did_process_full_trace_ = true;
    341       bytes_uploaded_to_dropbox_ = bytes_written;
    342       PERFETTO_ILOG("Uploaded %ld bytes into DropBox with tag %s",
    343                     bytes_written, dropbox_tag_.c_str());
    344     } else {
    345       PERFETTO_ELOG("DropBox upload failed: %s", status.toString8().c_str());
    346     }
    347 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
    348   }
    349   task_runner_.Quit();
    350 }
    351 
    352 bool PerfettoCmd::OpenOutputFile() {
    353   base::ScopedFile fd;
    354   if (!dropbox_tag_.empty()) {
    355 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
    356     // If we are tracing to DropBox, there's no need to make a
    357     // filesystem-visible temporary file.
    358     // TODO(skyostil): Fall back to base::TempFile for older devices.
    359     fd.reset(open(kTempDropBoxTraceDir, O_TMPFILE | O_RDWR, 0600));
    360     if (!fd) {
    361       PERFETTO_ELOG("Could not create a temporary trace file in %s",
    362                     kTempDropBoxTraceDir);
    363       return false;
    364     }
    365 #else
    366     PERFETTO_FATAL("Tracing to Dropbox requires the Android build.");
    367 #endif
    368   } else {
    369     fd.reset(open(trace_out_path_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600));
    370   }
    371   trace_out_stream_.reset(fdopen(fd.release(), "wb"));
    372   PERFETTO_CHECK(trace_out_stream_);
    373   return true;
    374 }
    375 
    376 void PerfettoCmd::SetupCtrlCSignalHandler() {
    377   // Setup the pipe used to deliver the CTRL-C notification from signal handler.
    378   int pipe_fds[2];
    379   PERFETTO_CHECK(pipe(pipe_fds) == 0);
    380   ctrl_c_pipe_rd_.reset(pipe_fds[0]);
    381   ctrl_c_pipe_wr_.reset(pipe_fds[1]);
    382 
    383   // Setup signal handler.
    384   struct sigaction sa {};
    385 
    386 // Glibc headers for sa_sigaction trigger this.
    387 #pragma GCC diagnostic push
    388 #if defined(__clang__)
    389 #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
    390 #endif
    391   sa.sa_handler = [](int) {
    392     PERFETTO_LOG("SIGINT received: disabling tracing");
    393     char one = '1';
    394     PERFETTO_CHECK(PERFETTO_EINTR(write(g_consumer_cmd->ctrl_c_pipe_wr(), &one,
    395                                         sizeof(one))) == 1);
    396   };
    397   sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
    398 #pragma GCC diagnostic pop
    399   sigaction(SIGINT, &sa, nullptr);
    400 
    401   task_runner_.AddFileDescriptorWatch(
    402       *ctrl_c_pipe_rd_, [this] { consumer_endpoint_->DisableTracing(); });
    403 }
    404 
    405 int __attribute__((visibility("default")))
    406 PerfettoCmdMain(int argc, char** argv) {
    407   g_consumer_cmd = new perfetto::PerfettoCmd();
    408   return g_consumer_cmd->Main(argc, argv);
    409 }
    410 
    411 }  // namespace perfetto
    412