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
18namespace base {
19class MessageLoop;
20}
21
22namespace 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
54class 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