diff options
-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 | ||||
-rw-r--r-- | content/content_browser.gypi | 3 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/public/browser/trace_controller.h | 3 | ||||
-rw-r--r-- | content/public/browser/tracing_controller.h | 131 |
8 files changed, 624 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_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 8b17172..23e5c0e 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -174,6 +174,7 @@ 'public/browser/stream_handle.h', 'public/browser/trace_controller.h', 'public/browser/trace_subscriber.h', + 'public/browser/tracing_controller.h', 'public/browser/user_metrics.h', 'public/browser/utility_process_host.h', 'public/browser/web_contents.cc', @@ -1104,6 +1105,8 @@ 'browser/tracing/trace_message_filter.h', 'browser/tracing/trace_subscriber_stdio.cc', 'browser/tracing/trace_subscriber_stdio.h', + 'browser/tracing/tracing_controller_impl.cc', + 'browser/tracing/tracing_controller_impl.h', 'browser/tracing/tracing_ui.cc', 'browser/tracing/tracing_ui.h', 'browser/udev_linux.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 3bb59bb..5a5b66a 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -842,6 +842,7 @@ 'browser/session_history_browsertest.cc', 'browser/site_per_process_browsertest.cc', 'browser/speech/speech_recognition_browsertest.cc', + 'browser/tracing/tracing_controller_browsertest.cc', 'browser/web_contents/touch_editable_impl_aura_browsertest.cc', 'browser/web_contents/web_contents_impl_browsertest.cc', 'browser/web_contents/web_contents_view_aura_browsertest.cc', diff --git a/content/public/browser/trace_controller.h b/content/public/browser/trace_controller.h index 0049555..ebcff7f 100644 --- a/content/public/browser/trace_controller.h +++ b/content/public/browser/trace_controller.h @@ -12,6 +12,9 @@ namespace content { class TraceSubscriber; +// Note: TraceController is going to be deprecated and replaced with +// TracingController. +// // TraceController is used on the browser processes to enable/disable // trace status and collect trace data. Only the browser UI thread is allowed // to interact with the TraceController object. All calls on the TraceSubscriber diff --git a/content/public/browser/tracing_controller.h b/content/public/browser/tracing_controller.h new file mode 100644 index 0000000..18d4355 --- /dev/null +++ b/content/public/browser/tracing_controller.h @@ -0,0 +1,131 @@ +// 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_PUBLIC_BROWSER_TRACING_CONTROLLER_H_ +#define CONTENT_PUBLIC_BROWSER_TRACING_CONTROLLER_H_ + +#include "base/debug/trace_event.h" +#include "content/common/content_export.h" + +namespace base { +class FilePath; +}; + +namespace content { + +class TracingController; + +// TracingController is used on the browser processes to enable/disable +// trace status and collect trace data. Only the browser UI thread is allowed +// to interact with the TracingController object. All callbacks are called on +// the UI thread. +class TracingController { + public: + enum Options { + ENABLE_SYSTRACE = 1 << 0, + ENABLE_SAMPLING = 1 << 1, + }; + + CONTENT_EXPORT static TracingController* GetInstance(); + + // Get a set of category groups. The category groups can change as + // new code paths are reached. + // + // Once all child processes have acked to the GetCategories request, + // GetCategoriesDoneCallback is called back with a set of category + // groups. + typedef base::Callback<void(const std::set<std::string>&)> + GetCategoriesDoneCallback; + virtual void GetCategories( + const GetCategoriesDoneCallback& callback) = 0; + + // Start recording on all processes. + // + // Recording begins immediately locally, and asynchronously on child processes + // as soon as they receive the EnableRecording request. + // + // Once all child processes have acked to the EnableRecording request, + // EnableRecordingDoneCallback will be called back. + // + // |filter| is a filter to control what category groups should be traced. + // A filter can have an optional '-' prefix to exclude category groups + // that contain a matching category. Having both included and excluded + // category patterns in the same list would not be supported. + // + // Examples: "test_MyTest*", + // "test_MyTest*,test_OtherStuff", + // "-excluded_category1,-excluded_category2" + // + // |options| controls what kind of tracing is enabled. + typedef base::Callback<void()> EnableRecordingDoneCallback; + virtual void EnableRecording( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableRecordingDoneCallback& callback) = 0; + + // Stop recording on all processes. + // + // Child processes typically are caching trace data and only rarely flush + // and send trace data back to the browser process. That is because it may be + // an expensive operation to send the trace data over IPC, and we would like + // to avoid much runtime overhead of tracing. So, to end tracing, we must + // asynchronously ask all child processes to flush any pending trace data. + // + // Once all child processes have acked to the DisableRecording request, + // TracingFileResultCallback will be called back with a file that contains + // the traced data. + typedef base::Callback<void(scoped_ptr<base::FilePath>)> + TracingFileResultCallback; + virtual void DisableRecording(const TracingFileResultCallback& callback) = 0; + + // Start monitoring on all processes. + // + // Monitoring begins immediately locally, and asynchronously on child + // processes as soon as they receive the EnableMonitoring request. + // + // Once all child processes have acked to the EnableMonitoring request, + // EnableMonitoringDoneCallback will be called back. + // + // |filter| is a filter to control what category groups should be traced. + // + // |options| controls what kind of tracing is enabled. + typedef base::Callback<void()> EnableMonitoringDoneCallback; + virtual void EnableMonitoring(const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableMonitoringDoneCallback& callback) = 0; + + // Stop monitoring on all processes. + // + // Once all child processes have acked to the DisableMonitoring request, + // DisableMonitoringDoneCallback is called back. + typedef base::Callback<void()> DisableMonitoringDoneCallback; + virtual void DisableMonitoring( + const DisableMonitoringDoneCallback& callback) = 0; + + // Get the current monitoring configuration. + virtual void GetMonitoringStatus(bool* out_enabled, + base::debug::CategoryFilter* out_filter, + TracingController::Options* out_options) = 0; + + // Get the current monitoring traced data. + // + // Child processes typically are caching trace data and only rarely flush + // and send trace data back to the browser process. That is because it may be + // an expensive operation to send the trace data over IPC, and we would like + // to avoid much runtime overhead of tracing. So, to end tracing, we must + // asynchronously ask all child processes to flush any pending trace data. + // + // Once all child processes have acked to the CaptureCurrentMonitoringSnapshot + // request, TracingFileResultCallback will be called back with a file that + // contains the traced data. + virtual void CaptureCurrentMonitoringSnapshot( + const TracingFileResultCallback& callback) = 0; + + protected: + virtual ~TracingController() {} +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_TRACING_CONTROLLER_H_ |