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 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 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 47 HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() { 48 DCHECK(histogram_synchronizer_ != NULL); 49 return histogram_synchronizer_; 50 } 51 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 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 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 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 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 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 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 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 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