summaryrefslogtreecommitdiffstats
path: root/content/browser/histogram_synchronizer.h
blob: 9ff69d5cfeb60e1c50ae90b10f023b43c116e75a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_
#define CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "content/browser/histogram_subscriber.h"

namespace base {
class MessageLoop;
}

namespace content {

// This class maintains state that is used to upload histogram data from the
// various child processes, into the browser process. Such transactions are
// usually instigated by the browser. In general, a child process will respond
// by gathering snapshots of all internal histograms, calculating what has
// changed since its last upload, and transmitting a pickled collection of
// deltas.
//
// There are actually two modes of update request.  One is synchronous (and
// blocks the UI thread, waiting to populate an about:histograms tab) and the
// other is asynchronous, and used by the metrics services in preparation for a
// log upload.
//
// To assure that all the processes have responded, a counter is maintained to
// indicate the number of pending (not yet responsive) processes. To avoid
// confusion about a response (i.e., is the process responding to a current
// request for an update, or to an old request for an update) we tag each group
// of requests with a sequence number. When an update arrives we can ignore it
// (relative to the counter) if it does not relate to a current outstanding
// sequence number.
//
// There is one final mode of use, where a renderer spontaneously decides to
// transmit a collection of histogram data.  This is designed for use when the
// renderer is terminating.  Unfortunately, renders may be terminated without
// warning, and the best we can do is periodically acquire data from a tab, such
// as when a page load has completed.  In this mode, the renderer uses a
// reserved sequence number, different from any sequence number that might be
// specified by a browser request.  Since this sequence number can't match an
// outstanding sequence number, the pickled data is accepted into the browser,
// but there is no impact on the counters.

class HistogramSynchronizer : public HistogramSubscriber {
 public:
  enum ProcessHistogramRequester {
    UNKNOWN,
    ASYNC_HISTOGRAMS,
  };

  // Return pointer to the singleton instance for the current process, or NULL
  // if none.
  static HistogramSynchronizer* GetInstance();

  // Contact all processes, and get them to upload to the browser any/all
  // changes to histograms. This method is called from about:histograms.
  static void FetchHistograms();

  // Contact all child processes, and get them to upload to the browser any/all
  // changes to histograms.  When all changes have been acquired, or when the
  // wait time expires (whichever is sooner), post the callback to the
  // specified message loop. Note the callback is posted exactly once.
  static void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread,
                                            const base::Closure& callback,
                                            base::TimeDelta wait_time);

 private:
  friend struct DefaultSingletonTraits<HistogramSynchronizer>;

  class RequestContext;

  HistogramSynchronizer();
  virtual ~HistogramSynchronizer();

  // Establish a new sequence number, and use it to notify all processes
  // (renderers, plugins, GPU, etc) of the need to supply, to the browser,
  // any/all changes to their histograms. |wait_time| specifies the amount of
  // time to wait before cancelling the requests for non-responsive processes.
  void RegisterAndNotifyAllProcesses(ProcessHistogramRequester requester,
                                     base::TimeDelta wait_time);

  // -------------------------------------------------------
  // HistogramSubscriber methods for browser child processes
  // -------------------------------------------------------

  // Update the number of pending processes for the given |sequence_number|.
  // This is called on UI thread.
  virtual void OnPendingProcesses(int sequence_number,
                                  int pending_processes,
                                  bool end) OVERRIDE;

  // Send histogram_data back to caller and also record that we are waiting
  // for one less histogram data from child process for the given sequence
  // number. This method is accessible on UI thread.
  virtual void OnHistogramDataCollected(
      int sequence_number,
      const std::vector<std::string>& pickled_histograms) OVERRIDE;

  // Set the callback_thread_ and callback_ members. If these members already
  // had values, then as a side effect, post the old callback_ to the old
  // callaback_thread_.  This side effect should not generally happen, but is in
  // place to assure correctness (that any tasks that were set, are eventually
  // called, and never merely discarded).
  void SetCallbackTaskAndThread(base::MessageLoop* callback_thread,
                                const base::Closure& callback);

  void ForceHistogramSynchronizationDoneCallback(int sequence_number);

  // Internal helper function, to post task, and record callback stats.
  void InternalPostTask(base::MessageLoop* thread,
                        const base::Closure& callback);

  // Gets a new sequence number to be sent to processes from browser process.
  int GetNextAvailableSequenceNumber(ProcessHistogramRequester requester);

  // This lock_ protects access to all members.
  base::Lock lock_;

  // When a request is made to asynchronously update the histograms, we store
  // the task and thread we use to post a completion notification in
  // callback_ and callback_thread_.
  base::Closure callback_;
  base::MessageLoop* callback_thread_;

  // We don't track the actual processes that are contacted for an update, only
  // the count of the number of processes, and we can sometimes time-out and
  // give up on a "slow to respond" process.  We use a sequence_number to be
  // sure a response from a process is associated with the current round of
  // requests (and not merely a VERY belated prior response).
  // All sequence numbers used are non-negative.
  // last_used_sequence_number_ is the most recently used number (used to avoid
  // reuse for a long time).
  int last_used_sequence_number_;

  // The sequence number used by the most recent asynchronous update request to
  // contact all processes.
  int async_sequence_number_;

  DISALLOW_COPY_AND_ASSIGN(HistogramSynchronizer);
};

}  // namespace content

#endif  // CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_