blob: 585179031e3b64c285ddc2a7c3fbc6405cb8d4a9 [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"
skyostil95082a62015-06-05 19:53:079#include "base/location.h"
[email protected]83ab4a282012-07-12 18:19:4510#include "base/logging.h"
11#include "base/metrics/histogram.h"
[email protected]6afa90f2013-10-23 01:16:0412#include "base/metrics/histogram_delta_serialization.h"
[email protected]c50c21d2013-01-11 21:52:4413#include "base/pickle.h"
skyostil95082a62015-06-05 19:53:0714#include "base/single_thread_task_runner.h"
[email protected]83ab4a282012-07-12 18:19:4515#include "base/threading/thread.h"
16#include "base/threading/thread_restrictions.h"
17#include "content/browser/histogram_controller.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/browser/histogram_fetcher.h"
20#include "content/public/common/content_constants.h"
21
22using base::Time;
23using base::TimeDelta;
24using base::TimeTicks;
25
26namespace {
27
28// Negative numbers are never used as sequence numbers. We explicitly pick a
29// negative number that is "so negative" that even when we add one (as is done
30// when we generated the next sequence number) that it will still be negative.
31// We have code that handles wrapping around on an overflow into negative
32// territory.
33static const int kNeverUsableSequenceNumber = -2;
34
35} // anonymous namespace
36
37namespace content {
38
39// The "RequestContext" structure describes an individual request received from
40// the UI. All methods are accessible on UI thread.
41class HistogramSynchronizer::RequestContext {
42 public:
43 // A map from sequence_number_ to the actual RequestContexts.
44 typedef std::map<int, RequestContext*> RequestContextMap;
45
46 RequestContext(const base::Closure& callback, int sequence_number)
47 : callback_(callback),
48 sequence_number_(sequence_number),
49 received_process_group_count_(0),
50 processes_pending_(0) {
51 }
52 ~RequestContext() {}
53
54 void SetReceivedProcessGroupCount(bool done) {
mostynb4c27d042015-03-18 21:47:4755 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4556 received_process_group_count_ = done;
57 }
58
59 // Methods for book keeping of processes_pending_.
60 void AddProcessesPending(int processes_pending) {
mostynb4c27d042015-03-18 21:47:4761 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4562 processes_pending_ += processes_pending;
63 }
64
65 void DecrementProcessesPending() {
mostynb4c27d042015-03-18 21:47:4766 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4567 --processes_pending_;
68 }
69
70 // Records that we are waiting for one less histogram data from a process for
71 // the given sequence number. If |received_process_group_count_| and
72 // |processes_pending_| are zero, then delete the current object by calling
73 // Unregister.
74 void DeleteIfAllDone() {
mostynb4c27d042015-03-18 21:47:4775 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4576
77 if (processes_pending_ <= 0 && received_process_group_count_)
78 RequestContext::Unregister(sequence_number_);
79 }
80
81 // Register |callback| in |outstanding_requests_| map for the given
82 // |sequence_number|.
83 static void Register(const base::Closure& callback, int sequence_number) {
mostynb4c27d042015-03-18 21:47:4784 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4585
86 RequestContext* request = new RequestContext(callback, sequence_number);
87 outstanding_requests_.Get()[sequence_number] = request;
88 }
89
90 // Find the |RequestContext| in |outstanding_requests_| map for the given
91 // |sequence_number|.
92 static RequestContext* GetRequestContext(int sequence_number) {
mostynb4c27d042015-03-18 21:47:4793 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4594
95 RequestContextMap::iterator it =
96 outstanding_requests_.Get().find(sequence_number);
97 if (it == outstanding_requests_.Get().end())
98 return NULL;
99
100 RequestContext* request = it->second;
101 DCHECK_EQ(sequence_number, request->sequence_number_);
102 return request;
103 }
104
105 // Delete the entry for the given |sequence_number| from
106 // |outstanding_requests_| map. This method is called when all changes have
107 // been acquired, or when the wait time expires (whichever is sooner).
108 static void Unregister(int sequence_number) {
mostynb4c27d042015-03-18 21:47:47109 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45110
111 RequestContextMap::iterator it =
112 outstanding_requests_.Get().find(sequence_number);
113 if (it == outstanding_requests_.Get().end())
114 return;
115
116 RequestContext* request = it->second;
117 DCHECK_EQ(sequence_number, request->sequence_number_);
118 bool received_process_group_count = request->received_process_group_count_;
119 int unresponsive_processes = request->processes_pending_;
120
121 request->callback_.Run();
122
123 delete request;
124 outstanding_requests_.Get().erase(it);
125
126 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
127 received_process_group_count);
128 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
129 unresponsive_processes);
130 }
131
132 // Delete all the entries in |outstanding_requests_| map.
133 static void OnShutdown() {
134 // Just in case we have any pending tasks, clear them out.
135 while (!outstanding_requests_.Get().empty()) {
136 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
137 delete it->second;
138 outstanding_requests_.Get().erase(it);
139 }
140 }
141
142 // Requests are made to asynchronously send data to the |callback_|.
143 base::Closure callback_;
144
145 // The sequence number used by the most recent update request to contact all
146 // processes.
147 int sequence_number_;
148
149 // Indicates if we have received all pending processes count.
150 bool received_process_group_count_;
151
152 // The number of pending processes (all renderer processes and browser child
153 // processes) that have not yet responded to requests.
154 int processes_pending_;
155
156 // Map of all outstanding RequestContexts, from sequence_number_ to
157 // RequestContext.
158 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
159};
160
161// static
162base::LazyInstance
163 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
164 HistogramSynchronizer::RequestContext::outstanding_requests_ =
165 LAZY_INSTANCE_INITIALIZER;
166
167HistogramSynchronizer::HistogramSynchronizer()
168 : lock_(),
169 callback_thread_(NULL),
170 last_used_sequence_number_(kNeverUsableSequenceNumber),
171 async_sequence_number_(kNeverUsableSequenceNumber) {
[email protected]46488322012-10-30 03:22:20172 HistogramController::GetInstance()->Register(this);
[email protected]83ab4a282012-07-12 18:19:45173}
174
175HistogramSynchronizer::~HistogramSynchronizer() {
176 RequestContext::OnShutdown();
177
178 // Just in case we have any pending tasks, clear them out.
179 SetCallbackTaskAndThread(NULL, base::Closure());
180}
181
182HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
183 return Singleton<HistogramSynchronizer,
184 LeakySingletonTraits<HistogramSynchronizer> >::get();
185}
186
187// static
188void HistogramSynchronizer::FetchHistograms() {
189 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
190 BrowserThread::PostTask(
191 BrowserThread::UI, FROM_HERE,
192 base::Bind(&HistogramSynchronizer::FetchHistograms));
193 return;
194 }
mostynb4c27d042015-03-18 21:47:47195 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45196
197 HistogramSynchronizer* current_synchronizer =
198 HistogramSynchronizer::GetInstance();
199 if (current_synchronizer == NULL)
200 return;
201
202 current_synchronizer->RegisterAndNotifyAllProcesses(
203 HistogramSynchronizer::UNKNOWN,
204 base::TimeDelta::FromMinutes(1));
205}
206
[email protected]dd32b1272013-05-04 14:17:11207void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread,
[email protected]83ab4a282012-07-12 18:19:45208 const base::Closure& callback,
209 base::TimeDelta wait_time) {
210 HistogramSynchronizer::FetchHistogramsAsynchronously(
211 callback_thread, callback, wait_time);
212}
213
214// static
215void HistogramSynchronizer::FetchHistogramsAsynchronously(
[email protected]dd32b1272013-05-04 14:17:11216 base::MessageLoop* callback_thread,
[email protected]83ab4a282012-07-12 18:19:45217 const base::Closure& callback,
218 base::TimeDelta wait_time) {
219 DCHECK(callback_thread != NULL);
220 DCHECK(!callback.is_null());
221
222 HistogramSynchronizer* current_synchronizer =
223 HistogramSynchronizer::GetInstance();
224 current_synchronizer->SetCallbackTaskAndThread(
225 callback_thread, callback);
226
227 current_synchronizer->RegisterAndNotifyAllProcesses(
228 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
229}
230
231void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
232 ProcessHistogramRequester requester,
233 base::TimeDelta wait_time) {
mostynb4c27d042015-03-18 21:47:47234 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45235
236 int sequence_number = GetNextAvailableSequenceNumber(requester);
237
238 base::Closure callback = base::Bind(
239 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
240 base::Unretained(this),
241 sequence_number);
242
243 RequestContext::Register(callback, sequence_number);
244
245 // Get histogram data from renderer and browser child processes.
[email protected]46488322012-10-30 03:22:20246 HistogramController::GetInstance()->GetHistogramData(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45247
248 // Post a task that would be called after waiting for wait_time. This acts
249 // as a watchdog, to cancel the requests for non-responsive processes.
250 BrowserThread::PostDelayedTask(
251 BrowserThread::UI, FROM_HERE,
252 base::Bind(&RequestContext::Unregister, sequence_number),
253 wait_time);
254}
255
256void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
257 int pending_processes,
258 bool end) {
mostynb4c27d042015-03-18 21:47:47259 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45260
261 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
262 if (!request)
263 return;
264 request->AddProcessesPending(pending_processes);
265 request->SetReceivedProcessGroupCount(end);
266 request->DeleteIfAllDone();
267}
268
269void HistogramSynchronizer::OnHistogramDataCollected(
270 int sequence_number,
271 const std::vector<std::string>& pickled_histograms) {
mostynb4c27d042015-03-18 21:47:47272 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45273
[email protected]6afa90f2013-10-23 01:16:04274 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
275 pickled_histograms);
276
[email protected]83ab4a282012-07-12 18:19:45277 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45278 if (!request)
279 return;
280
281 // Delete request if we have heard back from all child processes.
282 request->DecrementProcessesPending();
283 request->DeleteIfAllDone();
284}
285
286void HistogramSynchronizer::SetCallbackTaskAndThread(
[email protected]dd32b1272013-05-04 14:17:11287 base::MessageLoop* callback_thread,
[email protected]83ab4a282012-07-12 18:19:45288 const base::Closure& callback) {
289 base::Closure old_callback;
[email protected]dd32b1272013-05-04 14:17:11290 base::MessageLoop* old_thread = NULL;
[email protected]83ab4a282012-07-12 18:19:45291 {
292 base::AutoLock auto_lock(lock_);
293 old_callback = callback_;
294 callback_ = callback;
295 old_thread = callback_thread_;
296 callback_thread_ = callback_thread;
297 // Prevent premature calling of our new callbacks.
298 async_sequence_number_ = kNeverUsableSequenceNumber;
299 }
300 // Just in case there was a task pending....
301 InternalPostTask(old_thread, old_callback);
302}
303
304void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
305 int sequence_number) {
306 base::Closure callback;
[email protected]dd32b1272013-05-04 14:17:11307 base::MessageLoop* thread = NULL;
[email protected]83ab4a282012-07-12 18:19:45308 {
309 base::AutoLock lock(lock_);
310 if (sequence_number != async_sequence_number_)
311 return;
312 callback = callback_;
313 thread = callback_thread_;
314 callback_.Reset();
315 callback_thread_ = NULL;
316 }
317 InternalPostTask(thread, callback);
318}
319
[email protected]dd32b1272013-05-04 14:17:11320void HistogramSynchronizer::InternalPostTask(base::MessageLoop* thread,
[email protected]83ab4a282012-07-12 18:19:45321 const base::Closure& callback) {
322 if (callback.is_null() || !thread)
323 return;
skyostil95082a62015-06-05 19:53:07324 thread->task_runner()->PostTask(FROM_HERE, callback);
[email protected]83ab4a282012-07-12 18:19:45325}
326
327int HistogramSynchronizer::GetNextAvailableSequenceNumber(
328 ProcessHistogramRequester requester) {
329 base::AutoLock auto_lock(lock_);
330 ++last_used_sequence_number_;
331 // Watch out for wrapping to a negative number.
332 if (last_used_sequence_number_ < 0) {
333 // Bypass the reserved number, which is used when a renderer spontaneously
334 // decides to send some histogram data.
335 last_used_sequence_number_ =
336 kHistogramSynchronizerReservedSequenceNumber + 1;
337 }
338 DCHECK_NE(last_used_sequence_number_,
339 kHistogramSynchronizerReservedSequenceNumber);
340 if (requester == ASYNC_HISTOGRAMS)
341 async_sequence_number_ = last_used_sequence_number_;
342 return last_used_sequence_number_;
343}
344
345} // namespace content