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_