diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-03 04:06:40 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-03 04:06:40 +0000 |
commit | bcb6ee23dd5d6fe711f702a3d04e8412dbf60804 (patch) | |
tree | 3ac91d567a054186ffca8af8dca4e17a3a9f61f4 /content/browser/tracing | |
parent | 1afd68d45b0b59f346b51fa84c53689675e13f5c (diff) | |
download | chromium_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/OWNERS | 1 | ||||
-rw-r--r-- | content/browser/tracing/trace_controller_impl.cc | 414 | ||||
-rw-r--r-- | content/browser/tracing/trace_controller_impl.h | 115 | ||||
-rw-r--r-- | content/browser/tracing/trace_message_filter.cc | 128 | ||||
-rw-r--r-- | content/browser/tracing/trace_message_filter.h | 62 | ||||
-rw-r--r-- | content/browser/tracing/trace_subscriber_stdio.cc | 104 | ||||
-rw-r--r-- | content/browser/tracing/trace_subscriber_stdio.h | 39 | ||||
-rw-r--r-- | content/browser/tracing/trace_subscriber_stdio_unittest.cc | 39 | ||||
-rw-r--r-- | content/browser/tracing/tracing_ui.cc | 492 | ||||
-rw-r--r-- | content/browser/tracing/tracing_ui.h | 23 |
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_ |