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/pickle.h" 12 #include "base/threading/thread.h" 13 #include "base/threading/thread_restrictions.h" 14 #include "content/browser/histogram_controller.h" 15 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/histogram_fetcher.h" 17 #include "content/public/common/content_constants.h" 18 19 using base::Time; 20 using base::TimeDelta; 21 using base::TimeTicks; 22 23 namespace { 24 25 // Negative numbers are never used as sequence numbers. We explicitly pick a 26 // negative number that is "so negative" that even when we add one (as is done 27 // when we generated the next sequence number) that it will still be negative. 28 // We have code that handles wrapping around on an overflow into negative 29 // territory. 30 static const int kNeverUsableSequenceNumber = -2; 31 32 } // anonymous namespace 33 34 namespace content { 35 36 // The "RequestContext" structure describes an individual request received from 37 // the UI. All methods are accessible on UI thread. 38 class HistogramSynchronizer::RequestContext { 39 public: 40 // A map from sequence_number_ to the actual RequestContexts. 41 typedef std::map<int, RequestContext*> RequestContextMap; 42 43 RequestContext(const base::Closure& callback, int sequence_number) 44 : callback_(callback), 45 sequence_number_(sequence_number), 46 received_process_group_count_(0), 47 processes_pending_(0) { 48 } 49 ~RequestContext() {} 50 51 void SetReceivedProcessGroupCount(bool done) { 52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53 received_process_group_count_ = done; 54 } 55 56 // Methods for book keeping of processes_pending_. 57 void AddProcessesPending(int processes_pending) { 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 59 processes_pending_ += processes_pending; 60 } 61 62 void DecrementProcessesPending() { 63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 64 --processes_pending_; 65 } 66 67 // Records that we are waiting for one less histogram data from a process for 68 // the given sequence number. If |received_process_group_count_| and 69 // |processes_pending_| are zero, then delete the current object by calling 70 // Unregister. 71 void DeleteIfAllDone() { 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 73 74 if (processes_pending_ <= 0 && received_process_group_count_) 75 RequestContext::Unregister(sequence_number_); 76 } 77 78 // Register |callback| in |outstanding_requests_| map for the given 79 // |sequence_number|. 80 static void Register(const base::Closure& callback, int sequence_number) { 81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 82 83 RequestContext* request = new RequestContext(callback, sequence_number); 84 outstanding_requests_.Get()[sequence_number] = request; 85 } 86 87 // Find the |RequestContext| in |outstanding_requests_| map for the given 88 // |sequence_number|. 89 static RequestContext* GetRequestContext(int sequence_number) { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 91 92 RequestContextMap::iterator it = 93 outstanding_requests_.Get().find(sequence_number); 94 if (it == outstanding_requests_.Get().end()) 95 return NULL; 96 97 RequestContext* request = it->second; 98 DCHECK_EQ(sequence_number, request->sequence_number_); 99 return request; 100 } 101 102 // Delete the entry for the given |sequence_number| from 103 // |outstanding_requests_| map. This method is called when all changes have 104 // been acquired, or when the wait time expires (whichever is sooner). 105 static void Unregister(int sequence_number) { 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 107 108 RequestContextMap::iterator it = 109 outstanding_requests_.Get().find(sequence_number); 110 if (it == outstanding_requests_.Get().end()) 111 return; 112 113 RequestContext* request = it->second; 114 DCHECK_EQ(sequence_number, request->sequence_number_); 115 bool received_process_group_count = request->received_process_group_count_; 116 int unresponsive_processes = request->processes_pending_; 117 118 request->callback_.Run(); 119 120 delete request; 121 outstanding_requests_.Get().erase(it); 122 123 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount", 124 received_process_group_count); 125 UMA_HISTOGRAM_COUNTS("Histogram.PendingProcessNotResponding", 126 unresponsive_processes); 127 } 128 129 // Delete all the entries in |outstanding_requests_| map. 130 static void OnShutdown() { 131 // Just in case we have any pending tasks, clear them out. 132 while (!outstanding_requests_.Get().empty()) { 133 RequestContextMap::iterator it = outstanding_requests_.Get().begin(); 134 delete it->second; 135 outstanding_requests_.Get().erase(it); 136 } 137 } 138 139 // Requests are made to asynchronously send data to the |callback_|. 140 base::Closure callback_; 141 142 // The sequence number used by the most recent update request to contact all 143 // processes. 144 int sequence_number_; 145 146 // Indicates if we have received all pending processes count. 147 bool received_process_group_count_; 148 149 // The number of pending processes (all renderer processes and browser child 150 // processes) that have not yet responded to requests. 151 int processes_pending_; 152 153 // Map of all outstanding RequestContexts, from sequence_number_ to 154 // RequestContext. 155 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_; 156 }; 157 158 // static 159 base::LazyInstance 160 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky 161 HistogramSynchronizer::RequestContext::outstanding_requests_ = 162 LAZY_INSTANCE_INITIALIZER; 163 164 HistogramSynchronizer::HistogramSynchronizer() 165 : lock_(), 166 callback_thread_(NULL), 167 last_used_sequence_number_(kNeverUsableSequenceNumber), 168 async_sequence_number_(kNeverUsableSequenceNumber) { 169 HistogramController::GetInstance()->Register(this); 170 } 171 172 HistogramSynchronizer::~HistogramSynchronizer() { 173 RequestContext::OnShutdown(); 174 175 // Just in case we have any pending tasks, clear them out. 176 SetCallbackTaskAndThread(NULL, base::Closure()); 177 } 178 179 HistogramSynchronizer* HistogramSynchronizer::GetInstance() { 180 return Singleton<HistogramSynchronizer, 181 LeakySingletonTraits<HistogramSynchronizer> >::get(); 182 } 183 184 // static 185 void HistogramSynchronizer::FetchHistograms() { 186 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 187 BrowserThread::PostTask( 188 BrowserThread::UI, FROM_HERE, 189 base::Bind(&HistogramSynchronizer::FetchHistograms)); 190 return; 191 } 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 193 194 HistogramSynchronizer* current_synchronizer = 195 HistogramSynchronizer::GetInstance(); 196 if (current_synchronizer == NULL) 197 return; 198 199 current_synchronizer->RegisterAndNotifyAllProcesses( 200 HistogramSynchronizer::UNKNOWN, 201 base::TimeDelta::FromMinutes(1)); 202 } 203 204 void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread, 205 const base::Closure& callback, 206 base::TimeDelta wait_time) { 207 HistogramSynchronizer::FetchHistogramsAsynchronously( 208 callback_thread, callback, wait_time); 209 } 210 211 // static 212 void HistogramSynchronizer::FetchHistogramsAsynchronously( 213 base::MessageLoop* callback_thread, 214 const base::Closure& callback, 215 base::TimeDelta wait_time) { 216 DCHECK(callback_thread != NULL); 217 DCHECK(!callback.is_null()); 218 219 HistogramSynchronizer* current_synchronizer = 220 HistogramSynchronizer::GetInstance(); 221 current_synchronizer->SetCallbackTaskAndThread( 222 callback_thread, callback); 223 224 current_synchronizer->RegisterAndNotifyAllProcesses( 225 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time); 226 } 227 228 void HistogramSynchronizer::RegisterAndNotifyAllProcesses( 229 ProcessHistogramRequester requester, 230 base::TimeDelta wait_time) { 231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 232 233 int sequence_number = GetNextAvailableSequenceNumber(requester); 234 235 base::Closure callback = base::Bind( 236 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, 237 base::Unretained(this), 238 sequence_number); 239 240 RequestContext::Register(callback, sequence_number); 241 242 // Get histogram data from renderer and browser child processes. 243 HistogramController::GetInstance()->GetHistogramData(sequence_number); 244 245 // Post a task that would be called after waiting for wait_time. This acts 246 // as a watchdog, to cancel the requests for non-responsive processes. 247 BrowserThread::PostDelayedTask( 248 BrowserThread::UI, FROM_HERE, 249 base::Bind(&RequestContext::Unregister, sequence_number), 250 wait_time); 251 } 252 253 void HistogramSynchronizer::OnPendingProcesses(int sequence_number, 254 int pending_processes, 255 bool end) { 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 257 258 RequestContext* request = RequestContext::GetRequestContext(sequence_number); 259 if (!request) 260 return; 261 request->AddProcessesPending(pending_processes); 262 request->SetReceivedProcessGroupCount(end); 263 request->DeleteIfAllDone(); 264 } 265 266 void HistogramSynchronizer::OnHistogramDataCollected( 267 int sequence_number, 268 const std::vector<std::string>& pickled_histograms) { 269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 270 271 RequestContext* request = RequestContext::GetRequestContext(sequence_number); 272 273 for (std::vector<std::string>::const_iterator it = pickled_histograms.begin(); 274 it < pickled_histograms.end(); 275 ++it) { 276 Pickle pickle(it->data(), it->size()); 277 PickleIterator iter(pickle); 278 base::DeserializeHistogramAndAddSamples(&iter); 279 } 280 281 if (!request) 282 return; 283 284 // Delete request if we have heard back from all child processes. 285 request->DecrementProcessesPending(); 286 request->DeleteIfAllDone(); 287 } 288 289 void HistogramSynchronizer::SetCallbackTaskAndThread( 290 base::MessageLoop* callback_thread, 291 const base::Closure& callback) { 292 base::Closure old_callback; 293 base::MessageLoop* old_thread = NULL; 294 { 295 base::AutoLock auto_lock(lock_); 296 old_callback = callback_; 297 callback_ = callback; 298 old_thread = callback_thread_; 299 callback_thread_ = callback_thread; 300 // Prevent premature calling of our new callbacks. 301 async_sequence_number_ = kNeverUsableSequenceNumber; 302 } 303 // Just in case there was a task pending.... 304 InternalPostTask(old_thread, old_callback); 305 } 306 307 void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( 308 int sequence_number) { 309 base::Closure callback; 310 base::MessageLoop* thread = NULL; 311 { 312 base::AutoLock lock(lock_); 313 if (sequence_number != async_sequence_number_) 314 return; 315 callback = callback_; 316 thread = callback_thread_; 317 callback_.Reset(); 318 callback_thread_ = NULL; 319 } 320 InternalPostTask(thread, callback); 321 } 322 323 void HistogramSynchronizer::InternalPostTask(base::MessageLoop* thread, 324 const base::Closure& callback) { 325 if (callback.is_null() || !thread) 326 return; 327 thread->PostTask(FROM_HERE, callback); 328 } 329 330 int HistogramSynchronizer::GetNextAvailableSequenceNumber( 331 ProcessHistogramRequester requester) { 332 base::AutoLock auto_lock(lock_); 333 ++last_used_sequence_number_; 334 // Watch out for wrapping to a negative number. 335 if (last_used_sequence_number_ < 0) { 336 // Bypass the reserved number, which is used when a renderer spontaneously 337 // decides to send some histogram data. 338 last_used_sequence_number_ = 339 kHistogramSynchronizerReservedSequenceNumber + 1; 340 } 341 DCHECK_NE(last_used_sequence_number_, 342 kHistogramSynchronizerReservedSequenceNumber); 343 if (requester == ASYNC_HISTOGRAMS) 344 async_sequence_number_ = last_used_sequence_number_; 345 return last_used_sequence_number_; 346 } 347 348 } // namespace content 349