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 #ifndef CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_
      6 #define CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_
      7 
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/callback.h"
     13 #include "base/memory/singleton.h"
     14 #include "base/synchronization/lock.h"
     15 #include "base/time/time.h"
     16 #include "content/browser/histogram_subscriber.h"
     17 
     18 namespace base {
     19 class MessageLoop;
     20 }
     21 
     22 namespace content {
     23 
     24 // This class maintains state that is used to upload histogram data from the
     25 // various child processes, into the browser process. Such transactions are
     26 // usually instigated by the browser. In general, a child process will respond
     27 // by gathering snapshots of all internal histograms, calculating what has
     28 // changed since its last upload, and transmitting a pickled collection of
     29 // deltas.
     30 //
     31 // There are actually two modes of update request.  One is synchronous (and
     32 // blocks the UI thread, waiting to populate an about:histograms tab) and the
     33 // other is asynchronous, and used by the metrics services in preparation for a
     34 // log upload.
     35 //
     36 // To assure that all the processes have responded, a counter is maintained to
     37 // indicate the number of pending (not yet responsive) processes. To avoid
     38 // confusion about a response (i.e., is the process responding to a current
     39 // request for an update, or to an old request for an update) we tag each group
     40 // of requests with a sequence number. When an update arrives we can ignore it
     41 // (relative to the counter) if it does not relate to a current outstanding
     42 // sequence number.
     43 //
     44 // There is one final mode of use, where a renderer spontaneously decides to
     45 // transmit a collection of histogram data.  This is designed for use when the
     46 // renderer is terminating.  Unfortunately, renders may be terminated without
     47 // warning, and the best we can do is periodically acquire data from a tab, such
     48 // as when a page load has completed.  In this mode, the renderer uses a
     49 // reserved sequence number, different from any sequence number that might be
     50 // specified by a browser request.  Since this sequence number can't match an
     51 // outstanding sequence number, the pickled data is accepted into the browser,
     52 // but there is no impact on the counters.
     53 
     54 class HistogramSynchronizer : public HistogramSubscriber {
     55  public:
     56   enum ProcessHistogramRequester {
     57     UNKNOWN,
     58     ASYNC_HISTOGRAMS,
     59   };
     60 
     61   // Return pointer to the singleton instance for the current process, or NULL
     62   // if none.
     63   static HistogramSynchronizer* GetInstance();
     64 
     65   // Contact all processes, and get them to upload to the browser any/all
     66   // changes to histograms. This method is called from about:histograms.
     67   static void FetchHistograms();
     68 
     69   // Contact all child processes, and get them to upload to the browser any/all
     70   // changes to histograms.  When all changes have been acquired, or when the
     71   // wait time expires (whichever is sooner), post the callback to the
     72   // specified message loop. Note the callback is posted exactly once.
     73   static void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread,
     74                                             const base::Closure& callback,
     75                                             base::TimeDelta wait_time);
     76 
     77  private:
     78   friend struct DefaultSingletonTraits<HistogramSynchronizer>;
     79 
     80   class RequestContext;
     81 
     82   HistogramSynchronizer();
     83   virtual ~HistogramSynchronizer();
     84 
     85   // Establish a new sequence number, and use it to notify all processes
     86   // (renderers, plugins, GPU, etc) of the need to supply, to the browser,
     87   // any/all changes to their histograms. |wait_time| specifies the amount of
     88   // time to wait before cancelling the requests for non-responsive processes.
     89   void RegisterAndNotifyAllProcesses(ProcessHistogramRequester requester,
     90                                      base::TimeDelta wait_time);
     91 
     92   // -------------------------------------------------------
     93   // HistogramSubscriber methods for browser child processes
     94   // -------------------------------------------------------
     95 
     96   // Update the number of pending processes for the given |sequence_number|.
     97   // This is called on UI thread.
     98   virtual void OnPendingProcesses(int sequence_number,
     99                                   int pending_processes,
    100                                   bool end) OVERRIDE;
    101 
    102   // Send histogram_data back to caller and also record that we are waiting
    103   // for one less histogram data from child process for the given sequence
    104   // number. This method is accessible on UI thread.
    105   virtual void OnHistogramDataCollected(
    106       int sequence_number,
    107       const std::vector<std::string>& pickled_histograms) OVERRIDE;
    108 
    109   // Set the callback_thread_ and callback_ members. If these members already
    110   // had values, then as a side effect, post the old callback_ to the old
    111   // callaback_thread_.  This side effect should not generally happen, but is in
    112   // place to assure correctness (that any tasks that were set, are eventually
    113   // called, and never merely discarded).
    114   void SetCallbackTaskAndThread(base::MessageLoop* callback_thread,
    115                                 const base::Closure& callback);
    116 
    117   void ForceHistogramSynchronizationDoneCallback(int sequence_number);
    118 
    119   // Internal helper function, to post task, and record callback stats.
    120   void InternalPostTask(base::MessageLoop* thread,
    121                         const base::Closure& callback);
    122 
    123   // Gets a new sequence number to be sent to processes from browser process.
    124   int GetNextAvailableSequenceNumber(ProcessHistogramRequester requester);
    125 
    126   // This lock_ protects access to all members.
    127   base::Lock lock_;
    128 
    129   // When a request is made to asynchronously update the histograms, we store
    130   // the task and thread we use to post a completion notification in
    131   // callback_ and callback_thread_.
    132   base::Closure callback_;
    133   base::MessageLoop* callback_thread_;
    134 
    135   // We don't track the actual processes that are contacted for an update, only
    136   // the count of the number of processes, and we can sometimes time-out and
    137   // give up on a "slow to respond" process.  We use a sequence_number to be
    138   // sure a response from a process is associated with the current round of
    139   // requests (and not merely a VERY belated prior response).
    140   // All sequence numbers used are non-negative.
    141   // last_used_sequence_number_ is the most recently used number (used to avoid
    142   // reuse for a long time).
    143   int last_used_sequence_number_;
    144 
    145   // The sequence number used by the most recent asynchronous update request to
    146   // contact all processes.
    147   int async_sequence_number_;
    148 
    149   DISALLOW_COPY_AND_ASSIGN(HistogramSynchronizer);
    150 };
    151 
    152 }  // namespace content
    153 
    154 #endif  // CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_
    155