Home | History | Annotate | Download | only in metrics
      1 // Copyright (c) 2012 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 "chrome/browser/metrics/tracking_synchronizer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/threading/thread.h"
     10 #include "base/tracked_objects.h"
     11 #include "chrome/browser/metrics/tracking_synchronizer_observer.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/browser/profiler_controller.h"
     14 #include "content/public/common/process_type.h"
     15 
     16 using base::TimeTicks;
     17 using content::BrowserThread;
     18 
     19 namespace {
     20 
     21 // Negative numbers are never used as sequence numbers.  We explicitly pick a
     22 // negative number that is "so negative" that even when we add one (as is done
     23 // when we generated the next sequence number) that it will still be negative.
     24 // We have code that handles wrapping around on an overflow into negative
     25 // territory.
     26 const int kNeverUsableSequenceNumber = -2;
     27 
     28 // This singleton instance should be started during the single threaded
     29 // portion of main(). It initializes globals to provide support for all future
     30 // calls. This object is created on the UI thread, and it is destroyed after
     31 // all the other threads have gone away. As a result, it is ok to call it
     32 // from the UI thread, or for about:profiler.
     33 static chrome_browser_metrics::TrackingSynchronizer* g_tracking_synchronizer =
     34     NULL;
     35 
     36 }  // anonymous namespace
     37 
     38 namespace chrome_browser_metrics {
     39 
     40 // The "RequestContext" structure describes an individual request received
     41 // from the UI. All methods are accessible on UI thread.
     42 class TrackingSynchronizer::RequestContext {
     43  public:
     44   // A map from sequence_number_ to the actual RequestContexts.
     45   typedef std::map<int, RequestContext*> RequestContextMap;
     46 
     47   RequestContext(
     48       const base::WeakPtr<TrackingSynchronizerObserver>& callback_object,
     49       int sequence_number)
     50       : callback_object_(callback_object),
     51         sequence_number_(sequence_number),
     52         received_process_group_count_(0),
     53         processes_pending_(0) {
     54   }
     55   ~RequestContext() {}
     56 
     57   void SetReceivedProcessGroupCount(bool done) {
     58     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     59     received_process_group_count_ = done;
     60   }
     61 
     62   // Methods for book keeping of processes_pending_.
     63   void IncrementProcessesPending() {
     64     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     65     ++processes_pending_;
     66   }
     67 
     68   void AddProcessesPending(int processes_pending) {
     69     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     70     processes_pending_ += processes_pending;
     71   }
     72 
     73   void DecrementProcessesPending() {
     74     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     75     --processes_pending_;
     76   }
     77 
     78   // Records that we are waiting for one less tracking data from a process for
     79   // the given sequence number. If |received_process_group_count_| and
     80   // |processes_pending_| are zero, then delete the current object by calling
     81   // Unregister.
     82   void DeleteIfAllDone() {
     83     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     84 
     85     if (processes_pending_ <= 0 && received_process_group_count_)
     86       RequestContext::Unregister(sequence_number_);
     87   }
     88 
     89   // Register |callback_object| in |outstanding_requests_| map for the given
     90   // |sequence_number|.
     91   static RequestContext* Register(
     92       int sequence_number,
     93       const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
     94     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     95 
     96     RequestContext* request = new RequestContext(
     97         callback_object, sequence_number);
     98     outstanding_requests_.Get()[sequence_number] = request;
     99 
    100     return request;
    101   }
    102 
    103   // Find the |RequestContext| in |outstanding_requests_| map for the given
    104   // |sequence_number|.
    105   static RequestContext* GetRequestContext(int sequence_number) {
    106     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    107 
    108     RequestContextMap::iterator it =
    109         outstanding_requests_.Get().find(sequence_number);
    110     if (it == outstanding_requests_.Get().end())
    111       return NULL;
    112 
    113     RequestContext* request = it->second;
    114     DCHECK_EQ(sequence_number, request->sequence_number_);
    115     return request;
    116   }
    117 
    118   // Delete the entry for the given |sequence_number| from
    119   // |outstanding_requests_| map. This method is called when all changes have
    120   // been acquired, or when the wait time expires (whichever is sooner).
    121   static void Unregister(int sequence_number) {
    122     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    123 
    124     RequestContextMap::iterator it =
    125         outstanding_requests_.Get().find(sequence_number);
    126     if (it == outstanding_requests_.Get().end())
    127       return;
    128 
    129     RequestContext* request = it->second;
    130     DCHECK_EQ(sequence_number, request->sequence_number_);
    131     bool received_process_group_count = request->received_process_group_count_;
    132     int unresponsive_processes = request->processes_pending_;
    133 
    134     if (request->callback_object_.get())
    135       request->callback_object_->FinishedReceivingProfilerData();
    136 
    137     delete request;
    138     outstanding_requests_.Get().erase(it);
    139 
    140     UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
    141                           received_process_group_count);
    142     UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
    143                          unresponsive_processes);
    144   }
    145 
    146   // Delete all the entries in |outstanding_requests_| map.
    147   static void OnShutdown() {
    148     // Just in case we have any pending tasks, clear them out.
    149     while (!outstanding_requests_.Get().empty()) {
    150       RequestContextMap::iterator it = outstanding_requests_.Get().begin();
    151       delete it->second;
    152       outstanding_requests_.Get().erase(it);
    153     }
    154   }
    155 
    156   // Requests are made to asynchronously send data to the |callback_object_|.
    157   base::WeakPtr<TrackingSynchronizerObserver> callback_object_;
    158 
    159   // The sequence number used by the most recent update request to contact all
    160   // processes.
    161   int sequence_number_;
    162 
    163   // Indicates if we have received all pending processes count.
    164   bool received_process_group_count_;
    165 
    166   // The number of pending processes (browser, all renderer processes and
    167   // browser child processes) that have not yet responded to requests.
    168   int processes_pending_;
    169 
    170   // Map of all outstanding RequestContexts, from sequence_number_ to
    171   // RequestContext.
    172   static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
    173 };
    174 
    175 // static
    176 base::LazyInstance
    177     <TrackingSynchronizer::RequestContext::RequestContextMap>::Leaky
    178         TrackingSynchronizer::RequestContext::outstanding_requests_ =
    179             LAZY_INSTANCE_INITIALIZER;
    180 
    181 // TrackingSynchronizer methods and members.
    182 
    183 TrackingSynchronizer::TrackingSynchronizer()
    184     : last_used_sequence_number_(kNeverUsableSequenceNumber) {
    185   DCHECK(!g_tracking_synchronizer);
    186   g_tracking_synchronizer = this;
    187   content::ProfilerController::GetInstance()->Register(this);
    188 }
    189 
    190 TrackingSynchronizer::~TrackingSynchronizer() {
    191   content::ProfilerController::GetInstance()->Unregister(this);
    192 
    193   // Just in case we have any pending tasks, clear them out.
    194   RequestContext::OnShutdown();
    195 
    196   g_tracking_synchronizer = NULL;
    197 }
    198 
    199 // static
    200 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
    201     const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
    202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    203 
    204   if (!g_tracking_synchronizer) {
    205     // System teardown is happening.
    206     return;
    207   }
    208 
    209   int sequence_number = g_tracking_synchronizer->RegisterAndNotifyAllProcesses(
    210       callback_object);
    211 
    212   // Post a task that would be called after waiting for wait_time.  This acts
    213   // as a watchdog, to cancel the requests for non-responsive processes.
    214   BrowserThread::PostDelayedTask(
    215       BrowserThread::UI, FROM_HERE,
    216       base::Bind(&RequestContext::Unregister, sequence_number),
    217       base::TimeDelta::FromMinutes(1));
    218 }
    219 
    220 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
    221                                               int pending_processes,
    222                                               bool end) {
    223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    224 
    225   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
    226   if (!request)
    227     return;
    228   request->AddProcessesPending(pending_processes);
    229   request->SetReceivedProcessGroupCount(end);
    230   request->DeleteIfAllDone();
    231 }
    232 
    233 void TrackingSynchronizer::OnProfilerDataCollected(
    234     int sequence_number,
    235     const tracked_objects::ProcessDataSnapshot& profiler_data,
    236     int process_type) {
    237   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    238   DecrementPendingProcessesAndSendData(sequence_number, profiler_data,
    239                                        process_type);
    240 }
    241 
    242 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
    243     const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
    244   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    245 
    246   int sequence_number = GetNextAvailableSequenceNumber();
    247 
    248   RequestContext* request =
    249       RequestContext::Register(sequence_number, callback_object);
    250 
    251   // Increment pending process count for sending browser's profiler data.
    252   request->IncrementProcessesPending();
    253 
    254   // Get profiler data from renderer and browser child processes.
    255   content::ProfilerController::GetInstance()->GetProfilerData(sequence_number);
    256 
    257   // Send profiler_data from browser process.
    258   tracked_objects::ProcessDataSnapshot process_data;
    259   tracked_objects::ThreadData::Snapshot(false, &process_data);
    260   DecrementPendingProcessesAndSendData(sequence_number, process_data,
    261                                        content::PROCESS_TYPE_BROWSER);
    262 
    263   return sequence_number;
    264 }
    265 
    266 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
    267     int sequence_number,
    268     const tracked_objects::ProcessDataSnapshot& profiler_data,
    269     int process_type) {
    270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    271 
    272   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
    273   if (!request)
    274     return;
    275 
    276   if (request->callback_object_.get()) {
    277     request->callback_object_
    278         ->ReceivedProfilerData(profiler_data, process_type);
    279   }
    280 
    281   // Delete request if we have heard back from all child processes.
    282   request->DecrementProcessesPending();
    283   request->DeleteIfAllDone();
    284 }
    285 
    286 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
    287   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    288 
    289   ++last_used_sequence_number_;
    290 
    291   // Watch out for wrapping to a negative number.
    292   if (last_used_sequence_number_ < 0)
    293     last_used_sequence_number_ = 1;
    294   return last_used_sequence_number_;
    295 }
    296 
    297 }  // namespace chrome_browser_metrics
    298