Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / content / browser / histogram_synchronizer.cc
blobe0698b9154f9af119e8f8c7595ef333b32d32f7a
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/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_delta_serialization.h"
13 #include "base/pickle.h"
14 #include "base/single_thread_task_runner.h"
15 #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"
22 using base::Time;
23 using base::TimeDelta;
24 using base::TimeTicks;
26 namespace {
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.
33 static const int kNeverUsableSequenceNumber = -2;
35 } // anonymous namespace
37 namespace content {
39 // The "RequestContext" structure describes an individual request received from
40 // the UI. All methods are accessible on UI thread.
41 class HistogramSynchronizer::RequestContext {
42 public:
43 // A map from sequence_number_ to the actual RequestContexts.
44 typedef std::map<int, RequestContext*> RequestContextMap;
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) {
52 ~RequestContext() {}
54 void SetReceivedProcessGroupCount(bool done) {
55 DCHECK_CURRENTLY_ON(BrowserThread::UI);
56 received_process_group_count_ = done;
59 // Methods for book keeping of processes_pending_.
60 void AddProcessesPending(int processes_pending) {
61 DCHECK_CURRENTLY_ON(BrowserThread::UI);
62 processes_pending_ += processes_pending;
65 void DecrementProcessesPending() {
66 DCHECK_CURRENTLY_ON(BrowserThread::UI);
67 --processes_pending_;
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() {
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
77 if (processes_pending_ <= 0 && received_process_group_count_)
78 RequestContext::Unregister(sequence_number_);
81 // Register |callback| in |outstanding_requests_| map for the given
82 // |sequence_number|.
83 static void Register(const base::Closure& callback, int sequence_number) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
86 RequestContext* request = new RequestContext(callback, sequence_number);
87 outstanding_requests_.Get()[sequence_number] = request;
90 // Find the |RequestContext| in |outstanding_requests_| map for the given
91 // |sequence_number|.
92 static RequestContext* GetRequestContext(int sequence_number) {
93 DCHECK_CURRENTLY_ON(BrowserThread::UI);
95 RequestContextMap::iterator it =
96 outstanding_requests_.Get().find(sequence_number);
97 if (it == outstanding_requests_.Get().end())
98 return NULL;
100 RequestContext* request = it->second;
101 DCHECK_EQ(sequence_number, request->sequence_number_);
102 return request;
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) {
109 DCHECK_CURRENTLY_ON(BrowserThread::UI);
111 RequestContextMap::iterator it =
112 outstanding_requests_.Get().find(sequence_number);
113 if (it == outstanding_requests_.Get().end())
114 return;
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_;
121 request->callback_.Run();
123 delete request;
124 outstanding_requests_.Get().erase(it);
126 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
127 received_process_group_count);
128 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
129 unresponsive_processes);
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);
142 // Requests are made to asynchronously send data to the |callback_|.
143 base::Closure callback_;
145 // The sequence number used by the most recent update request to contact all
146 // processes.
147 int sequence_number_;
149 // Indicates if we have received all pending processes count.
150 bool received_process_group_count_;
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_;
156 // Map of all outstanding RequestContexts, from sequence_number_ to
157 // RequestContext.
158 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
161 // static
162 base::LazyInstance
163 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
164 HistogramSynchronizer::RequestContext::outstanding_requests_ =
165 LAZY_INSTANCE_INITIALIZER;
167 HistogramSynchronizer::HistogramSynchronizer()
168 : lock_(),
169 callback_thread_(NULL),
170 last_used_sequence_number_(kNeverUsableSequenceNumber),
171 async_sequence_number_(kNeverUsableSequenceNumber) {
172 HistogramController::GetInstance()->Register(this);
175 HistogramSynchronizer::~HistogramSynchronizer() {
176 RequestContext::OnShutdown();
178 // Just in case we have any pending tasks, clear them out.
179 SetCallbackTaskAndThread(NULL, base::Closure());
182 HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
183 return base::Singleton<
184 HistogramSynchronizer,
185 base::LeakySingletonTraits<HistogramSynchronizer>>::get();
188 // static
189 void HistogramSynchronizer::FetchHistograms() {
190 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
191 BrowserThread::PostTask(
192 BrowserThread::UI, FROM_HERE,
193 base::Bind(&HistogramSynchronizer::FetchHistograms));
194 return;
196 DCHECK_CURRENTLY_ON(BrowserThread::UI);
198 HistogramSynchronizer* current_synchronizer =
199 HistogramSynchronizer::GetInstance();
200 if (current_synchronizer == NULL)
201 return;
203 current_synchronizer->RegisterAndNotifyAllProcesses(
204 HistogramSynchronizer::UNKNOWN,
205 base::TimeDelta::FromMinutes(1));
208 void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread,
209 const base::Closure& callback,
210 base::TimeDelta wait_time) {
211 HistogramSynchronizer::FetchHistogramsAsynchronously(
212 callback_thread, callback, wait_time);
215 // static
216 void HistogramSynchronizer::FetchHistogramsAsynchronously(
217 base::MessageLoop* callback_thread,
218 const base::Closure& callback,
219 base::TimeDelta wait_time) {
220 DCHECK(callback_thread != NULL);
221 DCHECK(!callback.is_null());
223 HistogramSynchronizer* current_synchronizer =
224 HistogramSynchronizer::GetInstance();
225 current_synchronizer->SetCallbackTaskAndThread(
226 callback_thread, callback);
228 current_synchronizer->RegisterAndNotifyAllProcesses(
229 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
232 void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
233 ProcessHistogramRequester requester,
234 base::TimeDelta wait_time) {
235 DCHECK_CURRENTLY_ON(BrowserThread::UI);
237 int sequence_number = GetNextAvailableSequenceNumber(requester);
239 base::Closure callback = base::Bind(
240 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
241 base::Unretained(this),
242 sequence_number);
244 RequestContext::Register(callback, sequence_number);
246 // Get histogram data from renderer and browser child processes.
247 HistogramController::GetInstance()->GetHistogramData(sequence_number);
249 // Post a task that would be called after waiting for wait_time. This acts
250 // as a watchdog, to cancel the requests for non-responsive processes.
251 BrowserThread::PostDelayedTask(
252 BrowserThread::UI, FROM_HERE,
253 base::Bind(&RequestContext::Unregister, sequence_number),
254 wait_time);
257 void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
258 int pending_processes,
259 bool end) {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI);
262 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
263 if (!request)
264 return;
265 request->AddProcessesPending(pending_processes);
266 request->SetReceivedProcessGroupCount(end);
267 request->DeleteIfAllDone();
270 void HistogramSynchronizer::OnHistogramDataCollected(
271 int sequence_number,
272 const std::vector<std::string>& pickled_histograms) {
273 DCHECK_CURRENTLY_ON(BrowserThread::UI);
275 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
276 pickled_histograms);
278 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
279 if (!request)
280 return;
282 // Delete request if we have heard back from all child processes.
283 request->DecrementProcessesPending();
284 request->DeleteIfAllDone();
287 void HistogramSynchronizer::SetCallbackTaskAndThread(
288 base::MessageLoop* callback_thread,
289 const base::Closure& callback) {
290 base::Closure old_callback;
291 base::MessageLoop* old_thread = NULL;
293 base::AutoLock auto_lock(lock_);
294 old_callback = callback_;
295 callback_ = callback;
296 old_thread = callback_thread_;
297 callback_thread_ = callback_thread;
298 // Prevent premature calling of our new callbacks.
299 async_sequence_number_ = kNeverUsableSequenceNumber;
301 // Just in case there was a task pending....
302 InternalPostTask(old_thread, old_callback);
305 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
306 int sequence_number) {
307 base::Closure callback;
308 base::MessageLoop* thread = NULL;
310 base::AutoLock lock(lock_);
311 if (sequence_number != async_sequence_number_)
312 return;
313 callback = callback_;
314 thread = callback_thread_;
315 callback_.Reset();
316 callback_thread_ = NULL;
318 InternalPostTask(thread, callback);
321 void HistogramSynchronizer::InternalPostTask(base::MessageLoop* thread,
322 const base::Closure& callback) {
323 if (callback.is_null() || !thread)
324 return;
325 thread->task_runner()->PostTask(FROM_HERE, callback);
328 int HistogramSynchronizer::GetNextAvailableSequenceNumber(
329 ProcessHistogramRequester requester) {
330 base::AutoLock auto_lock(lock_);
331 ++last_used_sequence_number_;
332 // Watch out for wrapping to a negative number.
333 if (last_used_sequence_number_ < 0) {
334 // Bypass the reserved number, which is used when a renderer spontaneously
335 // decides to send some histogram data.
336 last_used_sequence_number_ =
337 kHistogramSynchronizerReservedSequenceNumber + 1;
339 DCHECK_NE(last_used_sequence_number_,
340 kHistogramSynchronizerReservedSequenceNumber);
341 if (requester == ASYNC_HISTOGRAMS)
342 async_sequence_number_ = last_used_sequence_number_;
343 return last_used_sequence_number_;
346 } // namespace content