• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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 "chrome/browser/metrics/histogram_synchronizer.h"
6 
7 #include "base/metrics/histogram.h"
8 #include "base/logging.h"
9 #include "base/threading/thread.h"
10 #include "chrome/common/chrome_constants.h"
11 #include "chrome/common/render_messages.h"
12 #include "content/browser/browser_thread.h"
13 #include "content/browser/renderer_host/render_process_host.h"
14 
15 using base::Time;
16 using base::TimeDelta;
17 using base::TimeTicks;
18 
19 // Negative numbers are never used as sequence numbers.  We explicitly pick a
20 // negative number that is "so negative" that even when we add one (as is done
21 // when we generated the next sequence number) that it will still be negative.
22 // We have code that handles wrapping around on an overflow into negative
23 // territory.
24 static const int kNeverUsableSequenceNumber = -2;
25 
HistogramSynchronizer()26 HistogramSynchronizer::HistogramSynchronizer()
27   : lock_(),
28     received_all_renderer_histograms_(&lock_),
29     callback_task_(NULL),
30     callback_thread_(NULL),
31     last_used_sequence_number_(kNeverUsableSequenceNumber),
32     async_sequence_number_(kNeverUsableSequenceNumber),
33     async_renderers_pending_(0),
34     synchronous_sequence_number_(kNeverUsableSequenceNumber),
35     synchronous_renderers_pending_(0) {
36   DCHECK(histogram_synchronizer_ == NULL);
37   histogram_synchronizer_ = this;
38 }
39 
~HistogramSynchronizer()40 HistogramSynchronizer::~HistogramSynchronizer() {
41   // Just in case we have any pending tasks, clear them out.
42   SetCallbackTaskAndThread(NULL, NULL);
43   histogram_synchronizer_ = NULL;
44 }
45 
46 // static
CurrentSynchronizer()47 HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() {
48   DCHECK(histogram_synchronizer_ != NULL);
49   return histogram_synchronizer_;
50 }
51 
FetchRendererHistogramsSynchronously(TimeDelta wait_time)52 void HistogramSynchronizer::FetchRendererHistogramsSynchronously(
53     TimeDelta wait_time) {
54   NotifyAllRenderers(SYNCHRONOUS_HISTOGRAMS);
55 
56   TimeTicks start = TimeTicks::Now();
57   TimeTicks end_time = start + wait_time;
58   int unresponsive_renderer_count;
59   {
60     base::AutoLock auto_lock(lock_);
61     while (synchronous_renderers_pending_ > 0 && TimeTicks::Now() < end_time) {
62       wait_time = end_time - TimeTicks::Now();
63       received_all_renderer_histograms_.TimedWait(wait_time);
64     }
65     unresponsive_renderer_count = synchronous_renderers_pending_;
66     synchronous_renderers_pending_ = 0;
67     synchronous_sequence_number_ = kNeverUsableSequenceNumber;
68   }
69   UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingSynchronous",
70                        unresponsive_renderer_count);
71   if (!unresponsive_renderer_count)
72     UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsSynchronously",
73                         TimeTicks::Now() - start);
74 }
75 
76 // static
FetchRendererHistogramsAsynchronously(MessageLoop * callback_thread,Task * callback_task,int wait_time)77 void HistogramSynchronizer::FetchRendererHistogramsAsynchronously(
78     MessageLoop* callback_thread,
79     Task* callback_task,
80     int wait_time) {
81   DCHECK(callback_thread != NULL);
82   DCHECK(callback_task != NULL);
83 
84   HistogramSynchronizer* current_synchronizer = CurrentSynchronizer();
85 
86   if (current_synchronizer == NULL) {
87     // System teardown is happening.
88     callback_thread->PostTask(FROM_HERE, callback_task);
89     return;
90   }
91 
92   current_synchronizer->SetCallbackTaskAndThread(callback_thread,
93                                                  callback_task);
94 
95   int sequence_number =
96       current_synchronizer->NotifyAllRenderers(ASYNC_HISTOGRAMS);
97 
98   // Post a task that would be called after waiting for wait_time.  This acts
99   // as a watchdog, to ensure that a non-responsive renderer won't block us from
100   // making the callback.
101   BrowserThread::PostDelayedTask(
102       BrowserThread::UI, FROM_HERE,
103       NewRunnableMethod(
104           current_synchronizer,
105           &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
106           sequence_number),
107       wait_time);
108 }
109 
110 // static
DeserializeHistogramList(int sequence_number,const std::vector<std::string> & histograms)111 void HistogramSynchronizer::DeserializeHistogramList(
112     int sequence_number,
113     const std::vector<std::string>& histograms) {
114   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
115   for (std::vector<std::string>::const_iterator it = histograms.begin();
116        it < histograms.end();
117        ++it) {
118     base::Histogram::DeserializeHistogramInfo(*it);
119   }
120 
121   HistogramSynchronizer* current_synchronizer = CurrentSynchronizer();
122   if (current_synchronizer == NULL)
123     return;
124 
125   // Record that we have received a histogram from renderer process.
126   current_synchronizer->DecrementPendingRenderers(sequence_number);
127 }
128 
NotifyAllRenderers(RendererHistogramRequester requester)129 int HistogramSynchronizer::NotifyAllRenderers(
130     RendererHistogramRequester requester) {
131   // To iterate over RenderProcessHosts, or to send messages to the hosts, we
132   // need to be on the UI thread.
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134 
135   int notification_count = 0;
136   for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
137        !it.IsAtEnd(); it.Advance())
138      ++notification_count;
139 
140   int sequence_number = GetNextAvailableSequenceNumber(requester,
141                                                        notification_count);
142   for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
143        !it.IsAtEnd(); it.Advance()) {
144     if (!it.GetCurrentValue()->Send(
145         new ViewMsg_GetRendererHistograms(sequence_number)))
146       DecrementPendingRenderers(sequence_number);
147   }
148 
149   return sequence_number;
150 }
151 
DecrementPendingRenderers(int sequence_number)152 void HistogramSynchronizer::DecrementPendingRenderers(int sequence_number) {
153   bool synchronous_completed = false;
154   bool asynchronous_completed = false;
155 
156   {
157     base::AutoLock auto_lock(lock_);
158     if (sequence_number == async_sequence_number_) {
159       if (--async_renderers_pending_ <= 0)
160         asynchronous_completed = true;
161     } else if (sequence_number == synchronous_sequence_number_) {
162       if (--synchronous_renderers_pending_ <= 0)
163         synchronous_completed = true;
164     }
165   }
166 
167   if (asynchronous_completed)
168     ForceHistogramSynchronizationDoneCallback(sequence_number);
169   else if (synchronous_completed)
170     received_all_renderer_histograms_.Signal();
171 }
172 
SetCallbackTaskAndThread(MessageLoop * callback_thread,Task * callback_task)173 void HistogramSynchronizer::SetCallbackTaskAndThread(
174     MessageLoop* callback_thread,
175     Task* callback_task) {
176   Task* old_task = NULL;
177   MessageLoop* old_thread = NULL;
178   TimeTicks old_start_time;
179   int unresponsive_renderers;
180   const TimeTicks now = TimeTicks::Now();
181   {
182     base::AutoLock auto_lock(lock_);
183     old_task = callback_task_;
184     callback_task_ = callback_task;
185     old_thread = callback_thread_;
186     callback_thread_ = callback_thread;
187     unresponsive_renderers = async_renderers_pending_;
188     old_start_time = async_callback_start_time_;
189     async_callback_start_time_ = now;
190     // Prevent premature calling of our new callbacks.
191     async_sequence_number_ = kNeverUsableSequenceNumber;
192   }
193   // Just in case there was a task pending....
194   InternalPostTask(old_thread, old_task, unresponsive_renderers,
195                    old_start_time);
196 }
197 
ForceHistogramSynchronizationDoneCallback(int sequence_number)198 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
199     int sequence_number) {
200   Task* task = NULL;
201   MessageLoop* thread = NULL;
202   TimeTicks started;
203   int unresponsive_renderers;
204   {
205     base::AutoLock lock(lock_);
206     if (sequence_number != async_sequence_number_)
207       return;
208     task = callback_task_;
209     thread = callback_thread_;
210     callback_task_ = NULL;
211     callback_thread_ = NULL;
212     started = async_callback_start_time_;
213     unresponsive_renderers = async_renderers_pending_;
214   }
215   InternalPostTask(thread, task, unresponsive_renderers, started);
216 }
217 
InternalPostTask(MessageLoop * thread,Task * task,int unresponsive_renderers,const base::TimeTicks & started)218 void HistogramSynchronizer::InternalPostTask(MessageLoop* thread, Task* task,
219                                              int unresponsive_renderers,
220                                              const base::TimeTicks& started) {
221   if (!task || !thread)
222     return;
223   UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous",
224                        unresponsive_renderers);
225   if (!unresponsive_renderers) {
226     UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously",
227                         TimeTicks::Now() - started);
228   }
229 
230   thread->PostTask(FROM_HERE, task);
231 }
232 
GetNextAvailableSequenceNumber(RendererHistogramRequester requester,int renderer_count)233 int HistogramSynchronizer::GetNextAvailableSequenceNumber(
234     RendererHistogramRequester requester,
235     int renderer_count) {
236   base::AutoLock auto_lock(lock_);
237   ++last_used_sequence_number_;
238   // Watch out for wrapping to a negative number.
239   if (last_used_sequence_number_ < 0) {
240     // Bypass the reserved number, which is used when a renderer spontaneously
241     // decides to send some histogram data.
242     last_used_sequence_number_ =
243         chrome::kHistogramSynchronizerReservedSequenceNumber + 1;
244   }
245   DCHECK_NE(last_used_sequence_number_,
246             chrome::kHistogramSynchronizerReservedSequenceNumber);
247   if (requester == ASYNC_HISTOGRAMS) {
248     async_sequence_number_ = last_used_sequence_number_;
249     async_renderers_pending_ = renderer_count;
250   } else if (requester == SYNCHRONOUS_HISTOGRAMS) {
251     synchronous_sequence_number_ = last_used_sequence_number_;
252     synchronous_renderers_pending_ = renderer_count;
253   }
254   return last_used_sequence_number_;
255 }
256 
257 // static
258 HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL;
259