histogram_synchronizer.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2009 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/thread.h" 10#include "chrome/browser/browser_thread.h" 11#include "chrome/browser/renderer_host/render_process_host.h" 12#include "chrome/common/chrome_constants.h" 13#include "chrome/common/render_messages.h" 14 15using base::Time; 16using base::TimeDelta; 17using base::TimeTicks; 18 19static const int kNeverUsableSequenceNumber = -2; 20 21HistogramSynchronizer::HistogramSynchronizer() 22 : lock_(), 23 received_all_renderer_histograms_(&lock_), 24 callback_task_(NULL), 25 callback_thread_(NULL), 26 next_available_sequence_number_(kNeverUsableSequenceNumber), 27 async_sequence_number_(kNeverUsableSequenceNumber), 28 async_renderers_pending_(0), 29 async_callback_start_time_(TimeTicks::Now()), 30 synchronous_sequence_number_(kNeverUsableSequenceNumber), 31 synchronous_renderers_pending_(0) { 32 DCHECK(histogram_synchronizer_ == NULL); 33 histogram_synchronizer_ = this; 34} 35 36HistogramSynchronizer::~HistogramSynchronizer() { 37 // Clean up. 38 delete callback_task_; 39 callback_task_ = NULL; 40 callback_thread_ = NULL; 41 histogram_synchronizer_ = NULL; 42} 43 44// static 45HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() { 46 DCHECK(histogram_synchronizer_ != NULL); 47 return histogram_synchronizer_; 48} 49 50void HistogramSynchronizer::FetchRendererHistogramsSynchronously( 51 TimeDelta wait_time) { 52 DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); 53 54 int sequence_number = GetNextAvailableSequenceNumber(SYNCHRONOUS_HISTOGRAMS); 55 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); 56 !it.IsAtEnd(); it.Advance()) { 57 IncrementPendingRenderers(SYNCHRONOUS_HISTOGRAMS); 58 it.GetCurrentValue()->Send( 59 new ViewMsg_GetRendererHistograms(sequence_number)); 60 } 61 // Send notification that we're done sending messages to renderers. 62 DecrementPendingRenderers(sequence_number); 63 64 TimeTicks start = TimeTicks::Now(); 65 TimeTicks end_time = start + wait_time; 66 int unresponsive_renderer_count; 67 { 68 AutoLock auto_lock(lock_); 69 while (synchronous_renderers_pending_ > 0 && TimeTicks::Now() < end_time) { 70 wait_time = end_time - TimeTicks::Now(); 71 received_all_renderer_histograms_.TimedWait(wait_time); 72 } 73 unresponsive_renderer_count = synchronous_renderers_pending_; 74 synchronous_renderers_pending_ = 0; 75 synchronous_sequence_number_ = kNeverUsableSequenceNumber; 76 } 77 UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingSynchronous", 78 unresponsive_renderer_count); 79 if (!unresponsive_renderer_count) 80 UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsSynchronously", 81 TimeTicks::Now() - start); 82} 83 84// static 85void HistogramSynchronizer::FetchRendererHistogramsAsynchronously( 86 MessageLoop* callback_thread, 87 Task* callback_task, 88 int wait_time) { 89 DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); 90 DCHECK(callback_thread != NULL); 91 DCHECK(callback_task != NULL); 92 93 HistogramSynchronizer* current_synchronizer = 94 HistogramSynchronizer::CurrentSynchronizer(); 95 96 if (current_synchronizer == NULL) { 97 // System teardown is happening. 98 callback_thread->PostTask(FROM_HERE, callback_task); 99 return; 100 } 101 102 // callback_task_ member can only be accessed on IO thread. 103 BrowserThread::PostTask( 104 BrowserThread::IO, FROM_HERE, 105 NewRunnableMethod( 106 current_synchronizer, 107 &HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms, 108 callback_thread, 109 callback_task)); 110 111 // Tell all renderer processes to send their histograms. 112 int sequence_number = 113 current_synchronizer->GetNextAvailableSequenceNumber(ASYNC_HISTOGRAMS); 114 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); 115 !it.IsAtEnd(); it.Advance()) { 116 current_synchronizer->IncrementPendingRenderers(ASYNC_HISTOGRAMS); 117 it.GetCurrentValue()->Send( 118 new ViewMsg_GetRendererHistograms(sequence_number)); 119 } 120 // Send notification that we're done sending messages to renderers. 121 current_synchronizer->DecrementPendingRenderers(sequence_number); 122 123 // Post a task that would be called after waiting for wait_time. 124 BrowserThread::PostDelayedTask( 125 BrowserThread::IO, FROM_HERE, 126 NewRunnableMethod( 127 current_synchronizer, 128 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, 129 sequence_number), 130 wait_time); 131} 132 133// static 134void HistogramSynchronizer::DeserializeHistogramList( 135 int sequence_number, 136 const std::vector<std::string>& histograms) { 137 HistogramSynchronizer* current_synchronizer = 138 HistogramSynchronizer::CurrentSynchronizer(); 139 if (current_synchronizer == NULL) 140 return; 141 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 143 144 for (std::vector<std::string>::const_iterator it = histograms.begin(); 145 it < histograms.end(); 146 ++it) { 147 base::Histogram::DeserializeHistogramInfo(*it); 148 } 149 150 // Record that we have received a histogram from renderer process. 151 current_synchronizer->DecrementPendingRenderers(sequence_number); 152} 153 154bool HistogramSynchronizer::DecrementPendingRenderers(int sequence_number) { 155 if (sequence_number == async_sequence_number_) { 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 157 if ((async_renderers_pending_ == 0) || 158 (--async_renderers_pending_ > 0)) 159 return false; 160 DCHECK(callback_task_ != NULL); 161 CallCallbackTaskAndResetData(); 162 return true; 163 } 164 165 { 166 AutoLock auto_lock(lock_); 167 if (sequence_number != synchronous_sequence_number_) { 168 // No need to do anything if the sequence_number does not match current 169 // synchronous_sequence_number_ or async_sequence_number_. 170 return true; 171 } 172 if (--synchronous_renderers_pending_ > 0) 173 return false; 174 DCHECK_EQ(synchronous_renderers_pending_, 0); 175 } 176 177 // We can call Signal() without holding the lock. 178 received_all_renderer_histograms_.Signal(); 179 return true; 180} 181 182// This method is called on the IO thread. 183void HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms( 184 MessageLoop* callback_thread, 185 Task* callback_task) { 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 187 188 // Test for the existence of a previous task, and post call to post it if it 189 // exists. We promised to post it after some timeout... and at this point, we 190 // should just force the posting. 191 if (callback_task_ != NULL) { 192 CallCallbackTaskAndResetData(); 193 } 194 195 // Assert there was no callback_task_ already. 196 DCHECK(callback_task_ == NULL); 197 198 // Save the thread and the callback_task. 199 DCHECK(callback_thread != NULL); 200 DCHECK(callback_task != NULL); 201 callback_task_ = callback_task; 202 callback_thread_ = callback_thread; 203 async_callback_start_time_ = TimeTicks::Now(); 204} 205 206void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( 207 int sequence_number) { 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 209 210 if (sequence_number == async_sequence_number_) { 211 CallCallbackTaskAndResetData(); 212 } 213} 214 215// If wait time has elapsed or if we have received all the histograms from all 216// the renderers, call the callback_task if a callback_task exists. This is 217// called on IO Thread. 218void HistogramSynchronizer::CallCallbackTaskAndResetData() { 219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 220 221 // callback_task_ would be set to NULL, if we have heard from all renderers 222 // and we would have called the callback_task already. 223 if (callback_task_ == NULL) { 224 return; 225 } 226 227 UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous", 228 async_renderers_pending_); 229 if (!async_renderers_pending_) 230 UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously", 231 TimeTicks::Now() - async_callback_start_time_); 232 233 DCHECK(callback_thread_ != NULL); 234 DCHECK(callback_task_ != NULL); 235 callback_thread_->PostTask(FROM_HERE, callback_task_); 236 async_renderers_pending_ = 0; 237 async_callback_start_time_ = TimeTicks::Now(); 238 callback_task_ = NULL; 239 callback_thread_ = NULL; 240 async_sequence_number_ = kNeverUsableSequenceNumber; 241} 242 243int HistogramSynchronizer::GetNextAvailableSequenceNumber( 244 RendererHistogramRequester requester) { 245 AutoLock auto_lock(lock_); 246 ++next_available_sequence_number_; 247 if (0 > next_available_sequence_number_) { 248 // We wrapped around, so we need to bypass the reserved number. 249 next_available_sequence_number_ = 250 chrome::kHistogramSynchronizerReservedSequenceNumber + 1; 251 } 252 DCHECK_NE(next_available_sequence_number_, 253 chrome::kHistogramSynchronizerReservedSequenceNumber); 254 if (requester == ASYNC_HISTOGRAMS) { 255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 256 async_sequence_number_ = next_available_sequence_number_; 257 async_renderers_pending_ = 1; 258 } else if (requester == SYNCHRONOUS_HISTOGRAMS) { 259 synchronous_sequence_number_ = next_available_sequence_number_; 260 synchronous_renderers_pending_ = 1; 261 } 262 return next_available_sequence_number_; 263} 264 265void HistogramSynchronizer::IncrementPendingRenderers( 266 RendererHistogramRequester requester) { 267 if (requester == ASYNC_HISTOGRAMS) { 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 269 DCHECK_GT(async_renderers_pending_, 0); 270 ++async_renderers_pending_; 271 } else { 272 AutoLock auto_lock(lock_); 273 DCHECK_GT(synchronous_renderers_pending_, 0); 274 ++synchronous_renderers_pending_; 275 } 276} 277 278// static 279HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL; 280