blob: 38fdfbcd34c02a214f715d5a759d441863e828a9 [file] [log] [blame]
// 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 StringTraceDataEndpoint : public TracingController::TraceDataEndpoint {
public:
typedef base::Callback<void(base::RefCountedString*)> CompletionCallback;
explicit StringTraceDataEndpoint(CompletionCallback callback)
: completion_callback_(callback) {}
void ReceiveTraceFinalContents(const std::string& contents) override {
std::string tmp = contents;
scoped_refptr<base::RefCountedString> str =
base::RefCountedString::TakeString(&tmp);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(completion_callback_, str));
}
private:
~StringTraceDataEndpoint() override {}
CompletionCallback completion_callback_;
DISALLOW_COPY_AND_ASSIGN(StringTraceDataEndpoint);
};
class FileTraceDataEndpoint : public TracingController::TraceDataEndpoint {
public:
explicit FileTraceDataEndpoint(const base::FilePath& trace_file_path,
const base::Closure& callback)
: file_path_(trace_file_path),
completion_callback_(callback),
file_(NULL) {}
void ReceiveTraceChunk(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(&FileTraceDataEndpoint::ReceiveTraceChunkOnFileThread, this,
chunk_ptr));
}
void ReceiveTraceFinalContents(const std::string& contents) override {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&FileTraceDataEndpoint::CloseOnFileThread, this));
}
private:
~FileTraceDataEndpoint() override { DCHECK(file_ == NULL); }
void ReceiveTraceChunkOnFileThread(
const scoped_refptr<base::RefCountedString> chunk) {
if (!OpenFileIfNeededOnFileThread())
return;
ignore_result(
fwrite(chunk->data().c_str(), chunk->data().size(), 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;
}
return true;
}
void CloseOnFileThread() {
if (OpenFileIfNeededOnFileThread()) {
base::CloseFile(file_);
file_ = NULL;
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileTraceDataEndpoint::FinalizeOnUIThread, this));
}
void FinalizeOnUIThread() { completion_callback_.Run(); }
base::FilePath file_path_;
base::Closure completion_callback_;
FILE* file_;
DISALLOW_COPY_AND_ASSIGN(FileTraceDataEndpoint);
};
class StringTraceDataSink : public TracingController::TraceDataSink {
public:
explicit StringTraceDataSink(
scoped_refptr<TracingController::TraceDataEndpoint> endpoint)
: endpoint_(endpoint) {}
void AddTraceChunk(const std::string& chunk) override {
std::string trace_string;
if (trace_.empty())
trace_string = "{\"traceEvents\":[";
else
trace_string = ",";
trace_string += chunk;
AddTraceChunkAndPassToEndpoint(trace_string);
}
void AddTraceChunkAndPassToEndpoint(const std::string& chunk) {
trace_ += chunk;
endpoint_->ReceiveTraceChunk(chunk);
}
void SetSystemTrace(const std::string& data) override {
system_trace_ = data;
}
void SetMetadata(const std::string& data) override {
metadata_ = data;
}
void SetPowerTrace(const std::string& data) override { power_trace_ = data; }
void Close() override {
AddTraceChunkAndPassToEndpoint("]");
if (!system_trace_.empty())
AddTraceChunkAndPassToEndpoint(",\"systemTraceEvents\": " +
system_trace_);
if (!metadata_.empty())
AddTraceChunkAndPassToEndpoint(",\"metadata\": " + metadata_);
if (!power_trace_.empty()) {
AddTraceChunkAndPassToEndpoint(",\"powerTraceAsString\": " +
power_trace_);
}
AddTraceChunkAndPassToEndpoint("}");
endpoint_->ReceiveTraceFinalContents(trace_);
}
private:
~StringTraceDataSink() override {}
scoped_refptr<TracingController::TraceDataEndpoint> endpoint_;
std::string trace_;
std::string system_trace_;
std::string metadata_;
std::string power_trace_;
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 SetMetadata(const std::string& data) override {
metadata_ = data;
}
void SetPowerTrace(const std::string& data) override { power_trace_ = data; }
void Close() override {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&CompressedStringTraceDataSink::CloseOnFileThread, this));
}
private:
~CompressedStringTraceDataSink() override {}
bool OpenZStreamOnFileThread() {
DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::FILE);
if (!OpenZStreamOnFileThread())
return;
if (compressed_trace_data_.empty())
AddTraceChunkAndCompressOnFileThread("{\"traceEvents\":[", false);
AddTraceChunkAndCompressOnFileThread("]", false);
if (!system_trace_.empty()) {
AddTraceChunkAndCompressOnFileThread(
",\"systemTraceEvents\": " + system_trace_, false);
}
if (!metadata_.empty()) {
AddTraceChunkAndCompressOnFileThread(",\"metadata\": " + metadata_,
false);
}
if (!power_trace_.empty()) {
AddTraceChunkAndCompressOnFileThread(
",\"powerTraceAsString\": " + power_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_;
std::string metadata_;
std::string power_trace_;
DISALLOW_COPY_AND_ASSIGN(CompressedStringTraceDataSink);
};
} // namespace
scoped_refptr<TracingController::TraceDataSink>
TracingController::CreateStringSink(
const base::Callback<void(base::RefCountedString*)>& callback) {
return new StringTraceDataSink(new StringTraceDataEndpoint(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 StringTraceDataSink(
CreateFileEndpoint(file_path, callback));
}
scoped_refptr<TracingController::TraceDataEndpoint>
TracingController::CreateCallbackEndpoint(
const base::Callback<void(base::RefCountedString*)>& callback) {
return new StringTraceDataEndpoint(callback);
}
scoped_refptr<TracingController::TraceDataEndpoint>
TracingController::CreateFileEndpoint(const base::FilePath& file_path,
const base::Closure& callback) {
return new FileTraceDataEndpoint(file_path, callback);
}
} // namespace content