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