Use lossless compression for CRD icon.
[chromium-blink-merge.git] / content / browser / histogram_synchronizer.cc
blob166172376ccf287605f4682feabe0423e2409d78
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/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"
20 using base::Time;
21 using base::TimeDelta;
22 using base::TimeTicks;
24 namespace {
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.
31 static const int kNeverUsableSequenceNumber = -2;
33 } // anonymous namespace
35 namespace content {
37 // The "RequestContext" structure describes an individual request received from
38 // the UI. All methods are accessible on UI thread.
39 class HistogramSynchronizer::RequestContext {
40 public:
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) {
50 ~RequestContext() {}
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));
65 --processes_pending_;
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() {
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
80 // |sequence_number|.
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
89 // |sequence_number|.
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())
96 return NULL;
98 RequestContext* request = it->second;
99 DCHECK_EQ(sequence_number, request->sequence_number_);
100 return request;
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())
112 return;
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();
121 delete request;
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();
135 delete it->second;
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
144 // processes.
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
155 // RequestContext.
156 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
159 // static
160 base::LazyInstance
161 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
162 HistogramSynchronizer::RequestContext::outstanding_requests_ =
163 LAZY_INSTANCE_INITIALIZER;
165 HistogramSynchronizer::HistogramSynchronizer()
166 : lock_(),
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();
185 // static
186 void HistogramSynchronizer::FetchHistograms() {
187 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
188 BrowserThread::PostTask(
189 BrowserThread::UI, FROM_HERE,
190 base::Bind(&HistogramSynchronizer::FetchHistograms));
191 return;
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195 HistogramSynchronizer* current_synchronizer =
196 HistogramSynchronizer::GetInstance();
197 if (current_synchronizer == NULL)
198 return;
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);
212 // static
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),
239 sequence_number);
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),
251 wait_time);
254 void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
255 int pending_processes,
256 bool end) {
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
259 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
260 if (!request)
261 return;
262 request->AddProcessesPending(pending_processes);
263 request->SetReceivedProcessGroupCount(end);
264 request->DeleteIfAllDone();
267 void HistogramSynchronizer::OnHistogramDataCollected(
268 int sequence_number,
269 const std::vector<std::string>& pickled_histograms) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
273 pickled_histograms);
275 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
276 if (!request)
277 return;
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_)
309 return;
310 callback = callback_;
311 thread = callback_thread_;
312 callback_.Reset();
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)
321 return;
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