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"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/histogram_delta_serialization.h"
12 #include "base/pickle.h"
13 #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"
21 using base::TimeDelta
;
22 using base::TimeTicks
;
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
31 static const int kNeverUsableSequenceNumber
= -2;
33 } // anonymous namespace
37 // The "RequestContext" structure describes an individual request received from
38 // the UI. All methods are accessible on UI thread.
39 class HistogramSynchronizer::RequestContext
{
41 // A map from sequence_number_ to the actual RequestContexts.
42 typedef std::map
<int, RequestContext
*> RequestContextMap
;
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) {
52 void SetReceivedProcessGroupCount(bool done
) {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
54 received_process_group_count_
= done
;
57 // Methods for book keeping of processes_pending_.
58 void AddProcessesPending(int processes_pending
) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
60 processes_pending_
+= processes_pending
;
63 void DecrementProcessesPending() {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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
72 void DeleteIfAllDone() {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
75 if (processes_pending_
<= 0 && received_process_group_count_
)
76 RequestContext::Unregister(sequence_number_
);
79 // Register |callback| in |outstanding_requests_| map for the given
81 static void Register(const base::Closure
& callback
, int sequence_number
) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
84 RequestContext
* request
= new RequestContext(callback
, sequence_number
);
85 outstanding_requests_
.Get()[sequence_number
] = request
;
88 // Find the |RequestContext| in |outstanding_requests_| map for the given
90 static RequestContext
* GetRequestContext(int sequence_number
) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
93 RequestContextMap::iterator it
=
94 outstanding_requests_
.Get().find(sequence_number
);
95 if (it
== outstanding_requests_
.Get().end())
98 RequestContext
* request
= it
->second
;
99 DCHECK_EQ(sequence_number
, request
->sequence_number_
);
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
) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
109 RequestContextMap::iterator it
=
110 outstanding_requests_
.Get().find(sequence_number
);
111 if (it
== outstanding_requests_
.Get().end())
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_
;
119 request
->callback_
.Run();
122 outstanding_requests_
.Get().erase(it
);
124 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
125 received_process_group_count
);
126 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
127 unresponsive_processes
);
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();
136 outstanding_requests_
.Get().erase(it
);
140 // Requests are made to asynchronously send data to the |callback_|.
141 base::Closure callback_
;
143 // The sequence number used by the most recent update request to contact all
145 int sequence_number_
;
147 // Indicates if we have received all pending processes count.
148 bool received_process_group_count_
;
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_
;
154 // Map of all outstanding RequestContexts, from sequence_number_ to
156 static base::LazyInstance
<RequestContextMap
>::Leaky outstanding_requests_
;
161 <HistogramSynchronizer::RequestContext::RequestContextMap
>::Leaky
162 HistogramSynchronizer::RequestContext::outstanding_requests_
=
163 LAZY_INSTANCE_INITIALIZER
;
165 HistogramSynchronizer::HistogramSynchronizer()
167 callback_thread_(NULL
),
168 last_used_sequence_number_(kNeverUsableSequenceNumber
),
169 async_sequence_number_(kNeverUsableSequenceNumber
) {
170 HistogramController::GetInstance()->Register(this);
173 HistogramSynchronizer::~HistogramSynchronizer() {
174 RequestContext::OnShutdown();
176 // Just in case we have any pending tasks, clear them out.
177 SetCallbackTaskAndThread(NULL
, base::Closure());
180 HistogramSynchronizer
* HistogramSynchronizer::GetInstance() {
181 return Singleton
<HistogramSynchronizer
,
182 LeakySingletonTraits
<HistogramSynchronizer
> >::get();
186 void HistogramSynchronizer::FetchHistograms() {
187 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
188 BrowserThread::PostTask(
189 BrowserThread::UI
, FROM_HERE
,
190 base::Bind(&HistogramSynchronizer::FetchHistograms
));
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
195 HistogramSynchronizer
* current_synchronizer
=
196 HistogramSynchronizer::GetInstance();
197 if (current_synchronizer
== NULL
)
200 current_synchronizer
->RegisterAndNotifyAllProcesses(
201 HistogramSynchronizer::UNKNOWN
,
202 base::TimeDelta::FromMinutes(1));
205 void FetchHistogramsAsynchronously(base::MessageLoop
* callback_thread
,
206 const base::Closure
& callback
,
207 base::TimeDelta wait_time
) {
208 HistogramSynchronizer::FetchHistogramsAsynchronously(
209 callback_thread
, callback
, wait_time
);
213 void HistogramSynchronizer::FetchHistogramsAsynchronously(
214 base::MessageLoop
* callback_thread
,
215 const base::Closure
& callback
,
216 base::TimeDelta wait_time
) {
217 DCHECK(callback_thread
!= NULL
);
218 DCHECK(!callback
.is_null());
220 HistogramSynchronizer
* current_synchronizer
=
221 HistogramSynchronizer::GetInstance();
222 current_synchronizer
->SetCallbackTaskAndThread(
223 callback_thread
, callback
);
225 current_synchronizer
->RegisterAndNotifyAllProcesses(
226 HistogramSynchronizer::ASYNC_HISTOGRAMS
, wait_time
);
229 void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
230 ProcessHistogramRequester requester
,
231 base::TimeDelta wait_time
) {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
234 int sequence_number
= GetNextAvailableSequenceNumber(requester
);
236 base::Closure callback
= base::Bind(
237 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback
,
238 base::Unretained(this),
241 RequestContext::Register(callback
, sequence_number
);
243 // Get histogram data from renderer and browser child processes.
244 HistogramController::GetInstance()->GetHistogramData(sequence_number
);
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
),
254 void HistogramSynchronizer::OnPendingProcesses(int sequence_number
,
255 int pending_processes
,
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
259 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
262 request
->AddProcessesPending(pending_processes
);
263 request
->SetReceivedProcessGroupCount(end
);
264 request
->DeleteIfAllDone();
267 void HistogramSynchronizer::OnHistogramDataCollected(
269 const std::vector
<std::string
>& pickled_histograms
) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
272 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
275 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
279 // Delete request if we have heard back from all child processes.
280 request
->DecrementProcessesPending();
281 request
->DeleteIfAllDone();
284 void HistogramSynchronizer::SetCallbackTaskAndThread(
285 base::MessageLoop
* callback_thread
,
286 const base::Closure
& callback
) {
287 base::Closure old_callback
;
288 base::MessageLoop
* old_thread
= NULL
;
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
;
298 // Just in case there was a task pending....
299 InternalPostTask(old_thread
, old_callback
);
302 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
303 int sequence_number
) {
304 base::Closure callback
;
305 base::MessageLoop
* thread
= NULL
;
307 base::AutoLock
lock(lock_
);
308 if (sequence_number
!= async_sequence_number_
)
310 callback
= callback_
;
311 thread
= callback_thread_
;
313 callback_thread_
= NULL
;
315 InternalPostTask(thread
, callback
);
318 void HistogramSynchronizer::InternalPostTask(base::MessageLoop
* thread
,
319 const base::Closure
& callback
) {
320 if (callback
.is_null() || !thread
)
322 thread
->PostTask(FROM_HERE
, callback
);
325 int 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;
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_
;
343 } // namespace content