diff options
Diffstat (limited to 'src/perfetto_cmd/perfetto_cmd.cc')
-rw-r--r-- | src/perfetto_cmd/perfetto_cmd.cc | 176 |
1 files changed, 148 insertions, 28 deletions
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc index cdf601718..1d731664e 100644 --- a/src/perfetto_cmd/perfetto_cmd.cc +++ b/src/perfetto_cmd/perfetto_cmd.cc @@ -21,7 +21,6 @@ #include <signal.h> #include <stdio.h> #include <sys/stat.h> -#include <sys/types.h> #include <time.h> #include <unistd.h> @@ -30,27 +29,39 @@ #include <iterator> #include <sstream> +#include "perfetto/base/file_utils.h" #include "perfetto/base/logging.h" +#include "perfetto/base/string_view.h" #include "perfetto/base/time.h" -#include "perfetto/ext/base/file_utils.h" -#include "perfetto/ext/base/string_view.h" -#include "perfetto/ext/base/utils.h" -#include "perfetto/ext/traced/traced.h" -#include "perfetto/ext/tracing/core/basic_types.h" -#include "perfetto/ext/tracing/core/trace_packet.h" -#include "perfetto/ext/tracing/ipc/default_socket.h" +#include "perfetto/base/utils.h" +#include "perfetto/common/tracing_service_state.pb.h" +#include "perfetto/config/trace_config.pb.h" #include "perfetto/protozero/proto_utils.h" +#include "perfetto/traced/traced.h" +#include "perfetto/tracing/core/basic_types.h" #include "perfetto/tracing/core/data_source_config.h" #include "perfetto/tracing/core/data_source_descriptor.h" #include "perfetto/tracing/core/trace_config.h" +#include "perfetto/tracing/core/trace_packet.h" #include "perfetto/tracing/core/tracing_service_state.h" #include "src/perfetto_cmd/config.h" #include "src/perfetto_cmd/packet_writer.h" #include "src/perfetto_cmd/pbtxt_to_pb.h" #include "src/perfetto_cmd/trigger_producer.h" +#include "src/tracing/ipc/default_socket.h" -#include "protos/perfetto/common/tracing_service_state.pb.h" -#include "protos/perfetto/config/trace_config.pb.h" +#include "google/protobuf/io/zero_copy_stream_impl_lite.h" + +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) +#include <sys/sendfile.h> + +#include <android/os/DropBoxManager.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +#include "src/android_internal/incident_service.h" +#include "src/android_internal/lazy_library_loader.h" +#endif // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) namespace perfetto { namespace { @@ -117,9 +128,35 @@ bool ParseTraceConfigPbtxt(const std::string& file_name, return true; } +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) +static bool StartIncidentReport(const TraceConfig::IncidentReportConfig& cfg) { + PERFETTO_LAZY_LOAD(android_internal::StartIncidentReport, start_incident_fn); + if (!start_incident_fn) + return false; + return start_incident_fn(cfg.destination_package().c_str(), + cfg.destination_class().c_str(), + cfg.privacy_level()); +} +#else +static bool StartIncidentReport(const TraceConfig::IncidentReportConfig&) { + PERFETTO_FATAL("should not be called"); +} +#endif + } // namespace -const char* kStateDir = "/data/misc/perfetto-traces"; +// Directory for temporary trace files. Note that this is automatically created +// by the system by setting setprop persist.traced.enable=1. +const char* kTempDropBoxTraceDir = "/data/misc/perfetto-traces"; + +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) +// If writing into an incident, the trace is written to a hardcoded location +// that is known to incidentd. +static const char kIncidentTraceLocation[] = + "/data/misc/perfetto-traces/incident-trace"; +static const char kTempIncidentTraceLocation[] = + "/data/misc/perfetto-traces/incident-trace.temp"; +#endif using protozero::proto_utils::MakeTagLengthDelimited; using protozero::proto_utils::WriteVarInt; @@ -170,8 +207,6 @@ Detach mode. DISCOURAGED, read https://docs.perfetto.dev/#/detached-mode : } int PerfettoCmd::Main(int argc, char** argv) { - umask(0000); // make sure that file creation is not affected by umask. - enum LongOption { OPT_ALERT_ID = 1000, OPT_CONFIG_ID, @@ -288,12 +323,13 @@ int PerfettoCmd::Main(int argc, char** argv) { } if (option == OPT_DROPBOX) { -#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) - PERFETTO_CHECK(optarg); +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) + if (!optarg) + PERFETTO_FATAL("optarg is null"); dropbox_tag_ = optarg; continue; #else - PERFETTO_ELOG("--dropbox is supported only on Android"); + PERFETTO_ELOG("DropBox is only supported with Android tree builds"); return 1; #endif } @@ -450,10 +486,6 @@ int PerfettoCmd::Main(int argc, char** argv) { return 1; } - if (trace_config_->trace_uuid().empty() || !dropbox_tag_.empty()) { - trace_config_->set_trace_uuid(base::UuidToString(base::Uuidv4())); - } - if (!trace_config_->incident_report_config().destination_package().empty()) { if (dropbox_tag_.empty()) { PERFETTO_ELOG("Unexpected IncidentReportConfig without --dropbox."); @@ -709,12 +741,9 @@ void PerfettoCmd::FinalizeTraceAndExit() { bytes_written_ = static_cast<size_t>(sz); } - if (!dropbox_tag_.empty()) { -#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) - SaveTraceIntoDropboxAndIncidentOrCrash(); -#endif - } else { + if (dropbox_tag_.empty()) { trace_out_stream_.reset(); + did_process_full_trace_ = true; if (trace_config_->write_into_file()) { // trace_out_path_ might be empty in the case of --attach. PERFETTO_LOG("Trace written into the output file"); @@ -722,17 +751,108 @@ void PerfettoCmd::FinalizeTraceAndExit() { PERFETTO_LOG("Wrote %" PRIu64 " bytes into %s", bytes_written_, trace_out_path_ == "-" ? "stdout" : trace_out_path_.c_str()); } + task_runner_.Quit(); + return; + } + + // Otherwise, write to Dropbox unless there's a special override in the + // incident report config. + if (!trace_config_->incident_report_config().skip_dropbox()) { + if (bytes_written_ == 0) { + PERFETTO_LOG("Skipping write to dropbox. Empty trace."); + } else { + SaveOutputToDropboxOrCrash(); + } + } + + // Optionally save the trace as an incident. This is either in addition to, or + // instead of, the Dropbox write. + if (!trace_config_->incident_report_config().destination_package().empty()) { + if (bytes_written_ == 0) { + PERFETTO_LOG("Skipping incident report. Empty trace."); + } else { + SaveOutputToIncidentTraceOrCrash(); + + // Ask incidentd to create a report, which will read the file we just + // wrote. + PERFETTO_CHECK( + StartIncidentReport(trace_config_->incident_report_config())); + } } did_process_full_trace_ = true; task_runner_.Quit(); } +void PerfettoCmd::SaveOutputToDropboxOrCrash() { +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) + android::sp<android::os::DropBoxManager> dropbox = + new android::os::DropBoxManager(); + PERFETTO_CHECK(fseek(*trace_out_stream_, 0, SEEK_SET) == 0); + // DropBox takes ownership of the file descriptor, so give it a duplicate. + // Also we need to give it a read-only copy of the fd or will hit a SELinux + // violation (about system_server ending up with a writable FD to our dir). + char fdpath[64]; + sprintf(fdpath, "/proc/self/fd/%d", fileno(*trace_out_stream_)); + base::ScopedFile read_only_fd(base::OpenFile(fdpath, O_RDONLY)); + PERFETTO_CHECK(read_only_fd); + android::binder::Status status = + dropbox->addFile(android::String16(dropbox_tag_.c_str()), + read_only_fd.release(), 0 /* flags */); + if (status.isOk()) { + PERFETTO_LOG("Wrote %" PRIu64 + " bytes (before compression) into DropBox with tag %s", + bytes_written_, dropbox_tag_.c_str()); + } else { + PERFETTO_FATAL("DropBox upload failed: %s", status.toString8().c_str()); + } +#endif +} + +// Open a staging file (unlinking the previous instance), copy the trace +// contents over, then rename to a final hardcoded path (known to incidentd). +// Such tracing sessions should not normally overlap. We do not use unique +// unique filenames to avoid creating an unbounded amount of files in case of +// errors. +void PerfettoCmd::SaveOutputToIncidentTraceOrCrash() { +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) + PERFETTO_CHECK(unlink(kTempIncidentTraceLocation) == 0 || errno == ENOENT); + + // SELinux constrains the set of readers. + base::ScopedFile staging_fd = + base::OpenFile(kTempIncidentTraceLocation, O_CREAT | O_RDWR, 0666); + PERFETTO_CHECK(staging_fd); + + // Set the desired permissions even if under a umask. + PERFETTO_CHECK(fchmod(*staging_fd, 0666) == 0); + + off_t offset = 0; + PERFETTO_CHECK(sendfile(*staging_fd, fileno(*trace_out_stream_), &offset, + bytes_written_) == bytes_written_); + + staging_fd.reset(); + PERFETTO_CHECK(rename(kTempIncidentTraceLocation, kIncidentTraceLocation) == + 0); +// Note: not calling fsync(2), as we're not interested in the file being +// consistent in case of a crash. +#endif +} + bool PerfettoCmd::OpenOutputFile() { base::ScopedFile fd; if (!dropbox_tag_.empty()) { -#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) - fd = OpenDropboxTmpFile(); +#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) + // If we are tracing to DropBox, there's no need to make a + // filesystem-visible temporary file. + // TODO(skyostil): Fall back to base::TempFile for older devices. + fd = base::OpenFile(kTempDropBoxTraceDir, O_TMPFILE | O_RDWR, 0600); + if (!fd) { + PERFETTO_PLOG("Could not create a temporary trace file in %s", + kTempDropBoxTraceDir); + return false; + } +#else + PERFETTO_FATAL("Tracing to Dropbox requires the Android build."); #endif } else if (trace_out_path_ == "-") { fd.reset(dup(STDOUT_FILENO)); @@ -850,7 +970,7 @@ void PerfettoCmd::PrintServiceState(bool success, printf("data_sources: {\n"); printf(" producer_id: %d\n", ds.producer_id()); printf(" descriptor: {\n"); - printf(" name: \"%s\"\n", ds.ds_descriptor().name().c_str()); + printf(" name: \"%s\"\n", ds.descriptor().name().c_str()); printf(" }\n"); printf("}\n"); } |