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