Home | History | Annotate | Download | only in tracing
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/tracing/tracing_controller_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/file_util.h"
     10 #include "base/json/string_escape.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "content/browser/tracing/trace_message_filter.h"
     13 #include "content/common/child_process_messages.h"
     14 #include "content/public/browser/browser_message_filter.h"
     15 #include "content/public/common/content_switches.h"
     16 
     17 using base::debug::TraceLog;
     18 
     19 namespace content {
     20 
     21 namespace {
     22 
     23 base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
     24     LAZY_INSTANCE_INITIALIZER;
     25 
     26 }  // namespace
     27 
     28 TracingController* TracingController::GetInstance() {
     29   return TracingControllerImpl::GetInstance();
     30 }
     31 
     32 class TracingControllerImpl::ResultFile {
     33  public:
     34   explicit ResultFile(const base::FilePath& path);
     35   void Write(const scoped_refptr<base::RefCountedString>& events_str_ptr) {
     36     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     37         base::Bind(&TracingControllerImpl::ResultFile::WriteTask,
     38                    base::Unretained(this), events_str_ptr));
     39   }
     40   void Close(const base::Closure& callback) {
     41     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     42         base::Bind(&TracingControllerImpl::ResultFile::CloseTask,
     43                    base::Unretained(this), callback));
     44   }
     45   const base::FilePath& path() const { return path_; }
     46 
     47  private:
     48   void OpenTask();
     49   void WriteTask(const scoped_refptr<base::RefCountedString>& events_str_ptr);
     50   void CloseTask(const base::Closure& callback);
     51 
     52   FILE* file_;
     53   base::FilePath path_;
     54   bool has_at_least_one_result_;
     55 
     56   DISALLOW_COPY_AND_ASSIGN(ResultFile);
     57 };
     58 
     59 TracingControllerImpl::ResultFile::ResultFile(const base::FilePath& path)
     60     : file_(NULL),
     61       path_(path),
     62       has_at_least_one_result_(false) {
     63   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     64       base::Bind(&TracingControllerImpl::ResultFile::OpenTask,
     65                  base::Unretained(this)));
     66 }
     67 
     68 void TracingControllerImpl::ResultFile::OpenTask() {
     69   if (path_.empty())
     70     base::CreateTemporaryFile(&path_);
     71   file_ = base::OpenFile(path_, "w");
     72   if (!file_) {
     73     LOG(ERROR) << "Failed to open " << path_.value();
     74     return;
     75   }
     76   const char* preamble = "{\"traceEvents\": [";
     77   size_t written = fwrite(preamble, strlen(preamble), 1, file_);
     78   DCHECK(written == 1);
     79 }
     80 
     81 void TracingControllerImpl::ResultFile::WriteTask(
     82     const scoped_refptr<base::RefCountedString>& events_str_ptr) {
     83   if (!file_)
     84     return;
     85 
     86   // If there is already a result in the file, then put a commma
     87   // before the next batch of results.
     88   if (has_at_least_one_result_) {
     89     size_t written = fwrite(",", 1, 1, file_);
     90     DCHECK(written == 1);
     91   }
     92   has_at_least_one_result_ = true;
     93   size_t written = fwrite(events_str_ptr->data().c_str(),
     94                           events_str_ptr->data().size(), 1,
     95                           file_);
     96   DCHECK(written == 1);
     97 }
     98 
     99 void TracingControllerImpl::ResultFile::CloseTask(
    100     const base::Closure& callback) {
    101   if (!file_)
    102     return;
    103 
    104   const char* trailout = "]}";
    105   size_t written = fwrite(trailout, strlen(trailout), 1, file_);
    106   DCHECK(written == 1);
    107   base::CloseFile(file_);
    108   file_ = NULL;
    109 
    110   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
    111 }
    112 
    113 
    114 TracingControllerImpl::TracingControllerImpl() :
    115     pending_disable_recording_ack_count_(0),
    116     pending_capture_monitoring_snapshot_ack_count_(0),
    117     pending_trace_buffer_percent_full_ack_count_(0),
    118     maximum_trace_buffer_percent_full_(0),
    119     // Tracing may have been enabled by ContentMainRunner if kTraceStartup
    120     // is specified in command line.
    121     is_recording_(TraceLog::GetInstance()->IsEnabled()),
    122     is_monitoring_(false) {
    123 }
    124 
    125 TracingControllerImpl::~TracingControllerImpl() {
    126   // This is a Leaky instance.
    127   NOTREACHED();
    128 }
    129 
    130 TracingControllerImpl* TracingControllerImpl::GetInstance() {
    131   return g_controller.Pointer();
    132 }
    133 
    134 bool TracingControllerImpl::GetCategories(
    135     const GetCategoriesDoneCallback& callback) {
    136   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    137 
    138   // Known categories come back from child processes with the EndTracingAck
    139   // message. So to get known categories, just begin and end tracing immediately
    140   // afterwards. This will ping all the child processes for categories.
    141   pending_get_categories_done_callback_ = callback;
    142   if (!EnableRecording("*", TracingController::Options(),
    143                        EnableRecordingDoneCallback())) {
    144     pending_get_categories_done_callback_.Reset();
    145     return false;
    146   }
    147 
    148   bool ok = DisableRecording(base::FilePath(), TracingFileResultCallback());
    149   DCHECK(ok);
    150   return true;
    151 }
    152 
    153 bool TracingControllerImpl::EnableRecording(
    154     const std::string& category_filter,
    155     TracingController::Options options,
    156     const EnableRecordingDoneCallback& callback) {
    157   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    158 
    159   if (!can_enable_recording())
    160     return false;
    161 
    162 #if defined(OS_ANDROID)
    163   if (pending_get_categories_done_callback_.is_null())
    164     TraceLog::GetInstance()->AddClockSyncMetadataEvent();
    165 #endif
    166 
    167   TraceLog::Options trace_options = (options & RECORD_CONTINUOUSLY) ?
    168       TraceLog::RECORD_CONTINUOUSLY : TraceLog::RECORD_UNTIL_FULL;
    169   if (options & ENABLE_SAMPLING) {
    170     trace_options = static_cast<TraceLog::Options>(
    171         trace_options | TraceLog::ENABLE_SAMPLING);
    172   }
    173   // TODO(haraken): How to handle ENABLE_SYSTRACE?
    174 
    175   TraceLog::GetInstance()->SetEnabled(
    176       base::debug::CategoryFilter(category_filter), trace_options);
    177   is_recording_ = true;
    178 
    179   // Notify all child processes.
    180   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    181       it != trace_message_filters_.end(); ++it) {
    182     it->get()->SendBeginTracing(category_filter, trace_options);
    183   }
    184 
    185   if (!callback.is_null())
    186     callback.Run();
    187   return true;
    188 }
    189 
    190 bool TracingControllerImpl::DisableRecording(
    191     const base::FilePath& result_file_path,
    192     const TracingFileResultCallback& callback) {
    193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    194 
    195   if (!can_disable_recording())
    196     return false;
    197 
    198   pending_disable_recording_done_callback_ = callback;
    199 
    200   // Disable local trace early to avoid traces during end-tracing process from
    201   // interfering with the process.
    202   TraceLog::GetInstance()->SetDisabled();
    203 
    204 #if defined(OS_ANDROID)
    205   if (pending_get_categories_done_callback_.is_null())
    206     TraceLog::GetInstance()->AddClockSyncMetadataEvent();
    207 #endif
    208 
    209   if (!callback.is_null() || !result_file_path.empty())
    210     result_file_.reset(new ResultFile(result_file_path));
    211 
    212   // Count myself (local trace) in pending_disable_recording_ack_count_,
    213   // acked below.
    214   pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1;
    215 
    216   // Handle special case of zero child processes by immediately telling the
    217   // caller that tracing has ended. Use asynchronous OnDisableRecordingAcked
    218   // to avoid recursive call back to the caller.
    219   if (pending_disable_recording_ack_count_ == 1) {
    220     // Ack asynchronously now, because we don't have any children to wait for.
    221     std::vector<std::string> category_groups;
    222     TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
    223     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    224         base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
    225                    base::Unretained(this), category_groups));
    226   }
    227 
    228   // Notify all child processes.
    229   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    230       it != trace_message_filters_.end(); ++it) {
    231     it->get()->SendEndTracing();
    232   }
    233   return true;
    234 }
    235 
    236 bool TracingControllerImpl::EnableMonitoring(
    237     const std::string& category_filter,
    238     TracingController::Options options,
    239     const EnableMonitoringDoneCallback& callback) {
    240   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    241 
    242   if (!can_enable_monitoring())
    243     return false;
    244   is_monitoring_ = true;
    245 
    246 #if defined(OS_ANDROID)
    247   TraceLog::GetInstance()->AddClockSyncMetadataEvent();
    248 #endif
    249 
    250   int monitoring_tracing_options = 0;
    251   if (options & ENABLE_SAMPLING)
    252     monitoring_tracing_options |= base::debug::TraceLog::MONITOR_SAMPLING;
    253 
    254   TraceLog::GetInstance()->SetEnabled(
    255       base::debug::CategoryFilter(category_filter),
    256       static_cast<TraceLog::Options>(monitoring_tracing_options));
    257 
    258   // Notify all child processes.
    259   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    260       it != trace_message_filters_.end(); ++it) {
    261     it->get()->SendEnableMonitoring(category_filter,
    262         static_cast<TraceLog::Options>(monitoring_tracing_options));
    263   }
    264 
    265   if (!callback.is_null())
    266     callback.Run();
    267   return true;
    268 }
    269 
    270 bool TracingControllerImpl::DisableMonitoring(
    271     const DisableMonitoringDoneCallback& callback) {
    272   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    273 
    274   if (!can_disable_monitoring())
    275     return false;
    276   is_monitoring_ = false;
    277 
    278   TraceLog::GetInstance()->SetDisabled();
    279 
    280   // Notify all child processes.
    281   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    282       it != trace_message_filters_.end(); ++it) {
    283     it->get()->SendDisableMonitoring();
    284   }
    285 
    286   if (!callback.is_null())
    287     callback.Run();
    288   return true;
    289 }
    290 
    291 void TracingControllerImpl::GetMonitoringStatus(
    292     bool* out_enabled,
    293     std::string* out_category_filter,
    294     TracingController::Options* out_options) {
    295   NOTIMPLEMENTED();
    296 }
    297 
    298 bool TracingControllerImpl::CaptureMonitoringSnapshot(
    299     const base::FilePath& result_file_path,
    300     const TracingFileResultCallback& callback) {
    301   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    302 
    303   if (!can_disable_monitoring())
    304     return false;
    305 
    306   if (callback.is_null() && result_file_path.empty())
    307     return false;
    308 
    309   pending_capture_monitoring_snapshot_done_callback_ = callback;
    310   monitoring_snapshot_file_.reset(new ResultFile(result_file_path));
    311 
    312   // Count myself in pending_capture_monitoring_snapshot_ack_count_,
    313   // acked below.
    314   pending_capture_monitoring_snapshot_ack_count_ =
    315       trace_message_filters_.size() + 1;
    316 
    317   // Handle special case of zero child processes by immediately telling the
    318   // caller that capturing snapshot has ended. Use asynchronous
    319   // OnCaptureMonitoringSnapshotAcked to avoid recursive call back to the
    320   // caller.
    321   if (pending_capture_monitoring_snapshot_ack_count_ == 1) {
    322     // Ack asynchronously now, because we don't have any children to wait for.
    323     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    324         base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
    325                    base::Unretained(this)));
    326   }
    327 
    328   // Notify all child processes.
    329   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    330       it != trace_message_filters_.end(); ++it) {
    331     it->get()->SendCaptureMonitoringSnapshot();
    332   }
    333 
    334 #if defined(OS_ANDROID)
    335   TraceLog::GetInstance()->AddClockSyncMetadataEvent();
    336 #endif
    337 
    338   return true;
    339 }
    340 
    341 bool TracingControllerImpl::GetTraceBufferPercentFull(
    342     const GetTraceBufferPercentFullCallback& callback) {
    343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    344 
    345   if (!can_get_trace_buffer_percent_full() || callback.is_null())
    346     return false;
    347 
    348   pending_trace_buffer_percent_full_callback_ = callback;
    349 
    350   // Count myself in pending_trace_buffer_percent_full_ack_count_, acked below.
    351   pending_trace_buffer_percent_full_ack_count_ =
    352       trace_message_filters_.size() + 1;
    353   maximum_trace_buffer_percent_full_ = 0;
    354 
    355   // Handle special case of zero child processes.
    356   if (pending_trace_buffer_percent_full_ack_count_ == 1) {
    357     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    358         base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
    359                    base::Unretained(this),
    360                    TraceLog::GetInstance()->GetBufferPercentFull()));
    361   }
    362 
    363   // Notify all child processes.
    364   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    365       it != trace_message_filters_.end(); ++it) {
    366     it->get()->SendGetTraceBufferPercentFull();
    367   }
    368   return true;
    369 }
    370 
    371 bool TracingControllerImpl::SetWatchEvent(
    372     const std::string& category_name,
    373     const std::string& event_name,
    374     const WatchEventCallback& callback) {
    375   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    376 
    377   if (callback.is_null())
    378     return false;
    379 
    380   watch_category_name_ = category_name;
    381   watch_event_name_ = event_name;
    382   watch_event_callback_ = callback;
    383 
    384   TraceLog::GetInstance()->SetWatchEvent(
    385       category_name, event_name,
    386       base::Bind(&TracingControllerImpl::OnWatchEventMatched,
    387                  base::Unretained(this)));
    388 
    389   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    390       it != trace_message_filters_.end(); ++it) {
    391     it->get()->SendSetWatchEvent(category_name, event_name);
    392   }
    393   return true;
    394 }
    395 
    396 bool TracingControllerImpl::CancelWatchEvent() {
    397   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    398 
    399   if (!can_cancel_watch_event())
    400     return false;
    401 
    402   for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
    403       it != trace_message_filters_.end(); ++it) {
    404     it->get()->SendCancelWatchEvent();
    405   }
    406 
    407   watch_event_callback_.Reset();
    408   return true;
    409 }
    410 
    411 void TracingControllerImpl::AddTraceMessageFilter(
    412     TraceMessageFilter* trace_message_filter) {
    413   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    414     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    415         base::Bind(&TracingControllerImpl::AddTraceMessageFilter,
    416                    base::Unretained(this),
    417                    make_scoped_refptr(trace_message_filter)));
    418     return;
    419   }
    420 
    421   trace_message_filters_.insert(trace_message_filter);
    422   if (can_cancel_watch_event()) {
    423     trace_message_filter->SendSetWatchEvent(watch_category_name_,
    424                                             watch_event_name_);
    425   }
    426   if (can_disable_recording()) {
    427     trace_message_filter->SendBeginTracing(
    428         TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(),
    429         TraceLog::GetInstance()->trace_options());
    430   }
    431 }
    432 
    433 void TracingControllerImpl::RemoveTraceMessageFilter(
    434     TraceMessageFilter* trace_message_filter) {
    435   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    436     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    437         base::Bind(&TracingControllerImpl::RemoveTraceMessageFilter,
    438                    base::Unretained(this),
    439                    make_scoped_refptr(trace_message_filter)));
    440     return;
    441   }
    442 
    443   trace_message_filters_.erase(trace_message_filter);
    444 }
    445 
    446 void TracingControllerImpl::OnDisableRecordingAcked(
    447     const std::vector<std::string>& known_category_groups) {
    448   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    449     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    450         base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
    451                    base::Unretained(this), known_category_groups));
    452     return;
    453   }
    454 
    455   // Merge known_category_groups with known_category_groups_
    456   known_category_groups_.insert(known_category_groups.begin(),
    457                                 known_category_groups.end());
    458 
    459   if (pending_disable_recording_ack_count_ == 0)
    460     return;
    461 
    462   if (--pending_disable_recording_ack_count_ == 1) {
    463     // All acks from subprocesses have been received. Now flush the local trace.
    464     // During or after this call, our OnLocalTraceDataCollected will be
    465     // called with the last of the local trace data.
    466     TraceLog::GetInstance()->Flush(
    467         base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
    468                    base::Unretained(this)));
    469     return;
    470   }
    471 
    472   if (pending_disable_recording_ack_count_ != 0)
    473     return;
    474 
    475   // All acks (including from the subprocesses and the local trace) have been
    476   // received.
    477   is_recording_ = false;
    478 
    479   // Trigger callback if one is set.
    480   if (!pending_get_categories_done_callback_.is_null()) {
    481     pending_get_categories_done_callback_.Run(known_category_groups_);
    482     pending_get_categories_done_callback_.Reset();
    483   } else if (result_file_) {
    484     result_file_->Close(
    485         base::Bind(&TracingControllerImpl::OnResultFileClosed,
    486                    base::Unretained(this)));
    487   }
    488 }
    489 
    490 void TracingControllerImpl::OnResultFileClosed() {
    491   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    492 
    493   if (!result_file_)
    494     return;
    495 
    496   if (!pending_disable_recording_done_callback_.is_null()) {
    497     pending_disable_recording_done_callback_.Run(result_file_->path());
    498     pending_disable_recording_done_callback_.Reset();
    499   }
    500   result_file_.reset();
    501 }
    502 
    503 void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked() {
    504   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    505     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    506         base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
    507                    base::Unretained(this)));
    508     return;
    509   }
    510 
    511   if (pending_capture_monitoring_snapshot_ack_count_ == 0)
    512     return;
    513 
    514   if (--pending_capture_monitoring_snapshot_ack_count_ == 1) {
    515     // All acks from subprocesses have been received. Now flush the local trace.
    516     // During or after this call, our OnLocalMonitoringTraceDataCollected
    517     // will be called with the last of the local trace data.
    518     TraceLog::GetInstance()->FlushButLeaveBufferIntact(
    519         base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected,
    520                    base::Unretained(this)));
    521     return;
    522   }
    523 
    524   if (pending_capture_monitoring_snapshot_ack_count_ != 0)
    525     return;
    526 
    527   if (monitoring_snapshot_file_) {
    528     monitoring_snapshot_file_->Close(
    529         base::Bind(&TracingControllerImpl::OnMonitoringSnapshotFileClosed,
    530                    base::Unretained(this)));
    531   }
    532 }
    533 
    534 void TracingControllerImpl::OnMonitoringSnapshotFileClosed() {
    535   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    536 
    537   if (!monitoring_snapshot_file_)
    538     return;
    539 
    540   if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) {
    541     pending_capture_monitoring_snapshot_done_callback_.Run(
    542         monitoring_snapshot_file_->path());
    543     pending_capture_monitoring_snapshot_done_callback_.Reset();
    544   }
    545   monitoring_snapshot_file_.reset();
    546 }
    547 
    548 void TracingControllerImpl::OnTraceDataCollected(
    549     const scoped_refptr<base::RefCountedString>& events_str_ptr) {
    550   // OnTraceDataCollected may be called from any browser thread, either by the
    551   // local event trace system or from child processes via TraceMessageFilter.
    552   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    553     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    554         base::Bind(&TracingControllerImpl::OnTraceDataCollected,
    555                    base::Unretained(this), events_str_ptr));
    556     return;
    557   }
    558 
    559   if (result_file_)
    560     result_file_->Write(events_str_ptr);
    561 }
    562 
    563 void TracingControllerImpl::OnMonitoringTraceDataCollected(
    564     const scoped_refptr<base::RefCountedString>& events_str_ptr) {
    565   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    566     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    567         base::Bind(&TracingControllerImpl::OnMonitoringTraceDataCollected,
    568                    base::Unretained(this), events_str_ptr));
    569     return;
    570   }
    571 
    572   if (monitoring_snapshot_file_)
    573     monitoring_snapshot_file_->Write(events_str_ptr);
    574 }
    575 
    576 void TracingControllerImpl::OnLocalTraceDataCollected(
    577     const scoped_refptr<base::RefCountedString>& events_str_ptr,
    578     bool has_more_events) {
    579   if (events_str_ptr->data().size())
    580     OnTraceDataCollected(events_str_ptr);
    581 
    582   if (has_more_events)
    583     return;
    584 
    585   // Simulate an DisableRecordingAcked for the local trace.
    586   std::vector<std::string> category_groups;
    587   TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
    588   OnDisableRecordingAcked(category_groups);
    589 }
    590 
    591 void TracingControllerImpl::OnLocalMonitoringTraceDataCollected(
    592     const scoped_refptr<base::RefCountedString>& events_str_ptr,
    593     bool has_more_events) {
    594   if (events_str_ptr->data().size())
    595     OnMonitoringTraceDataCollected(events_str_ptr);
    596 
    597   if (has_more_events)
    598     return;
    599 
    600   // Simulate an CaptureMonitoringSnapshotAcked for the local trace.
    601   OnCaptureMonitoringSnapshotAcked();
    602 }
    603 
    604 void TracingControllerImpl::OnTraceBufferPercentFullReply(float percent_full) {
    605   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    606     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    607         base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
    608                    base::Unretained(this), percent_full));
    609     return;
    610   }
    611 
    612   if (pending_trace_buffer_percent_full_ack_count_ == 0)
    613     return;
    614 
    615   maximum_trace_buffer_percent_full_ =
    616       std::max(maximum_trace_buffer_percent_full_, percent_full);
    617 
    618   if (--pending_trace_buffer_percent_full_ack_count_ == 0) {
    619     // Trigger callback if one is set.
    620     pending_trace_buffer_percent_full_callback_.Run(
    621         maximum_trace_buffer_percent_full_);
    622     pending_trace_buffer_percent_full_callback_.Reset();
    623   }
    624 
    625   if (pending_trace_buffer_percent_full_ack_count_ == 1) {
    626     // The last ack represents local trace, so we need to ack it now. Note that
    627     // this code only executes if there were child processes.
    628     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    629         base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
    630                    base::Unretained(this),
    631                    TraceLog::GetInstance()->GetBufferPercentFull()));
    632   }
    633 }
    634 
    635 void TracingControllerImpl::OnWatchEventMatched() {
    636   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    637     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    638         base::Bind(&TracingControllerImpl::OnWatchEventMatched,
    639                    base::Unretained(this)));
    640     return;
    641   }
    642 
    643   if (!watch_event_callback_.is_null())
    644     watch_event_callback_.Run();
    645 }
    646 
    647 }  // namespace content
    648