Broke ContentSettingBubbleModelTest.Plugins on Android.
[chromium-blink-merge.git] / content / browser / histogram_synchronizer.cc
blobb2db39ba5cfe2053a068e65856edb63958b96fdc
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.
5 #include "content/browser/histogram_synchronizer.h"
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"
19 using base::Time;
20 using base::TimeDelta;
21 using base::TimeTicks;
23 namespace {
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;
32 } // anonymous namespace
34 namespace content {
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;
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) {
49 ~RequestContext() {}
51 void SetReceivedProcessGroupCount(bool done) {
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53 received_process_group_count_ = done;
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;
62 void DecrementProcessesPending() {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
64 --processes_pending_;
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));
74 if (processes_pending_ <= 0 && received_process_group_count_)
75 RequestContext::Unregister(sequence_number_);
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));
83 RequestContext* request = new RequestContext(callback, sequence_number);
84 outstanding_requests_.Get()[sequence_number] = request;
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));
92 RequestContextMap::iterator it =
93 outstanding_requests_.Get().find(sequence_number);
94 if (it == outstanding_requests_.Get().end())
95 return NULL;
97 RequestContext* request = it->second;
98 DCHECK_EQ(sequence_number, request->sequence_number_);
99 return request;
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));
108 RequestContextMap::iterator it =
109 outstanding_requests_.Get().find(sequence_number);
110 if (it == outstanding_requests_.Get().end())
111 return;
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_;
118 request->callback_.Run();
120 delete request;
121 outstanding_requests_.Get().erase(it);
123 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
124 received_process_group_count);
125 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
126 unresponsive_processes);
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);
139 // Requests are made to asynchronously send data to the |callback_|.
140 base::Closure callback_;
142 // The sequence number used by the most recent update request to contact all
143 // processes.
144 int sequence_number_;
146 // Indicates if we have received all pending processes count.
147 bool received_process_group_count_;
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_;
153 // Map of all outstanding RequestContexts, from sequence_number_ to
154 // RequestContext.
155 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
158 // static
159 base::LazyInstance
160 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
161 HistogramSynchronizer::RequestContext::outstanding_requests_ =
162 LAZY_INSTANCE_INITIALIZER;
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);
172 HistogramSynchronizer::~HistogramSynchronizer() {
173 RequestContext::OnShutdown();
175 // Just in case we have any pending tasks, clear them out.
176 SetCallbackTaskAndThread(NULL, base::Closure());
179 HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
180 return Singleton<HistogramSynchronizer,
181 LeakySingletonTraits<HistogramSynchronizer> >::get();
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;
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
194 HistogramSynchronizer* current_synchronizer =
195 HistogramSynchronizer::GetInstance();
196 if (current_synchronizer == NULL)
197 return;
199 current_synchronizer->RegisterAndNotifyAllProcesses(
200 HistogramSynchronizer::UNKNOWN,
201 base::TimeDelta::FromMinutes(1));
204 void FetchHistogramsAsynchronously(MessageLoop* callback_thread,
205 const base::Closure& callback,
206 base::TimeDelta wait_time) {
207 HistogramSynchronizer::FetchHistogramsAsynchronously(
208 callback_thread, callback, wait_time);
211 // static
212 void HistogramSynchronizer::FetchHistogramsAsynchronously(
213 MessageLoop* callback_thread,
214 const base::Closure& callback,
215 base::TimeDelta wait_time) {
216 DCHECK(callback_thread != NULL);
217 DCHECK(!callback.is_null());
219 HistogramSynchronizer* current_synchronizer =
220 HistogramSynchronizer::GetInstance();
221 current_synchronizer->SetCallbackTaskAndThread(
222 callback_thread, callback);
224 current_synchronizer->RegisterAndNotifyAllProcesses(
225 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
228 void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
229 ProcessHistogramRequester requester,
230 base::TimeDelta wait_time) {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233 int sequence_number = GetNextAvailableSequenceNumber(requester);
235 base::Closure callback = base::Bind(
236 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
237 base::Unretained(this),
238 sequence_number);
240 RequestContext::Register(callback, sequence_number);
242 // Get histogram data from renderer and browser child processes.
243 HistogramController::GetInstance()->GetHistogramData(sequence_number);
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);
253 void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
254 int pending_processes,
255 bool end) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
258 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
259 if (!request)
260 return;
261 request->AddProcessesPending(pending_processes);
262 request->SetReceivedProcessGroupCount(end);
263 request->DeleteIfAllDone();
266 void HistogramSynchronizer::OnHistogramDataCollected(
267 int sequence_number,
268 const std::vector<std::string>& pickled_histograms) {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
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);
281 if (!request)
282 return;
284 // Delete request if we have heard back from all child processes.
285 request->DecrementProcessesPending();
286 request->DeleteIfAllDone();
289 void HistogramSynchronizer::SetCallbackTaskAndThread(
290 MessageLoop* callback_thread,
291 const base::Closure& callback) {
292 base::Closure old_callback;
293 MessageLoop* old_thread = NULL;
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;
303 // Just in case there was a task pending....
304 InternalPostTask(old_thread, old_callback);
307 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
308 int sequence_number) {
309 base::Closure callback;
310 MessageLoop* thread = NULL;
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;
320 InternalPostTask(thread, callback);
323 void HistogramSynchronizer::InternalPostTask(MessageLoop* thread,
324 const base::Closure& callback) {
325 if (callback.is_null() || !thread)
326 return;
327 thread->PostTask(FROM_HERE, callback);
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;
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_;
348 } // namespace content