summaryrefslogtreecommitdiffstats
path: root/content/browser/tracing/trace_controller_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/tracing/trace_controller_impl.cc')
-rw-r--r--content/browser/tracing/trace_controller_impl.cc371
1 files changed, 371 insertions, 0 deletions
diff --git a/content/browser/tracing/trace_controller_impl.cc b/content/browser/tracing/trace_controller_impl.cc
new file mode 100644
index 0000000..8b84338
--- /dev/null
+++ b/content/browser/tracing/trace_controller_impl.cc
@@ -0,0 +1,371 @@
+// Copyright (c) 2012 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/trace_controller_impl.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/tracing/tracing_messages.h"
+#include "content/browser/tracing/trace_message_filter.h"
+#include "content/browser/tracing/trace_subscriber_stdio.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<TraceControllerImpl>::Leaky g_controller =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+TraceController* TraceController::GetInstance() {
+ return TraceControllerImpl::GetInstance();
+}
+
+TraceControllerImpl::TraceControllerImpl() :
+ subscriber_(NULL),
+ pending_end_ack_count_(0),
+ pending_bpf_ack_count_(0),
+ maximum_bpf_(0.0f),
+ is_tracing_(false),
+ is_get_category_groups_(false),
+ category_filter_(
+ base::debug::CategoryFilter::kDefaultCategoryFilterString) {
+ TraceLog::GetInstance()->SetNotificationCallback(
+ base::Bind(&TraceControllerImpl::OnTraceNotification,
+ base::Unretained(this)));
+}
+
+TraceControllerImpl::~TraceControllerImpl() {
+ // No need to SetNotificationCallback(nil) on the TraceLog since this is a
+ // Leaky instance.
+ NOTREACHED();
+}
+
+TraceControllerImpl* TraceControllerImpl::GetInstance() {
+ return g_controller.Pointer();
+}
+
+bool TraceControllerImpl::GetKnownCategoryGroupsAsync(
+ TraceSubscriber* subscriber) {
+ 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.
+ is_get_category_groups_ = true;
+ bool success = BeginTracing(subscriber, "*",
+ TraceLog::GetInstance()->trace_options()) &&
+ EndTracingAsync(subscriber);
+ is_get_category_groups_ = success;
+ return success;
+}
+
+bool TraceControllerImpl::BeginTracing(TraceSubscriber* subscriber,
+ const std::string& category_patterns,
+ base::debug::TraceLog::Options options) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!can_begin_tracing(subscriber))
+ return false;
+
+#if defined(OS_ANDROID)
+ if (!is_get_category_groups_)
+ TraceLog::GetInstance()->AddClockSyncMetadataEvent();
+#endif
+
+ // Enable tracing
+ TraceLog::GetInstance()->SetEnabled(
+ base::debug::CategoryFilter(category_patterns), options);
+
+ OnTracingBegan(subscriber);
+
+ return true;
+}
+
+bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ 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();
+
+#if defined(OS_ANDROID)
+ if (!is_get_category_groups_)
+ TraceLog::GetInstance()->AddClockSyncMetadataEvent();
+#endif
+
+ // 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 OnEndTracingAck 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(&TraceControllerImpl::OnEndTracingAck,
+ base::Unretained(this), category_groups));
+ }
+
+ // Notify all child processes.
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
+ it->get()->SendEndTracing();
+ }
+
+ return true;
+}
+
+bool TraceControllerImpl::GetTraceBufferPercentFullAsync(
+ TraceSubscriber* subscriber) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!can_get_buffer_percent_full() || subscriber != subscriber_)
+ return false;
+
+ maximum_bpf_ = 0.0f;
+ pending_bpf_ack_count_ = filters_.size() + 1;
+
+ // Handle special case of zero child processes.
+ if (pending_bpf_ack_count_ == 1) {
+ // Ack asynchronously now, because we don't have any children to wait for.
+ float bpf = TraceLog::GetInstance()->GetBufferPercentFull();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
+ base::Unretained(this), bpf));
+ }
+
+ // Message all child processes.
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
+ it->get()->SendGetTraceBufferPercentFull();
+ }
+
+ return true;
+}
+
+bool TraceControllerImpl::SetWatchEvent(TraceSubscriber* subscriber,
+ const std::string& category_name,
+ const std::string& event_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (subscriber != subscriber_)
+ return false;
+
+ watch_category_ = category_name;
+ watch_name_ = event_name;
+
+ TraceLog::GetInstance()->SetWatchEvent(category_name, event_name);
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it)
+ it->get()->SendSetWatchEvent(category_name, event_name);
+
+ return true;
+}
+
+bool TraceControllerImpl::CancelWatchEvent(TraceSubscriber* subscriber) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (subscriber != subscriber_)
+ return false;
+
+ watch_category_.clear();
+ watch_name_.clear();
+
+ TraceLog::GetInstance()->CancelWatchEvent();
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it)
+ it->get()->SendCancelWatchEvent();
+
+ return true;
+}
+
+void TraceControllerImpl::CancelSubscriber(TraceSubscriber* subscriber) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (subscriber == subscriber_) {
+ subscriber_ = NULL;
+ // End tracing if necessary.
+ if (is_tracing_ && pending_end_ack_count_ == 0)
+ EndTracingAsync(NULL);
+ }
+}
+
+void TraceControllerImpl::AddFilter(TraceMessageFilter* filter) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::AddFilter, base::Unretained(this),
+ make_scoped_refptr(filter)));
+ return;
+ }
+
+ filters_.insert(filter);
+ if (is_tracing_enabled()) {
+ std::string cf_str = category_filter_.ToString();
+ filter->SendBeginTracing(cf_str, trace_options_);
+ if (!watch_category_.empty())
+ filter->SendSetWatchEvent(watch_category_, watch_name_);
+ }
+}
+
+void TraceControllerImpl::RemoveFilter(TraceMessageFilter* filter) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::RemoveFilter, base::Unretained(this),
+ make_scoped_refptr(filter)));
+ return;
+ }
+
+ filters_.erase(filter);
+}
+
+void TraceControllerImpl::OnTracingBegan(TraceSubscriber* subscriber) {
+ is_tracing_ = true;
+
+ subscriber_ = subscriber;
+
+ category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter();
+ trace_options_ = TraceLog::GetInstance()->trace_options();
+
+ // Notify all child processes.
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
+ it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_);
+ }
+}
+
+void TraceControllerImpl::OnEndTracingAck(
+ const std::vector<std::string>& known_category_groups) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnEndTracingAck,
+ 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(&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_) {
+ if (is_get_category_groups_)
+ subscriber_->OnKnownCategoriesCollected(known_category_groups_);
+ else
+ subscriber_->OnEndTracingComplete();
+ // Clear subscriber so that others can use TraceController.
+ subscriber_ = NULL;
+ }
+
+ is_get_category_groups_ = false;
+ }
+}
+
+void TraceControllerImpl::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(&TraceControllerImpl::OnTraceDataCollected,
+ base::Unretained(this), events_str_ptr));
+ return;
+ }
+
+ // Drop trace events if we are just getting categories.
+ if (subscriber_ && !is_get_category_groups_)
+ 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.
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnTraceNotification,
+ base::Unretained(this), notification));
+ return;
+ }
+
+ if (notification & base::debug::TraceLog::TRACE_BUFFER_FULL) {
+ // EndTracingAsync may return false if tracing is already in the process
+ // of being ended. That is ok.
+ EndTracingAsync(subscriber_);
+ }
+ if (notification & base::debug::TraceLog::EVENT_WATCH_NOTIFICATION) {
+ if (subscriber_)
+ subscriber_->OnEventWatchNotification();
+ }
+}
+
+void TraceControllerImpl::OnTraceBufferPercentFullReply(float percent_full) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
+ base::Unretained(this), percent_full));
+ return;
+ }
+
+ if (pending_bpf_ack_count_ == 0)
+ return;
+
+ maximum_bpf_ = (maximum_bpf_ > percent_full)? maximum_bpf_ : percent_full;
+
+ if (--pending_bpf_ack_count_ == 0) {
+ // Trigger callback if one is set.
+ if (subscriber_)
+ subscriber_->OnTraceBufferPercentFullReply(maximum_bpf_);
+ }
+
+ if (pending_bpf_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.
+ float bpf = TraceLog::GetInstance()->GetBufferPercentFull();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
+ base::Unretained(this), bpf));
+ }
+}
+
+} // namespace content