blob: bb90b0715c16133a677038ef0546f465647f6f13 [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"
11#include "base/threading/thread.h"
12#include "base/threading/thread_restrictions.h"
13#include "content/browser/histogram_controller.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/browser/histogram_fetcher.h"
16#include "content/public/common/content_constants.h"
17
18using base::Time;
19using base::TimeDelta;
20using base::TimeTicks;
21
22namespace {
23
24// Negative numbers are never used as sequence numbers. We explicitly pick a
25// negative number that is "so negative" that even when we add one (as is done
26// when we generated the next sequence number) that it will still be negative.
27// We have code that handles wrapping around on an overflow into negative
28// territory.
29static const int kNeverUsableSequenceNumber = -2;
30
31} // anonymous namespace
32
33namespace content {
34
35// The "RequestContext" structure describes an individual request received from
36// the UI. All methods are accessible on UI thread.
37class HistogramSynchronizer::RequestContext {
38 public:
39 // A map from sequence_number_ to the actual RequestContexts.
40 typedef std::map<int, RequestContext*> RequestContextMap;
41
42 RequestContext(const base::Closure& callback, int sequence_number)
43 : callback_(callback),
44 sequence_number_(sequence_number),
45 received_process_group_count_(0),
46 processes_pending_(0) {
47 }
48 ~RequestContext() {}
49
50 void SetReceivedProcessGroupCount(bool done) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 received_process_group_count_ = done;
53 }
54
55 // Methods for book keeping of processes_pending_.
56 void AddProcessesPending(int processes_pending) {
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58 processes_pending_ += processes_pending;
59 }
60
61 void DecrementProcessesPending() {
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
63 --processes_pending_;
64 }
65
66 // Records that we are waiting for one less histogram data from a process for
67 // the given sequence number. If |received_process_group_count_| and
68 // |processes_pending_| are zero, then delete the current object by calling
69 // Unregister.
70 void DeleteIfAllDone() {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
72
73 if (processes_pending_ <= 0 && received_process_group_count_)
74 RequestContext::Unregister(sequence_number_);
75 }
76
77 // Register |callback| in |outstanding_requests_| map for the given
78 // |sequence_number|.
79 static void Register(const base::Closure& callback, int sequence_number) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
81
82 RequestContext* request = new RequestContext(callback, sequence_number);
83 outstanding_requests_.Get()[sequence_number] = request;
84 }
85
86 // Find the |RequestContext| in |outstanding_requests_| map for the given
87 // |sequence_number|.
88 static RequestContext* GetRequestContext(int sequence_number) {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90
91 RequestContextMap::iterator it =
92 outstanding_requests_.Get().find(sequence_number);
93 if (it == outstanding_requests_.Get().end())
94 return NULL;
95
96 RequestContext* request = it->second;
97 DCHECK_EQ(sequence_number, request->sequence_number_);
98 return request;
99 }
100
101 // Delete the entry for the given |sequence_number| from
102 // |outstanding_requests_| map. This method is called when all changes have
103 // been acquired, or when the wait time expires (whichever is sooner).
104 static void Unregister(int sequence_number) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
106
107 RequestContextMap::iterator it =
108 outstanding_requests_.Get().find(sequence_number);
109 if (it == outstanding_requests_.Get().end())
110 return;
111
112 RequestContext* request = it->second;
113 DCHECK_EQ(sequence_number, request->sequence_number_);
114 bool received_process_group_count = request->received_process_group_count_;
115 int unresponsive_processes = request->processes_pending_;
116
117 request->callback_.Run();
118
119 delete request;
120 outstanding_requests_.Get().erase(it);
121
122 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
123 received_process_group_count);
124 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
125 unresponsive_processes);
126 }
127
128 // Delete all the entries in |outstanding_requests_| map.
129 static void OnShutdown() {
130 // Just in case we have any pending tasks, clear them out.
131 while (!outstanding_requests_.Get().empty()) {
132 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
133 delete it->second;
134 outstanding_requests_.Get().erase(it);
135 }
136 }
137
138 // Requests are made to asynchronously send data to the |callback_|.
139 base::Closure callback_;
140
141 // The sequence number used by the most recent update request to contact all
142 // processes.
143 int sequence_number_;
144
145 // Indicates if we have received all pending processes count.
146 bool received_process_group_count_;
147
148 // The number of pending processes (all renderer processes and browser child
149 // processes) that have not yet responded to requests.
150 int processes_pending_;
151
152 // Map of all outstanding RequestContexts, from sequence_number_ to
153 // RequestContext.
154 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
155};
156
157// static
158base::LazyInstance
159 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
160 HistogramSynchronizer::RequestContext::outstanding_requests_ =
161 LAZY_INSTANCE_INITIALIZER;
162
163HistogramSynchronizer::HistogramSynchronizer()
164 : lock_(),
165 callback_thread_(NULL),
166 last_used_sequence_number_(kNeverUsableSequenceNumber),
167 async_sequence_number_(kNeverUsableSequenceNumber) {
168 content::HistogramController::GetInstance()->Register(this);
169}
170
171HistogramSynchronizer::~HistogramSynchronizer() {
172 RequestContext::OnShutdown();
173
174 // Just in case we have any pending tasks, clear them out.
175 SetCallbackTaskAndThread(NULL, base::Closure());
176}
177
178HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
179 return Singleton<HistogramSynchronizer,
180 LeakySingletonTraits<HistogramSynchronizer> >::get();
181}
182
183// static
184void HistogramSynchronizer::FetchHistograms() {
185 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
186 BrowserThread::PostTask(
187 BrowserThread::UI, FROM_HERE,
188 base::Bind(&HistogramSynchronizer::FetchHistograms));
189 return;
190 }
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
192
193 HistogramSynchronizer* current_synchronizer =
194 HistogramSynchronizer::GetInstance();
195 if (current_synchronizer == NULL)
196 return;
197
198 current_synchronizer->RegisterAndNotifyAllProcesses(
199 HistogramSynchronizer::UNKNOWN,
200 base::TimeDelta::FromMinutes(1));
201}
202
203void FetchHistogramsAsynchronously(MessageLoop* callback_thread,
204 const base::Closure& callback,
205 base::TimeDelta wait_time) {
206 HistogramSynchronizer::FetchHistogramsAsynchronously(
207 callback_thread, callback, wait_time);
208}
209
210// static
211void HistogramSynchronizer::FetchHistogramsAsynchronously(
212 MessageLoop* callback_thread,
213 const base::Closure& callback,
214 base::TimeDelta wait_time) {
215 DCHECK(callback_thread != NULL);
216 DCHECK(!callback.is_null());
217
218 HistogramSynchronizer* current_synchronizer =
219 HistogramSynchronizer::GetInstance();
220 current_synchronizer->SetCallbackTaskAndThread(
221 callback_thread, callback);
222
223 current_synchronizer->RegisterAndNotifyAllProcesses(
224 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
225}
226
227void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
228 ProcessHistogramRequester requester,
229 base::TimeDelta wait_time) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231
232 int sequence_number = GetNextAvailableSequenceNumber(requester);
233
234 base::Closure callback = base::Bind(
235 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
236 base::Unretained(this),
237 sequence_number);
238
239 RequestContext::Register(callback, sequence_number);
240
241 // Get histogram data from renderer and browser child processes.
242 content::HistogramController::GetInstance()->GetHistogramData(
243 sequence_number);
244
245 // Post a task that would be called after waiting for wait_time. This acts
246 // as a watchdog, to cancel the requests for non-responsive processes.
247 BrowserThread::PostDelayedTask(
248 BrowserThread::UI, FROM_HERE,
249 base::Bind(&RequestContext::Unregister, sequence_number),
250 wait_time);
251}
252
253void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
254 int pending_processes,
255 bool end) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
257
258 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
259 if (!request)
260 return;
261 request->AddProcessesPending(pending_processes);
262 request->SetReceivedProcessGroupCount(end);
263 request->DeleteIfAllDone();
264}
265
266void HistogramSynchronizer::OnHistogramDataCollected(
267 int sequence_number,
268 const std::vector<std::string>& pickled_histograms) {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270
271 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
272
273 for (std::vector<std::string>::const_iterator it = pickled_histograms.begin();
274 it < pickled_histograms.end();
275 ++it) {
276 base::Histogram::DeserializeHistogramInfo(*it);
277 }
278
279 if (!request)
280 return;
281
282 // Delete request if we have heard back from all child processes.
283 request->DecrementProcessesPending();
284 request->DeleteIfAllDone();
285}
286
287void HistogramSynchronizer::SetCallbackTaskAndThread(
288 MessageLoop* callback_thread,
289 const base::Closure& callback) {
290 base::Closure old_callback;
291 MessageLoop* old_thread = NULL;
292 {
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;
300 }
301 // Just in case there was a task pending....
302 InternalPostTask(old_thread, old_callback);
303}
304
305void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
306 int sequence_number) {
307 base::Closure callback;
308 MessageLoop* thread = NULL;
309 {
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;
317 }
318 InternalPostTask(thread, callback);
319}
320
321void HistogramSynchronizer::InternalPostTask(MessageLoop* thread,
322 const base::Closure& callback) {
323 if (callback.is_null() || !thread)
324 return;
325 thread->PostTask(FROM_HERE, callback);
326}
327
328int 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;
338 }
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_;
344}
345
346} // namespace content