Make tracing work on Android

Previously, the trace coordinator would try to write a trace to the file
system, but it is awkward to write to the file system on Android. Instead, we
now stream the trace data to a data pipe. The Sky debugger consumes that data
pipe and sends it to the host via the command HTTP server.

R=eseidel@chromium.org, jamesr@chromium.org

Review URL: https://codereview.chromium.org/853943005
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index dcba9eb..d972277 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -18,6 +18,7 @@
     "//mojo/application",
     "//mojo/common",
     "//mojo/public/cpp/application",
+    "//mojo/public/cpp/system",
   ]
 }
 
diff --git a/services/tracing/main.cc b/services/tracing/main.cc
index 2c95ccd..dc2dec5 100644
--- a/services/tracing/main.cc
+++ b/services/tracing/main.cc
@@ -44,11 +44,9 @@
   }
 
   // tracing::TraceCoordinator implementation.
-  void Start(const mojo::String& base_name,
+  void Start(mojo::ScopedDataPipeProducerHandle stream,
              const mojo::String& categories) override {
-    base::FilePath base_name_path =
-        base::FilePath::FromUTF8Unsafe(base_name.To<std::string>());
-    sink_.reset(new TraceDataSink(base_name_path));
+    sink_.reset(new TraceDataSink(stream.Pass()));
     collector_bindings_.ForAllBindings([categories](
         TraceController* controller) { controller->StartTracing(categories); });
   }
diff --git a/services/tracing/trace_data_sink.cc b/services/tracing/trace_data_sink.cc
index aeae144..eadeb57 100644
--- a/services/tracing/trace_data_sink.cc
+++ b/services/tracing/trace_data_sink.cc
@@ -4,38 +4,44 @@
 
 #include "services/tracing/trace_data_sink.h"
 
-#include "base/files/file_util.h"
+#include "base/logging.h"
 
 namespace tracing {
+namespace {
 
-TraceDataSink::TraceDataSink(base::FilePath base_name) : empty_(true) {
-  file_ =
-      base::OpenFile(base_name.AddExtension(FILE_PATH_LITERAL(".trace")), "w+");
-  static const char start[] = "{\"traceEvents\":[";
-  fwrite(start, 1, strlen(start), file_);
+const char kStart[] = "{\"traceEvents\":[";
+const char kEnd[] = "]}";
+
+void Write(const mojo::ScopedDataPipeProducerHandle& pipe,
+           const char* string,
+           uint32_t num_bytes) {
+  CHECK_EQ(MOJO_RESULT_OK,
+           mojo::WriteDataRaw(pipe.get(), string, &num_bytes,
+                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+}
+}
+
+TraceDataSink::TraceDataSink(mojo::ScopedDataPipeProducerHandle pipe)
+    : pipe_(pipe.Pass()), empty_(true) {
+  Write(pipe_, kStart, strlen(kStart));
 }
 
 TraceDataSink::~TraceDataSink() {
-  if (file_)
+  if (pipe_.is_valid())
     Flush();
-  DCHECK(!file_);
+  DCHECK(!pipe_.is_valid());
 }
 
 void TraceDataSink::AddChunk(const std::string& json) {
-  if (!empty_) {
-    fwrite(",", 1, 1, file_);
-  }
-
+  if (!empty_)
+    Write(pipe_, ",", 1);
   empty_ = false;
-
-  fwrite(json.data(), 1, json.length(), file_);
+  Write(pipe_, json.data(), json.length());
 }
 
 void TraceDataSink::Flush() {
-  static const char end[] = "]}";
-  fwrite(end, 1, strlen(end), file_);
-  base::CloseFile(file_);
-  file_ = nullptr;
+  Write(pipe_, kEnd, strlen(kEnd));
+  pipe_.reset();
 }
 
 }  // namespace tracing
diff --git a/services/tracing/trace_data_sink.h b/services/tracing/trace_data_sink.h
index 4efcbf6..c5d3f14 100644
--- a/services/tracing/trace_data_sink.h
+++ b/services/tracing/trace_data_sink.h
@@ -7,20 +7,21 @@
 
 #include <string>
 
-#include "base/files/file_path.h"
+#include "base/basictypes.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 
 namespace tracing {
 
 class TraceDataSink {
  public:
-  explicit TraceDataSink(base::FilePath base_name);
+  explicit TraceDataSink(mojo::ScopedDataPipeProducerHandle pipe);
   ~TraceDataSink();
 
   void AddChunk(const std::string& json);
   void Flush();
 
  private:
-  FILE* file_;
+  mojo::ScopedDataPipeProducerHandle pipe_;
   bool empty_;
 
   DISALLOW_COPY_AND_ASSIGN(TraceDataSink);
diff --git a/services/tracing/tracing.mojom b/services/tracing/tracing.mojom
index e1d0ca0..1227988 100644
--- a/services/tracing/tracing.mojom
+++ b/services/tracing/tracing.mojom
@@ -22,9 +22,9 @@
 };
 
 interface TraceCoordinator {
-  // Request tracing data from all connected TraceControllers to save in a file
-  // derived from |base_name|.
-  Start(string base_name, string categories);
+  // Request tracing data from all connected TraceControllers to stream to
+  // |stream|.
+  Start(handle<data_pipe_producer> stream, string categories);
 
   // Stop tracing and flush results to file.
   StopAndFlush();
diff --git a/sky/tools/debugger/prompt/BUILD.gn b/sky/tools/debugger/prompt/BUILD.gn
index 578d976..59848f1 100644
--- a/sky/tools/debugger/prompt/BUILD.gn
+++ b/sky/tools/debugger/prompt/BUILD.gn
@@ -9,12 +9,16 @@
 
   sources = [
     "prompt.cc",
+    "trace_collector.cc",
+    "trace_collector.h",
   ]
 
   deps = [
     "//base",
     "//mojo/application",
+    "//mojo/common",
     "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
     "//mojo/public/cpp/utility",
     "//net",
     "//net:http_server",
diff --git a/sky/tools/debugger/prompt/prompt.cc b/sky/tools/debugger/prompt/prompt.cc
index e312336..1c7154e 100644
--- a/sky/tools/debugger/prompt/prompt.cc
+++ b/sky/tools/debugger/prompt/prompt.cc
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
+
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
@@ -16,11 +19,17 @@
 #include "net/socket/tcp_server_socket.h"
 #include "services/tracing/tracing.mojom.h"
 #include "sky/tools/debugger/debugger.mojom.h"
+#include "sky/tools/debugger/prompt/trace_collector.h"
 
 namespace sky {
 namespace debugger {
+namespace {
 
-class Prompt : public mojo::ApplicationDelegate, public net::HttpServer::Delegate {
+const size_t kMinSendBufferSize = 1024 * 1024;
+}
+
+class Prompt : public mojo::ApplicationDelegate,
+               public net::HttpServer::Delegate {
  public:
   Prompt()
       : is_tracing_(false),
@@ -98,6 +107,10 @@
   }
 
   void Respond(int connection_id, std::string response) {
+    // When sending tracing data back over the wire to the client, we can blow
+    // through the default send buffer size.
+    web_server_->SetSendBufferSize(
+        connection_id, std::max(kMinSendBufferSize, response.length()));
     web_server_->Send200(connection_id, response, "text/plain");
   }
 
@@ -138,16 +151,25 @@
   }
 
   void ToggleTracing(int connection_id) {
-    std::string response;
-    if (is_tracing_) {
-      response = "Stopping trace (writing to sky_viewer.trace)\n";
-      tracing_->StopAndFlush();
-    } else {
-      response = "Starting trace (type 'trace' to stop tracing)\n";
-      tracing_->Start(mojo::String("sky_viewer"), mojo::String("*"));
-    }
+    bool was_tracing = is_tracing_;
     is_tracing_ = !is_tracing_;
-    Respond(connection_id, response);
+
+    if (was_tracing) {
+      tracing_->StopAndFlush();
+      trace_collector_->GetTrace(base::Bind(
+          &Prompt::OnTraceAvailable, base::Unretained(this), connection_id));
+      return;
+    }
+
+    mojo::DataPipe pipe;
+    tracing_->Start(pipe.producer_handle.Pass(), mojo::String("*"));
+    trace_collector_.reset(new TraceCollector(pipe.consumer_handle.Pass()));
+    Respond(connection_id, "Starting trace (type 'trace' to stop tracing)\n");
+  }
+
+  void OnTraceAvailable(int connection_id, std::string trace) {
+    trace_collector_.reset();
+    Respond(connection_id, trace);
   }
 
   bool is_tracing_;
@@ -158,6 +180,8 @@
   scoped_ptr<net::HttpServer> web_server_;
   uint32_t command_port_;
 
+  scoped_ptr<TraceCollector> trace_collector_;
+
   DISALLOW_COPY_AND_ASSIGN(Prompt);
 };
 
diff --git a/sky/tools/debugger/prompt/trace_collector.cc b/sky/tools/debugger/prompt/trace_collector.cc
new file mode 100644
index 0000000..0948182
--- /dev/null
+++ b/sky/tools/debugger/prompt/trace_collector.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sky/tools/debugger/prompt/trace_collector.h"
+
+namespace sky {
+namespace debugger {
+
+TraceCollector::TraceCollector(mojo::ScopedDataPipeConsumerHandle source)
+    : drainer_(this, source.Pass()), is_complete_(false) {
+}
+
+TraceCollector::~TraceCollector() {
+}
+
+void TraceCollector::GetTrace(TraceCallback callback) {
+  DCHECK(!callback_.is_null());
+  if (is_complete_) {
+    callback.Run(GetTraceAsString());
+    return;
+  }
+  callback_ = callback;
+}
+
+void TraceCollector::OnDataAvailable(const void* data, size_t num_bytes) {
+  DCHECK(!is_complete_);
+  const char* chars = static_cast<const char*>(data);
+  trace_.insert(trace_.end(), chars, chars + num_bytes);
+}
+
+void TraceCollector::OnDataComplete() {
+  DCHECK(!is_complete_);
+  is_complete_ = true;
+  if (!callback_.is_null())
+    callback_.Run(GetTraceAsString());
+}
+
+std::string TraceCollector::GetTraceAsString() {
+  return std::string(&trace_.front(), trace_.size());
+}
+
+}  // namespace debugger
+}  // namespace sky
diff --git a/sky/tools/debugger/prompt/trace_collector.h b/sky/tools/debugger/prompt/trace_collector.h
new file mode 100644
index 0000000..773a402
--- /dev/null
+++ b/sky/tools/debugger/prompt/trace_collector.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKY_TOOLS_DEBUGGER_PROMPT_TRACE_COLLECTOR_H_
+#define SKY_TOOLS_DEBUGGER_PROMPT_TRACE_COLLECTOR_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "mojo/common/data_pipe_drainer.h"
+
+namespace sky {
+namespace debugger {
+
+class TraceCollector : public mojo::common::DataPipeDrainer::Client {
+ public:
+  typedef base::Callback<void(std::string)> TraceCallback;
+
+  explicit TraceCollector(mojo::ScopedDataPipeConsumerHandle source);
+  ~TraceCollector();
+
+  void GetTrace(TraceCallback callback);
+
+ private:
+  void OnDataAvailable(const void* data, size_t num_bytes) override;
+  void OnDataComplete() override;
+
+  std::string GetTraceAsString();
+
+  mojo::common::DataPipeDrainer drainer_;
+  std::vector<char> trace_;
+  bool is_complete_;
+  TraceCallback callback_;
+};
+
+}  // namespace debugger
+}  // namespace sky
+
+#endif  // SKY_TOOLS_DEBUGGER_PROMPT_TRACE_COLLECTOR_H_