blob: 480481fdb74b1151f55269cdc6324f1766721e49 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/tracing/tracing_controller_impl.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/string_escape.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/tracing/trace_message_filter.h"
#include "content/browser/tracing/tracing_ui.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/common/content_switches.h"
#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#endif
#if defined(OS_WIN)
#include "content/browser/tracing/etw_system_event_consumer_win.h"
#endif
using base::trace_event::TraceLog;
using base::trace_event::TraceOptions;
using base::trace_event::CategoryFilter;
namespace content {
namespace {
base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
LAZY_INSTANCE_INITIALIZER;
class FileTraceDataSink : public TracingController::TraceDataSink {
public:
explicit FileTraceDataSink(const base::FilePath& trace_file_path,
const base::Closure& callback)
: file_path_(trace_file_path),
completion_callback_(callback),
file_(NULL) {}
void AddTraceChunk(const std::string& chunk) override {
std::string tmp = chunk;
scoped_refptr<base::RefCountedString> chunk_ptr =
base::RefCountedString::TakeString(&tmp);
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(
&FileTraceDataSink::AddTraceChunkOnFileThread, this, chunk_ptr));
}
void SetSystemTrace(const std::string& data) override {
system_trace_ = data;
}
void Close() override {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&FileTraceDataSink::CloseOnFileThread, this));
}
private:
~FileTraceDataSink() override { DCHECK(file_ == NULL); }
void AddTraceChunkOnFileThread(
const scoped_refptr<base::RefCountedString> chunk) {
if (file_ != NULL)
fputc(',', file_);
else if (!OpenFileIfNeededOnFileThread())
return;
ignore_result(fwrite(chunk->data().c_str(), strlen(chunk->data().c_str()),
1, file_));
}
bool OpenFileIfNeededOnFileThread() {
if (file_ != NULL)
return true;
file_ = base::OpenFile(file_path_, "w");
if (file_ == NULL) {
LOG(ERROR) << "Failed to open " << file_path_.value();
return false;
}
const char preamble[] = "{\"traceEvents\": [";
ignore_result(fwrite(preamble, strlen(preamble), 1, file_));
return true;
}
void CloseOnFileThread() {
if (OpenFileIfNeededOnFileThread()) {
fputc(']', file_);
if (!system_trace_.empty()) {
const char systemTraceEvents[] = ",\"systemTraceEvents\": ";
ignore_result(fwrite(systemTraceEvents, strlen(systemTraceEvents),
1, file_));
ignore_result(fwrite(system_trace_.c_str(),
strlen(system_trace_.c_str()), 1, file_));
}
fputc('}', file_);
base::CloseFile(file_);
file_ = NULL;
}
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&FileTraceDataSink::FinalizeOnUIThread, this));
}
void FinalizeOnUIThread() { completion_callback_.Run(); }
base::FilePath file_path_;
base::Closure completion_callback_;
FILE* file_;
std::string system_trace_;
DISALLOW_COPY_AND_ASSIGN(FileTraceDataSink);
};
class StringTraceDataSink : public TracingController::TraceDataSink {
public:
typedef base::Callback<void(base::RefCountedString*)> CompletionCallback;
explicit StringTraceDataSink(CompletionCallback callback)
: completion_callback_(callback) {}
// TracingController::TraceDataSink implementation
void AddTraceChunk(const std::string& chunk) override {
if (!trace_.empty())
trace_ += ",";
trace_ += chunk;
}
void SetSystemTrace(const std::string& data) override {
system_trace_ = data;
}
void Close() override {
std::string result = "{\"traceEvents\":[" + trace_ + "]";
if (!system_trace_.empty())
result += ",\"systemTraceEvents\": " + system_trace_;
result += "}";
scoped_refptr<base::RefCountedString> str =
base::RefCountedString::TakeString(&result);
completion_callback_.Run(str.get());
}
private:
~StringTraceDataSink() override {}
std::string trace_;
std::string system_trace_;
CompletionCallback completion_callback_;
DISALLOW_COPY_AND_ASSIGN(StringTraceDataSink);
};
} // namespace
TracingController* TracingController::GetInstance() {
return TracingControllerImpl::GetInstance();
}
TracingControllerImpl::TracingControllerImpl()
: pending_disable_recording_ack_count_(0),
pending_capture_monitoring_snapshot_ack_count_(0),
pending_trace_log_status_ack_count_(0),
maximum_trace_buffer_usage_(0),
approximate_event_count_(0),
// Tracing may have been enabled by ContentMainRunner if kTraceStartup
// is specified in command line.
#if defined(OS_CHROMEOS) || defined(OS_WIN)
is_system_tracing_(false),
#endif
is_recording_(TraceLog::GetInstance()->IsEnabled()),
is_monitoring_(false) {
}
TracingControllerImpl::~TracingControllerImpl() {
// This is a Leaky instance.
NOTREACHED();
}
TracingControllerImpl* TracingControllerImpl::GetInstance() {
return g_controller.Pointer();
}
bool TracingControllerImpl::GetCategories(
const GetCategoriesDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Known categories come back from child processes with the EndTracingAck
// message. So to get known categories, just begin and end tracing immediately
// afterwards. This will ping all the child processes for categories.
pending_get_categories_done_callback_ = callback;
if (!EnableRecording(
CategoryFilter("*"), TraceOptions(), EnableRecordingDoneCallback())) {
pending_get_categories_done_callback_.Reset();
return false;
}
bool ok = DisableRecording(NULL);
DCHECK(ok);
return true;
}
void TracingControllerImpl::SetEnabledOnFileThread(
const CategoryFilter& category_filter,
int mode,
const TraceOptions& trace_options,
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
TraceLog::GetInstance()->SetEnabled(
category_filter, static_cast<TraceLog::Mode>(mode), trace_options);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}
void TracingControllerImpl::SetDisabledOnFileThread(
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
TraceLog::GetInstance()->SetDisabled();
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}
bool TracingControllerImpl::EnableRecording(
const CategoryFilter& category_filter,
const TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_enable_recording())
return false;
is_recording_ = true;
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
trace_options_ = trace_options;
if (trace_options.enable_systrace) {
#if defined(OS_CHROMEOS)
DCHECK(!is_system_tracing_);
chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
StartSystemTracing();
is_system_tracing_ = true;
#elif defined(OS_WIN)
DCHECK(!is_system_tracing_);
is_system_tracing_ =
EtwSystemEventConsumer::GetInstance()->StartSystemTracing();
#endif
}
base::Closure on_enable_recording_done_callback =
base::Bind(&TracingControllerImpl::OnEnableRecordingDone,
base::Unretained(this),
category_filter, trace_options, callback);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetEnabledOnFileThread,
base::Unretained(this),
category_filter,
base::trace_event::TraceLog::RECORDING_MODE,
trace_options,
on_enable_recording_done_callback));
return true;
}
void TracingControllerImpl::OnEnableRecordingDone(
const CategoryFilter& category_filter,
const TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Notify all child processes.
for (TraceMessageFilterSet::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();
}
bool TracingControllerImpl::DisableRecording(
const scoped_refptr<TraceDataSink>& trace_data_sink) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_disable_recording())
return false;
trace_data_sink_ = trace_data_sink;
trace_options_ = TraceOptions();
// Disable local trace early to avoid traces during end-tracing process from
// interfering with the process.
base::Closure on_disable_recording_done_callback = base::Bind(
&TracingControllerImpl::OnDisableRecordingDone, base::Unretained(this));
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
base::Unretained(this),
on_disable_recording_done_callback));
return true;
}
void TracingControllerImpl::OnDisableRecordingDone() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
// Count myself (local trace) in pending_disable_recording_ack_count_,
// acked below.
pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1;
pending_disable_recording_filters_ = trace_message_filters_;
#if defined(OS_CHROMEOS) || defined(OS_WIN)
if (is_system_tracing_) {
// Disable system tracing.
is_system_tracing_ = false;
++pending_disable_recording_ack_count_;
#if defined(OS_CHROMEOS)
scoped_refptr<base::TaskRunner> task_runner =
BrowserThread::GetBlockingPool();
chromeos::DBusThreadManager::Get()
->GetDebugDaemonClient()
->RequestStopSystemTracing(
task_runner,
base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
base::Unretained(this)));
#elif defined(OS_WIN)
EtwSystemEventConsumer::GetInstance()->StopSystemTracing(
base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
base::Unretained(this)));
#endif
}
#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
// Handle special case of zero child processes by immediately flushing the
// trace log. Once the flush has completed the caller will be notified that
// tracing has ended.
if (pending_disable_recording_ack_count_ == 1) {
// Flush asynchronously now, because we don't have any children to wait for.
TraceLog::GetInstance()->Flush(
base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
base::Unretained(this)));
}
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendEndTracing();
}
}
bool TracingControllerImpl::EnableMonitoring(
const CategoryFilter& category_filter,
const TraceOptions& trace_options,
const EnableMonitoringDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_enable_monitoring())
return false;
OnMonitoringStateChanged(true);
#if defined(OS_ANDROID)
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
trace_options_ = trace_options;
base::Closure on_enable_monitoring_done_callback =
base::Bind(&TracingControllerImpl::OnEnableMonitoringDone,
base::Unretained(this),
category_filter, trace_options, callback);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetEnabledOnFileThread,
base::Unretained(this),
category_filter,
base::trace_event::TraceLog::MONITORING_MODE,
trace_options,
on_enable_monitoring_done_callback));
return true;
}
void TracingControllerImpl::OnEnableMonitoringDone(
const CategoryFilter& category_filter,
const TraceOptions& trace_options,
const EnableMonitoringDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendEnableMonitoring(category_filter, trace_options);
}
if (!callback.is_null())
callback.Run();
}
bool TracingControllerImpl::DisableMonitoring(
const DisableMonitoringDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_disable_monitoring())
return false;
trace_options_ = TraceOptions();
base::Closure on_disable_monitoring_done_callback =
base::Bind(&TracingControllerImpl::OnDisableMonitoringDone,
base::Unretained(this), callback);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
base::Unretained(this),
on_disable_monitoring_done_callback));
return true;
}
scoped_refptr<TracingController::TraceDataSink>
TracingController::CreateStringSink(
const base::Callback<void(base::RefCountedString*)>& callback) {
return new StringTraceDataSink(callback);
}
scoped_refptr<TracingController::TraceDataSink>
TracingController::CreateFileSink(const base::FilePath& file_path,
const base::Closure& callback) {
return new FileTraceDataSink(file_path, callback);
}
void TracingControllerImpl::OnDisableMonitoringDone(
const DisableMonitoringDoneCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnMonitoringStateChanged(false);
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendDisableMonitoring();
}
if (!callback.is_null())
callback.Run();
}
void TracingControllerImpl::GetMonitoringStatus(
bool* out_enabled,
CategoryFilter* out_category_filter,
TraceOptions* out_trace_options) {
*out_enabled = is_monitoring_;
*out_category_filter = TraceLog::GetInstance()->GetCurrentCategoryFilter();
*out_trace_options = trace_options_;
}
bool TracingControllerImpl::CaptureMonitoringSnapshot(
const scoped_refptr<TraceDataSink>& monitoring_data_sink) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_disable_monitoring())
return false;
if (!monitoring_data_sink.get())
return false;
monitoring_data_sink_ = monitoring_data_sink;
// Count myself in pending_capture_monitoring_snapshot_ack_count_,
// acked below.
pending_capture_monitoring_snapshot_ack_count_ =
trace_message_filters_.size() + 1;
pending_capture_monitoring_filters_ = trace_message_filters_;
// Handle special case of zero child processes by immediately flushing the
// trace log. Once the flush has completed the caller will be notified that
// the capture snapshot has ended.
if (pending_capture_monitoring_snapshot_ack_count_ == 1) {
// Flush asynchronously now, because we don't have any children to wait for.
TraceLog::GetInstance()->FlushButLeaveBufferIntact(
base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected,
base::Unretained(this)));
}
// Notify all child processes.
for (TraceMessageFilterSet::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::GetTraceBufferUsage(
const GetTraceBufferUsageCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!can_get_trace_buffer_usage() || callback.is_null())
return false;
pending_trace_buffer_usage_callback_ = callback;
// Count myself in pending_trace_log_status_ack_count_, acked below.
pending_trace_log_status_ack_count_ = trace_message_filters_.size() + 1;
pending_trace_log_status_filters_ = trace_message_filters_;
maximum_trace_buffer_usage_ = 0;
approximate_event_count_ = 0;
base::trace_event::TraceLogStatus status =
TraceLog::GetInstance()->GetStatus();
// Call OnTraceLogStatusReply unconditionally for the browser process.
// This will result in immediate execution of the callback if there are no
// child processes.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnTraceLogStatusReply,
base::Unretained(this), scoped_refptr<TraceMessageFilter>(),
status));
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendGetTraceLogStatus();
}
return true;
}
bool TracingControllerImpl::SetWatchEvent(
const std::string& category_name,
const std::string& event_name,
const WatchEventCallback& callback) {
DCHECK_CURRENTLY_ON(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 (TraceMessageFilterSet::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_CURRENTLY_ON(BrowserThread::UI);
if (!can_cancel_watch_event())
return false;
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendCancelWatchEvent();
}
watch_event_callback_.Reset();
return true;
}
void TracingControllerImpl::AddTraceMessageFilter(
TraceMessageFilter* trace_message_filter) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::AddTraceMessageFilter,
base::Unretained(this),
make_scoped_refptr(trace_message_filter)));
return;
}
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(),
TraceLog::GetInstance()->GetCurrentTraceOptions());
}
if (can_disable_monitoring()) {
trace_message_filter->SendEnableMonitoring(
TraceLog::GetInstance()->GetCurrentCategoryFilter(),
TraceLog::GetInstance()->GetCurrentTraceOptions());
}
}
void TracingControllerImpl::RemoveTraceMessageFilter(
TraceMessageFilter* trace_message_filter) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::RemoveTraceMessageFilter,
base::Unretained(this),
make_scoped_refptr(trace_message_filter)));
return;
}
// If a filter is removed while a response from that filter is pending then
// simulate the response. Otherwise the response count will be wrong and the
// completion callback will never be executed.
if (pending_disable_recording_ack_count_ > 0) {
TraceMessageFilterSet::const_iterator it =
pending_disable_recording_filters_.find(trace_message_filter);
if (it != pending_disable_recording_filters_.end()) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
base::Unretained(this),
make_scoped_refptr(trace_message_filter),
std::vector<std::string>()));
}
}
if (pending_capture_monitoring_snapshot_ack_count_ > 0) {
TraceMessageFilterSet::const_iterator it =
pending_capture_monitoring_filters_.find(trace_message_filter);
if (it != pending_capture_monitoring_filters_.end()) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
base::Unretained(this),
make_scoped_refptr(trace_message_filter)));
}
}
if (pending_trace_log_status_ack_count_ > 0) {
TraceMessageFilterSet::const_iterator it =
pending_trace_log_status_filters_.find(trace_message_filter);
if (it != pending_trace_log_status_filters_.end()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnTraceLogStatusReply,
base::Unretained(this),
make_scoped_refptr(trace_message_filter),
base::trace_event::TraceLogStatus()));
}
}
trace_message_filters_.erase(trace_message_filter);
}
void TracingControllerImpl::OnDisableRecordingAcked(
TraceMessageFilter* trace_message_filter,
const std::vector<std::string>& known_category_groups) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
base::Unretained(this),
make_scoped_refptr(trace_message_filter),
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_disable_recording_ack_count_ == 0)
return;
if (trace_message_filter &&
!pending_disable_recording_filters_.erase(trace_message_filter)) {
// The response from the specified message filter has already been received.
return;
}
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_disable_recording_ack_count_ != 0)
return;
// All acks (including from the subprocesses and the local trace) have been
// received.
is_recording_ = false;
// Trigger callback if one is set.
if (!pending_get_categories_done_callback_.is_null()) {
pending_get_categories_done_callback_.Run(known_category_groups_);
pending_get_categories_done_callback_.Reset();
} else if (trace_data_sink_.get()) {
trace_data_sink_->Close();
trace_data_sink_ = NULL;
}
}
#if defined(OS_CHROMEOS) || defined(OS_WIN)
void TracingControllerImpl::OnEndSystemTracingAcked(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (trace_data_sink_.get()) {
#if defined(OS_WIN)
// The Windows kernel events are kept into a JSon format stored as string
// and must not be escaped.
std::string json_string = events_str_ptr->data();
#else
std::string json_string =
base::GetQuotedJSONString(events_str_ptr->data());
#endif
trace_data_sink_->SetSystemTrace(json_string);
}
DCHECK(!is_system_tracing_);
std::vector<std::string> category_groups;
OnDisableRecordingAcked(NULL, category_groups);
}
#endif
void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked(
TraceMessageFilter* trace_message_filter) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
base::Unretained(this),
make_scoped_refptr(trace_message_filter)));
return;
}
if (pending_capture_monitoring_snapshot_ack_count_ == 0)
return;
if (trace_message_filter &&
!pending_capture_monitoring_filters_.erase(trace_message_filter)) {
// The response from the specified message filter has already been received.
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_data_sink_.get()) {
monitoring_data_sink_->Close();
monitoring_data_sink_ = NULL;
}
}
void TracingControllerImpl::OnTraceDataCollected(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
// OnTraceDataCollected may be called from any browser thread, either by the
// local event trace system or from child processes via TraceMessageFilter.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnTraceDataCollected,
base::Unretained(this), events_str_ptr));
return;
}
if (trace_data_sink_.get())
trace_data_sink_->AddTraceChunk(events_str_ptr->data());
}
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;
}
if (monitoring_data_sink_.get())
monitoring_data_sink_->AddTraceChunk(events_str_ptr->data());
}
void TracingControllerImpl::OnLocalTraceDataCollected(
const scoped_refptr<base::RefCountedString>& events_str_ptr,
bool has_more_events) {
if (events_str_ptr->data().size())
OnTraceDataCollected(events_str_ptr);
if (has_more_events)
return;
// Simulate an DisableRecordingAcked for the local trace.
std::vector<std::string> category_groups;
TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
OnDisableRecordingAcked(NULL, 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(NULL);
}
void TracingControllerImpl::OnTraceLogStatusReply(
TraceMessageFilter* trace_message_filter,
const base::trace_event::TraceLogStatus& status) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnTraceLogStatusReply,
base::Unretained(this),
make_scoped_refptr(trace_message_filter), status));
return;
}
if (pending_trace_log_status_ack_count_ == 0)
return;
if (trace_message_filter &&
!pending_trace_log_status_filters_.erase(trace_message_filter)) {
// The response from the specified message filter has already been received.
return;
}
float percent_full = static_cast<float>(
static_cast<double>(status.event_count) / status.event_capacity);
maximum_trace_buffer_usage_ =
std::max(maximum_trace_buffer_usage_, percent_full);
approximate_event_count_ += status.event_count;
if (--pending_trace_log_status_ack_count_ == 0) {
// Trigger callback if one is set.
pending_trace_buffer_usage_callback_.Run(maximum_trace_buffer_usage_,
approximate_event_count_);
pending_trace_buffer_usage_callback_.Reset();
}
}
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();
}
void TracingControllerImpl::RegisterTracingUI(TracingUI* tracing_ui) {
DCHECK(tracing_uis_.find(tracing_ui) == tracing_uis_.end());
tracing_uis_.insert(tracing_ui);
}
void TracingControllerImpl::UnregisterTracingUI(TracingUI* tracing_ui) {
std::set<TracingUI*>::iterator it = tracing_uis_.find(tracing_ui);
DCHECK(it != tracing_uis_.end());
tracing_uis_.erase(it);
}
void TracingControllerImpl::OnMonitoringStateChanged(bool is_monitoring) {
if (is_monitoring_ == is_monitoring)
return;
is_monitoring_ = is_monitoring;
#if !defined(OS_ANDROID)
for (std::set<TracingUI*>::iterator it = tracing_uis_.begin();
it != tracing_uis_.end(); it++) {
(*it)->OnMonitoringStateChanged(is_monitoring);
}
#endif
}
} // namespace content