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