Home | History | Annotate | Download | only in browser
      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 "content/browser/histogram_synchronizer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/logging.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/metrics/histogram_delta_serialization.h"
     12 #include "base/pickle.h"
     13 #include "base/threading/thread.h"
     14 #include "base/threading/thread_restrictions.h"
     15 #include "content/browser/histogram_controller.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/histogram_fetcher.h"
     18 #include "content/public/common/content_constants.h"
     19 
     20 using base::Time;
     21 using base::TimeDelta;
     22 using base::TimeTicks;
     23 
     24 namespace {
     25 
     26 // Negative numbers are never used as sequence numbers.  We explicitly pick a
     27 // negative number that is "so negative" that even when we add one (as is done
     28 // when we generated the next sequence number) that it will still be negative.
     29 // We have code that handles wrapping around on an overflow into negative
     30 // territory.
     31 static const int kNeverUsableSequenceNumber = -2;
     32 
     33 }  // anonymous namespace
     34 
     35 namespace content {
     36 
     37 // The "RequestContext" structure describes an individual request received from
     38 // the UI. All methods are accessible on UI thread.
     39 class HistogramSynchronizer::RequestContext {
     40  public:
     41   // A map from sequence_number_ to the actual RequestContexts.
     42   typedef std::map<int, RequestContext*> RequestContextMap;
     43 
     44   RequestContext(const base::Closure& callback, int sequence_number)
     45       : callback_(callback),
     46         sequence_number_(sequence_number),
     47         received_process_group_count_(0),
     48         processes_pending_(0) {
     49   }
     50   ~RequestContext() {}
     51 
     52   void SetReceivedProcessGroupCount(bool done) {
     53     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     54     received_process_group_count_ = done;
     55   }
     56 
     57   // Methods for book keeping of processes_pending_.
     58   void AddProcessesPending(int processes_pending) {
     59     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     60     processes_pending_ += processes_pending;
     61   }
     62 
     63   void DecrementProcessesPending() {
     64     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     65     --processes_pending_;
     66   }
     67 
     68   // Records that we are waiting for one less histogram data from a process for
     69   // the given sequence number. If |received_process_group_count_| and
     70   // |processes_pending_| are zero, then delete the current object by calling
     71   // Unregister.
     72   void DeleteIfAllDone() {
     73     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     74 
     75     if (processes_pending_ <= 0 && received_process_group_count_)
     76       RequestContext::Unregister(sequence_number_);
     77   }
     78 
     79   // Register |callback| in |outstanding_requests_| map for the given
     80   // |sequence_number|.
     81   static void Register(const base::Closure& callback, int sequence_number) {
     82     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     83 
     84     RequestContext* request = new RequestContext(callback, sequence_number);
     85     outstanding_requests_.Get()[sequence_number] = request;
     86   }
     87 
     88   // Find the |RequestContext| in |outstanding_requests_| map for the given
     89   // |sequence_number|.
     90   static RequestContext* GetRequestContext(int sequence_number) {
     91     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     92 
     93     RequestContextMap::iterator it =
     94         outstanding_requests_.Get().find(sequence_number);
     95     if (it == outstanding_requests_.Get().end())
     96       return NULL;
     97 
     98     RequestContext* request = it->second;
     99     DCHECK_EQ(sequence_number, request->sequence_number_);
    100     return request;
    101   }
    102 
    103   // Delete the entry for the given |sequence_number| from
    104   // |outstanding_requests_| map. This method is called when all changes have
    105   // been acquired, or when the wait time expires (whichever is sooner).
    106   static void Unregister(int sequence_number) {
    107     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    108 
    109     RequestContextMap::iterator it =
    110         outstanding_requests_.Get().find(sequence_number);
    111     if (it == outstanding_requests_.Get().end())
    112       return;
    113 
    114     RequestContext* request = it->second;
    115     DCHECK_EQ(sequence_number, request->sequence_number_);
    116     bool received_process_group_count = request->received_process_group_count_;
    117     int unresponsive_processes = request->processes_pending_;
    118 
    119     request->callback_.Run();
    120 
    121     delete request;
    122     outstanding_requests_.Get().erase(it);
    123 
    124     UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
    125                           received_process_group_count);
    126     UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
    127                          unresponsive_processes);
    128   }
    129 
    130   // Delete all the entries in |outstanding_requests_| map.
    131   static void OnShutdown() {
    132     // Just in case we have any pending tasks, clear them out.
    133     while (!outstanding_requests_.Get().empty()) {
    134       RequestContextMap::iterator it = outstanding_requests_.Get().begin();
    135       delete it->second;
    136       outstanding_requests_.Get().erase(it);
    137     }
    138   }
    139 
    140   // Requests are made to asynchronously send data to the |callback_|.
    141   base::Closure callback_;
    142 
    143   // The sequence number used by the most recent update request to contact all
    144   // processes.
    145   int sequence_number_;
    146 
    147   // Indicates if we have received all pending processes count.
    148   bool received_process_group_count_;
    149 
    150   // The number of pending processes (all renderer processes and browser child
    151   // processes) that have not yet responded to requests.
    152   int processes_pending_;
    153 
    154   // Map of all outstanding RequestContexts, from sequence_number_ to
    155   // RequestContext.
    156   static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
    157 };
    158 
    159 // static
    160 base::LazyInstance
    161     <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
    162         HistogramSynchronizer::RequestContext::outstanding_requests_ =
    163             LAZY_INSTANCE_INITIALIZER;
    164 
    165 HistogramSynchronizer::HistogramSynchronizer()
    166     : lock_(),
    167       callback_thread_(NULL),
    168       last_used_sequence_number_(kNeverUsableSequenceNumber),
    169       async_sequence_number_(kNeverUsableSequenceNumber) {
    170   HistogramController::GetInstance()->Register(this);
    171 }
    172 
    173 HistogramSynchronizer::~HistogramSynchronizer() {
    174   RequestContext::OnShutdown();
    175 
    176   // Just in case we have any pending tasks, clear them out.
    177   SetCallbackTaskAndThread(NULL, base::Closure());
    178 }
    179 
    180 HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
    181   return Singleton<HistogramSynchronizer,
    182       LeakySingletonTraits<HistogramSynchronizer> >::get();
    183 }
    184 
    185 // static
    186 void HistogramSynchronizer::FetchHistograms() {
    187   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    188     BrowserThread::PostTask(
    189         BrowserThread::UI, FROM_HERE,
    190         base::Bind(&HistogramSynchronizer::FetchHistograms));
    191     return;
    192   }
    193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    194 
    195   HistogramSynchronizer* current_synchronizer =
    196       HistogramSynchronizer::GetInstance();
    197   if (current_synchronizer == NULL)
    198     return;
    199 
    200   current_synchronizer->RegisterAndNotifyAllProcesses(
    201       HistogramSynchronizer::UNKNOWN,
    202       base::TimeDelta::FromMinutes(1));
    203 }
    204 
    205 void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread,
    206                                    const base::Closure& callback,
    207                                    base::TimeDelta wait_time) {
    208   HistogramSynchronizer::FetchHistogramsAsynchronously(
    209       callback_thread, callback, wait_time);
    210 }
    211 
    212 // static
    213 void HistogramSynchronizer::FetchHistogramsAsynchronously(
    214     base::MessageLoop* callback_thread,
    215     const base::Closure& callback,
    216     base::TimeDelta wait_time) {
    217   DCHECK(callback_thread != NULL);
    218   DCHECK(!callback.is_null());
    219 
    220   HistogramSynchronizer* current_synchronizer =
    221       HistogramSynchronizer::GetInstance();
    222   current_synchronizer->SetCallbackTaskAndThread(
    223       callback_thread, callback);
    224 
    225   current_synchronizer->RegisterAndNotifyAllProcesses(
    226       HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
    227 }
    228 
    229 void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
    230     ProcessHistogramRequester requester,
    231     base::TimeDelta wait_time) {
    232   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    233 
    234   int sequence_number = GetNextAvailableSequenceNumber(requester);
    235 
    236   base::Closure callback = base::Bind(
    237       &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
    238       base::Unretained(this),
    239       sequence_number);
    240 
    241   RequestContext::Register(callback, sequence_number);
    242 
    243   // Get histogram data from renderer and browser child processes.
    244   HistogramController::GetInstance()->GetHistogramData(sequence_number);
    245 
    246   // Post a task that would be called after waiting for wait_time.  This acts
    247   // as a watchdog, to cancel the requests for non-responsive processes.
    248   BrowserThread::PostDelayedTask(
    249       BrowserThread::UI, FROM_HERE,
    250       base::Bind(&RequestContext::Unregister, sequence_number),
    251       wait_time);
    252 }
    253 
    254 void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
    255                                                int pending_processes,
    256                                                bool end) {
    257   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    258 
    259   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
    260   if (!request)
    261     return;
    262   request->AddProcessesPending(pending_processes);
    263   request->SetReceivedProcessGroupCount(end);
    264   request->DeleteIfAllDone();
    265 }
    266 
    267 void HistogramSynchronizer::OnHistogramDataCollected(
    268     int sequence_number,
    269     const std::vector<std::string>& pickled_histograms) {
    270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    271 
    272   base::HistogramDeltaSerialization::DeserializeAndAddSamples(
    273       pickled_histograms);
    274 
    275   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
    276   if (!request)
    277     return;
    278 
    279   // Delete request if we have heard back from all child processes.
    280   request->DecrementProcessesPending();
    281   request->DeleteIfAllDone();
    282 }
    283 
    284 void HistogramSynchronizer::SetCallbackTaskAndThread(
    285     base::MessageLoop* callback_thread,
    286     const base::Closure& callback) {
    287   base::Closure old_callback;
    288   base::MessageLoop* old_thread = NULL;
    289   {
    290     base::AutoLock auto_lock(lock_);
    291     old_callback = callback_;
    292     callback_ = callback;
    293     old_thread = callback_thread_;
    294     callback_thread_ = callback_thread;
    295     // Prevent premature calling of our new callbacks.
    296     async_sequence_number_ = kNeverUsableSequenceNumber;
    297   }
    298   // Just in case there was a task pending....
    299   InternalPostTask(old_thread, old_callback);
    300 }
    301 
    302 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
    303     int sequence_number) {
    304   base::Closure callback;
    305   base::MessageLoop* thread = NULL;
    306   {
    307     base::AutoLock lock(lock_);
    308     if (sequence_number != async_sequence_number_)
    309       return;
    310     callback = callback_;
    311     thread = callback_thread_;
    312     callback_.Reset();
    313     callback_thread_ = NULL;
    314   }
    315   InternalPostTask(thread, callback);
    316 }
    317 
    318 void HistogramSynchronizer::InternalPostTask(base::MessageLoop* thread,
    319                                              const base::Closure& callback) {
    320   if (callback.is_null() || !thread)
    321     return;
    322   thread->PostTask(FROM_HERE, callback);
    323 }
    324 
    325 int HistogramSynchronizer::GetNextAvailableSequenceNumber(
    326     ProcessHistogramRequester requester) {
    327   base::AutoLock auto_lock(lock_);
    328   ++last_used_sequence_number_;
    329   // Watch out for wrapping to a negative number.
    330   if (last_used_sequence_number_ < 0) {
    331     // Bypass the reserved number, which is used when a renderer spontaneously
    332     // decides to send some histogram data.
    333     last_used_sequence_number_ =
    334         kHistogramSynchronizerReservedSequenceNumber + 1;
    335   }
    336   DCHECK_NE(last_used_sequence_number_,
    337             kHistogramSynchronizerReservedSequenceNumber);
    338   if (requester == ASYNC_HISTOGRAMS)
    339     async_sequence_number_ = last_used_sequence_number_;
    340   return last_used_sequence_number_;
    341 }
    342 
    343 }  // namespace content
    344