• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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