diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/content/browser/tracing | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) | |
download | qtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/content/browser/tracing')
11 files changed, 881 insertions, 1563 deletions
diff --git a/chromium/content/browser/tracing/trace_controller_impl.cc b/chromium/content/browser/tracing/trace_controller_impl.cc deleted file mode 100644 index fe08639876d..00000000000 --- a/chromium/content/browser/tracing/trace_controller_impl.cc +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/tracing/trace_controller_impl.h" - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/debug/trace_event.h" -#include "base/strings/string_number_conversions.h" -#include "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 base::FilePath& file_path) - : TraceSubscriberStdio(file_path, - FILE_TYPE_PROPERTY_LIST, - false) {} - - static void EndStartupTrace(AutoStopTraceSubscriberStdio* subscriber) { - if (!TraceControllerImpl::GetInstance()->EndTracingAsync(subscriber)) - delete subscriber; - // else, the tracing will end asynchronously in OnEndTracingComplete(). - } - - virtual void OnEndTracingComplete() OVERRIDE { - 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_category_groups_(false), - category_filter_( - base::debug::CategoryFilter::kDefaultCategoryFilterString) { - TraceLog::GetInstance()->SetNotificationCallback( - base::Bind(&TraceControllerImpl::OnTraceNotification, - base::Unretained(this))); -} - -TraceControllerImpl::~TraceControllerImpl() { - // No need to SetNotificationCallback(nil) on the TraceLog since this is a - // Leaky instance. - NOTREACHED(); -} - -TraceControllerImpl* TraceControllerImpl::GetInstance() { - return g_controller.Pointer(); -} - -void TraceControllerImpl::InitStartupTracing(const CommandLine& command_line) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - base::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 == base::FilePath().AppendASCII("none")) - return; - - if (trace_file.empty()) { - // Default to saving the startup trace into the current dir. - trace_file = base::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::GetKnownCategoryGroupsAsync( - TraceSubscriber* subscriber) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Known categories come back from child processes with the EndTracingAck - // message. So to get known categories, just begin and end tracing immediately - // afterwards. This will ping all the child processes for categories. - is_get_category_groups_ = true; - bool success = BeginTracing(subscriber, "*", - TraceLog::GetInstance()->trace_options()) && - EndTracingAsync(subscriber); - is_get_category_groups_ = success; - return success; -} - -bool TraceControllerImpl::BeginTracing(TraceSubscriber* subscriber, - const std::string& category_patterns, - base::debug::TraceLog::Options options) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - if (!can_begin_tracing(subscriber)) - return false; - - // Enable tracing - TraceLog::GetInstance()->SetEnabled( - base::debug::CategoryFilter(category_patterns), options); - - OnTracingBegan(subscriber); - - return true; -} - -bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - if (!can_end_tracing() || subscriber != subscriber_) - return false; - - // Disable local trace early to avoid traces during end-tracing process from - // interfering with the process. - TraceLog::GetInstance()->SetDisabled(); - - // There could be a case where there are no child processes and filters_ - // is empty. In that case we can immediately tell the subscriber that tracing - // has ended. To avoid recursive calls back to the subscriber, we will just - // use the existing asynchronous OnEndTracingAck code. - // Count myself (local trace) in pending_end_ack_count_, acked below. - pending_end_ack_count_ = filters_.size() + 1; - - // Handle special case of zero child processes. - if (pending_end_ack_count_ == 1) { - // Ack asynchronously now, because we don't have any children to wait for. - std::vector<std::string> category_groups; - TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnEndTracingAck, - base::Unretained(this), category_groups)); - } - - // Notify all child processes. - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { - it->get()->SendEndTracing(); - } - - return true; -} - -bool TraceControllerImpl::GetTraceBufferPercentFullAsync( - TraceSubscriber* subscriber) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - if (!can_get_buffer_percent_full() || subscriber != subscriber_) - return false; - - maximum_bpf_ = 0.0f; - pending_bpf_ack_count_ = filters_.size() + 1; - - // Handle special case of zero child processes. - if (pending_bpf_ack_count_ == 1) { - // Ack asynchronously now, because we don't have any children to wait for. - float bpf = TraceLog::GetInstance()->GetBufferPercentFull(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, - base::Unretained(this), bpf)); - } - - // Message all child processes. - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { - it->get()->SendGetTraceBufferPercentFull(); - } - - return true; -} - -bool TraceControllerImpl::SetWatchEvent(TraceSubscriber* subscriber, - const std::string& category_name, - const std::string& event_name) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (subscriber != subscriber_) - return false; - - watch_category_ = category_name; - watch_name_ = event_name; - - TraceLog::GetInstance()->SetWatchEvent(category_name, event_name); - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) - it->get()->SendSetWatchEvent(category_name, event_name); - - return true; -} - -bool TraceControllerImpl::CancelWatchEvent(TraceSubscriber* subscriber) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (subscriber != subscriber_) - return false; - - watch_category_.clear(); - watch_name_.clear(); - - TraceLog::GetInstance()->CancelWatchEvent(); - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) - it->get()->SendCancelWatchEvent(); - - return true; -} - -void TraceControllerImpl::CancelSubscriber(TraceSubscriber* subscriber) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - if (subscriber == subscriber_) { - subscriber_ = NULL; - // End tracing if necessary. - if (is_tracing_ && pending_end_ack_count_ == 0) - EndTracingAsync(NULL); - } -} - -void TraceControllerImpl::AddFilter(TraceMessageFilter* filter) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::AddFilter, base::Unretained(this), - make_scoped_refptr(filter))); - return; - } - - filters_.insert(filter); - if (is_tracing_enabled()) { - std::string cf_str = category_filter_.ToString(); - filter->SendBeginTracing(cf_str, trace_options_); - if (!watch_category_.empty()) - filter->SendSetWatchEvent(watch_category_, watch_name_); - } -} - -void TraceControllerImpl::RemoveFilter(TraceMessageFilter* filter) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::RemoveFilter, base::Unretained(this), - make_scoped_refptr(filter))); - return; - } - - filters_.erase(filter); -} - -void TraceControllerImpl::OnTracingBegan(TraceSubscriber* subscriber) { - is_tracing_ = true; - - subscriber_ = subscriber; - - category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter(); - trace_options_ = TraceLog::GetInstance()->trace_options(); - - // Notify all child processes. - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { - it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_); - } -} - -void TraceControllerImpl::OnEndTracingAck( - const std::vector<std::string>& known_category_groups) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnEndTracingAck, - base::Unretained(this), known_category_groups)); - return; - } - - // Merge known_category_groups with known_category_groups_ - known_category_groups_.insert(known_category_groups.begin(), - known_category_groups.end()); - - if (pending_end_ack_count_ == 0) - return; - - - if (--pending_end_ack_count_ == 1) { - // All acks from subprocesses have been received. Now flush the local trace. - // During or after this call, our OnLocalTraceDataCollected will be - // called with the last of the local trace data. - TraceLog::GetInstance()->Flush( - base::Bind(&TraceControllerImpl::OnLocalTraceDataCollected, - base::Unretained(this))); - } - - if (pending_end_ack_count_ == 0) { - // All acks (including from the subprocesses and the local trace) have been - // received. - is_tracing_ = false; - - // Trigger callback if one is set. - if (subscriber_) { - if (is_get_category_groups_) - subscriber_->OnKnownCategoriesCollected(known_category_groups_); - else - subscriber_->OnEndTracingComplete(); - // Clear subscriber so that others can use TraceController. - subscriber_ = NULL; - } - - is_get_category_groups_ = false; - } -} - -void TraceControllerImpl::OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& events_str_ptr) { - // OnTraceDataCollected may be called from any browser thread, either by the - // local event trace system or from child processes via TraceMessageFilter. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnTraceDataCollected, - base::Unretained(this), events_str_ptr)); - return; - } - - // Drop trace events if we are just getting categories. - if (subscriber_ && !is_get_category_groups_) - subscriber_->OnTraceDataCollected(events_str_ptr); -} - -void TraceControllerImpl::OnLocalTraceDataCollected( - const scoped_refptr<base::RefCountedString>& events_str_ptr, - bool has_more_events) { - if (events_str_ptr->data().size()) - OnTraceDataCollected(events_str_ptr); - - if (!has_more_events) { - // Simulate an EndTrackingAck for the local trace. - std::vector<std::string> category_groups; - TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); - OnEndTracingAck(category_groups); - } -} - -void TraceControllerImpl::OnTraceNotification(int notification) { - // OnTraceNotification may be called from any browser thread, either by the - // local event trace system or from child processes via TraceMessageFilter. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnTraceNotification, - base::Unretained(this), notification)); - return; - } - - if (notification & base::debug::TraceLog::TRACE_BUFFER_FULL) { - // EndTracingAsync may return false if tracing is already in the process - // of being ended. That is ok. - EndTracingAsync(subscriber_); - } - if (notification & base::debug::TraceLog::EVENT_WATCH_NOTIFICATION) { - if (subscriber_) - subscriber_->OnEventWatchNotification(); - } -} - -void TraceControllerImpl::OnTraceBufferPercentFullReply(float percent_full) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, - base::Unretained(this), percent_full)); - return; - } - - if (pending_bpf_ack_count_ == 0) - return; - - maximum_bpf_ = (maximum_bpf_ > percent_full)? maximum_bpf_ : percent_full; - - if (--pending_bpf_ack_count_ == 0) { - // Trigger callback if one is set. - if (subscriber_) - subscriber_->OnTraceBufferPercentFullReply(maximum_bpf_); - } - - if (pending_bpf_ack_count_ == 1) { - // The last ack represents local trace, so we need to ack it now. Note that - // this code only executes if there were child processes. - float bpf = TraceLog::GetInstance()->GetBufferPercentFull(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, - base::Unretained(this), bpf)); - } -} - -} // namespace content diff --git a/chromium/content/browser/tracing/trace_controller_impl.h b/chromium/content/browser/tracing/trace_controller_impl.h deleted file mode 100644 index 6f2ccb5d481..00000000000 --- a/chromium/content/browser/tracing/trace_controller_impl.h +++ /dev/null @@ -1,109 +0,0 @@ -// 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); - - // TraceController implementation: - virtual bool BeginTracing(TraceSubscriber* subscriber, - const std::string& category_patterns, - base::debug::TraceLog::Options options) 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; - virtual bool GetKnownCategoryGroupsAsync(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_category_groups); - void OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& events_str_ptr); - void OnTraceNotification(int notification); - void OnTraceBufferPercentFullReply(float percent_full); - - // Callback of TraceLog::Flush() for the local trace. - void OnLocalTraceDataCollected( - const scoped_refptr<base::RefCountedString>& events_str_ptr, - bool has_more_events); - - 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_category_groups_; - std::set<std::string> known_category_groups_; - std::string watch_category_; - std::string watch_name_; - base::debug::TraceLog::Options trace_options_; - base::debug::CategoryFilter category_filter_; - - DISALLOW_COPY_AND_ASSIGN(TraceControllerImpl); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_TRACING_TRACE_CONTROLLER_IMPL_H_ diff --git a/chromium/content/browser/tracing/trace_message_filter.cc b/chromium/content/browser/tracing/trace_message_filter.cc index e75d786142d..35f23dc1025 100644 --- a/chromium/content/browser/tracing/trace_message_filter.cc +++ b/chromium/content/browser/tracing/trace_message_filter.cc @@ -5,33 +5,31 @@ #include "content/browser/tracing/trace_message_filter.h" #include "components/tracing/tracing_messages.h" -#include "content/browser/tracing/trace_controller_impl.h" +#include "content/browser/tracing/tracing_controller_impl.h" namespace content { TraceMessageFilter::TraceMessageFilter() : has_child_(false), is_awaiting_end_ack_(false), + is_awaiting_capture_monitoring_snapshot_ack_(false), is_awaiting_buffer_percent_full_ack_(false) { } -void TraceMessageFilter::OnFilterAdded(IPC::Channel* channel) { - // Always on IO thread (BrowserMessageFilter guarantee). - BrowserMessageFilter::OnFilterAdded(channel); -} +TraceMessageFilter::~TraceMessageFilter() {} 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_capture_monitoring_snapshot_ack_) + OnCaptureMonitoringSnapshotAcked(); + if (is_awaiting_buffer_percent_full_ack_) OnTraceBufferPercentFullReply(0.0f); - TraceControllerImpl::GetInstance()->RemoveFilter(this); + TracingControllerImpl::GetInstance()->RemoveTraceMessageFilter(this); } } @@ -43,10 +41,14 @@ bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(TracingHostMsg_ChildSupportsTracing, OnChildSupportsTracing) IPC_MESSAGE_HANDLER(TracingHostMsg_EndTracingAck, OnEndTracingAck) + IPC_MESSAGE_HANDLER(TracingHostMsg_CaptureMonitoringSnapshotAck, + OnCaptureMonitoringSnapshotAcked) IPC_MESSAGE_HANDLER(TracingHostMsg_TraceDataCollected, OnTraceDataCollected) - IPC_MESSAGE_HANDLER(TracingHostMsg_TraceNotification, - OnTraceNotification) + IPC_MESSAGE_HANDLER(TracingHostMsg_MonitoringTraceDataCollected, + OnMonitoringTraceDataCollected) + IPC_MESSAGE_HANDLER(TracingHostMsg_WatchEventMatched, + OnWatchEventMatched) IPC_MESSAGE_HANDLER(TracingHostMsg_TraceBufferPercentFullReply, OnTraceBufferPercentFullReply) IPC_MESSAGE_UNHANDLED(handled = false) @@ -70,6 +72,27 @@ void TraceMessageFilter::SendEndTracing() { Send(new TracingMsg_EndTracing); } +void TraceMessageFilter::SendEnableMonitoring( + const std::string& category_filter_str, + base::debug::TraceLog::Options options) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + Send(new TracingMsg_EnableMonitoring(category_filter_str, + base::TimeTicks::NowFromSystemTraceTime(), + options)); +} + +void TraceMessageFilter::SendDisableMonitoring() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + Send(new TracingMsg_DisableMonitoring); +} + +void TraceMessageFilter::SendCaptureMonitoringSnapshot() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!is_awaiting_capture_monitoring_snapshot_ack_); + is_awaiting_capture_monitoring_snapshot_ack_ = true; + Send(new TracingMsg_CaptureMonitoringSnapshot); +} + void TraceMessageFilter::SendGetTraceBufferPercentFull() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!is_awaiting_buffer_percent_full_ack_); @@ -86,11 +109,9 @@ void TraceMessageFilter::SendCancelWatchEvent() { Send(new TracingMsg_CancelWatchEvent); } -TraceMessageFilter::~TraceMessageFilter() {} - void TraceMessageFilter::OnChildSupportsTracing() { has_child_ = true; - TraceControllerImpl::GetInstance()->AddFilter(this); + TracingControllerImpl::GetInstance()->AddTraceMessageFilter(this); } void TraceMessageFilter::OnEndTracingAck( @@ -99,7 +120,19 @@ void TraceMessageFilter::OnEndTracingAck( // child process is compromised. if (is_awaiting_end_ack_) { is_awaiting_end_ack_ = false; - TraceControllerImpl::GetInstance()->OnEndTracingAck(known_categories); + TracingControllerImpl::GetInstance()->OnDisableRecordingAcked( + known_categories); + } else { + NOTREACHED(); + } +} + +void TraceMessageFilter::OnCaptureMonitoringSnapshotAcked() { + // is_awaiting_capture_monitoring_snapshot_ack_ should always be true here, + // but check in case the child process is compromised. + if (is_awaiting_capture_monitoring_snapshot_ack_) { + is_awaiting_capture_monitoring_snapshot_ack_ = false; + TracingControllerImpl::GetInstance()->OnCaptureMonitoringSnapshotAcked(); } else { NOTREACHED(); } @@ -108,17 +141,25 @@ void TraceMessageFilter::OnEndTracingAck( 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); + TracingControllerImpl::GetInstance()->OnTraceDataCollected(data_ptr); +} + +void TraceMessageFilter::OnMonitoringTraceDataCollected( + const std::string& data) { + scoped_refptr<base::RefCountedString> data_ptr(new base::RefCountedString()); + data_ptr->data() = data; + TracingControllerImpl::GetInstance()->OnMonitoringTraceDataCollected( + data_ptr); } -void TraceMessageFilter::OnTraceNotification(int notification) { - TraceControllerImpl::GetInstance()->OnTraceNotification(notification); +void TraceMessageFilter::OnWatchEventMatched() { + TracingControllerImpl::GetInstance()->OnWatchEventMatched(); } void TraceMessageFilter::OnTraceBufferPercentFullReply(float percent_full) { if (is_awaiting_buffer_percent_full_ack_) { is_awaiting_buffer_percent_full_ack_ = false; - TraceControllerImpl::GetInstance()->OnTraceBufferPercentFullReply( + TracingControllerImpl::GetInstance()->OnTraceBufferPercentFullReply( percent_full); } else { NOTREACHED(); diff --git a/chromium/content/browser/tracing/trace_message_filter.h b/chromium/content/browser/tracing/trace_message_filter.h index 41fd017827f..0f9e18ed9c3 100644 --- a/chromium/content/browser/tracing/trace_message_filter.h +++ b/chromium/content/browser/tracing/trace_message_filter.h @@ -20,9 +20,6 @@ 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, @@ -31,6 +28,10 @@ class TraceMessageFilter : public BrowserMessageFilter { void SendBeginTracing(const std::string& category_filter_str, base::debug::TraceLog::Options options); void SendEndTracing(); + void SendEnableMonitoring(const std::string& category_filter_str, + base::debug::TraceLog::Options options); + void SendDisableMonitoring(); + void SendCaptureMonitoringSnapshot(); void SendGetTraceBufferPercentFull(); void SendSetWatchEvent(const std::string& category_name, const std::string& event_name); @@ -43,15 +44,19 @@ class TraceMessageFilter : public BrowserMessageFilter { // Message handlers. void OnChildSupportsTracing(); void OnEndTracingAck(const std::vector<std::string>& known_categories); - void OnTraceNotification(int notification); + void OnCaptureMonitoringSnapshotAcked(); + void OnWatchEventMatched(); void OnTraceBufferPercentFullReply(float percent_full); void OnTraceDataCollected(const std::string& data); + void OnMonitoringTraceDataCollected(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 SendCaptureMonitoringSnapshot + bool is_awaiting_capture_monitoring_snapshot_ack_; // Awaiting ack for previously sent SendGetTraceBufferPercentFull bool is_awaiting_buffer_percent_full_ack_; diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.cc b/chromium/content/browser/tracing/trace_subscriber_stdio.cc deleted file mode 100644 index 2c6aede56ce..00000000000 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.cc +++ /dev/null @@ -1,201 +0,0 @@ -// 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/file_util.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 TraceSubscriberStdio::TraceSubscriberStdioWorker - : public base::RefCountedThreadSafe<TraceSubscriberStdioWorker> { - public: - TraceSubscriberStdioWorker(const base::FilePath& path, - FileType file_type, - bool has_system_trace) - : path_(path), - file_type_(file_type), - has_system_trace_(has_system_trace), - file_(0), - needs_comma_(false), - wrote_trace_(false), - has_pending_system_trace_(false), - wrote_system_trace_(false) {} - - void OnTraceStart() { - DCHECK(!file_); - file_ = file_util::OpenFile(path_, "w+"); - if (!IsValid()) { - LOG(ERROR) << "Failed to open performance trace file: " << path_.value(); - return; - } - - LOG(INFO) << "Logging performance trace to file: " << path_.value(); - if (file_type_ == FILE_TYPE_PROPERTY_LIST) - WriteString("{\"traceEvents\":"); - WriteString("["); - } - - void OnTraceData(const scoped_refptr<base::RefCountedString>& data_ptr) { - if (!IsValid()) - return; - DCHECK(!data_ptr->data().empty()); - if (needs_comma_) - WriteString(","); - WriteString(data_ptr->data()); - needs_comma_ = true; - } - - void OnSystemTraceData( - const scoped_refptr<base::RefCountedString>& data_ptr) { - if (wrote_trace_) { - WriteSystemTrace(data_ptr); - End(); - } else { - pending_system_trace_ = data_ptr; - has_pending_system_trace_ = true; - } - } - - void OnTraceEnd() { - if (!IsValid()) - return; - WriteString("]"); - - wrote_trace_ = true; - - if (!has_system_trace_ || wrote_system_trace_) { - End(); - return; - } - - WriteString(","); - if (has_pending_system_trace_) { - WriteSystemTrace(pending_system_trace_); - End(); - } - } - - private: - friend class base::RefCountedThreadSafe<TraceSubscriberStdioWorker>; - - ~TraceSubscriberStdioWorker() { - CloseFile(); - } - - bool IsValid() const { - return file_ && (0 == ferror(file_)); - } - - void CloseFile() { - if (file_) { - fclose(file_); - file_ = 0; - - } - } - - void End() { - if (file_type_ == FILE_TYPE_PROPERTY_LIST) - WriteString("}"); - CloseFile(); - } - - void WriteSystemTrace(const scoped_refptr<base::RefCountedString>& data_ptr) { - // Newlines need to be replaced with the string "\n" to be parsed correctly. - // Double quotes need to be replaced with the string "\"". - // System logs are ASCII. - const std::string& data = data_ptr->data(); - const char* chars = data.c_str(); - WriteString("\"systemTraceEvents\":\""); - size_t old_index = 0; - for (size_t new_index = data.find_first_of("\n\""); - std::string::npos != new_index; - old_index = new_index + 1, - new_index = data.find_first_of("\n\"", old_index)) { - WriteChars(chars + old_index, new_index - old_index); - if (chars[new_index] == '\n') - WriteChars("\\n", 2); - else - WriteChars("\\\"", 2); - } - WriteChars(chars + old_index, data.size() - old_index); - WriteString("\""); - wrote_system_trace_ = true; - } - - void WriteChars(const char* output_chars, size_t size) { - if (size == 0) - return; - - if (IsValid()) { - size_t written = fwrite(output_chars, 1, size, file_); - if (written != size) { - LOG(ERROR) << "Error " << ferror(file_) << " in fwrite() to trace file"; - CloseFile(); - } - } - } - - void WriteString(const std::string& output_str) { - WriteChars(output_str.data(), output_str.size()); - } - - base::FilePath path_; - const FileType file_type_; - const bool has_system_trace_; - FILE* file_; - bool needs_comma_; - bool wrote_trace_; - bool has_pending_system_trace_; - bool wrote_system_trace_; - scoped_refptr<base::RefCountedString> pending_system_trace_; - DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdioWorker); -}; - -TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path, - FileType file_type, - bool has_system_trace) - : worker_(new TraceSubscriberStdioWorker(path, - file_type, - has_system_trace)) { - if (has_system_trace) - CHECK_EQ(FILE_TYPE_PROPERTY_LIST, file_type); - BrowserThread::PostBlockingPoolSequencedTask( - __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioWorker::OnTraceStart, worker_)); -} - -TraceSubscriberStdio::~TraceSubscriberStdio() { -} - -void TraceSubscriberStdio::OnEndTracingComplete() { - BrowserThread::PostBlockingPoolSequencedTask( - __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioWorker::OnTraceEnd, worker_)); -} - -void TraceSubscriberStdio::OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& data_ptr) { - BrowserThread::PostBlockingPoolSequencedTask( - __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioWorker::OnTraceData, worker_, data_ptr)); -} - -void TraceSubscriberStdio::OnEndSystemTracing( - const scoped_refptr<base::RefCountedString>& events_str_ptr) { - BrowserThread::PostBlockingPoolSequencedTask( - __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioWorker::OnSystemTraceData, - worker_, - events_str_ptr)); -} - -} // namespace content diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.h b/chromium/content/browser/tracing/trace_subscriber_stdio.h deleted file mode 100644 index b9fc4f74c19..00000000000 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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 "content/public/browser/trace_subscriber.h" -#include "content/common/content_export.h" - -namespace base { -class FilePath; -} - -namespace content { - -// Stdio implementation of TraceSubscriber. Use this to write traces to a file. -class CONTENT_EXPORT TraceSubscriberStdio - : NON_EXPORTED_BASE(public TraceSubscriber) { - public: - enum FileType { - // Output file as array, representing trace events: - // [event1, event2, ...] - FILE_TYPE_ARRAY, - // Output file as property list with one or two items: - // {traceEvents: [event1, event2, ...], - // systemTraceEvents: "event1\nevent2\n..." // optional} - FILE_TYPE_PROPERTY_LIST - }; - - // has_system_trace indicates whether system trace events are expected. - TraceSubscriberStdio(const base::FilePath& path, - FileType file_type, - bool has_system_trace); - virtual ~TraceSubscriberStdio(); - - // Implementation of TraceSubscriber - virtual void OnEndTracingComplete() OVERRIDE; - virtual void OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& data_ptr) OVERRIDE; - - // To be used as callback to DebugDaemonClient::RequestStopSystemTracing(). - virtual void OnEndSystemTracing( - const scoped_refptr<base::RefCountedString>& events_str_ptr); - - private: - class TraceSubscriberStdioWorker; - scoped_refptr<TraceSubscriberStdioWorker> worker_; - DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdio); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_TRACING_TRACE_SUBSCRIBER_STDIO_H_ diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc b/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc deleted file mode 100644 index 10e51a968df..00000000000 --- a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc +++ /dev/null @@ -1,132 +0,0 @@ -// 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/file_util.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, CanWriteArray) { - base::ScopedTempDir trace_dir; - ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); - base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); - { - TraceSubscriberStdio subscriber(trace_file, - TraceSubscriberStdio::FILE_TYPE_ARRAY, - false); - - 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(base::ReadFileToString(trace_file, &result)); - EXPECT_EQ("[foo,bar]", result); -} - -TEST_F(TraceSubscriberStdioTest, CanWritePropertyList) { - base::ScopedTempDir trace_dir; - ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); - base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); - { - TraceSubscriberStdio subscriber( - trace_file, - TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, - false); - - 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(base::ReadFileToString(trace_file, &result)); - EXPECT_EQ("{\"traceEvents\":[foo,bar]}", result); -} - -TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataFirst) { - base::ScopedTempDir trace_dir; - ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); - base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); - { - TraceSubscriberStdio subscriber( - trace_file, - TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, - true); - - 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))); - - std::string systemTrace("event1\nev\"ent\"2\n"); - subscriber.OnEndSystemTracing( - make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); - subscriber.OnEndTracingComplete(); - } - BrowserThread::GetBlockingPool()->FlushForTesting(); - std::string result; - EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); - EXPECT_EQ( - "{\"traceEvents\":[foo,bar],\"" - "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", - result); -} - -TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataLast) { - base::ScopedTempDir trace_dir; - ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); - base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); - { - TraceSubscriberStdio subscriber( - trace_file, - TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, - true); - - 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))); - - std::string systemTrace("event1\nev\"ent\"2\n"); - subscriber.OnEndTracingComplete(); - subscriber.OnEndSystemTracing( - make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); - } - BrowserThread::GetBlockingPool()->FlushForTesting(); - std::string result; - EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); - EXPECT_EQ( - "{\"traceEvents\":[foo,bar],\"" - "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", - result); -} - -} // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_browsertest.cc b/chromium/content/browser/tracing/tracing_controller_browsertest.cc index c88932e0597..bccf90af44a 100644 --- a/chromium/content/browser/tracing/tracing_controller_browsertest.cc +++ b/chromium/content/browser/tracing/tracing_controller_browsertest.cc @@ -20,6 +20,9 @@ class TracingControllerTest : public ContentBrowserTest { get_categories_done_callback_count_ = 0; enable_recording_done_callback_count_ = 0; disable_recording_done_callback_count_ = 0; + enable_monitoring_done_callback_count_ = 0; + disable_monitoring_done_callback_count_ = 0; + capture_monitoring_snapshot_done_callback_count_ = 0; ContentBrowserTest::SetUp(); } @@ -44,10 +47,35 @@ class TracingControllerTest : public ContentBrowserTest { } void DisableRecordingDoneCallbackTest(base::Closure quit_callback, - scoped_ptr<base::FilePath> file_path) { + const base::FilePath& file_path) { disable_recording_done_callback_count_++; - EXPECT_TRUE(PathExists(*file_path)); + EXPECT_TRUE(PathExists(file_path)); + int64 file_size; + base::GetFileSize(file_path, &file_size); + EXPECT_TRUE(file_size > 0); quit_callback.Run(); + last_actual_recording_file_path_ = file_path; + } + + void EnableMonitoringDoneCallbackTest(base::Closure quit_callback) { + enable_monitoring_done_callback_count_++; + quit_callback.Run(); + } + + void DisableMonitoringDoneCallbackTest(base::Closure quit_callback) { + disable_monitoring_done_callback_count_++; + quit_callback.Run(); + } + + void CaptureMonitoringSnapshotDoneCallbackTest( + base::Closure quit_callback, const base::FilePath& file_path) { + capture_monitoring_snapshot_done_callback_count_++; + EXPECT_TRUE(PathExists(file_path)); + int64 file_size; + base::GetFileSize(file_path, &file_size); + EXPECT_TRUE(file_size > 0); + quit_callback.Run(); + last_actual_monitoring_file_path_ = file_path; } int get_categories_done_callback_count() const { @@ -62,10 +90,111 @@ class TracingControllerTest : public ContentBrowserTest { return disable_recording_done_callback_count_; } + int enable_monitoring_done_callback_count() const { + return enable_monitoring_done_callback_count_; + } + + int disable_monitoring_done_callback_count() const { + return disable_monitoring_done_callback_count_; + } + + int capture_monitoring_snapshot_done_callback_count() const { + return capture_monitoring_snapshot_done_callback_count_; + } + + base::FilePath last_actual_recording_file_path() const { + return last_actual_recording_file_path_; + } + + base::FilePath last_actual_monitoring_file_path() const { + return last_actual_monitoring_file_path_; + } + + void TestEnableAndDisableRecording(const base::FilePath& result_file_path) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + { + base::RunLoop run_loop; + TracingController::EnableRecordingDoneCallback callback = + base::Bind(&TracingControllerTest::EnableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + bool result = controller->EnableRecording( + "", TracingController::DEFAULT_OPTIONS, callback); + ASSERT_TRUE(result); + run_loop.Run(); + EXPECT_EQ(enable_recording_done_callback_count(), 1); + } + + { + base::RunLoop run_loop; + TracingController::TracingFileResultCallback callback = + base::Bind(&TracingControllerTest::DisableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + bool result = controller->DisableRecording(result_file_path, callback); + ASSERT_TRUE(result); + run_loop.Run(); + EXPECT_EQ(disable_recording_done_callback_count(), 1); + } + } + + void TestEnableCaptureAndDisableMonitoring( + const base::FilePath& result_file_path) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + { + base::RunLoop run_loop; + TracingController::EnableMonitoringDoneCallback callback = + base::Bind(&TracingControllerTest::EnableMonitoringDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + bool result = controller->EnableMonitoring( + "", TracingController::ENABLE_SAMPLING, callback); + ASSERT_TRUE(result); + run_loop.Run(); + EXPECT_EQ(enable_monitoring_done_callback_count(), 1); + } + + { + base::RunLoop run_loop; + TracingController::TracingFileResultCallback callback = + base::Bind(&TracingControllerTest:: + CaptureMonitoringSnapshotDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + ASSERT_TRUE(controller->CaptureMonitoringSnapshot(result_file_path, + callback)); + run_loop.Run(); + EXPECT_EQ(capture_monitoring_snapshot_done_callback_count(), 1); + } + + { + base::RunLoop run_loop; + TracingController::DisableMonitoringDoneCallback callback = + base::Bind(&TracingControllerTest::DisableMonitoringDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + bool result = controller->DisableMonitoring(callback); + ASSERT_TRUE(result); + run_loop.Run(); + EXPECT_EQ(disable_monitoring_done_callback_count(), 1); + } + } + private: int get_categories_done_callback_count_; int enable_recording_done_callback_count_; int disable_recording_done_callback_count_; + int enable_monitoring_done_callback_count_; + int disable_monitoring_done_callback_count_; + int capture_monitoring_snapshot_done_callback_count_; + base::FilePath last_actual_recording_file_path_; + base::FilePath last_actual_monitoring_file_path_; }; IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) { @@ -78,38 +207,64 @@ IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) { base::Bind(&TracingControllerTest::GetCategoriesDoneCallbackTest, base::Unretained(this), run_loop.QuitClosure()); - controller->GetCategories(callback); + ASSERT_TRUE(controller->GetCategories(callback)); run_loop.Run(); EXPECT_EQ(get_categories_done_callback_count(), 1); } IN_PROC_BROWSER_TEST_F(TracingControllerTest, EnableAndDisableRecording) { + TestEnableAndDisableRecording(base::FilePath()); +} + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, + EnableAndDisableRecordingWithFilePath) { + base::FilePath file_path; + base::CreateTemporaryFile(&file_path); + TestEnableAndDisableRecording(file_path); + EXPECT_EQ(file_path.value(), last_actual_recording_file_path().value()); +} + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, + EnableAndDisableRecordingWithEmptyFileAndNullCallback) { Navigate(shell()); TracingController* controller = TracingController::GetInstance(); + EXPECT_TRUE(controller->EnableRecording( + "", TracingController::DEFAULT_OPTIONS, + TracingController::EnableRecordingDoneCallback())); + EXPECT_TRUE(controller->DisableRecording( + base::FilePath(), TracingController::TracingFileResultCallback())); + base::RunLoop().RunUntilIdle(); +} - { - base::RunLoop run_loop; - TracingController::EnableRecordingDoneCallback callback = - base::Bind(&TracingControllerTest::EnableRecordingDoneCallbackTest, - base::Unretained(this), - run_loop.QuitClosure()); - controller->EnableRecording(base::debug::CategoryFilter("*"), - TracingController::Options(), callback); - run_loop.Run(); - EXPECT_EQ(enable_recording_done_callback_count(), 1); - } - - { - base::RunLoop run_loop; - TracingController::TracingFileResultCallback callback = - base::Bind(&TracingControllerTest::DisableRecordingDoneCallbackTest, - base::Unretained(this), - run_loop.QuitClosure()); - controller->DisableRecording(callback); - run_loop.Run(); - EXPECT_EQ(disable_recording_done_callback_count(), 1); - } +IN_PROC_BROWSER_TEST_F(TracingControllerTest, + EnableCaptureAndDisableMonitoring) { + TestEnableCaptureAndDisableMonitoring(base::FilePath()); +} + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, + EnableCaptureAndDisableMonitoringWithFilePath) { + base::FilePath file_path; + base::CreateTemporaryFile(&file_path); + TestEnableCaptureAndDisableMonitoring(file_path); + EXPECT_EQ(file_path.value(), last_actual_monitoring_file_path().value()); +} + +IN_PROC_BROWSER_TEST_F( + TracingControllerTest, + EnableCaptureAndDisableMonitoringWithEmptyFileAndNullCallback) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + EXPECT_TRUE(controller->EnableMonitoring( + "", TracingController::ENABLE_SAMPLING, + TracingController::EnableMonitoringDoneCallback())); + controller->CaptureMonitoringSnapshot( + base::FilePath(), TracingController::TracingFileResultCallback()); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(controller->DisableMonitoring( + TracingController::DisableMonitoringDoneCallback())); + base::RunLoop().RunUntilIdle(); } } // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.cc b/chromium/content/browser/tracing/tracing_controller_impl.cc index 1c1c43aabf9..bc7cee2f930 100644 --- a/chromium/content/browser/tracing/tracing_controller_impl.cc +++ b/chromium/content/browser/tracing/tracing_controller_impl.cc @@ -5,6 +5,7 @@ #include "content/browser/tracing/tracing_controller_impl.h" #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/file_util.h" #include "base/json/string_escape.h" #include "base/strings/string_number_conversions.h" @@ -28,11 +29,97 @@ TracingController* TracingController::GetInstance() { return TracingControllerImpl::GetInstance(); } +class TracingControllerImpl::ResultFile { + public: + explicit ResultFile(const base::FilePath& path); + void Write(const scoped_refptr<base::RefCountedString>& events_str_ptr) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&TracingControllerImpl::ResultFile::WriteTask, + base::Unretained(this), events_str_ptr)); + } + void Close(const base::Closure& callback) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&TracingControllerImpl::ResultFile::CloseTask, + base::Unretained(this), callback)); + } + const base::FilePath& path() const { return path_; } + + private: + void OpenTask(); + void WriteTask(const scoped_refptr<base::RefCountedString>& events_str_ptr); + void CloseTask(const base::Closure& callback); + + FILE* file_; + base::FilePath path_; + bool has_at_least_one_result_; + + DISALLOW_COPY_AND_ASSIGN(ResultFile); +}; + +TracingControllerImpl::ResultFile::ResultFile(const base::FilePath& path) + : file_(NULL), + path_(path), + has_at_least_one_result_(false) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&TracingControllerImpl::ResultFile::OpenTask, + base::Unretained(this))); +} + +void TracingControllerImpl::ResultFile::OpenTask() { + if (path_.empty()) + base::CreateTemporaryFile(&path_); + file_ = base::OpenFile(path_, "w"); + if (!file_) { + LOG(ERROR) << "Failed to open " << path_.value(); + return; + } + const char* preamble = "{\"traceEvents\": ["; + size_t written = fwrite(preamble, strlen(preamble), 1, file_); + DCHECK(written == 1); +} + +void TracingControllerImpl::ResultFile::WriteTask( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + if (!file_) + return; + + // If there is already a result in the file, then put a commma + // before the next batch of results. + if (has_at_least_one_result_) { + size_t written = fwrite(",", 1, 1, file_); + DCHECK(written == 1); + } + has_at_least_one_result_ = true; + size_t written = fwrite(events_str_ptr->data().c_str(), + events_str_ptr->data().size(), 1, + file_); + DCHECK(written == 1); +} + +void TracingControllerImpl::ResultFile::CloseTask( + const base::Closure& callback) { + if (!file_) + return; + + const char* trailout = "]}"; + size_t written = fwrite(trailout, strlen(trailout), 1, file_); + DCHECK(written == 1); + base::CloseFile(file_); + file_ = NULL; + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); +} + + TracingControllerImpl::TracingControllerImpl() : - pending_end_ack_count_(0), - is_recording_(false), - category_filter_( - base::debug::CategoryFilter::kDefaultCategoryFilterString) { + pending_disable_recording_ack_count_(0), + pending_capture_monitoring_snapshot_ack_count_(0), + pending_trace_buffer_percent_full_ack_count_(0), + maximum_trace_buffer_percent_full_(0), + // Tracing may have been enabled by ContentMainRunner if kTraceStartup + // is specified in command line. + is_recording_(TraceLog::GetInstance()->IsEnabled()), + is_monitoring_(false) { } TracingControllerImpl::~TracingControllerImpl() { @@ -44,7 +131,7 @@ TracingControllerImpl* TracingControllerImpl::GetInstance() { return g_controller.Pointer(); } -void TracingControllerImpl::GetCategories( +bool TracingControllerImpl::GetCategories( const GetCategoriesDoneCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -52,42 +139,61 @@ void TracingControllerImpl::GetCategories( // message. So to get known categories, just begin and end tracing immediately // afterwards. This will ping all the child processes for categories. pending_get_categories_done_callback_ = callback; - EnableRecording(base::debug::CategoryFilter("*"), - TracingController::Options(), - EnableRecordingDoneCallback()); - DisableRecording(TracingFileResultCallback()); + if (!EnableRecording("*", TracingController::Options(), + EnableRecordingDoneCallback())) { + pending_get_categories_done_callback_.Reset(); + return false; + } + + bool ok = DisableRecording(base::FilePath(), TracingFileResultCallback()); + DCHECK(ok); + return true; } -void TracingControllerImpl::EnableRecording( - const base::debug::CategoryFilter& filter, +bool TracingControllerImpl::EnableRecording( + const std::string& category_filter, TracingController::Options options, const EnableRecordingDoneCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!can_enable_recording()) - return; - - trace_options_ = TraceLog::GetInstance()->trace_options(); - TraceLog::GetInstance()->SetEnabled(filter, trace_options_); + return false; + +#if defined(OS_ANDROID) + if (pending_get_categories_done_callback_.is_null()) + TraceLog::GetInstance()->AddClockSyncMetadataEvent(); +#endif + + TraceLog::Options trace_options = (options & RECORD_CONTINUOUSLY) ? + TraceLog::RECORD_CONTINUOUSLY : TraceLog::RECORD_UNTIL_FULL; + if (options & ENABLE_SAMPLING) { + trace_options = static_cast<TraceLog::Options>( + trace_options | TraceLog::ENABLE_SAMPLING); + } + // TODO(haraken): How to handle ENABLE_SYSTRACE? + TraceLog::GetInstance()->SetEnabled( + base::debug::CategoryFilter(category_filter), trace_options); is_recording_ = true; - category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter(); // Notify all child processes. - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { - it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_); + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendBeginTracing(category_filter, trace_options); } if (!callback.is_null()) callback.Run(); + return true; } -void TracingControllerImpl::DisableRecording( +bool TracingControllerImpl::DisableRecording( + const base::FilePath& result_file_path, const TracingFileResultCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!can_end_recording()) - return; + if (!can_disable_recording()) + return false; pending_disable_recording_done_callback_ = callback; @@ -95,22 +201,22 @@ void TracingControllerImpl::DisableRecording( // interfering with the process. TraceLog::GetInstance()->SetDisabled(); - // We don't need to create a temporary file when getting categories. - if (pending_get_categories_done_callback_.is_null()) { - base::FilePath temporary_file; - file_util::CreateTemporaryFile(&temporary_file); - recording_result_file_.reset(new base::FilePath(temporary_file)); - } +#if defined(OS_ANDROID) + if (pending_get_categories_done_callback_.is_null()) + TraceLog::GetInstance()->AddClockSyncMetadataEvent(); +#endif - // There could be a case where there are no child processes and filters_ - // is empty. In that case we can immediately tell the subscriber that tracing - // has ended. To avoid recursive calls back to the subscriber, we will just - // use the existing asynchronous OnDisableRecordingAcked code. - // Count myself (local trace) in pending_end_ack_count_, acked below. - pending_end_ack_count_ = filters_.size() + 1; + if (!callback.is_null() || !result_file_path.empty()) + result_file_.reset(new ResultFile(result_file_path)); - // Handle special case of zero child processes. - if (pending_end_ack_count_ == 1) { + // Count myself (local trace) in pending_disable_recording_ack_count_, + // acked below. + pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1; + + // Handle special case of zero child processes by immediately telling the + // caller that tracing has ended. Use asynchronous OnDisableRecordingAcked + // to avoid recursive call back to the caller. + if (pending_disable_recording_ack_count_ == 1) { // Ack asynchronously now, because we don't have any children to wait for. std::vector<std::string> category_groups; TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); @@ -120,59 +226,221 @@ void TracingControllerImpl::DisableRecording( } // Notify all child processes. - for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { it->get()->SendEndTracing(); } + return true; } -void TracingControllerImpl::EnableMonitoring( - const base::debug::CategoryFilter& filter, +bool TracingControllerImpl::EnableMonitoring( + const std::string& category_filter, TracingController::Options options, const EnableMonitoringDoneCallback& callback) { - NOTIMPLEMENTED(); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_enable_monitoring()) + return false; + is_monitoring_ = true; + +#if defined(OS_ANDROID) + TraceLog::GetInstance()->AddClockSyncMetadataEvent(); +#endif + + int monitoring_tracing_options = 0; + if (options & ENABLE_SAMPLING) + monitoring_tracing_options |= base::debug::TraceLog::MONITOR_SAMPLING; + + TraceLog::GetInstance()->SetEnabled( + base::debug::CategoryFilter(category_filter), + static_cast<TraceLog::Options>(monitoring_tracing_options)); + + // Notify all child processes. + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendEnableMonitoring(category_filter, + static_cast<TraceLog::Options>(monitoring_tracing_options)); + } + + if (!callback.is_null()) + callback.Run(); + return true; } -void TracingControllerImpl::DisableMonitoring( +bool TracingControllerImpl::DisableMonitoring( const DisableMonitoringDoneCallback& callback) { - NOTIMPLEMENTED(); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_disable_monitoring()) + return false; + is_monitoring_ = false; + + TraceLog::GetInstance()->SetDisabled(); + + // Notify all child processes. + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendDisableMonitoring(); + } + + if (!callback.is_null()) + callback.Run(); + return true; } void TracingControllerImpl::GetMonitoringStatus( bool* out_enabled, - base::debug::CategoryFilter* out_filter, + std::string* out_category_filter, TracingController::Options* out_options) { NOTIMPLEMENTED(); } -void TracingControllerImpl::CaptureCurrentMonitoringSnapshot( +bool TracingControllerImpl::CaptureMonitoringSnapshot( + const base::FilePath& result_file_path, const TracingFileResultCallback& callback) { - NOTIMPLEMENTED(); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_disable_monitoring()) + return false; + + if (callback.is_null() && result_file_path.empty()) + return false; + + pending_capture_monitoring_snapshot_done_callback_ = callback; + monitoring_snapshot_file_.reset(new ResultFile(result_file_path)); + + // Count myself in pending_capture_monitoring_snapshot_ack_count_, + // acked below. + pending_capture_monitoring_snapshot_ack_count_ = + trace_message_filters_.size() + 1; + + // Handle special case of zero child processes by immediately telling the + // caller that capturing snapshot has ended. Use asynchronous + // OnCaptureMonitoringSnapshotAcked to avoid recursive call back to the + // caller. + if (pending_capture_monitoring_snapshot_ack_count_ == 1) { + // Ack asynchronously now, because we don't have any children to wait for. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked, + base::Unretained(this))); + } + + // Notify all child processes. + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendCaptureMonitoringSnapshot(); + } + +#if defined(OS_ANDROID) + TraceLog::GetInstance()->AddClockSyncMetadataEvent(); +#endif + + return true; +} + +bool TracingControllerImpl::GetTraceBufferPercentFull( + const GetTraceBufferPercentFullCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_get_trace_buffer_percent_full() || callback.is_null()) + return false; + + pending_trace_buffer_percent_full_callback_ = callback; + + // Count myself in pending_trace_buffer_percent_full_ack_count_, acked below. + pending_trace_buffer_percent_full_ack_count_ = + trace_message_filters_.size() + 1; + maximum_trace_buffer_percent_full_ = 0; + + // Handle special case of zero child processes. + if (pending_trace_buffer_percent_full_ack_count_ == 1) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply, + base::Unretained(this), + TraceLog::GetInstance()->GetBufferPercentFull())); + } + + // Notify all child processes. + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendGetTraceBufferPercentFull(); + } + return true; +} + +bool TracingControllerImpl::SetWatchEvent( + const std::string& category_name, + const std::string& event_name, + const WatchEventCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (callback.is_null()) + return false; + + watch_category_name_ = category_name; + watch_event_name_ = event_name; + watch_event_callback_ = callback; + + TraceLog::GetInstance()->SetWatchEvent( + category_name, event_name, + base::Bind(&TracingControllerImpl::OnWatchEventMatched, + base::Unretained(this))); + + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendSetWatchEvent(category_name, event_name); + } + return true; +} + +bool TracingControllerImpl::CancelWatchEvent() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_cancel_watch_event()) + return false; + + for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin(); + it != trace_message_filters_.end(); ++it) { + it->get()->SendCancelWatchEvent(); + } + + watch_event_callback_.Reset(); + return true; } -void TracingControllerImpl::AddFilter(TraceMessageFilter* filter) { +void TracingControllerImpl::AddTraceMessageFilter( + TraceMessageFilter* trace_message_filter) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TracingControllerImpl::AddFilter, base::Unretained(this), - make_scoped_refptr(filter))); + base::Bind(&TracingControllerImpl::AddTraceMessageFilter, + base::Unretained(this), + make_scoped_refptr(trace_message_filter))); return; } - filters_.insert(filter); - if (is_recording_enabled()) { - std::string cf_str = category_filter_.ToString(); - filter->SendBeginTracing(cf_str, trace_options_); + trace_message_filters_.insert(trace_message_filter); + if (can_cancel_watch_event()) { + trace_message_filter->SendSetWatchEvent(watch_category_name_, + watch_event_name_); + } + if (can_disable_recording()) { + trace_message_filter->SendBeginTracing( + TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(), + TraceLog::GetInstance()->trace_options()); } } -void TracingControllerImpl::RemoveFilter(TraceMessageFilter* filter) { +void TracingControllerImpl::RemoveTraceMessageFilter( + TraceMessageFilter* trace_message_filter) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TracingControllerImpl::RemoveFilter, base::Unretained(this), - make_scoped_refptr(filter))); + base::Bind(&TracingControllerImpl::RemoveTraceMessageFilter, + base::Unretained(this), + make_scoped_refptr(trace_message_filter))); return; } - filters_.erase(filter); + trace_message_filters_.erase(trace_message_filter); } void TracingControllerImpl::OnDisableRecordingAcked( @@ -188,19 +456,20 @@ void TracingControllerImpl::OnDisableRecordingAcked( known_category_groups_.insert(known_category_groups.begin(), known_category_groups.end()); - if (pending_end_ack_count_ == 0) + if (pending_disable_recording_ack_count_ == 0) return; - if (--pending_end_ack_count_ == 1) { + if (--pending_disable_recording_ack_count_ == 1) { // All acks from subprocesses have been received. Now flush the local trace. // During or after this call, our OnLocalTraceDataCollected will be // called with the last of the local trace data. TraceLog::GetInstance()->Flush( base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected, base::Unretained(this))); + return; } - if (pending_end_ack_count_ != 0) + if (pending_disable_recording_ack_count_ != 0) return; // All acks (including from the subprocesses and the local trace) have been @@ -211,17 +480,69 @@ void TracingControllerImpl::OnDisableRecordingAcked( if (!pending_get_categories_done_callback_.is_null()) { pending_get_categories_done_callback_.Run(known_category_groups_); pending_get_categories_done_callback_.Reset(); - } else { - OnEndTracingComplete(); + } else if (result_file_) { + result_file_->Close( + base::Bind(&TracingControllerImpl::OnResultFileClosed, + base::Unretained(this))); } } -void TracingControllerImpl::OnEndTracingComplete() { - if (pending_disable_recording_done_callback_.is_null()) +void TracingControllerImpl::OnResultFileClosed() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!result_file_) return; - pending_disable_recording_done_callback_.Run(recording_result_file_.Pass()); - pending_disable_recording_done_callback_.Reset(); + if (!pending_disable_recording_done_callback_.is_null()) { + pending_disable_recording_done_callback_.Run(result_file_->path()); + pending_disable_recording_done_callback_.Reset(); + } + result_file_.reset(); +} + +void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked() { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked, + base::Unretained(this))); + return; + } + + if (pending_capture_monitoring_snapshot_ack_count_ == 0) + return; + + if (--pending_capture_monitoring_snapshot_ack_count_ == 1) { + // All acks from subprocesses have been received. Now flush the local trace. + // During or after this call, our OnLocalMonitoringTraceDataCollected + // will be called with the last of the local trace data. + TraceLog::GetInstance()->FlushButLeaveBufferIntact( + base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected, + base::Unretained(this))); + return; + } + + if (pending_capture_monitoring_snapshot_ack_count_ != 0) + return; + + if (monitoring_snapshot_file_) { + monitoring_snapshot_file_->Close( + base::Bind(&TracingControllerImpl::OnMonitoringSnapshotFileClosed, + base::Unretained(this))); + } +} + +void TracingControllerImpl::OnMonitoringSnapshotFileClosed() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!monitoring_snapshot_file_) + return; + + if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) { + pending_capture_monitoring_snapshot_done_callback_.Run( + monitoring_snapshot_file_->path()); + pending_capture_monitoring_snapshot_done_callback_.Reset(); + } + monitoring_snapshot_file_.reset(); } void TracingControllerImpl::OnTraceDataCollected( @@ -235,21 +556,21 @@ void TracingControllerImpl::OnTraceDataCollected( return; } - // Drop trace events if we are just getting categories. - if (!pending_get_categories_done_callback_.is_null()) - return; - - std::string javascript; - javascript.reserve(events_str_ptr->size() * 2); - base::JsonDoubleQuote(events_str_ptr->data(), false, &javascript); + if (result_file_) + result_file_->Write(events_str_ptr); +} - // Intentionally append a , to the traceData. This technically causes all - // traceData that we pass back to JS to end with a comma, but that is - // actually something the JS side strips away anyway - javascript.append(","); +void TracingControllerImpl::OnMonitoringTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnMonitoringTraceDataCollected, + base::Unretained(this), events_str_ptr)); + return; + } - file_util::WriteFile(*recording_result_file_, - javascript.c_str(), javascript.length()); + if (monitoring_snapshot_file_) + monitoring_snapshot_file_->Write(events_str_ptr); } void TracingControllerImpl::OnLocalTraceDataCollected( @@ -267,4 +588,60 @@ void TracingControllerImpl::OnLocalTraceDataCollected( OnDisableRecordingAcked(category_groups); } +void TracingControllerImpl::OnLocalMonitoringTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events) { + if (events_str_ptr->data().size()) + OnMonitoringTraceDataCollected(events_str_ptr); + + if (has_more_events) + return; + + // Simulate an CaptureMonitoringSnapshotAcked for the local trace. + OnCaptureMonitoringSnapshotAcked(); +} + +void TracingControllerImpl::OnTraceBufferPercentFullReply(float percent_full) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply, + base::Unretained(this), percent_full)); + return; + } + + if (pending_trace_buffer_percent_full_ack_count_ == 0) + return; + + maximum_trace_buffer_percent_full_ = + std::max(maximum_trace_buffer_percent_full_, percent_full); + + if (--pending_trace_buffer_percent_full_ack_count_ == 0) { + // Trigger callback if one is set. + pending_trace_buffer_percent_full_callback_.Run( + maximum_trace_buffer_percent_full_); + pending_trace_buffer_percent_full_callback_.Reset(); + } + + if (pending_trace_buffer_percent_full_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. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply, + base::Unretained(this), + TraceLog::GetInstance()->GetBufferPercentFull())); + } +} + +void TracingControllerImpl::OnWatchEventMatched() { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnWatchEventMatched, + base::Unretained(this))); + return; + } + + if (!watch_event_callback_.is_null()) + watch_event_callback_.Run(); +} + } // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.h b/chromium/content/browser/tracing/tracing_controller_impl.h index 22192255444..ed3c8fd6519 100644 --- a/chromium/content/browser/tracing/tracing_controller_impl.h +++ b/chromium/content/browser/tracing/tracing_controller_impl.h @@ -9,42 +9,54 @@ #include <string> #include <vector> +#include "base/files/file_path.h" #include "base/lazy_instance.h" -#include "content/public/browser/trace_subscriber.h" #include "content/public/browser/tracing_controller.h" +namespace base { +class RefCountedString; +} + namespace content { class TraceMessageFilter; -class TracingControllerImpl : - public TracingController, public TraceSubscriber { +class TracingControllerImpl : public TracingController { public: static TracingControllerImpl* GetInstance(); // TracingController implementation. - virtual void GetCategories( + virtual bool GetCategories( const GetCategoriesDoneCallback& callback) OVERRIDE; - virtual void EnableRecording( - const base::debug::CategoryFilter& filter, + virtual bool EnableRecording( + const std::string& category_filter, TracingController::Options options, const EnableRecordingDoneCallback& callback) OVERRIDE; - virtual void DisableRecording( + virtual bool DisableRecording( + const base::FilePath& result_file_path, const TracingFileResultCallback& callback) OVERRIDE; - virtual void EnableMonitoring(const base::debug::CategoryFilter& filter, + virtual bool EnableMonitoring(const std::string& category_filter, TracingController::Options options, const EnableMonitoringDoneCallback& callback) OVERRIDE; - virtual void DisableMonitoring( + virtual bool DisableMonitoring( const DisableMonitoringDoneCallback& callback) OVERRIDE; virtual void GetMonitoringStatus( bool* out_enabled, - base::debug::CategoryFilter* out_filter, + std::string* out_category_filter, TracingController::Options* out_options) OVERRIDE; - virtual void CaptureCurrentMonitoringSnapshot( + virtual bool CaptureMonitoringSnapshot( + const base::FilePath& result_file_path, const TracingFileResultCallback& callback) OVERRIDE; + virtual bool GetTraceBufferPercentFull( + const GetTraceBufferPercentFullCallback& callback) OVERRIDE; + virtual bool SetWatchEvent(const std::string& category_name, + const std::string& event_name, + const WatchEventCallback& callback) OVERRIDE; + virtual bool CancelWatchEvent() OVERRIDE; private: - typedef std::set<scoped_refptr<TraceMessageFilter> > FilterMap; + typedef std::set<scoped_refptr<TraceMessageFilter> > TraceMessageFilterMap; + class ResultFile; friend struct base::DefaultLazyInstanceTraits<TracingControllerImpl>; friend class TraceMessageFilter; @@ -52,46 +64,83 @@ class TracingControllerImpl : TracingControllerImpl(); virtual ~TracingControllerImpl(); - // TraceSubscriber implementation. - virtual void OnEndTracingComplete() OVERRIDE; - virtual void OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& events_str_ptr) OVERRIDE; - bool can_enable_recording() const { return !is_recording_; } - bool can_end_recording() const { - return is_recording_ && pending_end_ack_count_ == 0; + bool can_disable_recording() const { + return is_recording_ && !result_file_; + } + + bool can_enable_monitoring() const { + return !is_monitoring_; + } + + bool can_disable_monitoring() const { + return is_monitoring_ && !monitoring_snapshot_file_; + } + + bool can_get_trace_buffer_percent_full() const { + return pending_trace_buffer_percent_full_callback_.is_null(); } - bool is_recording_enabled() const { - return can_end_recording(); + bool can_cancel_watch_event() const { + return !watch_event_callback_.is_null(); } // Methods for use by TraceMessageFilter. - void AddFilter(TraceMessageFilter* filter); - void RemoveFilter(TraceMessageFilter* filter); + void AddTraceMessageFilter(TraceMessageFilter* trace_message_filter); + void RemoveTraceMessageFilter(TraceMessageFilter* trace_message_filter); + + void OnTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr); + void OnMonitoringTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr); // Callback of TraceLog::Flush() for the local trace. void OnLocalTraceDataCollected( const scoped_refptr<base::RefCountedString>& events_str_ptr, bool has_more_events); + // Callback of TraceLog::FlushMonitoring() for the local trace. + void OnLocalMonitoringTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events); void OnDisableRecordingAcked( const std::vector<std::string>& known_category_groups); + void OnResultFileClosed(); + + void OnCaptureMonitoringSnapshotAcked(); + void OnMonitoringSnapshotFileClosed(); + + void OnTraceBufferPercentFullReply(float percent_full); - FilterMap filters_; + void OnWatchEventMatched(); + + TraceMessageFilterMap trace_message_filters_; // Pending acks for DisableRecording. - int pending_end_ack_count_; + int pending_disable_recording_ack_count_; + // Pending acks for CaptureMonitoringSnapshot. + int pending_capture_monitoring_snapshot_ack_count_; + // Pending acks for GetTraceBufferPercentFull. + int pending_trace_buffer_percent_full_ack_count_; + float maximum_trace_buffer_percent_full_; + bool is_recording_; + bool is_monitoring_; + GetCategoriesDoneCallback pending_get_categories_done_callback_; TracingFileResultCallback pending_disable_recording_done_callback_; - std::set<std::string> known_category_groups_; - base::debug::TraceLog::Options trace_options_; - base::debug::CategoryFilter category_filter_; - scoped_ptr<base::FilePath> recording_result_file_; + TracingFileResultCallback pending_capture_monitoring_snapshot_done_callback_; + GetTraceBufferPercentFullCallback pending_trace_buffer_percent_full_callback_; + std::string watch_category_name_; + std::string watch_event_name_; + WatchEventCallback watch_event_callback_; + + std::set<std::string> known_category_groups_; + scoped_ptr<ResultFile> result_file_; + scoped_ptr<ResultFile> monitoring_snapshot_file_; DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl); }; diff --git a/chromium/content/browser/tracing/tracing_ui.cc b/chromium/content/browser/tracing/tracing_ui.cc index 15bd2cf2590..e59f5667cb4 100644 --- a/chromium/content/browser/tracing/tracing_ui.cc +++ b/chromium/content/browser/tracing/tracing_ui.cc @@ -6,540 +6,143 @@ #include <string> +#include "base/base64.h" #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/json/string_escape.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" -#include "base/safe_numerics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/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/tracing_controller.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/tracing_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(kChromeUITracingHost); - - source->SetJsonPath("strings.js"); - source->SetDefaultResource(IDR_TRACING_HTML); - source->AddResourcePath("tracing.js", IDR_TRACING_JS); - 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() OVERRIDE; - - // SelectFileDialog::Listener implementation - virtual void FileSelected(const base::FilePath& path, - int index, - void* params) OVERRIDE; - virtual void FileSelectionCanceled(void* params) OVERRIDE; - - // TraceSubscriber implementation. - virtual void OnEndTracingComplete() OVERRIDE; - virtual void OnTraceDataCollected( - const scoped_refptr<base::RefCountedString>& trace_fragment) OVERRIDE; - virtual void OnTraceBufferPercentFullReply(float percent_full) OVERRIDE; - virtual void OnKnownCategoriesCollected( - const std::set<std::string>& known_categories) OVERRIDE; - - // Messages. - void OnTracingControllerInitialized(const base::ListValue* list); - void OnBeginTracing(const base::ListValue* list); - void OnEndTracingAsync(const base::ListValue* list); - void OnBeginRequestBufferPercentFull(const base::ListValue* list); - void OnLoadTraceFile(const base::ListValue* list); - void OnSaveTraceFile(const base::ListValue* list); - void OnGetKnownCategories(const base::ListValue* list); - - // Callbacks. - void LoadTraceFileComplete(string16* file_contents, - const base::FilePath &path); - void SaveTraceFileComplete(); +void OnGotCategories(const WebUIDataSource::GotDataCallback& callback, + const std::set<std::string>& categorySet) { - 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, - const base::FilePath& path) { - if (handler_.get()) - handler_->LoadTraceFileComplete(file_contents, path); - delete file_contents; - } - - void SaveTraceFileCompleteProxy() { - if (handler_.get()) - handler_->SaveTraceFileComplete(); + scoped_ptr<base::ListValue> category_list(new base::ListValue()); + for (std::set<std::string>::const_iterator it = categorySet.begin(); + it != categorySet.end(); it++) { + category_list->AppendString(*it); } - 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) { + base::RefCountedString* res = new base::RefCountedString(); + base::JSONWriter::Write(category_list.get(), &res->data()); + callback.Run(res); } -TracingMessageHandler::~TracingMessageHandler() { - if (select_trace_file_dialog_.get()) - 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))); - web_ui()->RegisterMessageCallback("getKnownCategories", - base::Bind(&TracingMessageHandler::OnGetKnownCategories, - base::Unretained(this))); -} - -void TracingMessageHandler::OnTracingControllerInitialized( - const base::ListValue* args) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Send the client info to the tracingController - { - scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); - dict->SetString("version", GetContentClient()->GetProduct()); +void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback); - dict->SetString("command_line", - CommandLine::ForCurrentProcess()->GetCommandLineString()); - - web_ui()->CallJavascriptFunction("tracingController.onClientInfoUpdate", - *dict); +bool OnBeginRecording(const std::string& data64, + const WebUIDataSource::GotDataCallback& callback) { + std::string data; + if (!base::Base64Decode(data64, &data)) { + LOG(ERROR) << "Options were not base64 encoded."; + return false; } -} - -void TracingMessageHandler::OnBeginRequestBufferPercentFull( - const base::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 base::FilePath& path) { - std::string file_contents; - if (!base::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); + scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data)); + if (!optionsRaw) { + LOG(ERROR) << "Options were not valid JSON"; + return false; } - 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(), - path)); -} - -// A callback used for asynchronously writing a file from a string. Calls the -// TaskProxy callback when writing is complete. -void WriteTraceFileCallback(TaskProxy* proxy, - const base::FilePath& path, - std::string* contents) { - int size = base::checked_numeric_cast<int>(contents->size()); - if (file_util::WriteFile(path, contents->c_str(), size) != size) - return; - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&TaskProxy::SaveTraceFileCompleteProxy, proxy)); -} - -void TracingMessageHandler::FileSelected( - const base::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())); + base::DictionaryValue* options; + if (!optionsRaw->GetAsDictionary(&options)) { + LOG(ERROR) << "Options must be dict"; + return false; } - 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"); + std::string category_filter_string; + bool use_system_tracing; + bool use_continuous_tracing; + bool use_sampling; + + bool options_ok = true; + options_ok &= options->GetString("categoryFilter", &category_filter_string); + options_ok &= options->GetBoolean("useSystemTracing", &use_system_tracing); + options_ok &= options->GetBoolean("useContinuousTracing", + &use_continuous_tracing); + options_ok &= options->GetBoolean("useSampling", &use_sampling); + if (!options_ok) { + LOG(ERROR) << "Malformed options"; + return false; } -} - -void TracingMessageHandler::OnLoadTraceFile(const base::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(), - base::FilePath(), - NULL, - 0, - base::FilePath::StringType(), - web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), - NULL); -} -void TracingMessageHandler::LoadTraceFileComplete(string16* contents, - const base::FilePath& path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + int tracing_options = 0; + if (use_system_tracing) + tracing_options |= TracingController::ENABLE_SYSTRACE; + if (use_sampling) + tracing_options |= TracingController::ENABLE_SAMPLING; + if (use_continuous_tracing) + tracing_options |= TracingController::RECORD_CONTINUOUSLY; - // 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); - } - - // The CallJavascriptFunction is not used because we need to pass - // the first param |window.traceData| through as an un-quoted string. - rvh->ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16( - "tracingController.onLoadTraceFileComplete(window.traceData," + - base::GetDoubleQuotedJson(path.value()) + ");" + - "delete window.traceData;")); + return TracingController::GetInstance()->EnableRecording( + category_filter_string, + static_cast<TracingController::Options>(tracing_options), + base::Bind(&OnRecordingEnabledAck, callback)); } -void TracingMessageHandler::OnSaveTraceFile(const base::ListValue* list) { - // Only allow a single dialog at a time. - if (select_trace_file_dialog_.get()) - return; - - DCHECK_EQ(1U, list->GetSize()); - - 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(), - base::FilePath(), - NULL, - 0, - base::FilePath::StringType(), - web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), - NULL); +void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) { + base::RefCountedString* res = new base::RefCountedString(); + callback.Run(res); } -void TracingMessageHandler::SaveTraceFileComplete() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - web_ui()->CallJavascriptFunction("tracingController.onSaveTraceFileComplete"); +void OnTraceBufferPercentFullResult( + const WebUIDataSource::GotDataCallback& callback, float result) { + std::string str = base::DoubleToString(result); + callback.Run(base::RefCountedString::TakeString(&str)); } -void TracingMessageHandler::OnBeginTracing(const base::ListValue* args) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_GE(args->GetSize(), (size_t) 2); - DCHECK_LE(args->GetSize(), (size_t) 3); - - 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); - - base::debug::TraceLog::Options options = - base::debug::TraceLog::RECORD_UNTIL_FULL; - if (args->GetSize() >= 3) { - std::string options_; - ok = args->GetString(2, &options_); - DCHECK(ok); - options = base::debug::TraceLog::TraceOptionsFromString(options_); - } - - 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, - options); - - 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 ReadRecordingResult(const WebUIDataSource::GotDataCallback& callback, + const base::FilePath& path) { + std::string tmp; + if (!base::ReadFileToString(path, &tmp)) + LOG(ERROR) << "Failed to read file " << path.value(); + base::DeleteFile(path, false); + callback.Run(base::RefCountedString::TakeString(&tmp)); } -void TracingMessageHandler::OnEndTracingAsync(const base::ListValue* list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // This is really us beginning to end tracing, rather than tracing being truly - // over. When this function yields, we expect to get some number of - // OnTraceDataCollected callbacks, which will append data to window.traceData. - // To set up for this, set window.traceData to the empty string. - web_ui()->GetWebContents()->GetRenderViewHost()-> - ExecuteJavascriptInWebFrame(string16(), - UTF8ToUTF16("window.traceData = '';")); - - // 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 BeginReadingRecordingResult( + const WebUIDataSource::GotDataCallback& callback, + const base::FilePath& path) { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(ReadRecordingResult, callback, path)); } -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 +bool OnBeginRequest(const std::string& path, + const WebUIDataSource::GotDataCallback& callback) { + if (path == "json/categories") { + TracingController::GetInstance()->GetCategories( + base::Bind(OnGotCategories, callback)); + return true; } - - RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost(); - rvh->ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16( - "tracingController.onEndTracingComplete(window.traceData);" - "delete window.traceData;")); -} - -void TracingMessageHandler::OnEndSystemTracingAck( - const scoped_refptr<base::RefCountedString>& events_str_ptr) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - web_ui()->CallJavascriptFunction( - "tracingController.onSystemTraceDataCollected", - *scoped_ptr<base::Value>(new base::StringValue(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)); - - std::string javascript; - javascript.reserve(trace_fragment->size() * 2); - javascript.append("window.traceData += \""); - base::JsonDoubleQuote(trace_fragment->data(), false, &javascript); - - // Intentionally append a , to the traceData. This technically causes all - // traceData that we pass back to JS to end with a comma, but that is actually - // something the JS side strips away anyway - javascript.append(",\";"); - - web_ui()->GetWebContents()->GetRenderViewHost()-> - ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(javascript)); -} - -void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - web_ui()->CallJavascriptFunction( - "tracingController.onRequestBufferPercentFullComplete", - *scoped_ptr<base::Value>(new base::FundamentalValue(percent_full))); -} - -void TracingMessageHandler::OnGetKnownCategories(const base::ListValue* list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!TraceController::GetInstance()->GetKnownCategoryGroupsAsync(this)) { - std::set<std::string> ret; - OnKnownCategoriesCollected(ret); + const char* beginRecordingPath = "json/begin_recording?"; + if (path.find(beginRecordingPath) == 0) { + std::string data = path.substr(strlen(beginRecordingPath)); + return OnBeginRecording(data, callback); } -} - -void TracingMessageHandler::OnKnownCategoriesCollected( - const std::set<std::string>& known_categories) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - scoped_ptr<base::ListValue> categories(new base::ListValue()); - for (std::set<std::string>::const_iterator iter = known_categories.begin(); - iter != known_categories.end(); - ++iter) { - categories->AppendString(*iter); + if (path == "json/get_buffer_percent_full") { + return TracingController::GetInstance()->GetTraceBufferPercentFull( + base::Bind(OnTraceBufferPercentFullResult, callback)); } - - web_ui()->CallJavascriptFunction( - "tracingController.onKnownCategoriesCollected", *categories); + if (path == "json/end_recording") { + return TracingController::GetInstance()->DisableRecording( + base::FilePath(), base::Bind(BeginReadingRecordingResult, callback)); + } + if (StartsWithASCII(path, "json/", true)) + LOG(ERROR) << "Unhandled request to " << path; + return false; } } // namespace @@ -552,12 +155,16 @@ void TracingMessageHandler::OnKnownCategoriesCollected( //////////////////////////////////////////////////////////////////////////////// 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()); + + WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost); + source->SetJsonPath("strings.js"); + source->SetDefaultResource(IDR_TRACING_HTML); + source->AddResourcePath("tracing.js", IDR_TRACING_JS); + source->SetRequestFilter(base::Bind(OnBeginRequest)); + WebUIDataSource::Add(browser_context, source); } } // namespace content |