Index: content/browser/tracing/tracing_controller_impl_data_sinks.cc |
diff --git a/content/browser/tracing/tracing_controller_impl_data_sinks.cc b/content/browser/tracing/tracing_controller_impl_data_sinks.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8a2a2bf66393e9e90ca65263e17af42cd3ceba58 |
--- /dev/null |
+++ b/content/browser/tracing/tracing_controller_impl_data_sinks.cc |
@@ -0,0 +1,280 @@ |
+// Copyright 2015 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 "content/browser/tracing/tracing_controller_impl.h" |
+ |
+#include "base/bind.h" |
+#include "base/files/file_util.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "third_party/zlib/zlib.h" |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+class FileTraceDataSink : public TracingController::TraceDataSink { |
+ public: |
+ explicit FileTraceDataSink(const base::FilePath& trace_file_path, |
+ const base::Closure& callback) |
+ : file_path_(trace_file_path), |
+ completion_callback_(callback), |
+ file_(NULL) {} |
+ |
+ void AddTraceChunk(const std::string& chunk) override { |
+ std::string tmp = chunk; |
+ scoped_refptr<base::RefCountedString> chunk_ptr = |
+ base::RefCountedString::TakeString(&tmp); |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind( |
+ &FileTraceDataSink::AddTraceChunkOnFileThread, this, chunk_ptr)); |
+ } |
+ void SetSystemTrace(const std::string& data) override { |
+ system_trace_ = data; |
+ } |
+ void Close() override { |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&FileTraceDataSink::CloseOnFileThread, this)); |
+ } |
+ |
+ private: |
+ ~FileTraceDataSink() override { DCHECK(file_ == NULL); } |
+ |
+ void AddTraceChunkOnFileThread( |
+ const scoped_refptr<base::RefCountedString> chunk) { |
+ if (file_ != NULL) |
+ fputc(',', file_); |
+ else if (!OpenFileIfNeededOnFileThread()) |
+ return; |
+ ignore_result(fwrite(chunk->data().c_str(), strlen(chunk->data().c_str()), |
+ 1, file_)); |
+ } |
+ |
+ bool OpenFileIfNeededOnFileThread() { |
+ if (file_ != NULL) |
+ return true; |
+ file_ = base::OpenFile(file_path_, "w"); |
+ if (file_ == NULL) { |
+ LOG(ERROR) << "Failed to open " << file_path_.value(); |
+ return false; |
+ } |
+ const char preamble[] = "{\"traceEvents\": ["; |
+ ignore_result(fwrite(preamble, strlen(preamble), 1, file_)); |
+ return true; |
+ } |
+ |
+ void CloseOnFileThread() { |
+ if (OpenFileIfNeededOnFileThread()) { |
+ fputc(']', file_); |
+ if (!system_trace_.empty()) { |
+ const char systemTraceEvents[] = ",\"systemTraceEvents\": "; |
+ ignore_result(fwrite(systemTraceEvents, strlen(systemTraceEvents), |
+ 1, file_)); |
+ ignore_result(fwrite(system_trace_.c_str(), |
+ strlen(system_trace_.c_str()), 1, file_)); |
+ } |
+ fputc('}', file_); |
+ base::CloseFile(file_); |
+ file_ = NULL; |
+ } |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&FileTraceDataSink::FinalizeOnUIThread, this)); |
+ } |
+ |
+ void FinalizeOnUIThread() { completion_callback_.Run(); } |
+ |
+ base::FilePath file_path_; |
+ base::Closure completion_callback_; |
+ FILE* file_; |
+ std::string system_trace_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FileTraceDataSink); |
+}; |
+ |
+class StringTraceDataSink : public TracingController::TraceDataSink { |
+ public: |
+ typedef base::Callback<void(base::RefCountedString*)> CompletionCallback; |
+ |
+ explicit StringTraceDataSink(CompletionCallback callback) |
+ : completion_callback_(callback) {} |
+ |
+ // TracingController::TraceDataSink implementation |
+ void AddTraceChunk(const std::string& chunk) override { |
+ if (!trace_.empty()) |
+ trace_ += ","; |
+ trace_ += chunk; |
+ } |
+ void SetSystemTrace(const std::string& data) override { |
+ system_trace_ = data; |
+ } |
+ void Close() override { |
+ std::string result = "{\"traceEvents\":[" + trace_ + "]"; |
+ if (!system_trace_.empty()) |
+ result += ",\"systemTraceEvents\": " + system_trace_; |
+ result += "}"; |
+ |
+ scoped_refptr<base::RefCountedString> str = |
+ base::RefCountedString::TakeString(&result); |
+ completion_callback_.Run(str.get()); |
+ } |
+ |
+ private: |
+ ~StringTraceDataSink() override {} |
+ |
+ std::string trace_; |
+ std::string system_trace_; |
+ CompletionCallback completion_callback_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(StringTraceDataSink); |
+}; |
+ |
+class CompressedStringTraceDataSink : public TracingController::TraceDataSink { |
+ public: |
+ explicit CompressedStringTraceDataSink( |
+ scoped_refptr<TracingController::TraceDataEndpoint> endpoint) |
+ : endpoint_(endpoint), already_tried_open_(false) {} |
+ |
+ void AddTraceChunk(const std::string& chunk) override { |
+ std::string tmp = chunk; |
+ scoped_refptr<base::RefCountedString> chunk_ptr = |
+ base::RefCountedString::TakeString(&tmp); |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&CompressedStringTraceDataSink::AddTraceChunkOnFileThread, |
+ this, chunk_ptr)); |
+ } |
+ |
+ void SetSystemTrace(const std::string& data) override { |
+ system_trace_ = data; |
+ } |
+ |
+ void Close() override { |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&CompressedStringTraceDataSink::CloseOnFileThread, this)); |
+ } |
+ |
+ private: |
+ ~CompressedStringTraceDataSink() override {} |
+ |
+ bool OpenZStreamOnFileThread() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ if (stream_) |
+ return true; |
+ |
+ if (already_tried_open_) |
+ return false; |
+ |
+ already_tried_open_ = true; |
+ stream_.reset(new z_stream); |
+ *stream_ = {0}; |
+ stream_->zalloc = Z_NULL; |
+ stream_->zfree = Z_NULL; |
+ stream_->opaque = Z_NULL; |
+ |
+ int result = deflateInit2(stream_.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, |
+ // 16 is added to produce a gzip header + trailer. |
+ MAX_WBITS + 16, |
+ 8, // memLevel = 8 is default. |
+ Z_DEFAULT_STRATEGY); |
+ return result == 0; |
+ } |
+ |
+ void AddTraceChunkOnFileThread( |
+ const scoped_refptr<base::RefCountedString> chunk_ptr) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ std::string trace; |
+ if (compressed_trace_data_.empty()) |
+ trace = "{\"traceEvents\":["; |
+ else |
+ trace = ","; |
+ trace += chunk_ptr->data(); |
+ AddTraceChunkAndCompressOnFileThread(trace, false); |
+ } |
+ |
+ void AddTraceChunkAndCompressOnFileThread(const std::string& chunk, |
+ bool finished) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ if (!OpenZStreamOnFileThread()) |
+ return; |
+ |
+ const int kChunkSize = 0x4000; |
+ |
+ char buffer[kChunkSize]; |
+ int err; |
+ stream_->avail_in = chunk.size(); |
+ stream_->next_in = (unsigned char*)chunk.data(); |
+ do { |
+ stream_->avail_out = kChunkSize; |
+ stream_->next_out = (unsigned char*)buffer; |
+ err = deflate(stream_.get(), finished ? Z_FINISH : Z_NO_FLUSH); |
+ if (err != Z_OK && (err != Z_STREAM_END && finished)) { |
+ stream_.reset(); |
+ return; |
+ } |
+ |
+ int bytes = kChunkSize - stream_->avail_out; |
+ if (bytes) { |
+ std::string compressed_chunk = std::string(buffer, bytes); |
+ compressed_trace_data_ += compressed_chunk; |
+ endpoint_->ReceiveTraceChunk(compressed_chunk); |
+ } |
+ } while (stream_->avail_out == 0); |
+ } |
+ |
+ void CloseOnFileThread() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ if (!OpenZStreamOnFileThread()) |
+ return; |
+ |
+ if (compressed_trace_data_.empty()) |
+ AddTraceChunkAndCompressOnFileThread("{\"traceEvents\":[", false); |
+ |
+ AddTraceChunkAndCompressOnFileThread("]", false); |
+ if (!system_trace_.empty()) { |
+ AddTraceChunkAndCompressOnFileThread( |
+ ",\"systemTraceEvents\": " + system_trace_, false); |
+ } |
+ AddTraceChunkAndCompressOnFileThread("}", true); |
+ |
+ deflateEnd(stream_.get()); |
+ stream_.reset(); |
+ |
+ endpoint_->ReceiveTraceFinalContents(compressed_trace_data_); |
+ } |
+ |
+ scoped_refptr<TracingController::TraceDataEndpoint> endpoint_; |
+ scoped_ptr<z_stream> stream_; |
+ bool already_tried_open_; |
+ std::string compressed_trace_data_; |
+ std::string system_trace_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CompressedStringTraceDataSink); |
+}; |
+ |
+} // namespace |
+ |
+scoped_refptr<TracingController::TraceDataSink> |
+TracingController::CreateStringSink( |
+ const base::Callback<void(base::RefCountedString*)>& callback) { |
+ return new StringTraceDataSink(callback); |
+} |
+ |
+scoped_refptr<TracingController::TraceDataSink> |
+TracingController::CreateCompressedStringSink( |
+ scoped_refptr<TracingController::TraceDataEndpoint> endpoint) { |
+ return new CompressedStringTraceDataSink(endpoint); |
+} |
+ |
+scoped_refptr<TracingController::TraceDataSink> |
+TracingController::CreateFileSink(const base::FilePath& file_path, |
+ const base::Closure& callback) { |
+ return new FileTraceDataSink(file_path, callback); |
+} |
+ |
+} // namespace content |