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.
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/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"
19
20 using base::Time;
21 using base::TimeDelta;
22 using base::TimeTicks;
23
24 namespace {
25
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;
32
33 } // anonymous namespace
34
35 namespace content {
36
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;
43
RequestContext(const base::Closure & callback,int sequence_number)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) {
49 }
~RequestContext()50 ~RequestContext() {}
51
SetReceivedProcessGroupCount(bool done)52 void SetReceivedProcessGroupCount(bool done) {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
54 received_process_group_count_ = done;
55 }
56
57 // Methods for book keeping of processes_pending_.
AddProcessesPending(int processes_pending)58 void AddProcessesPending(int processes_pending) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 processes_pending_ += processes_pending;
61 }
62
DecrementProcessesPending()63 void DecrementProcessesPending() {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65 --processes_pending_;
66 }
67
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.
DeleteIfAllDone()72 void DeleteIfAllDone() {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74
75 if (processes_pending_ <= 0 && received_process_group_count_)
76 RequestContext::Unregister(sequence_number_);
77 }
78
79 // Register |callback| in |outstanding_requests_| map for the given
80 // |sequence_number|.
Register(const base::Closure & callback,int sequence_number)81 static void Register(const base::Closure& callback, int sequence_number) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83
84 RequestContext* request = new RequestContext(callback, sequence_number);
85 outstanding_requests_.Get()[sequence_number] = request;
86 }
87
88 // Find the |RequestContext| in |outstanding_requests_| map for the given
89 // |sequence_number|.
GetRequestContext(int sequence_number)90 static RequestContext* GetRequestContext(int sequence_number) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92
93 RequestContextMap::iterator it =
94 outstanding_requests_.Get().find(sequence_number);
95 if (it == outstanding_requests_.Get().end())
96 return NULL;
97
98 RequestContext* request = it->second;
99 DCHECK_EQ(sequence_number, request->sequence_number_);
100 return request;
101 }
102
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).
Unregister(int sequence_number)106 static void Unregister(int sequence_number) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108
109 RequestContextMap::iterator it =
110 outstanding_requests_.Get().find(sequence_number);
111 if (it == outstanding_requests_.Get().end())
112 return;
113
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_;
118
119 request->callback_.Run();
120
121 delete request;
122 outstanding_requests_.Get().erase(it);
123
124 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
125 received_process_group_count);
126 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding",
127 unresponsive_processes);
128 }
129
130 // Delete all the entries in |outstanding_requests_| map.
OnShutdown()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);
137 }
138 }
139
140 // Requests are made to asynchronously send data to the |callback_|.
141 base::Closure callback_;
142
143 // The sequence number used by the most recent update request to contact all
144 // processes.
145 int sequence_number_;
146
147 // Indicates if we have received all pending processes count.
148 bool received_process_group_count_;
149
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_;
153
154 // Map of all outstanding RequestContexts, from sequence_number_ to
155 // RequestContext.
156 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
157 };
158
159 // static
160 base::LazyInstance
161 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
162 HistogramSynchronizer::RequestContext::outstanding_requests_ =
163 LAZY_INSTANCE_INITIALIZER;
164
HistogramSynchronizer()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);
171 }
172
~HistogramSynchronizer()173 HistogramSynchronizer::~HistogramSynchronizer() {
174 RequestContext::OnShutdown();
175
176 // Just in case we have any pending tasks, clear them out.
177 SetCallbackTaskAndThread(NULL, base::Closure());
178 }
179
GetInstance()180 HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
181 return Singleton<HistogramSynchronizer,
182 LeakySingletonTraits<HistogramSynchronizer> >::get();
183 }
184
185 // static
FetchHistograms()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;
192 }
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
194
195 HistogramSynchronizer* current_synchronizer =
196 HistogramSynchronizer::GetInstance();
197 if (current_synchronizer == NULL)
198 return;
199
200 current_synchronizer->RegisterAndNotifyAllProcesses(
201 HistogramSynchronizer::UNKNOWN,
202 base::TimeDelta::FromMinutes(1));
203 }
204
FetchHistogramsAsynchronously(base::MessageLoop * callback_thread,const base::Closure & callback,base::TimeDelta wait_time)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);
210 }
211
212 // static
FetchHistogramsAsynchronously(base::MessageLoop * callback_thread,const base::Closure & callback,base::TimeDelta 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());
219
220 HistogramSynchronizer* current_synchronizer =
221 HistogramSynchronizer::GetInstance();
222 current_synchronizer->SetCallbackTaskAndThread(
223 callback_thread, callback);
224
225 current_synchronizer->RegisterAndNotifyAllProcesses(
226 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
227 }
228
RegisterAndNotifyAllProcesses(ProcessHistogramRequester requester,base::TimeDelta wait_time)229 void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
230 ProcessHistogramRequester requester,
231 base::TimeDelta wait_time) {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233
234 int sequence_number = GetNextAvailableSequenceNumber(requester);
235
236 base::Closure callback = base::Bind(
237 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
238 base::Unretained(this),
239 sequence_number);
240
241 RequestContext::Register(callback, sequence_number);
242
243 // Get histogram data from renderer and browser child processes.
244 HistogramController::GetInstance()->GetHistogramData(sequence_number);
245
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);
252 }
253
OnPendingProcesses(int sequence_number,int pending_processes,bool end)254 void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
255 int pending_processes,
256 bool end) {
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
258
259 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
260 if (!request)
261 return;
262 request->AddProcessesPending(pending_processes);
263 request->SetReceivedProcessGroupCount(end);
264 request->DeleteIfAllDone();
265 }
266
OnHistogramDataCollected(int sequence_number,const std::vector<std::string> & pickled_histograms)267 void HistogramSynchronizer::OnHistogramDataCollected(
268 int sequence_number,
269 const std::vector<std::string>& pickled_histograms) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
273 pickled_histograms);
274
275 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
276 if (!request)
277 return;
278
279 // Delete request if we have heard back from all child processes.
280 request->DecrementProcessesPending();
281 request->DeleteIfAllDone();
282 }
283
SetCallbackTaskAndThread(base::MessageLoop * callback_thread,const base::Closure & callback)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;
289 {
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;
297 }
298 // Just in case there was a task pending....
299 InternalPostTask(old_thread, old_callback);
300 }
301
ForceHistogramSynchronizationDoneCallback(int sequence_number)302 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
303 int sequence_number) {
304 base::Closure callback;
305 base::MessageLoop* thread = NULL;
306 {
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;
314 }
315 InternalPostTask(thread, callback);
316 }
317
InternalPostTask(base::MessageLoop * thread,const base::Closure & 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);
323 }
324
GetNextAvailableSequenceNumber(ProcessHistogramRequester requester)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;
335 }
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_;
341 }
342
343 } // namespace content
344