aboutsummaryrefslogtreecommitdiff
path: root/src/perfetto_cmd/perfetto_cmd.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/perfetto_cmd/perfetto_cmd.cc')
-rw-r--r--src/perfetto_cmd/perfetto_cmd.cc176
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");
}