summaryrefslogtreecommitdiffstats
path: root/content/browser/tracing
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-03 04:06:40 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-03 04:06:40 +0000
commitbcb6ee23dd5d6fe711f702a3d04e8412dbf60804 (patch)
tree3ac91d567a054186ffca8af8dca4e17a3a9f61f4 /content/browser/tracing
parent1afd68d45b0b59f346b51fa84c53689675e13f5c (diff)
downloadchromium_src-bcb6ee23dd5d6fe711f702a3d04e8412dbf60804.zip
chromium_src-bcb6ee23dd5d6fe711f702a3d04e8412dbf60804.tar.gz
chromium_src-bcb6ee23dd5d6fe711f702a3d04e8412dbf60804.tar.bz2
undelete content/browser/tracing
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180287 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/tracing')
-rw-r--r--content/browser/tracing/OWNERS1
-rw-r--r--content/browser/tracing/trace_controller_impl.cc414
-rw-r--r--content/browser/tracing/trace_controller_impl.h115
-rw-r--r--content/browser/tracing/trace_message_filter.cc128
-rw-r--r--content/browser/tracing/trace_message_filter.h62
-rw-r--r--content/browser/tracing/trace_subscriber_stdio.cc104
-rw-r--r--content/browser/tracing/trace_subscriber_stdio.h39
-rw-r--r--content/browser/tracing/trace_subscriber_stdio_unittest.cc39
-rw-r--r--content/browser/tracing/tracing_ui.cc492
-rw-r--r--content/browser/tracing/tracing_ui.h23
10 files changed, 1417 insertions, 0 deletions
diff --git a/content/browser/tracing/OWNERS b/content/browser/tracing/OWNERS
new file mode 100644
index 0000000..93d1471
--- /dev/null
+++ b/content/browser/tracing/OWNERS
@@ -0,0 +1 @@
+nduca@chromium.org
diff --git a/content/browser/tracing/trace_controller_impl.cc b/content/browser/tracing/trace_controller_impl.cc
new file mode 100644
index 0000000..40a3f56
--- /dev/null
+++ b/content/browser/tracing/trace_controller_impl.cc
@@ -0,0 +1,414 @@
+// 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/string_number_conversions.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;
+
+class AutoStopTraceSubscriberStdio : public TraceSubscriberStdio {
+ public:
+ AutoStopTraceSubscriberStdio(const FilePath& file_path)
+ : TraceSubscriberStdio(file_path) {}
+
+ static void EndStartupTrace(TraceSubscriberStdio* subscriber) {
+ if (!TraceControllerImpl::GetInstance()->EndTracingAsync(subscriber))
+ delete subscriber;
+ // else, the tracing will end asynchronously in OnEndTracingComplete().
+ }
+
+ virtual void OnEndTracingComplete() {
+ TraceSubscriberStdio::OnEndTracingComplete();
+ delete this;
+ // TODO(joth): this would be the time to automatically open up
+ // chrome://tracing/ and load up the trace data collected.
+ }
+};
+
+} // 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_categories_(false) {
+ 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();
+}
+
+void TraceControllerImpl::InitStartupTracing(const CommandLine& command_line) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ FilePath trace_file = command_line.GetSwitchValuePath(
+ switches::kTraceStartupFile);
+ // trace_file = "none" means that startup events will show up for the next
+ // begin/end tracing (via about:tracing or AutomationProxy::BeginTracing/
+ // EndTracing, for example).
+ if (trace_file == FilePath().AppendASCII("none"))
+ return;
+
+ if (trace_file.empty()) {
+ // Default to saving the startup trace into the current dir.
+ trace_file = FilePath().AppendASCII("chrometrace.log");
+ }
+ scoped_ptr<AutoStopTraceSubscriberStdio> subscriber(
+ new AutoStopTraceSubscriberStdio(trace_file));
+ DCHECK(can_begin_tracing(subscriber.get()));
+
+ std::string delay_str = command_line.GetSwitchValueASCII(
+ switches::kTraceStartupDuration);
+ int delay_secs = 5;
+ if (!delay_str.empty() && !base::StringToInt(delay_str, &delay_secs)) {
+ DLOG(WARNING) << "Could not parse --" << switches::kTraceStartupDuration
+ << "=" << delay_str << " defaulting to 5 (secs)";
+ delay_secs = 5;
+ }
+
+ OnTracingBegan(subscriber.get());
+ BrowserThread::PostDelayedTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&AutoStopTraceSubscriberStdio::EndStartupTrace,
+ base::Unretained(subscriber.release())),
+ base::TimeDelta::FromSeconds(delay_secs));
+}
+
+bool TraceControllerImpl::GetKnownCategoriesAsync(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_categories_ = true;
+ bool success = BeginTracing(subscriber, "*") &&
+ EndTracingAsync(subscriber);
+ is_get_categories_ = success;
+ return success;
+}
+
+bool TraceControllerImpl::BeginTracing(
+ TraceSubscriber* subscriber,
+ const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!can_begin_tracing(subscriber))
+ return false;
+
+ // Enable tracing
+ TraceLog::GetInstance()->SetEnabled(included_categories, excluded_categories);
+ OnTracingBegan(subscriber);
+
+ return true;
+}
+
+bool TraceControllerImpl::BeginTracing(TraceSubscriber* subscriber,
+ const std::string& categories) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!can_begin_tracing(subscriber))
+ return false;
+
+ // Enable tracing
+ TraceLog::GetInstance()->SetEnabled(categories);
+
+ OnTracingBegan(subscriber);
+
+ return true;
+}
+
+bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!can_end_tracing() || subscriber != subscriber_)
+ return false;
+
+ // 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> categories;
+ TraceLog::GetInstance()->GetKnownCategories(&categories);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnEndTracingAck,
+ base::Unretained(this), categories));
+ }
+
+ // 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()) {
+ filter->SendBeginTracing(included_categories_, excluded_categories_);
+ 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;
+
+ TraceLog::GetInstance()->GetEnabledTraceCategories(&included_categories_,
+ &excluded_categories_);
+ // Notify all child processes.
+ for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
+ it->get()->SendBeginTracing(included_categories_, excluded_categories_);
+ }
+}
+
+void TraceControllerImpl::OnEndTracingAck(
+ const std::vector<std::string>& known_categories) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnEndTracingAck,
+ base::Unretained(this), known_categories));
+ return;
+ }
+
+ // Merge known_categories with known_categories_
+ known_categories_.insert(known_categories.begin(), known_categories.end());
+
+ 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.
+ TraceLog::GetInstance()->Flush(
+ base::Bind(&TraceControllerImpl::OnTraceDataCollected,
+ base::Unretained(this)));
+
+ // Trigger callback if one is set.
+ if (subscriber_) {
+ if (is_get_categories_)
+ subscriber_->OnKnownCategoriesCollected(known_categories_);
+ else
+ subscriber_->OnEndTracingComplete();
+ // Clear subscriber so that others can use TraceController.
+ subscriber_ = NULL;
+ }
+
+ is_get_categories_ = 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> categories;
+ TraceLog::GetInstance()->GetKnownCategories(&categories);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TraceControllerImpl::OnEndTracingAck,
+ base::Unretained(this), categories));
+ }
+}
+
+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_categories_)
+ subscriber_->OnTraceDataCollected(events_str_ptr);
+}
+
+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
diff --git a/content/browser/tracing/trace_controller_impl.h b/content/browser/tracing/trace_controller_impl.h
new file mode 100644
index 0000000..501f561
--- /dev/null
+++ b/content/browser/tracing/trace_controller_impl.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_TRACING_TRACE_CONTROLLER_IMPL_H_
+#define CONTENT_BROWSER_TRACING_TRACE_CONTROLLER_IMPL_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
+#include "content/public/browser/trace_controller.h"
+
+class CommandLine;
+
+namespace content {
+class TraceMessageFilter;
+
+class TraceControllerImpl : public TraceController {
+ public:
+ static TraceControllerImpl* GetInstance();
+
+ // Called on the main thread of the browser process to initialize
+ // startup tracing.
+ void InitStartupTracing(const CommandLine& command_line);
+
+ // Get set of known categories. This can change as new code paths are reached.
+ // If true is returned, subscriber->OnKnownCategoriesCollected will be called
+ // once the categories are retrieved from child processes.
+ bool GetKnownCategoriesAsync(TraceSubscriber* subscriber);
+
+ // Same as above, but specifies which categories to trace.
+ // If both included_categories and excluded_categories are empty,
+ // all categories are traced.
+ // Else if included_categories is non-empty, only those are traced.
+ // Else if excluded_categories is non-empty, everything but those are traced.
+ bool BeginTracing(TraceSubscriber* subscriber,
+ const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories);
+
+ // TraceController implementation:
+ virtual bool BeginTracing(TraceSubscriber* subscriber,
+ const std::string& categories) OVERRIDE;
+ virtual bool EndTracingAsync(TraceSubscriber* subscriber) OVERRIDE;
+ virtual bool GetTraceBufferPercentFullAsync(
+ TraceSubscriber* subscriber) OVERRIDE;
+ virtual bool SetWatchEvent(TraceSubscriber* subscriber,
+ const std::string& category_name,
+ const std::string& event_name) OVERRIDE;
+ virtual bool CancelWatchEvent(TraceSubscriber* subscriber) OVERRIDE;
+ virtual void CancelSubscriber(TraceSubscriber* subscriber) OVERRIDE;
+
+ private:
+ typedef std::set<scoped_refptr<TraceMessageFilter> > FilterMap;
+
+ friend struct base::DefaultLazyInstanceTraits<TraceControllerImpl>;
+ friend class TraceMessageFilter;
+
+ TraceControllerImpl();
+ virtual ~TraceControllerImpl();
+
+ bool is_tracing_enabled() const {
+ return can_end_tracing();
+ }
+
+ bool can_end_tracing() const {
+ return is_tracing_ && pending_end_ack_count_ == 0;
+ }
+
+ // Can get Buffer Percent Full
+ bool can_get_buffer_percent_full() const {
+ return is_tracing_ &&
+ pending_end_ack_count_ == 0 &&
+ pending_bpf_ack_count_ == 0;
+ }
+
+ bool can_begin_tracing(TraceSubscriber* subscriber) const {
+ return !is_tracing_ &&
+ (subscriber_ == NULL || subscriber == subscriber_);
+ }
+
+ // Methods for use by TraceMessageFilter.
+
+ void AddFilter(TraceMessageFilter* filter);
+ void RemoveFilter(TraceMessageFilter* filter);
+ void OnTracingBegan(TraceSubscriber* subscriber);
+ void OnEndTracingAck(const std::vector<std::string>& known_categories);
+ void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr);
+ void OnTraceNotification(int notification);
+ void OnTraceBufferPercentFullReply(float percent_full);
+
+ FilterMap filters_;
+ TraceSubscriber* subscriber_;
+ // Pending acks for EndTracingAsync:
+ int pending_end_ack_count_;
+ // Pending acks for GetTraceBufferPercentFullAsync:
+ int pending_bpf_ack_count_;
+ float maximum_bpf_;
+ bool is_tracing_;
+ bool is_get_categories_;
+ std::set<std::string> known_categories_;
+ std::vector<std::string> included_categories_;
+ std::vector<std::string> excluded_categories_;
+ std::string watch_category_;
+ std::string watch_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceControllerImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TRACING_TRACE_CONTROLLER_IMPL_H_
diff --git a/content/browser/tracing/trace_message_filter.cc b/content/browser/tracing/trace_message_filter.cc
new file mode 100644
index 0000000..de5b2fd
--- /dev/null
+++ b/content/browser/tracing/trace_message_filter.cc
@@ -0,0 +1,128 @@
+// 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_message_filter.h"
+
+#include "components/tracing/tracing_messages.h"
+#include "content/browser/tracing/trace_controller_impl.h"
+
+namespace content {
+
+TraceMessageFilter::TraceMessageFilter() :
+ has_child_(false),
+ is_awaiting_end_ack_(false),
+ is_awaiting_buffer_percent_full_ack_(false) {
+}
+
+void TraceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ // Always on IO thread (BrowserMessageFilter guarantee).
+ BrowserMessageFilter::OnFilterAdded(channel);
+}
+
+void TraceMessageFilter::OnChannelClosing() {
+ // Always on IO thread (BrowserMessageFilter guarantee).
+ BrowserMessageFilter::OnChannelClosing();
+
+ if (has_child_) {
+ if (is_awaiting_end_ack_)
+ OnEndTracingAck(std::vector<std::string>());
+
+ if (is_awaiting_buffer_percent_full_ack_)
+ OnTraceBufferPercentFullReply(0.0f);
+
+ TraceControllerImpl::GetInstance()->RemoveFilter(this);
+ }
+}
+
+bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ // Always on IO thread (BrowserMessageFilter guarantee).
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(TraceMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(TracingHostMsg_ChildSupportsTracing,
+ OnChildSupportsTracing)
+ IPC_MESSAGE_HANDLER(TracingHostMsg_EndTracingAck, OnEndTracingAck)
+ IPC_MESSAGE_HANDLER(TracingHostMsg_TraceDataCollected,
+ OnTraceDataCollected)
+ IPC_MESSAGE_HANDLER(TracingHostMsg_TraceNotification,
+ OnTraceNotification)
+ IPC_MESSAGE_HANDLER(TracingHostMsg_TraceBufferPercentFullReply,
+ OnTraceBufferPercentFullReply)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void TraceMessageFilter::SendBeginTracing(
+ const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Send(new TracingMsg_BeginTracing(included_categories,
+ excluded_categories,
+ base::TimeTicks::NowFromSystemTraceTime()));
+}
+
+void TraceMessageFilter::SendEndTracing() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_awaiting_end_ack_);
+ is_awaiting_end_ack_ = true;
+ Send(new TracingMsg_EndTracing);
+}
+
+void TraceMessageFilter::SendGetTraceBufferPercentFull() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_awaiting_buffer_percent_full_ack_);
+ is_awaiting_buffer_percent_full_ack_ = true;
+ Send(new TracingMsg_GetTraceBufferPercentFull);
+}
+
+void TraceMessageFilter::SendSetWatchEvent(const std::string& category_name,
+ const std::string& event_name) {
+ Send(new TracingMsg_SetWatchEvent(category_name, event_name));
+}
+
+void TraceMessageFilter::SendCancelWatchEvent() {
+ Send(new TracingMsg_CancelWatchEvent);
+}
+
+TraceMessageFilter::~TraceMessageFilter() {}
+
+void TraceMessageFilter::OnChildSupportsTracing() {
+ has_child_ = true;
+ TraceControllerImpl::GetInstance()->AddFilter(this);
+}
+
+void TraceMessageFilter::OnEndTracingAck(
+ const std::vector<std::string>& known_categories) {
+ // is_awaiting_end_ack_ should always be true here, but check in case the
+ // child process is compromised.
+ if (is_awaiting_end_ack_) {
+ is_awaiting_end_ack_ = false;
+ TraceControllerImpl::GetInstance()->OnEndTracingAck(known_categories);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void TraceMessageFilter::OnTraceDataCollected(const std::string& data) {
+ scoped_refptr<base::RefCountedString> data_ptr(new base::RefCountedString());
+ data_ptr->data() = data;
+ TraceControllerImpl::GetInstance()->OnTraceDataCollected(data_ptr);
+}
+
+void TraceMessageFilter::OnTraceNotification(int notification) {
+ TraceControllerImpl::GetInstance()->OnTraceNotification(notification);
+}
+
+void TraceMessageFilter::OnTraceBufferPercentFullReply(float percent_full) {
+ if (is_awaiting_buffer_percent_full_ack_) {
+ is_awaiting_buffer_percent_full_ack_ = false;
+ TraceControllerImpl::GetInstance()->OnTraceBufferPercentFullReply(
+ percent_full);
+ } else {
+ NOTREACHED();
+ }
+}
+
+} // namespace content
diff --git a/content/browser/tracing/trace_message_filter.h b/content/browser/tracing/trace_message_filter.h
new file mode 100644
index 0000000..ae6d571
--- /dev/null
+++ b/content/browser/tracing/trace_message_filter.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
+
+#include <string>
+#include <vector>
+
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+
+// This class sends and receives trace messages on the browser process.
+// See also: trace_controller.h
+// See also: child_trace_message_filter.h
+class TraceMessageFilter : public BrowserMessageFilter {
+ public:
+ TraceMessageFilter();
+
+ // BrowserMessageFilter override.
+ virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) OVERRIDE;
+
+ void SendBeginTracing(const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories);
+ void SendEndTracing();
+ void SendGetTraceBufferPercentFull();
+ void SendSetWatchEvent(const std::string& category_name,
+ const std::string& event_name);
+ void SendCancelWatchEvent();
+
+ protected:
+ virtual ~TraceMessageFilter();
+
+ private:
+ // Message handlers.
+ void OnChildSupportsTracing();
+ void OnEndTracingAck(const std::vector<std::string>& known_categories);
+ void OnTraceNotification(int notification);
+ void OnTraceBufferPercentFullReply(float percent_full);
+ void OnTraceDataCollected(const std::string& data);
+
+ // ChildTraceMessageFilter exists:
+ bool has_child_;
+
+ // Awaiting ack for previously sent SendEndTracing
+ bool is_awaiting_end_ack_;
+ // Awaiting ack for previously sent SendGetTraceBufferPercentFull
+ bool is_awaiting_buffer_percent_full_ack_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TRACING_TRACE_MESSAGE_FILTER_H_
diff --git a/content/browser/tracing/trace_subscriber_stdio.cc b/content/browser/tracing/trace_subscriber_stdio.cc
new file mode 100644
index 0000000..d366f1b
--- /dev/null
+++ b/content/browser/tracing/trace_subscriber_stdio.cc
@@ -0,0 +1,104 @@
+// 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_subscriber_stdio.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+// All method calls on this class are done on a SequencedWorkerPool thread.
+class TraceSubscriberStdioImpl
+ : public base::RefCountedThreadSafe<TraceSubscriberStdioImpl> {
+ public:
+ explicit TraceSubscriberStdioImpl(const FilePath& path)
+ : path_(path),
+ file_(0) {}
+
+ void OnStart() {
+ 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 {
+ LOG(ERROR) << "Failed to open performance trace file: " << path_.value();
+ }
+ }
+
+ void OnData(const scoped_refptr<base::RefCountedString>& data_ptr) {
+ trace_buffer_.AddFragment(data_ptr->data());
+ }
+
+ void OnEnd() {
+ trace_buffer_.Finish();
+ CloseFile();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TraceSubscriberStdioImpl>;
+
+ ~TraceSubscriberStdioImpl() {
+ CloseFile();
+ }
+
+ bool IsValid() const {
+ return file_ && (0 == ferror(file_));
+ }
+
+ void CloseFile() {
+ if (file_) {
+ fclose(file_);
+ file_ = 0;
+ }
+ // This is important, as it breaks a reference cycle.
+ trace_buffer_.SetOutputCallback(
+ base::debug::TraceResultBuffer::OutputCallback());
+ }
+
+ void Write(const std::string& output_str) {
+ if (IsValid()) {
+ size_t written = fwrite(output_str.data(), 1, output_str.size(), file_);
+ if (written != output_str.size()) {
+ LOG(ERROR) << "Error " << ferror(file_) << " in fwrite() to trace file";
+ CloseFile();
+ }
+ }
+ }
+
+ FilePath path_;
+ FILE* file_;
+ base::debug::TraceResultBuffer trace_buffer_;
+};
+
+TraceSubscriberStdio::TraceSubscriberStdio(const FilePath& path)
+ : impl_(new TraceSubscriberStdioImpl(path)) {
+ BrowserThread::PostBlockingPoolSequencedTask(
+ __FILE__, FROM_HERE,
+ base::Bind(&TraceSubscriberStdioImpl::OnStart, impl_));
+}
+
+TraceSubscriberStdio::~TraceSubscriberStdio() {
+}
+
+void TraceSubscriberStdio::OnEndTracingComplete() {
+ BrowserThread::PostBlockingPoolSequencedTask(
+ __FILE__, FROM_HERE,
+ base::Bind(&TraceSubscriberStdioImpl::OnEnd, impl_));
+}
+
+void TraceSubscriberStdio::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& data_ptr) {
+ BrowserThread::PostBlockingPoolSequencedTask(
+ __FILE__, FROM_HERE,
+ base::Bind(&TraceSubscriberStdioImpl::OnData, impl_, data_ptr));
+}
+
+} // namespace content
diff --git a/content/browser/tracing/trace_subscriber_stdio.h b/content/browser/tracing/trace_subscriber_stdio.h
new file mode 100644
index 0000000..7fa37e5
--- /dev/null
+++ b/content/browser/tracing/trace_subscriber_stdio.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_TRACING_TRACE_SUBSCRIBER_STDIO_H_
+#define CONTENT_BROWSER_TRACING_TRACE_SUBSCRIBER_STDIO_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "content/public/browser/trace_subscriber.h"
+#include "content/common/content_export.h"
+
+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 FilePath& path);
+ virtual ~TraceSubscriberStdio();
+
+ // Implementation of TraceSubscriber
+ virtual void OnEndTracingComplete() OVERRIDE;
+ virtual void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& data_ptr) OVERRIDE;
+
+ private:
+ scoped_refptr<TraceSubscriberStdioImpl> impl_;
+ DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdio);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TRACING_TRACE_SUBSCRIBER_STDIO_H_
diff --git a/content/browser/tracing/trace_subscriber_stdio_unittest.cc b/content/browser/tracing/trace_subscriber_stdio_unittest.cc
new file mode 100644
index 0000000..73ca474
--- /dev/null
+++ b/content/browser/tracing/trace_subscriber_stdio_unittest.cc
@@ -0,0 +1,39 @@
+// 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_subscriber_stdio.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "content/public/browser/browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class TraceSubscriberStdioTest : public ::testing::Test {};
+
+TEST_F(TraceSubscriberStdioTest, CanWriteDataToFile) {
+ base::ScopedTempDir trace_dir;
+ ASSERT_TRUE(trace_dir.CreateUniqueTempDir());
+ FilePath trace_file(trace_dir.path().AppendASCII("trace.txt"));
+ {
+ TraceSubscriberStdio subscriber(trace_file);
+
+ 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(file_util::ReadFileToString(trace_file, &result));
+ EXPECT_EQ("[foo,bar]", result);
+}
+
+} // namespace content
diff --git a/content/browser/tracing/tracing_ui.cc b/content/browser/tracing/tracing_ui.cc
new file mode 100644
index 0000000..ff1f1ec
--- /dev/null
+++ b/content/browser/tracing/tracing_ui.cc
@@ -0,0 +1,492 @@
+// 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/tracing_ui.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/trace_controller.h"
+#include "content/public/browser/trace_subscriber.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/url_constants.h"
+#include "grit/content_resources.h"
+#include "ipc/ipc_channel.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#endif
+
+namespace content {
+namespace {
+
+WebUIDataSource* CreateTracingHTMLSource() {
+ WebUIDataSource* source =
+ WebUIDataSource::Create(chrome::kChromeUITracingHost);
+
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_TRACING_HTML);
+ source->AddResourcePath("tracing.js", IDR_TRACING_JS);
+ source->AddLocalizedString("tracingTitle", IDS_TRACING_TITLE);
+ return source;
+}
+
+// This class receives javascript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's methods are expected to run on the UI thread.
+class TracingMessageHandler
+ : public WebUIMessageHandler,
+ public ui::SelectFileDialog::Listener,
+ public base::SupportsWeakPtr<TracingMessageHandler>,
+ public TraceSubscriber {
+ public:
+ TracingMessageHandler();
+ virtual ~TracingMessageHandler();
+
+ // WebUIMessageHandler implementation.
+ virtual void RegisterMessages();
+
+ // SelectFileDialog::Listener implementation
+ virtual void FileSelected(const FilePath& path, int index, void* params);
+ virtual void FileSelectionCanceled(void* params);
+
+ // TraceSubscriber implementation.
+ virtual void OnEndTracingComplete();
+ virtual void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment);
+ virtual void OnTraceBufferPercentFullReply(float percent_full);
+
+ // Messages.
+ void OnTracingControllerInitialized(const ListValue* list);
+ void OnBeginTracing(const ListValue* list);
+ void OnEndTracingAsync(const ListValue* list);
+ void OnBeginRequestBufferPercentFull(const ListValue* list);
+ void OnLoadTraceFile(const ListValue* list);
+ void OnSaveTraceFile(const ListValue* list);
+
+ // Callbacks.
+ void LoadTraceFileComplete(string16* file_contents);
+ void SaveTraceFileComplete();
+
+ private:
+ // The file dialog to select a file for loading or saving traces.
+ scoped_refptr<ui::SelectFileDialog> select_trace_file_dialog_;
+
+ // The type of the file dialog as the same one is used for loading or saving
+ // traces.
+ ui::SelectFileDialog::Type select_trace_file_dialog_type_;
+
+ // The trace data that is to be written to the file on saving.
+ scoped_ptr<std::string> trace_data_to_save_;
+
+ // True while tracing is active.
+ bool trace_enabled_;
+
+ // True while system tracing is active.
+ bool system_trace_in_progress_;
+
+ void OnEndSystemTracingAck(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr);
+
+ DISALLOW_COPY_AND_ASSIGN(TracingMessageHandler);
+};
+
+// A proxy passed to the Read and Write tasks used when loading or saving trace
+// data.
+class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
+ public:
+ explicit TaskProxy(const base::WeakPtr<TracingMessageHandler>& handler)
+ : handler_(handler) {}
+ void LoadTraceFileCompleteProxy(string16* file_contents) {
+ if (handler_)
+ handler_->LoadTraceFileComplete(file_contents);
+ delete file_contents;
+ }
+
+ void SaveTraceFileCompleteProxy() {
+ if (handler_)
+ handler_->SaveTraceFileComplete();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TaskProxy>;
+ ~TaskProxy() {}
+
+ // The message handler to call callbacks on.
+ base::WeakPtr<TracingMessageHandler> handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskProxy);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TracingMessageHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TracingMessageHandler::TracingMessageHandler()
+ : select_trace_file_dialog_type_(ui::SelectFileDialog::SELECT_NONE),
+ trace_enabled_(false),
+ system_trace_in_progress_(false) {
+}
+
+TracingMessageHandler::~TracingMessageHandler() {
+ if (select_trace_file_dialog_)
+ select_trace_file_dialog_->ListenerDestroyed();
+
+ // If we are the current subscriber, this will result in ending tracing.
+ TraceController::GetInstance()->CancelSubscriber(this);
+
+ // Shutdown any system tracing too.
+ if (system_trace_in_progress_) {
+#if defined(OS_CHROMEOS)
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ RequestStopSystemTracing(
+ chromeos::DebugDaemonClient::EmptyStopSystemTracingCallback());
+#endif
+ }
+}
+
+void TracingMessageHandler::RegisterMessages() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ web_ui()->RegisterMessageCallback("tracingControllerInitialized",
+ base::Bind(&TracingMessageHandler::OnTracingControllerInitialized,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("beginTracing",
+ base::Bind(&TracingMessageHandler::OnBeginTracing,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("endTracingAsync",
+ base::Bind(&TracingMessageHandler::OnEndTracingAsync,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("beginRequestBufferPercentFull",
+ base::Bind(&TracingMessageHandler::OnBeginRequestBufferPercentFull,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("loadTraceFile",
+ base::Bind(&TracingMessageHandler::OnLoadTraceFile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("saveTraceFile",
+ base::Bind(&TracingMessageHandler::OnSaveTraceFile,
+ base::Unretained(this)));
+}
+
+void TracingMessageHandler::OnTracingControllerInitialized(
+ const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Send the client info to the tracingController
+ {
+ scoped_ptr<DictionaryValue> dict(new DictionaryValue());
+ dict->SetString("version", GetContentClient()->GetProduct());
+
+ dict->SetString("command_line",
+ CommandLine::ForCurrentProcess()->GetCommandLineString());
+
+ web_ui()->CallJavascriptFunction("tracingController.onClientInfoUpdate",
+ *dict);
+ }
+}
+
+void TracingMessageHandler::OnBeginRequestBufferPercentFull(
+ const ListValue* list) {
+ TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this);
+}
+
+// A callback used for asynchronously reading a file to a string. Calls the
+// TaskProxy callback when reading is complete.
+void ReadTraceFileCallback(TaskProxy* proxy, const FilePath& path) {
+ std::string file_contents;
+ if (!file_util::ReadFileToString(path, &file_contents))
+ return;
+
+ // We need to escape the file contents, because it will go into a javascript
+ // quoted string in TracingMessageHandler::LoadTraceFileComplete. We need to
+ // escape control characters (to have well-formed javascript statements), as
+ // well as \ and ' (the only special characters in a ''-quoted string).
+ // Do the escaping on this thread, it may take a little while for big files
+ // and we don't want to block the UI during that time. Also do the UTF-16
+ // conversion here.
+ // Note: we're using UTF-16 because we'll need to cut the string into slices
+ // to give to Javascript, and it's easier to cut than UTF-8 (since JS strings
+ // are arrays of 16-bit values, UCS-2 really, whereas we can't cut inside of a
+ // multibyte UTF-8 codepoint).
+ size_t size = file_contents.size();
+ std::string escaped_contents;
+ escaped_contents.reserve(size);
+ for (size_t i = 0; i < size; ++i) {
+ char c = file_contents[i];
+ if (c < ' ') {
+ escaped_contents += base::StringPrintf("\\u%04x", c);
+ continue;
+ }
+ if (c == '\\' || c == '\'')
+ escaped_contents.push_back('\\');
+ escaped_contents.push_back(c);
+ }
+ file_contents.clear();
+
+ scoped_ptr<string16> contents16(new string16);
+ UTF8ToUTF16(escaped_contents).swap(*contents16);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&TaskProxy::LoadTraceFileCompleteProxy, proxy,
+ contents16.release()));
+}
+
+// A callback used for asynchronously writing a file from a string. Calls the
+// TaskProxy callback when writing is complete.
+void WriteTraceFileCallback(TaskProxy* proxy,
+ const FilePath& path,
+ std::string* contents) {
+ if (!file_util::WriteFile(path, contents->c_str(), contents->size()))
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&TaskProxy::SaveTraceFileCompleteProxy, proxy));
+}
+
+void TracingMessageHandler::FileSelected(
+ const FilePath& path, int index, void* params) {
+ if (select_trace_file_dialog_type_ ==
+ ui::SelectFileDialog::SELECT_OPEN_FILE) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&ReadTraceFileCallback,
+ make_scoped_refptr(new TaskProxy(AsWeakPtr())), path));
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&WriteTraceFileCallback,
+ make_scoped_refptr(new TaskProxy(AsWeakPtr())), path,
+ trace_data_to_save_.release()));
+ }
+
+ select_trace_file_dialog_ = NULL;
+}
+
+void TracingMessageHandler::FileSelectionCanceled(void* params) {
+ select_trace_file_dialog_ = NULL;
+ if (select_trace_file_dialog_type_ ==
+ ui::SelectFileDialog::SELECT_OPEN_FILE) {
+ web_ui()->CallJavascriptFunction(
+ "tracingController.onLoadTraceFileCanceled");
+ } else {
+ web_ui()->CallJavascriptFunction(
+ "tracingController.onSaveTraceFileCanceled");
+ }
+}
+
+void TracingMessageHandler::OnLoadTraceFile(const ListValue* list) {
+ // Only allow a single dialog at a time.
+ if (select_trace_file_dialog_.get())
+ return;
+ select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
+ select_trace_file_dialog_ = ui::SelectFileDialog::Create(
+ this,
+ GetContentClient()->browser()->CreateSelectFilePolicy(
+ web_ui()->GetWebContents()));
+ select_trace_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ string16(),
+ FilePath(),
+ NULL, 0, FILE_PATH_LITERAL(""),
+ web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
+}
+
+void TracingMessageHandler::LoadTraceFileComplete(string16* contents) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // We need to pass contents to tracingController.onLoadTraceFileComplete, but
+ // that may be arbitrarily big, and IPCs messages are limited in size. So we
+ // need to cut it into pieces and rebuild the string in Javascript.
+ // |contents| has already been escaped in ReadTraceFileCallback.
+ // IPC::Channel::kMaximumMessageSize is in bytes, and we need to account for
+ // overhead.
+ const size_t kMaxSize = IPC::Channel::kMaximumMessageSize / 2 - 128;
+ string16 first_prefix = UTF8ToUTF16("window.traceData = '");
+ string16 prefix = UTF8ToUTF16("window.traceData += '");
+ string16 suffix = UTF8ToUTF16("';");
+
+ RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
+ for (size_t i = 0; i < contents->size(); i += kMaxSize) {
+ string16 javascript = i == 0 ? first_prefix : prefix;
+ javascript += contents->substr(i, kMaxSize) + suffix;
+ rvh->ExecuteJavascriptInWebFrame(string16(), javascript);
+ }
+ rvh->ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(
+ "tracingController.onLoadTraceFileComplete(JSON.parse(window.traceData));"
+ "delete window.traceData;"));
+}
+
+void TracingMessageHandler::OnSaveTraceFile(const ListValue* list) {
+ // Only allow a single dialog at a time.
+ if (select_trace_file_dialog_.get())
+ return;
+
+ DCHECK(list->GetSize() == 1);
+
+ std::string* trace_data = new std::string();
+ bool ok = list->GetString(0, trace_data);
+ DCHECK(ok);
+ trace_data_to_save_.reset(trace_data);
+
+ select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
+ select_trace_file_dialog_ = ui::SelectFileDialog::Create(
+ this,
+ GetContentClient()->browser()->CreateSelectFilePolicy(
+ web_ui()->GetWebContents()));
+ select_trace_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE,
+ string16(),
+ FilePath(),
+ NULL, 0, FILE_PATH_LITERAL(""),
+ web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
+}
+
+void TracingMessageHandler::SaveTraceFileComplete() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui()->CallJavascriptFunction("tracingController.onSaveTraceFileComplete");
+}
+
+void TracingMessageHandler::OnBeginTracing(const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(args->GetSize(), (size_t) 2);
+
+ bool system_tracing_requested = false;
+ bool ok = args->GetBoolean(0, &system_tracing_requested);
+ DCHECK(ok);
+
+ std::string chrome_categories;
+ ok = args->GetString(1, &chrome_categories);
+ DCHECK(ok);
+
+ trace_enabled_ = true;
+ // TODO(jbates) This may fail, but that's OK for current use cases.
+ // Ex: Multiple about:gpu traces can not trace simultaneously.
+ // TODO(nduca) send feedback to javascript about whether or not BeginTracing
+ // was successful.
+ TraceController::GetInstance()->BeginTracing(this, chrome_categories);
+
+ if (system_tracing_requested) {
+#if defined(OS_CHROMEOS)
+ DCHECK(!system_trace_in_progress_);
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ StartSystemTracing();
+ // TODO(sleffler) async, could wait for completion
+ system_trace_in_progress_ = true;
+#endif
+ }
+}
+
+void TracingMessageHandler::OnEndTracingAsync(const ListValue* list) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true
+ // here. triggered a false condition by just clicking stop
+ // trace a few times when it was going slow, and maybe switching
+ // between tabs.
+ if (trace_enabled_ &&
+ !TraceController::GetInstance()->EndTracingAsync(this)) {
+ // Set to false now, since it turns out we never were the trace subscriber.
+ OnEndTracingComplete();
+ }
+}
+
+void TracingMessageHandler::OnEndTracingComplete() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ trace_enabled_ = false;
+ if (system_trace_in_progress_) {
+ // Disable system tracing now that the local trace has shutdown.
+ // This must be done last because we potentially need to push event
+ // records into the system event log for synchronizing system event
+ // timestamps with chrome event timestamps--and since the system event
+ // log is a ring-buffer (on linux) adding them at the end is the only
+ // way we're confident we'll have them in the final result.
+ system_trace_in_progress_ = false;
+#if defined(OS_CHROMEOS)
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ RequestStopSystemTracing(
+ base::Bind(&TracingMessageHandler::OnEndSystemTracingAck,
+ base::Unretained(this)));
+ return;
+#endif
+ }
+ web_ui()->CallJavascriptFunction("tracingController.onEndTracingComplete");
+}
+
+void TracingMessageHandler::OnEndSystemTracingAck(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ web_ui()->CallJavascriptFunction(
+ "tracingController.onSystemTraceDataCollected",
+ *scoped_ptr<Value>(Value::CreateStringValue(events_str_ptr->data())));
+ DCHECK(!system_trace_in_progress_);
+
+ OnEndTracingComplete();
+}
+
+void TracingMessageHandler::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ base::debug::TraceResultBuffer::SimpleOutput output;
+ base::debug::TraceResultBuffer trace_buffer;
+ trace_buffer.SetOutputCallback(output.GetCallback());
+ output.Append("tracingController.onTraceDataCollected(");
+ trace_buffer.Start();
+ trace_buffer.AddFragment(trace_fragment->data());
+ trace_buffer.Finish();
+ output.Append(");");
+
+ web_ui()->GetWebContents()->GetRenderViewHost()->
+ ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(output.json_output));
+}
+
+void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui()->CallJavascriptFunction(
+ "tracingController.onRequestBufferPercentFullComplete",
+ *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full)));
+}
+
+} // namespace
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TracingUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(new TracingMessageHandler());
+
+ // Set up the chrome://tracing/ source.
+ BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ WebUIDataSource::Add(browser_context, CreateTracingHTMLSource());
+}
+
+} // namespace content
diff --git a/content/browser/tracing/tracing_ui.h b/content/browser/tracing/tracing_ui.h
new file mode 100644
index 0000000..85bbce1
--- /dev/null
+++ b/content/browser/tracing/tracing_ui.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 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_UI_H_
+#define CONTENT_BROWSER_TRACING_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+
+// The C++ back-end for the chrome://tracing webui page.
+class TracingUI : public WebUIController {
+ public:
+ explicit TracingUI(WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TracingUI);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TRACING_UI_H_