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