diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/content/browser/tracing | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) | |
download | qtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/content/browser/tracing')
11 files changed, 772 insertions, 66 deletions
diff --git a/chromium/content/browser/tracing/OWNERS b/chromium/content/browser/tracing/OWNERS index 93d1471e1ef..9130707df06 100644 --- a/chromium/content/browser/tracing/OWNERS +++ b/chromium/content/browser/tracing/OWNERS @@ -1 +1,2 @@ nduca@chromium.org +dsinclair@chromium.org diff --git a/chromium/content/browser/tracing/trace_controller_impl.cc b/chromium/content/browser/tracing/trace_controller_impl.cc index 54e0c86cb42..fe08639876d 100644 --- a/chromium/content/browser/tracing/trace_controller_impl.cc +++ b/chromium/content/browser/tracing/trace_controller_impl.cc @@ -26,9 +26,11 @@ base::LazyInstance<TraceControllerImpl>::Leaky g_controller = class AutoStopTraceSubscriberStdio : public TraceSubscriberStdio { public: AutoStopTraceSubscriberStdio(const base::FilePath& file_path) - : TraceSubscriberStdio(file_path) {} + : TraceSubscriberStdio(file_path, + FILE_TYPE_PROPERTY_LIST, + false) {} - static void EndStartupTrace(TraceSubscriberStdio* subscriber) { + static void EndStartupTrace(AutoStopTraceSubscriberStdio* subscriber) { if (!TraceControllerImpl::GetInstance()->EndTracingAsync(subscriber)) delete subscriber; // else, the tracing will end asynchronously in OnEndTracingComplete(). @@ -146,6 +148,10 @@ bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) { if (!can_end_tracing() || subscriber != subscriber_) return false; + // Disable local trace early to avoid traces during end-tracing process from + // interfering with the process. + TraceLog::GetInstance()->SetDisabled(); + // There could be a case where there are no child processes and filters_ // is empty. In that case we can immediately tell the subscriber that tracing // has ended. To avoid recursive calls back to the subscriber, we will just @@ -299,20 +305,20 @@ void TraceControllerImpl::OnEndTracingAck( if (pending_end_ack_count_ == 0) return; - if (--pending_end_ack_count_ == 0) { - // All acks have been received. - is_tracing_ = false; - - // Disable local trace. - TraceLog::GetInstance()->SetDisabled(); - // During this call, our OnTraceDataCollected will be - // called with the last of the local trace data. Since we are on the UI - // thread, the call to OnTraceDataCollected will be synchronous, so we can - // immediately call OnEndTracingComplete below. + if (--pending_end_ack_count_ == 1) { + // All acks from subprocesses have been received. Now flush the local trace. + // During or after this call, our OnLocalTraceDataCollected will be + // called with the last of the local trace data. TraceLog::GetInstance()->Flush( - base::Bind(&TraceControllerImpl::OnTraceDataCollected, + base::Bind(&TraceControllerImpl::OnLocalTraceDataCollected, base::Unretained(this))); + } + + if (pending_end_ack_count_ == 0) { + // All acks (including from the subprocesses and the local trace) have been + // received. + is_tracing_ = false; // Trigger callback if one is set. if (subscriber_) { @@ -326,16 +332,6 @@ void TraceControllerImpl::OnEndTracingAck( is_get_category_groups_ = false; } - - if (pending_end_ack_count_ == 1) { - // The last ack represents local trace, so we need to ack it now. Note that - // this code only executes if there were child processes. - std::vector<std::string> category_groups; - TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnEndTracingAck, - base::Unretained(this), category_groups)); - } } void TraceControllerImpl::OnTraceDataCollected( @@ -354,6 +350,20 @@ void TraceControllerImpl::OnTraceDataCollected( subscriber_->OnTraceDataCollected(events_str_ptr); } +void TraceControllerImpl::OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events) { + if (events_str_ptr->data().size()) + OnTraceDataCollected(events_str_ptr); + + if (!has_more_events) { + // Simulate an EndTrackingAck for the local trace. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + OnEndTracingAck(category_groups); + } +} + void TraceControllerImpl::OnTraceNotification(int notification) { // OnTraceNotification may be called from any browser thread, either by the // local event trace system or from child processes via TraceMessageFilter. diff --git a/chromium/content/browser/tracing/trace_controller_impl.h b/chromium/content/browser/tracing/trace_controller_impl.h index f24d283a125..6f2ccb5d481 100644 --- a/chromium/content/browser/tracing/trace_controller_impl.h +++ b/chromium/content/browser/tracing/trace_controller_impl.h @@ -81,6 +81,11 @@ class TraceControllerImpl : public TraceController { void OnTraceNotification(int notification); void OnTraceBufferPercentFullReply(float percent_full); + // Callback of TraceLog::Flush() for the local trace. + void OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events); + FilterMap filters_; TraceSubscriber* subscriber_; // Pending acks for EndTracingAsync: diff --git a/chromium/content/browser/tracing/trace_message_filter.h b/chromium/content/browser/tracing/trace_message_filter.h index 8da34534604..41fd017827f 100644 --- a/chromium/content/browser/tracing/trace_message_filter.h +++ b/chromium/content/browser/tracing/trace_message_filter.h @@ -14,7 +14,7 @@ namespace content { // This class sends and receives trace messages on the browser process. -// See also: trace_controller.h +// See also: tracing_controller.h // See also: child_trace_message_filter.h class TraceMessageFilter : public BrowserMessageFilter { public: diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.cc b/chromium/content/browser/tracing/trace_subscriber_stdio.cc index b5f6e828c60..2c6aede56ce 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.cc +++ b/chromium/content/browser/tracing/trace_subscriber_stdio.cc @@ -14,39 +14,79 @@ namespace content { // All method calls on this class are done on a SequencedWorkerPool thread. -class TraceSubscriberStdioImpl - : public base::RefCountedThreadSafe<TraceSubscriberStdioImpl> { +class TraceSubscriberStdio::TraceSubscriberStdioWorker + : public base::RefCountedThreadSafe<TraceSubscriberStdioWorker> { public: - explicit TraceSubscriberStdioImpl(const base::FilePath& path) + TraceSubscriberStdioWorker(const base::FilePath& path, + FileType file_type, + bool has_system_trace) : path_(path), - file_(0) {} - - void OnStart() { + file_type_(file_type), + has_system_trace_(has_system_trace), + file_(0), + needs_comma_(false), + wrote_trace_(false), + has_pending_system_trace_(false), + wrote_system_trace_(false) {} + + void OnTraceStart() { DCHECK(!file_); - trace_buffer_.SetOutputCallback( - base::Bind(&TraceSubscriberStdioImpl::Write, this)); file_ = file_util::OpenFile(path_, "w+"); - if (IsValid()) { - LOG(INFO) << "Logging performance trace to file: " << path_.value(); - trace_buffer_.Start(); - } else { + if (!IsValid()) { LOG(ERROR) << "Failed to open performance trace file: " << path_.value(); + return; } + + LOG(INFO) << "Logging performance trace to file: " << path_.value(); + if (file_type_ == FILE_TYPE_PROPERTY_LIST) + WriteString("{\"traceEvents\":"); + WriteString("["); } - void OnData(const scoped_refptr<base::RefCountedString>& data_ptr) { - trace_buffer_.AddFragment(data_ptr->data()); + void OnTraceData(const scoped_refptr<base::RefCountedString>& data_ptr) { + if (!IsValid()) + return; + DCHECK(!data_ptr->data().empty()); + if (needs_comma_) + WriteString(","); + WriteString(data_ptr->data()); + needs_comma_ = true; } - void OnEnd() { - trace_buffer_.Finish(); - CloseFile(); + void OnSystemTraceData( + const scoped_refptr<base::RefCountedString>& data_ptr) { + if (wrote_trace_) { + WriteSystemTrace(data_ptr); + End(); + } else { + pending_system_trace_ = data_ptr; + has_pending_system_trace_ = true; + } + } + + void OnTraceEnd() { + if (!IsValid()) + return; + WriteString("]"); + + wrote_trace_ = true; + + if (!has_system_trace_ || wrote_system_trace_) { + End(); + return; + } + + WriteString(","); + if (has_pending_system_trace_) { + WriteSystemTrace(pending_system_trace_); + End(); + } } private: - friend class base::RefCountedThreadSafe<TraceSubscriberStdioImpl>; + friend class base::RefCountedThreadSafe<TraceSubscriberStdioWorker>; - ~TraceSubscriberStdioImpl() { + ~TraceSubscriberStdioWorker() { CloseFile(); } @@ -58,32 +98,79 @@ class TraceSubscriberStdioImpl if (file_) { fclose(file_); file_ = 0; + + } + } + + void End() { + if (file_type_ == FILE_TYPE_PROPERTY_LIST) + WriteString("}"); + CloseFile(); + } + + void WriteSystemTrace(const scoped_refptr<base::RefCountedString>& data_ptr) { + // Newlines need to be replaced with the string "\n" to be parsed correctly. + // Double quotes need to be replaced with the string "\"". + // System logs are ASCII. + const std::string& data = data_ptr->data(); + const char* chars = data.c_str(); + WriteString("\"systemTraceEvents\":\""); + size_t old_index = 0; + for (size_t new_index = data.find_first_of("\n\""); + std::string::npos != new_index; + old_index = new_index + 1, + new_index = data.find_first_of("\n\"", old_index)) { + WriteChars(chars + old_index, new_index - old_index); + if (chars[new_index] == '\n') + WriteChars("\\n", 2); + else + WriteChars("\\\"", 2); } - // This is important, as it breaks a reference cycle. - trace_buffer_.SetOutputCallback( - base::debug::TraceResultBuffer::OutputCallback()); + WriteChars(chars + old_index, data.size() - old_index); + WriteString("\""); + wrote_system_trace_ = true; } - void Write(const std::string& output_str) { + void WriteChars(const char* output_chars, size_t size) { + if (size == 0) + return; + if (IsValid()) { - size_t written = fwrite(output_str.data(), 1, output_str.size(), file_); - if (written != output_str.size()) { + size_t written = fwrite(output_chars, 1, size, file_); + if (written != size) { LOG(ERROR) << "Error " << ferror(file_) << " in fwrite() to trace file"; CloseFile(); } } } + void WriteString(const std::string& output_str) { + WriteChars(output_str.data(), output_str.size()); + } + base::FilePath path_; + const FileType file_type_; + const bool has_system_trace_; FILE* file_; - base::debug::TraceResultBuffer trace_buffer_; + bool needs_comma_; + bool wrote_trace_; + bool has_pending_system_trace_; + bool wrote_system_trace_; + scoped_refptr<base::RefCountedString> pending_system_trace_; + DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdioWorker); }; -TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path) - : impl_(new TraceSubscriberStdioImpl(path)) { +TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path, + FileType file_type, + bool has_system_trace) + : worker_(new TraceSubscriberStdioWorker(path, + file_type, + has_system_trace)) { + if (has_system_trace) + CHECK_EQ(FILE_TYPE_PROPERTY_LIST, file_type); BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnStart, impl_)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceStart, worker_)); } TraceSubscriberStdio::~TraceSubscriberStdio() { @@ -92,14 +179,23 @@ TraceSubscriberStdio::~TraceSubscriberStdio() { void TraceSubscriberStdio::OnEndTracingComplete() { BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnEnd, impl_)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceEnd, worker_)); } void TraceSubscriberStdio::OnTraceDataCollected( const scoped_refptr<base::RefCountedString>& data_ptr) { BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnData, impl_, data_ptr)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceData, worker_, data_ptr)); +} + +void TraceSubscriberStdio::OnEndSystemTracing( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + BrowserThread::PostBlockingPoolSequencedTask( + __FILE__, FROM_HERE, + base::Bind(&TraceSubscriberStdioWorker::OnSystemTraceData, + worker_, + events_str_ptr)); } } // namespace content diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.h b/chromium/content/browser/tracing/trace_subscriber_stdio.h index 06a70db8c88..b9fc4f74c19 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.h +++ b/chromium/content/browser/tracing/trace_subscriber_stdio.h @@ -17,14 +17,24 @@ class FilePath; namespace content { -class TraceSubscriberStdioImpl; - // Stdio implementation of TraceSubscriber. Use this to write traces to a file. class CONTENT_EXPORT TraceSubscriberStdio : NON_EXPORTED_BASE(public TraceSubscriber) { public: - // Creates or overwrites the specified file. Check IsValid() for success. - explicit TraceSubscriberStdio(const base::FilePath& path); + enum FileType { + // Output file as array, representing trace events: + // [event1, event2, ...] + FILE_TYPE_ARRAY, + // Output file as property list with one or two items: + // {traceEvents: [event1, event2, ...], + // systemTraceEvents: "event1\nevent2\n..." // optional} + FILE_TYPE_PROPERTY_LIST + }; + + // has_system_trace indicates whether system trace events are expected. + TraceSubscriberStdio(const base::FilePath& path, + FileType file_type, + bool has_system_trace); virtual ~TraceSubscriberStdio(); // Implementation of TraceSubscriber @@ -32,8 +42,13 @@ class CONTENT_EXPORT TraceSubscriberStdio virtual void OnTraceDataCollected( const scoped_refptr<base::RefCountedString>& data_ptr) OVERRIDE; + // To be used as callback to DebugDaemonClient::RequestStopSystemTracing(). + virtual void OnEndSystemTracing( + const scoped_refptr<base::RefCountedString>& events_str_ptr); + private: - scoped_refptr<TraceSubscriberStdioImpl> impl_; + class TraceSubscriberStdioWorker; + scoped_refptr<TraceSubscriberStdioWorker> worker_; DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdio); }; diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc b/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc index 0b0e7c2ee80..10e51a968df 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc +++ b/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc @@ -14,12 +14,14 @@ namespace content { class TraceSubscriberStdioTest : public ::testing::Test {}; -TEST_F(TraceSubscriberStdioTest, CanWriteDataToFile) { +TEST_F(TraceSubscriberStdioTest, CanWriteArray) { base::ScopedTempDir trace_dir; ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); { - TraceSubscriberStdio subscriber(trace_file); + TraceSubscriberStdio subscriber(trace_file, + TraceSubscriberStdio::FILE_TYPE_ARRAY, + false); std::string foo("foo"); subscriber.OnTraceDataCollected( @@ -33,8 +35,98 @@ TEST_F(TraceSubscriberStdioTest, CanWriteDataToFile) { } BrowserThread::GetBlockingPool()->FlushForTesting(); std::string result; - EXPECT_TRUE(file_util::ReadFileToString(trace_file, &result)); + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); EXPECT_EQ("[foo,bar]", result); } +TEST_F(TraceSubscriberStdioTest, CanWritePropertyList) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + false); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + subscriber.OnEndTracingComplete(); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ("{\"traceEvents\":[foo,bar]}", result); +} + +TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataFirst) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + true); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + std::string systemTrace("event1\nev\"ent\"2\n"); + subscriber.OnEndSystemTracing( + make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); + subscriber.OnEndTracingComplete(); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ( + "{\"traceEvents\":[foo,bar],\"" + "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", + result); +} + +TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataLast) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + true); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + std::string systemTrace("event1\nev\"ent\"2\n"); + subscriber.OnEndTracingComplete(); + subscriber.OnEndSystemTracing( + make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ( + "{\"traceEvents\":[foo,bar],\"" + "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", + result); +} + } // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_browsertest.cc b/chromium/content/browser/tracing/tracing_controller_browsertest.cc new file mode 100644 index 00000000000..c88932e0597 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_browsertest.cc @@ -0,0 +1,115 @@ +// Copyright 2013 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 "base/file_util.h" +#include "base/run_loop.h" +#include "content/browser/tracing/tracing_controller_impl.h" +#include "content/public/test/browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/content_browser_test.h" +#include "content/test/content_browser_test_utils.h" + +namespace content { + +class TracingControllerTest : public ContentBrowserTest { + public: + TracingControllerTest() {} + + virtual void SetUp() OVERRIDE { + get_categories_done_callback_count_ = 0; + enable_recording_done_callback_count_ = 0; + disable_recording_done_callback_count_ = 0; + ContentBrowserTest::SetUp(); + } + + virtual void TearDown() OVERRIDE { + ContentBrowserTest::TearDown(); + } + + void Navigate(Shell* shell) { + NavigateToURL(shell, GetTestUrl("", "title.html")); + } + + void GetCategoriesDoneCallbackTest(base::Closure quit_callback, + const std::set<std::string>& categories) { + get_categories_done_callback_count_++; + EXPECT_TRUE(categories.size() > 0); + quit_callback.Run(); + } + + void EnableRecordingDoneCallbackTest(base::Closure quit_callback) { + enable_recording_done_callback_count_++; + quit_callback.Run(); + } + + void DisableRecordingDoneCallbackTest(base::Closure quit_callback, + scoped_ptr<base::FilePath> file_path) { + disable_recording_done_callback_count_++; + EXPECT_TRUE(PathExists(*file_path)); + quit_callback.Run(); + } + + int get_categories_done_callback_count() const { + return get_categories_done_callback_count_; + } + + int enable_recording_done_callback_count() const { + return enable_recording_done_callback_count_; + } + + int disable_recording_done_callback_count() const { + return disable_recording_done_callback_count_; + } + + private: + int get_categories_done_callback_count_; + int enable_recording_done_callback_count_; + int disable_recording_done_callback_count_; +}; + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + base::RunLoop run_loop; + TracingController::GetCategoriesDoneCallback callback = + base::Bind(&TracingControllerTest::GetCategoriesDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->GetCategories(callback); + run_loop.Run(); + EXPECT_EQ(get_categories_done_callback_count(), 1); +} + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, EnableAndDisableRecording) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + { + base::RunLoop run_loop; + TracingController::EnableRecordingDoneCallback callback = + base::Bind(&TracingControllerTest::EnableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->EnableRecording(base::debug::CategoryFilter("*"), + TracingController::Options(), callback); + run_loop.Run(); + EXPECT_EQ(enable_recording_done_callback_count(), 1); + } + + { + base::RunLoop run_loop; + TracingController::TracingFileResultCallback callback = + base::Bind(&TracingControllerTest::DisableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->DisableRecording(callback); + run_loop.Run(); + EXPECT_EQ(disable_recording_done_callback_count(), 1); + } +} + +} // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.cc b/chromium/content/browser/tracing/tracing_controller_impl.cc new file mode 100644 index 00000000000..1c1c43aabf9 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_impl.cc @@ -0,0 +1,270 @@ +// Copyright 2013 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/file_util.h" +#include "base/json/string_escape.h" +#include "base/strings/string_number_conversions.h" +#include "content/browser/tracing/trace_message_filter.h" +#include "content/common/child_process_messages.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/common/content_switches.h" + +using base::debug::TraceLog; + +namespace content { + +namespace { + +base::LazyInstance<TracingControllerImpl>::Leaky g_controller = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +TracingController* TracingController::GetInstance() { + return TracingControllerImpl::GetInstance(); +} + +TracingControllerImpl::TracingControllerImpl() : + pending_end_ack_count_(0), + is_recording_(false), + category_filter_( + base::debug::CategoryFilter::kDefaultCategoryFilterString) { +} + +TracingControllerImpl::~TracingControllerImpl() { + // This is a Leaky instance. + NOTREACHED(); +} + +TracingControllerImpl* TracingControllerImpl::GetInstance() { + return g_controller.Pointer(); +} + +void TracingControllerImpl::GetCategories( + const GetCategoriesDoneCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Known categories come back from child processes with the EndTracingAck + // message. So to get known categories, just begin and end tracing immediately + // afterwards. This will ping all the child processes for categories. + pending_get_categories_done_callback_ = callback; + EnableRecording(base::debug::CategoryFilter("*"), + TracingController::Options(), + EnableRecordingDoneCallback()); + DisableRecording(TracingFileResultCallback()); +} + +void TracingControllerImpl::EnableRecording( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableRecordingDoneCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_enable_recording()) + return; + + trace_options_ = TraceLog::GetInstance()->trace_options(); + TraceLog::GetInstance()->SetEnabled(filter, trace_options_); + + is_recording_ = true; + category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter(); + + // Notify all child processes. + for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { + it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_); + } + + if (!callback.is_null()) + callback.Run(); +} + +void TracingControllerImpl::DisableRecording( + const TracingFileResultCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_end_recording()) + return; + + pending_disable_recording_done_callback_ = callback; + + // Disable local trace early to avoid traces during end-tracing process from + // interfering with the process. + TraceLog::GetInstance()->SetDisabled(); + + // We don't need to create a temporary file when getting categories. + if (pending_get_categories_done_callback_.is_null()) { + base::FilePath temporary_file; + file_util::CreateTemporaryFile(&temporary_file); + recording_result_file_.reset(new base::FilePath(temporary_file)); + } + + // There could be a case where there are no child processes and filters_ + // is empty. In that case we can immediately tell the subscriber that tracing + // has ended. To avoid recursive calls back to the subscriber, we will just + // use the existing asynchronous OnDisableRecordingAcked code. + // Count myself (local trace) in pending_end_ack_count_, acked below. + pending_end_ack_count_ = filters_.size() + 1; + + // Handle special case of zero child processes. + if (pending_end_ack_count_ == 1) { + // Ack asynchronously now, because we don't have any children to wait for. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnDisableRecordingAcked, + base::Unretained(this), category_groups)); + } + + // Notify all child processes. + for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { + it->get()->SendEndTracing(); + } +} + +void TracingControllerImpl::EnableMonitoring( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableMonitoringDoneCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::DisableMonitoring( + const DisableMonitoringDoneCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::GetMonitoringStatus( + bool* out_enabled, + base::debug::CategoryFilter* out_filter, + TracingController::Options* out_options) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::CaptureCurrentMonitoringSnapshot( + const TracingFileResultCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::AddFilter(TraceMessageFilter* filter) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::AddFilter, base::Unretained(this), + make_scoped_refptr(filter))); + return; + } + + filters_.insert(filter); + if (is_recording_enabled()) { + std::string cf_str = category_filter_.ToString(); + filter->SendBeginTracing(cf_str, trace_options_); + } +} + +void TracingControllerImpl::RemoveFilter(TraceMessageFilter* filter) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::RemoveFilter, base::Unretained(this), + make_scoped_refptr(filter))); + return; + } + + filters_.erase(filter); +} + +void TracingControllerImpl::OnDisableRecordingAcked( + const std::vector<std::string>& known_category_groups) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnDisableRecordingAcked, + base::Unretained(this), known_category_groups)); + return; + } + + // Merge known_category_groups with known_category_groups_ + known_category_groups_.insert(known_category_groups.begin(), + known_category_groups.end()); + + if (pending_end_ack_count_ == 0) + return; + + if (--pending_end_ack_count_ == 1) { + // All acks from subprocesses have been received. Now flush the local trace. + // During or after this call, our OnLocalTraceDataCollected will be + // called with the last of the local trace data. + TraceLog::GetInstance()->Flush( + base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected, + base::Unretained(this))); + } + + if (pending_end_ack_count_ != 0) + return; + + // All acks (including from the subprocesses and the local trace) have been + // received. + is_recording_ = false; + + // Trigger callback if one is set. + if (!pending_get_categories_done_callback_.is_null()) { + pending_get_categories_done_callback_.Run(known_category_groups_); + pending_get_categories_done_callback_.Reset(); + } else { + OnEndTracingComplete(); + } +} + +void TracingControllerImpl::OnEndTracingComplete() { + if (pending_disable_recording_done_callback_.is_null()) + return; + + pending_disable_recording_done_callback_.Run(recording_result_file_.Pass()); + pending_disable_recording_done_callback_.Reset(); +} + +void TracingControllerImpl::OnTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + // OnTraceDataCollected may be called from any browser thread, either by the + // local event trace system or from child processes via TraceMessageFilter. + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnTraceDataCollected, + base::Unretained(this), events_str_ptr)); + return; + } + + // Drop trace events if we are just getting categories. + if (!pending_get_categories_done_callback_.is_null()) + return; + + std::string javascript; + javascript.reserve(events_str_ptr->size() * 2); + base::JsonDoubleQuote(events_str_ptr->data(), false, &javascript); + + // Intentionally append a , to the traceData. This technically causes all + // traceData that we pass back to JS to end with a comma, but that is + // actually something the JS side strips away anyway + javascript.append(","); + + file_util::WriteFile(*recording_result_file_, + javascript.c_str(), javascript.length()); +} + +void TracingControllerImpl::OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events) { + if (events_str_ptr->data().size()) + OnTraceDataCollected(events_str_ptr); + + if (has_more_events) + return; + + // Simulate an DisableRecordingAcked for the local trace. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + OnDisableRecordingAcked(category_groups); +} + +} // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.h b/chromium/content/browser/tracing/tracing_controller_impl.h new file mode 100644 index 00000000000..22192255444 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_impl.h @@ -0,0 +1,100 @@ +// Copyright 2013 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 CONTENT_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ +#define CONTENT_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/lazy_instance.h" +#include "content/public/browser/trace_subscriber.h" +#include "content/public/browser/tracing_controller.h" + +namespace content { + +class TraceMessageFilter; + +class TracingControllerImpl : + public TracingController, public TraceSubscriber { + public: + static TracingControllerImpl* GetInstance(); + + // TracingController implementation. + virtual void GetCategories( + const GetCategoriesDoneCallback& callback) OVERRIDE; + virtual void EnableRecording( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableRecordingDoneCallback& callback) OVERRIDE; + virtual void DisableRecording( + const TracingFileResultCallback& callback) OVERRIDE; + virtual void EnableMonitoring(const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableMonitoringDoneCallback& callback) OVERRIDE; + virtual void DisableMonitoring( + const DisableMonitoringDoneCallback& callback) OVERRIDE; + virtual void GetMonitoringStatus( + bool* out_enabled, + base::debug::CategoryFilter* out_filter, + TracingController::Options* out_options) OVERRIDE; + virtual void CaptureCurrentMonitoringSnapshot( + const TracingFileResultCallback& callback) OVERRIDE; + + private: + typedef std::set<scoped_refptr<TraceMessageFilter> > FilterMap; + + friend struct base::DefaultLazyInstanceTraits<TracingControllerImpl>; + friend class TraceMessageFilter; + + TracingControllerImpl(); + virtual ~TracingControllerImpl(); + + // TraceSubscriber implementation. + virtual void OnEndTracingComplete() OVERRIDE; + virtual void OnTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr) OVERRIDE; + + bool can_enable_recording() const { + return !is_recording_; + } + + bool can_end_recording() const { + return is_recording_ && pending_end_ack_count_ == 0; + } + + bool is_recording_enabled() const { + return can_end_recording(); + } + + // Methods for use by TraceMessageFilter. + void AddFilter(TraceMessageFilter* filter); + void RemoveFilter(TraceMessageFilter* filter); + + // Callback of TraceLog::Flush() for the local trace. + void OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events); + + void OnDisableRecordingAcked( + const std::vector<std::string>& known_category_groups); + + FilterMap filters_; + // Pending acks for DisableRecording. + int pending_end_ack_count_; + bool is_recording_; + GetCategoriesDoneCallback pending_get_categories_done_callback_; + TracingFileResultCallback pending_disable_recording_done_callback_; + std::set<std::string> known_category_groups_; + base::debug::TraceLog::Options trace_options_; + base::debug::CategoryFilter category_filter_; + scoped_ptr<base::FilePath> recording_result_file_; + + DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ diff --git a/chromium/content/browser/tracing/tracing_ui.cc b/chromium/content/browser/tracing/tracing_ui.cc index aab652d5bf1..15bd2cf2590 100644 --- a/chromium/content/browser/tracing/tracing_ui.cc +++ b/chromium/content/browser/tracing/tracing_ui.cc @@ -13,6 +13,7 @@ #include "base/file_util.h" #include "base/json/string_escape.h" #include "base/memory/scoped_ptr.h" +#include "base/safe_numerics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -225,7 +226,7 @@ void TracingMessageHandler::OnBeginRequestBufferPercentFull( // TaskProxy callback when reading is complete. void ReadTraceFileCallback(TaskProxy* proxy, const base::FilePath& path) { std::string file_contents; - if (!file_util::ReadFileToString(path, &file_contents)) + if (!base::ReadFileToString(path, &file_contents)) return; // We need to escape the file contents, because it will go into a javascript @@ -269,7 +270,8 @@ void ReadTraceFileCallback(TaskProxy* proxy, const base::FilePath& path) { void WriteTraceFileCallback(TaskProxy* proxy, const base::FilePath& path, std::string* contents) { - if (!file_util::WriteFile(path, contents->c_str(), contents->size())) + int size = base::checked_numeric_cast<int>(contents->size()); + if (file_util::WriteFile(path, contents->c_str(), size) != size) return; BrowserThread::PostTask( @@ -363,7 +365,7 @@ void TracingMessageHandler::OnSaveTraceFile(const base::ListValue* list) { if (select_trace_file_dialog_.get()) return; - DCHECK(list->GetSize() == 1); + DCHECK_EQ(1U, list->GetSize()); std::string* trace_data = new std::string(); bool ok = list->GetString(0, trace_data); |