0

tracing: get rid of files in TracingController interface

The only way to get the tracing data used to be by reading the file
provided in TracingController::DisableTracing() interface. This does
not fit well for most usages, so this introduces a TraceDataSink
interface that is backed either by a file, string, or, in case of
DevTools, by the protocol client.

This resolves the OOM in browser and imporves performance while
recording very large traces through DevTools, streamlines
TracingController core and reduces coplexity of code for most
TracingController clients.

BUG=409733,361045

Review URL: https://codereview.chromium.org/541763002

Cr-Commit-Position: refs/heads/master@{#294801}
This commit is contained in:
caseq
2014-09-15 03:50:10 -07:00
committed by Commit bot
parent ad03444a48
commit b85bb8fc41
14 changed files with 369 additions and 428 deletions

@ -19,6 +19,30 @@ namespace {
using content::BrowserThread;
class StringTraceSink : public content::TracingController::TraceDataSink {
public:
StringTraceSink(std::string* result, const base::Closure& callback)
: result_(result), completion_callback_(callback) {}
virtual void AddTraceChunk(const std::string& chunk) OVERRIDE {
*result_ += result_->empty() ? "[" : ",";
*result_ += chunk;
}
virtual void Close() OVERRIDE {
if (!result_->empty())
*result_ += "]";
completion_callback_.Run();
}
private:
virtual ~StringTraceSink() {}
std::string* result_;
base::Closure completion_callback_;
DISALLOW_COPY_AND_ASSIGN(StringTraceSink);
};
class InProcessTraceController {
public:
static InProcessTraceController* GetInstance() {
@ -87,12 +111,12 @@ class InProcessTraceController {
using namespace base::debug;
if (!content::TracingController::GetInstance()->DisableRecording(
base::FilePath(),
base::Bind(&InProcessTraceController::OnTraceDataCollected,
base::Unretained(this),
base::Unretained(json_trace_output))))
new StringTraceSink(
json_trace_output,
base::Bind(&InProcessTraceController::OnTracingComplete,
base::Unretained(this))))) {
return false;
}
// Wait for OnEndTracingComplete() to quit the message loop.
message_loop_runner_ = new content::MessageLoopRunner;
message_loop_runner_->Run();
@ -110,38 +134,7 @@ class InProcessTraceController {
message_loop_runner_->Quit();
}
void OnEndTracingComplete() {
message_loop_runner_->Quit();
}
void OnTraceDataCollected(std::string* json_trace_output,
const base::FilePath& path) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&InProcessTraceController::ReadTraceData,
base::Unretained(this),
base::Unretained(json_trace_output),
path));
}
void ReadTraceData(std::string* json_trace_output,
const base::FilePath& path) {
json_trace_output->clear();
bool ok = base::ReadFileToString(path, json_trace_output);
DCHECK(ok);
base::DeleteFile(path, false);
// The callers expect an array of trace events.
const char* preamble = "{\"traceEvents\": ";
const char* trailout = "}";
DCHECK(StartsWithASCII(*json_trace_output, preamble, true));
DCHECK(EndsWith(*json_trace_output, trailout, true));
json_trace_output->erase(0, strlen(preamble));
json_trace_output->erase(json_trace_output->end() - 1);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&InProcessTraceController::OnEndTracingComplete,
base::Unretained(this)));
}
void OnTracingComplete() { message_loop_runner_->Quit(); }
void OnWatchEventMatched() {
if (watch_notification_count_ == 0)

@ -5,14 +5,13 @@
#include "components/feedback/tracing_manager.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.h"
#include "base/message_loop/message_loop_proxy.h"
#include "components/feedback/feedback_util.h"
#include "content/public/browser/tracing_controller.h"
namespace {
// Only once trace manager can exist at a time.
TracingManager* g_tracing_manager = NULL;
// Trace IDs start at 1 and increase.
@ -43,9 +42,9 @@ int TracingManager::RequestTrace() {
current_trace_id_ = g_next_trace_id;
++g_next_trace_id;
content::TracingController::GetInstance()->DisableRecording(
base::FilePath(),
base::Bind(&TracingManager::OnTraceDataCollected,
weak_ptr_factory_.GetWeakPtr()));
content::TracingController::CreateStringSink(
base::Bind(&TracingManager::OnTraceDataCollected,
weak_ptr_factory_.GetWeakPtr())));
return current_trace_id_;
}
@ -96,20 +95,13 @@ void TracingManager::StartTracing() {
content::TracingController::EnableRecordingDoneCallback());
}
void TracingManager::OnTraceDataCollected(const base::FilePath& path) {
void TracingManager::OnTraceDataCollected(base::RefCountedString* trace_data) {
if (!current_trace_id_)
return;
std::string data;
if (!base::ReadFileToString(path, &data)) {
LOG(ERROR) << "Failed to read trace data from: " << path.value();
return;
}
base::DeleteFile(path, false);
std::string output_val;
feedback_util::ZipString(
base::FilePath(kTracingFilename), data, &output_val);
base::FilePath(kTracingFilename), trace_data->data(), &output_val);
scoped_refptr<base::RefCountedString> output(
base::RefCountedString::TakeString(&output_val));

@ -58,7 +58,7 @@ class TracingManager {
TracingManager();
void StartTracing();
void OnTraceDataCollected(const base::FilePath& path);
void OnTraceDataCollected(base::RefCountedString* data);
// ID of the trace that is being collected.
int current_trace_id_;

@ -54,11 +54,12 @@ void TracingControllerAndroid::StopTracing(JNIEnv* env,
base::FilePath file_path(
base::android::ConvertJavaStringToUTF8(env, jfilepath));
if (!TracingController::GetInstance()->DisableRecording(
file_path,
base::Bind(&TracingControllerAndroid::OnTracingStopped,
weak_factory_.GetWeakPtr()))) {
TracingController::CreateFileSink(
file_path,
base::Bind(&TracingControllerAndroid::OnTracingStopped,
weak_factory_.GetWeakPtr())))) {
LOG(ERROR) << "EndTracingAsync failed, forcing an immediate stop";
OnTracingStopped(file_path);
OnTracingStopped();
}
}
@ -71,8 +72,7 @@ void TracingControllerAndroid::GenerateTracingFilePath(
base::android::ConvertJavaStringToUTF8(env, jfilename.obj()));
}
void TracingControllerAndroid::OnTracingStopped(
const base::FilePath& file_path) {
void TracingControllerAndroid::OnTracingStopped() {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> obj = weak_java_object_.get(env);
if (obj.obj())

@ -29,7 +29,7 @@ class TracingControllerAndroid {
private:
~TracingControllerAndroid();
void OnTracingStopped(const base::FilePath& file_path);
void OnTracingStopped();
void OnKnownCategoriesReceived(
const std::set<std::string>& categories_received);

@ -7,7 +7,6 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
@ -1197,7 +1196,9 @@ void BrowserMainLoop::InitStartupTracing(
void BrowserMainLoop::EndStartupTracing() {
is_tracing_startup_ = false;
TracingController::GetInstance()->DisableRecording(
startup_trace_file_, base::Bind(&OnStoppedStartupTracing));
TracingController::CreateFileSink(
startup_trace_file_,
base::Bind(OnStoppedStartupTracing, startup_trace_file_)));
}
} // namespace content

@ -7,13 +7,7 @@
#include <cmath>
#include "base/bind.h"
#include "base/callback.h"
#include "base/debug/trace_event_impl.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
@ -33,18 +27,25 @@ const char kRecordContinuously[] = "record-continuously";
const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
const char kEnableSampling[] = "enable-sampling";
void ReadFile(
const base::FilePath& path,
const base::Callback<void(const scoped_refptr<base::RefCountedString>&)>
callback) {
std::string trace_data;
if (!base::ReadFileToString(path, &trace_data))
LOG(ERROR) << "Failed to read file: " << path.value();
base::DeleteFile(path, false);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(callback, make_scoped_refptr(
base::RefCountedString::TakeString(&trace_data))));
}
class DevToolsTraceSinkProxy : public TracingController::TraceDataSink {
public:
explicit DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler)
: tracing_handler_(handler) {}
virtual void AddTraceChunk(const std::string& chunk) OVERRIDE {
if (DevToolsTracingHandler* h = tracing_handler_.get())
h->OnTraceDataCollected(chunk);
}
virtual void Close() OVERRIDE {
if (DevToolsTracingHandler* h = tracing_handler_.get())
h->OnTraceComplete();
}
private:
virtual ~DevToolsTraceSinkProxy() {}
base::WeakPtr<DevToolsTracingHandler> tracing_handler_;
};
} // namespace
@ -70,58 +71,22 @@ DevToolsTracingHandler::DevToolsTracingHandler(
DevToolsTracingHandler::~DevToolsTracingHandler() {
}
void DevToolsTracingHandler::BeginReadingRecordingResult(
const base::FilePath& path) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&ReadFile, path,
base::Bind(&DevToolsTracingHandler::ReadRecordingResult,
weak_factory_.GetWeakPtr())));
}
void DevToolsTracingHandler::ReadRecordingResult(
const scoped_refptr<base::RefCountedString>& trace_data) {
if (trace_data->data().size()) {
scoped_ptr<base::Value> trace_value(base::JSONReader::Read(
trace_data->data()));
base::DictionaryValue* dictionary = NULL;
bool ok = trace_value->GetAsDictionary(&dictionary);
DCHECK(ok);
base::ListValue* list = NULL;
ok = dictionary->GetList("traceEvents", &list);
DCHECK(ok);
std::string buffer;
for (size_t i = 0; i < list->GetSize(); ++i) {
std::string item;
base::Value* item_value;
list->Get(i, &item_value);
base::JSONWriter::Write(item_value, &item);
if (buffer.size())
buffer.append(",");
buffer.append(item);
const size_t kMessageSizeThreshold = 1024 * 1024;
if (buffer.size() > kMessageSizeThreshold) {
OnTraceDataCollected(buffer);
buffer.clear();
}
}
if (buffer.size())
OnTraceDataCollected(buffer);
}
SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
}
void DevToolsTracingHandler::OnTraceDataCollected(
const std::string& trace_fragment) {
// Hand-craft protocol notification message so we can substitute JSON
// that we already got as string as a bare object, not a quoted string.
std::string message = base::StringPrintf(
"{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }",
devtools::Tracing::dataCollected::kName,
devtools::Tracing::dataCollected::kParamValue,
trace_fragment.c_str());
SendRawMessage(message);
std::string message =
base::StringPrintf("{ \"method\": \"%s\", \"params\": { \"%s\": [",
devtools::Tracing::dataCollected::kName,
devtools::Tracing::dataCollected::kParamValue);
const size_t messageSuffixSize = 10;
message.reserve(message.size() + trace_fragment.size() + messageSuffixSize);
message += trace_fragment;
message += "] } }", SendRawMessage(message);
}
void DevToolsTracingHandler::OnTraceComplete() {
SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
}
base::debug::TraceOptions DevToolsTracingHandler::TraceOptionsFromString(
@ -221,23 +186,20 @@ DevToolsTracingHandler::OnEnd(
// tracing agent in the renderer.
if (target_ == Renderer)
return NULL;
DisableRecording(
base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult,
weak_factory_.GetWeakPtr()));
DisableRecording(false);
return command->SuccessResponse(NULL);
}
void DevToolsTracingHandler::DisableRecording(
const TracingController::TracingFileResultCallback& callback) {
void DevToolsTracingHandler::DisableRecording(bool abort) {
is_recording_ = false;
buffer_usage_poll_timer_.reset();
TracingController::GetInstance()->DisableRecording(base::FilePath(),
callback);
TracingController::GetInstance()->DisableRecording(
abort ? NULL : new DevToolsTraceSinkProxy(weak_factory_.GetWeakPtr()));
}
void DevToolsTracingHandler::OnClientDetached() {
if (is_recording_)
DisableRecording();
DisableRecording(true);
}
scoped_refptr<DevToolsProtocol::Response>
@ -283,9 +245,7 @@ void DevToolsTracingHandler::DisableTracing() {
if (!is_recording_)
return;
is_recording_ = false;
DisableRecording(
base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult,
weak_factory_.GetWeakPtr()));
DisableRecording(false);
}

@ -33,10 +33,10 @@ class DevToolsTracingHandler : public DevToolsProtocol::Handler {
void EnableTracing(const std::string& category_filter);
void DisableTracing();
private:
void BeginReadingRecordingResult(const base::FilePath& path);
void ReadRecordingResult(const scoped_refptr<base::RefCountedString>& result);
void OnTraceDataCollected(const std::string& trace_fragment);
void OnTraceComplete();
private:
void OnRecordingEnabled(scoped_refptr<DevToolsProtocol::Command> command);
void OnBufferUsage(float usage);
@ -55,9 +55,7 @@ class DevToolsTracingHandler : public DevToolsProtocol::Handler {
void SetupTimer(double usage_reporting_interval);
void DisableRecording(
const TracingController::TracingFileResultCallback& callback =
TracingController::TracingFileResultCallback());
void DisableRecording(bool abort);
base::WeakPtrFactory<DevToolsTracingHandler> weak_factory_;
scoped_ptr<base::Timer> buffer_usage_poll_timer_;

@ -3,8 +3,9 @@
// found in the LICENSE file.
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/run_loop.h"
#include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/browser/tracing_controller.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
@ -51,8 +52,15 @@ class TracingControllerTest : public ContentBrowserTest {
quit_callback.Run();
}
void DisableRecordingDoneCallbackTest(base::Closure quit_callback,
const base::FilePath& file_path) {
void DisableRecordingStringDoneCallbackTest(base::Closure quit_callback,
base::RefCountedString* data) {
disable_recording_done_callback_count_++;
EXPECT_TRUE(data->size() > 0);
quit_callback.Run();
}
void DisableRecordingFileDoneCallbackTest(base::Closure quit_callback,
const base::FilePath& file_path) {
disable_recording_done_callback_count_++;
EXPECT_TRUE(PathExists(file_path));
int64 file_size;
@ -115,7 +123,7 @@ class TracingControllerTest : public ContentBrowserTest {
return last_actual_monitoring_file_path_;
}
void TestEnableAndDisableRecording(const base::FilePath& result_file_path) {
void TestEnableAndDisableRecordingString() {
Navigate(shell());
TracingController* controller = TracingController::GetInstance();
@ -135,11 +143,46 @@ class TracingControllerTest : public ContentBrowserTest {
{
base::RunLoop run_loop;
TracingController::TracingFileResultCallback callback =
base::Bind(&TracingControllerTest::DisableRecordingDoneCallbackTest,
base::Callback<void(base::RefCountedString*)> callback = base::Bind(
&TracingControllerTest::DisableRecordingStringDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure());
bool result = controller->DisableRecording(
TracingController::CreateStringSink(callback));
ASSERT_TRUE(result);
run_loop.Run();
EXPECT_EQ(disable_recording_done_callback_count(), 1);
}
}
void TestEnableAndDisableRecordingFile(
const base::FilePath& result_file_path) {
Navigate(shell());
TracingController* controller = TracingController::GetInstance();
{
base::RunLoop run_loop;
TracingController::EnableRecordingDoneCallback callback =
base::Bind(&TracingControllerTest::EnableRecordingDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure());
bool result = controller->DisableRecording(result_file_path, callback);
bool result = controller->EnableRecording(
CategoryFilter(), TraceOptions(), callback);
ASSERT_TRUE(result);
run_loop.Run();
EXPECT_EQ(enable_recording_done_callback_count(), 1);
}
{
base::RunLoop run_loop;
base::Closure callback = base::Bind(
&TracingControllerTest::DisableRecordingFileDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure(),
result_file_path);
bool result = controller->DisableRecording(
TracingController::CreateFileSink(result_file_path, callback));
ASSERT_TRUE(result);
run_loop.Run();
EXPECT_EQ(disable_recording_done_callback_count(), 1);
@ -199,13 +242,13 @@ class TracingControllerTest : public ContentBrowserTest {
{
base::RunLoop run_loop;
TracingController::TracingFileResultCallback callback =
base::Bind(&TracingControllerTest::
CaptureMonitoringSnapshotDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure());
ASSERT_TRUE(controller->CaptureMonitoringSnapshot(result_file_path,
callback));
base::Closure callback = base::Bind(
&TracingControllerTest::CaptureMonitoringSnapshotDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure(),
result_file_path);
ASSERT_TRUE(controller->CaptureMonitoringSnapshot(
TracingController::CreateFileSink(result_file_path, callback)));
run_loop.Run();
EXPECT_EQ(capture_monitoring_snapshot_done_callback_count(), 1);
}
@ -264,14 +307,14 @@ IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) {
}
IN_PROC_BROWSER_TEST_F(TracingControllerTest, EnableAndDisableRecording) {
TestEnableAndDisableRecording(base::FilePath());
TestEnableAndDisableRecordingString();
}
IN_PROC_BROWSER_TEST_F(TracingControllerTest,
EnableAndDisableRecordingWithFilePath) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
TestEnableAndDisableRecording(file_path);
TestEnableAndDisableRecordingFile(file_path);
EXPECT_EQ(file_path.value(), last_actual_recording_file_path().value());
}
@ -284,14 +327,15 @@ IN_PROC_BROWSER_TEST_F(TracingControllerTest,
CategoryFilter(),
TraceOptions(),
TracingController::EnableRecordingDoneCallback()));
EXPECT_TRUE(controller->DisableRecording(
base::FilePath(), TracingController::TracingFileResultCallback()));
EXPECT_TRUE(controller->DisableRecording(NULL));
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(TracingControllerTest,
EnableCaptureAndDisableMonitoring) {
TestEnableCaptureAndDisableMonitoring(base::FilePath());
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
TestEnableCaptureAndDisableMonitoring(file_path);
}
IN_PROC_BROWSER_TEST_F(TracingControllerTest,
@ -322,8 +366,7 @@ IN_PROC_BROWSER_TEST_F(
CategoryFilter("*"),
trace_options,
TracingController::EnableMonitoringDoneCallback()));
controller->CaptureMonitoringSnapshot(
base::FilePath(), TracingController::TracingFileResultCallback());
controller->CaptureMonitoringSnapshot(NULL);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(controller->DisableMonitoring(
TracingController::DisableMonitoringDoneCallback()));

@ -34,134 +34,126 @@ namespace {
base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
LAZY_INSTANCE_INITIALIZER;
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) {}
virtual 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));
}
virtual void SetSystemTrace(const std::string& data) OVERRIDE {
system_trace_ = data;
}
virtual void Close() OVERRIDE {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&FileTraceDataSink::CloseOnFileThread, this));
}
private:
virtual ~FileTraceDataSink() { DCHECK(file_ == NULL); }
void AddTraceChunkOnFileThread(
const scoped_refptr<base::RefCountedString> chunk) {
if (!OpenFileIfNeededOnFileThread())
return;
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\": [";
fwrite(preamble, strlen(preamble), 1, file_);
return true;
}
void CloseOnFileThread() {
if (OpenFileIfNeededOnFileThread()) {
fputc(']', file_);
if (!system_trace_.empty()) {
const char systemTraceEvents[] = ",\"systemTraceEvents\": ";
fwrite(systemTraceEvents, strlen(systemTraceEvents), 1, file_);
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
virtual void AddTraceChunk(const std::string& chunk) OVERRIDE {
if (!trace_.empty())
trace_ += ",";
trace_ += chunk;
}
virtual void SetSystemTrace(const std::string& data) OVERRIDE {
system_trace_ = data;
}
virtual void Close() OVERRIDE {
std::string result = "{\"traceEvents\":[" + trace_ + "]";
if (!system_trace_.empty())
result += ",\"systemTraceEvents\": " + system_trace_;
result += "}";
completion_callback_.Run(base::RefCountedString::TakeString(&result));
}
private:
virtual ~StringTraceDataSink() {}
std::string trace_;
std::string system_trace_;
CompletionCallback completion_callback_;
DISALLOW_COPY_AND_ASSIGN(StringTraceDataSink);
};
} // namespace
TracingController* TracingController::GetInstance() {
return TracingControllerImpl::GetInstance();
}
class TracingControllerImpl::ResultFile {
public:
explicit ResultFile(const base::FilePath& path);
void Write(const scoped_refptr<base::RefCountedString>& events_str_ptr) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::ResultFile::WriteTask,
base::Unretained(this), events_str_ptr));
}
void Close(const base::Closure& callback) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::ResultFile::CloseTask,
base::Unretained(this), callback));
}
void WriteSystemTrace(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&TracingControllerImpl::ResultFile::WriteSystemTraceTask,
base::Unretained(this), events_str_ptr));
}
const base::FilePath& path() const { return path_; }
private:
void OpenTask();
void WriteTask(const scoped_refptr<base::RefCountedString>& events_str_ptr);
void WriteSystemTraceTask(
const scoped_refptr<base::RefCountedString>& events_str_ptr);
void CloseTask(const base::Closure& callback);
FILE* file_;
base::FilePath path_;
bool has_at_least_one_result_;
scoped_refptr<base::RefCountedString> system_trace_;
DISALLOW_COPY_AND_ASSIGN(ResultFile);
};
TracingControllerImpl::ResultFile::ResultFile(const base::FilePath& path)
: file_(NULL),
path_(path),
has_at_least_one_result_(false) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::ResultFile::OpenTask,
base::Unretained(this)));
}
void TracingControllerImpl::ResultFile::OpenTask() {
if (path_.empty())
base::CreateTemporaryFile(&path_);
file_ = base::OpenFile(path_, "w");
if (!file_) {
LOG(ERROR) << "Failed to open " << path_.value();
return;
}
const char* preamble = "{\"traceEvents\": [";
size_t written = fwrite(preamble, strlen(preamble), 1, file_);
DCHECK(written == 1);
}
void TracingControllerImpl::ResultFile::WriteTask(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
if (!file_ || !events_str_ptr->data().size())
return;
// If there is already a result in the file, then put a comma
// before the next batch of results.
if (has_at_least_one_result_) {
size_t written = fwrite(",", 1, 1, file_);
DCHECK(written == 1);
}
has_at_least_one_result_ = true;
size_t written = fwrite(events_str_ptr->data().c_str(),
events_str_ptr->data().size(), 1,
file_);
DCHECK(written == 1);
}
void TracingControllerImpl::ResultFile::WriteSystemTraceTask(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
system_trace_ = events_str_ptr;
}
void TracingControllerImpl::ResultFile::CloseTask(
const base::Closure& callback) {
if (!file_)
return;
const char* trailevents = "]";
size_t written = fwrite(trailevents, strlen(trailevents), 1, file_);
DCHECK(written == 1);
if (system_trace_.get()) {
#if defined(OS_WIN)
// The Windows kernel events are kept into a JSon format stored as string
// and must not be escaped.
std::string json_string = system_trace_->data();
#else
std::string json_string = base::GetQuotedJSONString(system_trace_->data());
#endif
const char* systemTraceHead = ",\n\"systemTraceEvents\": ";
written = fwrite(systemTraceHead, strlen(systemTraceHead), 1, file_);
DCHECK(written == 1);
written = fwrite(json_string.data(), json_string.size(), 1, file_);
DCHECK(written == 1);
system_trace_ = NULL;
}
const char* trailout = "}";
written = fwrite(trailout, strlen(trailout), 1, file_);
DCHECK(written == 1);
base::CloseFile(file_);
file_ = NULL;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}
TracingControllerImpl::TracingControllerImpl() :
pending_disable_recording_ack_count_(0),
pending_capture_monitoring_snapshot_ack_count_(0),
@ -199,7 +191,7 @@ bool TracingControllerImpl::GetCategories(
return false;
}
bool ok = DisableRecording(base::FilePath(), TracingFileResultCallback());
bool ok = DisableRecording(NULL);
DCHECK(ok);
return true;
}
@ -286,20 +278,18 @@ void TracingControllerImpl::OnEnableRecordingDone(
}
bool TracingControllerImpl::DisableRecording(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) {
const scoped_refptr<TraceDataSink>& trace_data_sink) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!can_disable_recording())
return false;
trace_data_sink_ = trace_data_sink;
trace_options_ = TraceOptions();
// Disable local trace early to avoid traces during end-tracing process from
// interfering with the process.
base::Closure on_disable_recording_done_callback =
base::Bind(&TracingControllerImpl::OnDisableRecordingDone,
base::Unretained(this),
result_file_path, callback);
base::Closure on_disable_recording_done_callback = base::Bind(
&TracingControllerImpl::OnDisableRecordingDone, base::Unretained(this));
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
base::Unretained(this),
@ -307,21 +297,14 @@ bool TracingControllerImpl::DisableRecording(
return true;
}
void TracingControllerImpl::OnDisableRecordingDone(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) {
void TracingControllerImpl::OnDisableRecordingDone() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
pending_disable_recording_done_callback_ = callback;
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
if (!callback.is_null() || !result_file_path.empty())
result_file_.reset(new ResultFile(result_file_path));
// Count myself (local trace) in pending_disable_recording_ack_count_,
// acked below.
pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1;
@ -431,6 +414,18 @@ bool TracingControllerImpl::DisableMonitoring(
return true;
}
scoped_refptr<TracingController::TraceDataSink>
TracingController::CreateStringSink(
const base::Callback<void(base::RefCountedString*)>& callback) {
return new StringTraceDataSink(callback);
}
scoped_refptr<TracingController::TraceDataSink>
TracingController::CreateFileSink(const base::FilePath& file_path,
const base::Closure& callback) {
return new FileTraceDataSink(file_path, callback);
}
void TracingControllerImpl::OnDisableMonitoringDone(
const DisableMonitoringDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@ -442,7 +437,6 @@ void TracingControllerImpl::OnDisableMonitoringDone(
it != trace_message_filters_.end(); ++it) {
it->get()->SendDisableMonitoring();
}
if (!callback.is_null())
callback.Run();
}
@ -457,18 +451,16 @@ void TracingControllerImpl::GetMonitoringStatus(
}
bool TracingControllerImpl::CaptureMonitoringSnapshot(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) {
const scoped_refptr<TraceDataSink>& monitoring_data_sink) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!can_disable_monitoring())
return false;
if (callback.is_null() && result_file_path.empty())
if (!monitoring_data_sink.get())
return false;
pending_capture_monitoring_snapshot_done_callback_ = callback;
monitoring_snapshot_file_.reset(new ResultFile(result_file_path));
monitoring_data_sink_ = monitoring_data_sink;
// Count myself in pending_capture_monitoring_snapshot_ack_count_,
// acked below.
@ -685,10 +677,6 @@ void TracingControllerImpl::OnDisableRecordingAcked(
if (pending_disable_recording_ack_count_ != 0)
return;
OnDisableRecordingComplete();
}
void TracingControllerImpl::OnDisableRecordingComplete() {
// All acks (including from the subprocesses and the local trace) have been
// received.
is_recording_ = false;
@ -697,34 +685,28 @@ void TracingControllerImpl::OnDisableRecordingComplete() {
if (!pending_get_categories_done_callback_.is_null()) {
pending_get_categories_done_callback_.Run(known_category_groups_);
pending_get_categories_done_callback_.Reset();
} else if (result_file_) {
result_file_->Close(
base::Bind(&TracingControllerImpl::OnResultFileClosed,
base::Unretained(this)));
} else if (trace_data_sink_.get()) {
trace_data_sink_->Close();
trace_data_sink_ = NULL;
}
}
void TracingControllerImpl::OnResultFileClosed() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!result_file_)
return;
if (!pending_disable_recording_done_callback_.is_null()) {
pending_disable_recording_done_callback_.Run(result_file_->path());
pending_disable_recording_done_callback_.Reset();
}
result_file_.reset();
}
#if defined(OS_CHROMEOS) || defined(OS_WIN)
void TracingControllerImpl::OnEndSystemTracingAcked(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (result_file_)
result_file_->WriteSystemTrace(events_str_ptr);
if (trace_data_sink_.get()) {
#if defined(OS_WIN)
// The Windows kernel events are kept into a JSon format stored as string
// and must not be escaped.
std::string json_string = events_str_ptr->data();
#else
std::string json_string =
base::GetQuotedJSONString(events_str_ptr->data());
#endif
trace_data_sink_->SetSystemTrace(json_string);
}
DCHECK(!is_system_tracing_);
std::vector<std::string> category_groups;
OnDisableRecordingAcked(NULL, category_groups);
@ -763,27 +745,12 @@ void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked(
if (pending_capture_monitoring_snapshot_ack_count_ != 0)
return;
if (monitoring_snapshot_file_) {
monitoring_snapshot_file_->Close(
base::Bind(&TracingControllerImpl::OnMonitoringSnapshotFileClosed,
base::Unretained(this)));
if (monitoring_data_sink_.get()) {
monitoring_data_sink_->Close();
monitoring_data_sink_ = NULL;
}
}
void TracingControllerImpl::OnMonitoringSnapshotFileClosed() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!monitoring_snapshot_file_)
return;
if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) {
pending_capture_monitoring_snapshot_done_callback_.Run(
monitoring_snapshot_file_->path());
pending_capture_monitoring_snapshot_done_callback_.Reset();
}
monitoring_snapshot_file_.reset();
}
void TracingControllerImpl::OnTraceDataCollected(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
// OnTraceDataCollected may be called from any browser thread, either by the
@ -795,8 +762,8 @@ void TracingControllerImpl::OnTraceDataCollected(
return;
}
if (result_file_)
result_file_->Write(events_str_ptr);
if (trace_data_sink_.get())
trace_data_sink_->AddTraceChunk(events_str_ptr->data());
}
void TracingControllerImpl::OnMonitoringTraceDataCollected(
@ -808,8 +775,8 @@ void TracingControllerImpl::OnMonitoringTraceDataCollected(
return;
}
if (monitoring_snapshot_file_)
monitoring_snapshot_file_->Write(events_str_ptr);
if (monitoring_data_sink_.get())
monitoring_data_sink_->AddTraceChunk(events_str_ptr->data());
}
void TracingControllerImpl::OnLocalTraceDataCollected(

@ -9,12 +9,12 @@
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "content/public/browser/tracing_controller.h"
namespace base {
class RefCountedString;
class RefCountedMemory;
}
namespace content {
@ -34,8 +34,7 @@ class TracingControllerImpl : public TracingController {
const base::debug::TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback) OVERRIDE;
virtual bool DisableRecording(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) OVERRIDE;
const scoped_refptr<TraceDataSink>& sink) OVERRIDE;
virtual bool EnableMonitoring(
const base::debug::CategoryFilter& category_filter,
const base::debug::TraceOptions& trace_options,
@ -47,8 +46,7 @@ class TracingControllerImpl : public TracingController {
base::debug::CategoryFilter* out_category_filter,
base::debug::TraceOptions* out_trace_options) OVERRIDE;
virtual bool CaptureMonitoringSnapshot(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) OVERRIDE;
const scoped_refptr<TraceDataSink>& sink) OVERRIDE;
virtual bool GetTraceBufferPercentFull(
const GetTraceBufferPercentFullCallback& callback) OVERRIDE;
virtual bool SetWatchEvent(const std::string& category_name,
@ -61,7 +59,6 @@ class TracingControllerImpl : public TracingController {
private:
typedef std::set<scoped_refptr<TraceMessageFilter> > TraceMessageFilterSet;
class ResultFile;
friend struct base::DefaultLazyInstanceTraits<TracingControllerImpl>;
friend class TraceMessageFilter;
@ -74,7 +71,7 @@ class TracingControllerImpl : public TracingController {
}
bool can_disable_recording() const {
return is_recording_ && !result_file_;
return is_recording_ && !trace_data_sink_.get();
}
bool can_enable_monitoring() const {
@ -82,7 +79,7 @@ class TracingControllerImpl : public TracingController {
}
bool can_disable_monitoring() const {
return is_monitoring_ && !monitoring_snapshot_file_;
return is_monitoring_ && !monitoring_data_sink_.get();
}
bool can_get_trace_buffer_percent_full() const {
@ -114,8 +111,6 @@ class TracingControllerImpl : public TracingController {
void OnDisableRecordingAcked(
TraceMessageFilter* trace_message_filter,
const std::vector<std::string>& known_category_groups);
void OnDisableRecordingComplete();
void OnResultFileClosed();
#if defined(OS_CHROMEOS) || defined(OS_WIN)
void OnEndSystemTracingAcked(
@ -124,7 +119,6 @@ class TracingControllerImpl : public TracingController {
void OnCaptureMonitoringSnapshotAcked(
TraceMessageFilter* trace_message_filter);
void OnMonitoringSnapshotFileClosed();
void OnTraceBufferPercentFullReply(
TraceMessageFilter* trace_message_filter,
@ -141,8 +135,7 @@ class TracingControllerImpl : public TracingController {
void OnEnableRecordingDone(const base::debug::CategoryFilter& category_filter,
const base::debug::TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback);
void OnDisableRecordingDone(const base::FilePath& result_file_path,
const TracingFileResultCallback& callback);
void OnDisableRecordingDone();
void OnEnableMonitoringDone(
const base::debug::CategoryFilter& category_filter,
const base::debug::TraceOptions& trace_options,
@ -172,8 +165,6 @@ class TracingControllerImpl : public TracingController {
base::debug::TraceOptions trace_options_;
GetCategoriesDoneCallback pending_get_categories_done_callback_;
TracingFileResultCallback pending_disable_recording_done_callback_;
TracingFileResultCallback pending_capture_monitoring_snapshot_done_callback_;
GetTraceBufferPercentFullCallback pending_trace_buffer_percent_full_callback_;
std::string watch_category_name_;
@ -182,8 +173,8 @@ class TracingControllerImpl : public TracingController {
std::set<std::string> known_category_groups_;
std::set<TracingUI*> tracing_uis_;
scoped_ptr<ResultFile> result_file_;
scoped_ptr<ResultFile> monitoring_snapshot_file_;
scoped_refptr<TraceDataSink> trace_data_sink_;
scoped_refptr<TraceDataSink> monitoring_data_sink_;
DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl);
};

@ -13,7 +13,6 @@
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
@ -136,23 +135,6 @@ void OnTraceBufferPercentFullResult(
callback.Run(base::RefCountedString::TakeString(&str));
}
void ReadRecordingResult(const WebUIDataSource::GotDataCallback& callback,
const base::FilePath& path) {
std::string tmp;
if (!base::ReadFileToString(path, &tmp))
LOG(ERROR) << "Failed to read file " << path.value();
base::DeleteFile(path, false);
callback.Run(base::RefCountedString::TakeString(&tmp));
}
void BeginReadingRecordingResult(
const WebUIDataSource::GotDataCallback& callback,
const base::FilePath& path) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(ReadRecordingResult, callback, path));
}
void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
bool EnableMonitoring(const std::string& data64,
@ -205,21 +187,9 @@ void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
callback.Run(monitoring_options_base64);
}
void ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback& callback,
const base::FilePath& path) {
std::string tmp;
if (!base::ReadFileToString(path, &tmp))
LOG(ERROR) << "Failed to read file " << path.value();
base::DeleteFile(path, false);
callback.Run(base::RefCountedString::TakeString(&tmp));
}
void OnMonitoringSnapshotCaptured(
const WebUIDataSource::GotDataCallback& callback,
const base::FilePath& path) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(ReadMonitoringSnapshot, callback, path));
void TracingCallbackWrapper(const WebUIDataSource::GotDataCallback& callback,
base::RefCountedString* data) {
callback.Run(data);
}
bool OnBeginJSONRequest(const std::string& path,
@ -240,7 +210,8 @@ bool OnBeginJSONRequest(const std::string& path,
}
if (path == "json/end_recording") {
return TracingController::GetInstance()->DisableRecording(
base::FilePath(), base::Bind(BeginReadingRecordingResult, callback));
TracingControllerImpl::CreateStringSink(
base::Bind(TracingCallbackWrapper, callback)));
}
const char* enableMonitoringPath = "json/begin_monitoring?";
@ -254,7 +225,8 @@ bool OnBeginJSONRequest(const std::string& path,
}
if (path == "json/capture_monitoring") {
TracingController::GetInstance()->CaptureMonitoringSnapshot(
base::FilePath(), base::Bind(OnMonitoringSnapshotCaptured, callback));
TracingControllerImpl::CreateStringSink(
base::Bind(TracingCallbackWrapper, callback)));
return true;
}
if (path == "json/get_monitoring_status") {

@ -10,12 +10,9 @@
#include "base/callback.h"
#include "base/debug/trace_event.h"
#include "base/memory/ref_counted.h"
#include "content/common/content_export.h"
namespace base {
class FilePath;
};
namespace content {
class TracingController;
@ -29,6 +26,36 @@ class TracingController {
CONTENT_EXPORT static TracingController* GetInstance();
// An interface for trace data consumer. An implemnentation of this interface
// is passed to either DisableTracing() or CaptureMonitoringSnapshot() and
// receives the trace data followed by a notification that all child processes
// have completed tracing and the data collection is over.
// All methods are called on the UI thread.
// Close method will be called exactly once and no methods will be
// called after that.
class CONTENT_EXPORT TraceDataSink
: public base::RefCountedThreadSafe<TraceDataSink> {
public:
virtual void AddTraceChunk(const std::string& chunk) {}
virtual void SetSystemTrace(const std::string& data) {}
virtual void Close() {}
protected:
friend class base::RefCountedThreadSafe<TraceDataSink>;
virtual ~TraceDataSink() {}
};
// Create a trace sink that may be supplied to DisableRecording or
// CaptureMonitoringSnapshot to capture the trace data as a string.
CONTENT_EXPORT static scoped_refptr<TraceDataSink> CreateStringSink(
const base::Callback<void(base::RefCountedString*)>& callback);
// Create a trace sink that may be supplied to DisableRecording or
// CaptureMonitoringSnapshot to dump the trace data to a file.
CONTENT_EXPORT static scoped_refptr<TraceDataSink> CreateFileSink(
const base::FilePath& file_path,
const base::Closure& callback);
// Get a set of category groups. The category groups can change as
// new code paths are reached.
//
@ -76,15 +103,12 @@ class TracingController {
// TracingFileResultCallback will be called back with a file that contains
// the traced data.
//
// Trace data will be written into |result_file_path| if it is not empty, or
// into a temporary file. The actual file path will be passed to |callback| if
// it's not null.
// If |trace_data_sink| is not null, it will receive chunks of trace data
// as a comma-separated sequences of JSON-stringified events, followed by
// a notification that the trace collection is finished.
//
// If |result_file_path| is empty and |callback| is null, trace data won't be
// written to any file.
typedef base::Callback<void(const base::FilePath&)> TracingFileResultCallback;
virtual bool DisableRecording(const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) = 0;
virtual bool DisableRecording(
const scoped_refptr<TraceDataSink>& trace_data_sink) = 0;
// Start monitoring on all processes.
//
@ -130,14 +154,11 @@ class TracingController {
// request, TracingFileResultCallback will be called back with a file that
// contains the traced data.
//
// Trace data will be written into |result_file_path| if it is not empty, or
// into a temporary file. The actual file path will be passed to |callback|.
//
// If |result_file_path| is empty and |callback| is null, trace data won't be
// written to any file.
// If |trace_data_sink| is not null, it will receive chunks of trace data
// as a comma-separated sequences of JSON-stringified events, followed by
// a notification that the trace collection is finished.
virtual bool CaptureMonitoringSnapshot(
const base::FilePath& result_file_path,
const TracingFileResultCallback& callback) = 0;
const scoped_refptr<TraceDataSink>& trace_data_sink) = 0;
// Get the maximum across processes of trace buffer percent full state.
// When the TraceBufferPercentFull value is determined, the callback is

@ -14,8 +14,8 @@
#include "base/test/test_timeouts.h"
#include "content/public/app/content_main.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/tracing_controller.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/test/test_launcher.h"
@ -301,8 +301,11 @@ void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
// Wait for tracing to collect results from the renderers.
base::RunLoop run_loop;
TracingController::GetInstance()->DisableRecording(
trace_file,
base::Bind(&TraceDisableRecordingComplete, run_loop.QuitClosure()));
TracingControllerImpl::CreateFileSink(
trace_file,
base::Bind(&TraceDisableRecordingComplete,
run_loop.QuitClosure(),
trace_file)));
run_loop.Run();
}
}