diff options
author | haraken@chromium.org <haraken@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-11 14:28:02 +0000 |
---|---|---|
committer | haraken@chromium.org <haraken@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-11 14:28:02 +0000 |
commit | a8ba174a916c832509e473cd4aee2c675bd9f577 (patch) | |
tree | 9451f38c83ec87d267e6758cd7a5d888721a898c /content/browser/tracing | |
parent | bfbab88d09783757953440e017a29413d07e247e (diff) | |
download | chromium_src-a8ba174a916c832509e473cd4aee2c675bd9f577.zip chromium_src-a8ba174a916c832509e473cd4aee2c675bd9f577.tar.gz chromium_src-a8ba174a916c832509e473cd4aee2c675bd9f577.tar.bz2 |
We're refactoring the architecture of the tracing controller, as documented here:
https://docs.google.com/a/chromium.org/document/d/1qzprwk_jw6kiZTiOKMjuaoX4eIrivfqMZLYBxQglqkc/edit?disco=AAAAAGg-QgI#
tracing_controller.h will list up the C++ APIs for tracing,and This CL declares the APIs.
The API contents are implemented in tracing_controller_impl.{h,cpp}
BUG=285471
Review URL: https://chromiumcodereview.appspot.com/23752004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222545 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/tracing')
-rw-r--r-- | content/browser/tracing/trace_message_filter.h | 2 | ||||
-rw-r--r-- | content/browser/tracing/tracing_controller_browsertest.cc | 115 | ||||
-rw-r--r-- | content/browser/tracing/tracing_controller_impl.cc | 270 | ||||
-rw-r--r-- | content/browser/tracing/tracing_controller_impl.h | 100 |
4 files changed, 486 insertions, 1 deletions
diff --git a/content/browser/tracing/trace_message_filter.h b/content/browser/tracing/trace_message_filter.h index 8da3453..41fd017 100644 --- a/content/browser/tracing/trace_message_filter.h +++ b/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/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc new file mode 100644 index 0000000..c88932e --- /dev/null +++ b/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/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc new file mode 100644 index 0000000..1c1c43a --- /dev/null +++ b/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/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h new file mode 100644 index 0000000..2219225 --- /dev/null +++ b/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_ |