Pass trace fd directly to DropBox

Pass the trace file descriptor directly to DropBox in order to avoid
creating a temporary file in the filesystem (which we would have to
clean up in case of crashes).

Change-Id: I1a72c79c46b6283e276cf8be3cea639d5700f689
diff --git a/src/traced/perfetto_cmd/perfetto_cmd.cc b/src/traced/perfetto_cmd/perfetto_cmd.cc
index 5e90987..f3c236f 100644
--- a/src/traced/perfetto_cmd/perfetto_cmd.cc
+++ b/src/traced/perfetto_cmd/perfetto_cmd.cc
@@ -53,8 +53,19 @@
 // from this process.
 namespace perfetto {
 namespace {
-const char kTempTraceDir[] = "/data/misc/perfetto-traces";
+
+// Temporary directory for DropBox traces. Note that this is automatically
+// created by the system by setting setprop persist.traced.enable=1.
+const char kTempDropBoxTraceDir[] = "/data/misc/perfetto-traces";
 const char kDefaultDropBoxTag[] = "perfetto";
+
+std::string GetDirName(const std::string& path) {
+  size_t sep = path.find_last_of('/');
+  if (sep == std::string::npos)
+    return ".";
+  return path.substr(0, sep);
+}
+
 }  // namespace
 
 using protozero::proto_utils::WriteVarInt;
@@ -79,8 +90,7 @@
   void OnTraceData(std::vector<TracePacket>, bool has_more) override;
 
  private:
-  base::ScopedFile CreateTemporaryFile(std::string* out_path);
-  void SaveTraceFileAs(const std::string& name);
+  bool OpenOutputFile();
 
   PlatformTaskRunner task_runner_;
   std::unique_ptr<perfetto::Service::ConsumerEndpoint> consumer_endpoint_;
@@ -191,17 +201,6 @@
     return PrintUsage(argv[0]);
   }
 
-  if (access(kTempTraceDir, F_OK) == -1 && mkdir(kTempTraceDir, 0770) == -1) {
-    PERFETTO_ELOG("Could not create temporary trace directory: %s",
-                  kTempTraceDir);
-    return 1;
-  }
-
-  base::ScopedFile fd;
-  fd = CreateTemporaryFile(&tmp_trace_out_path_);
-  trace_out_stream_.reset(fdopen(fd.release(), "wb"));
-  PERFETTO_CHECK(trace_out_stream_);
-
   perfetto::protos::TraceConfig trace_config_proto;
   PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
   bool parsed = trace_config_proto.ParseFromString(trace_config_raw);
@@ -213,6 +212,9 @@
   trace_config_->FromProto(trace_config_proto);
   trace_config_raw.clear();
 
+  if (!OpenOutputFile())
+    return 1;
+
   if (background) {
     PERFETTO_CHECK(daemon(0 /*nochdir*/, 0 /*noclose*/) == 0);
     PERFETTO_DLOG("Continuing in background");
@@ -275,21 +277,17 @@
   consumer_endpoint_->FreeBuffers();
   task_runner_.Quit();
 
-  long bytes_written = ftell(trace_out_stream_.get());
+  fflush(*trace_out_stream_);
+  long bytes_written = ftell(*trace_out_stream_);
   if (!dropbox_tag_.empty()) {
 #if defined(PERFETTO_BUILD_WITH_ANDROID)
-    // DropBox needs a path to the uploaded file, so make a temporarily visible
-    // file.
-    // TODO(skyostil): Modify DropBox to take an fd directly.
-    std::string tmp_path;
-    CreateTemporaryFile(&tmp_path);
-    SaveTraceFileAs(tmp_path);
-
     android::sp<android::os::DropBoxManager> dropbox =
         new android::os::DropBoxManager();
+    fseek(*trace_out_stream_, 0, SEEK_SET);
+    // DropBox takes ownership of the file descriptor, so give it a duplicate.
+    base::ScopedFile fd(dup(fileno(*trace_out_stream_)));
     android::binder::Status status = dropbox->addFile(
-        android::String16(dropbox_tag_.c_str()), tmp_path, 0 /* flags */);
-    unlink(tmp_path.c_str());
+        android::String16(dropbox_tag_.c_str()), fd.release(), 0 /* flags */);
     if (!status.isOk()) {
       PERFETTO_ELOG("DropBox upload failed: %s", status.toString8().c_str());
       return;
@@ -298,22 +296,37 @@
                   dropbox_tag_.c_str());
 #endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
   } else {
-    SaveTraceFileAs(trace_out_path_);
+    PERFETTO_CHECK(
+        rename(tmp_trace_out_path_.c_str(), trace_out_path_.c_str()) == 0);
     PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
                   trace_out_path_.c_str());
   }
+  trace_out_stream_.reset();
   did_process_full_trace_ = true;
 }
 
-base::ScopedFile PerfettoCmd::CreateTemporaryFile(std::string* out_path) {
-  *out_path = std::string(kTempTraceDir) + "/perfetto-traceXXXXXX";
-  return base::ScopedFile(mkstemp(&(*out_path)[0]));
-}
-
-void PerfettoCmd::SaveTraceFileAs(const std::string& name) {
-  PERFETTO_DCHECK(trace_out_stream_);
-  PERFETTO_CHECK(rename(tmp_trace_out_path_.c_str(), name.c_str()) == 0);
-  trace_out_stream_.reset();
+bool PerfettoCmd::OpenOutputFile() {
+  base::ScopedFile fd;
+  if (!dropbox_tag_.empty()) {
+    // If we are tracing to DropBox, there's no need to make a
+    // filesystem-visible temporary file.
+    // TODO(skyostil): Fall back to mkstemp() + open() + unlink() for older
+    // devices.
+    fd.reset(open(kTempDropBoxTraceDir, O_TMPFILE | O_RDWR, 0600));
+    if (!fd) {
+      PERFETTO_ELOG("Could not create a temporary trace file in %s",
+                    kTempDropBoxTraceDir);
+      return false;
+    }
+  } else {
+    // Otherwise create a temporary file in the directory where the final trace
+    // is going to be.
+    tmp_trace_out_path_ = GetDirName(trace_out_path_) + "/perfetto-traceXXXXXX";
+    fd.reset(mkstemp(&tmp_trace_out_path_[0]));
+  }
+  trace_out_stream_.reset(fdopen(fd.release(), "wb"));
+  PERFETTO_CHECK(trace_out_stream_);
+  return true;
 }
 
 int __attribute__((visibility("default")))