diff options
Diffstat (limited to 'chromium/content/browser/tracing/tracing_controller_impl.cc')
-rw-r--r-- | chromium/content/browser/tracing/tracing_controller_impl.cc | 531 |
1 files changed, 454 insertions, 77 deletions
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 |