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 "chrome/browser/metrics/tracking_synchronizer.h" 6 7 #include "base/bind.h" 8 #include "base/metrics/histogram.h" 9 #include "base/threading/thread.h" 10 #include "base/tracked_objects.h" 11 #include "chrome/browser/metrics/tracking_synchronizer_observer.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/profiler_controller.h" 14 #include "content/public/common/process_type.h" 15 16 using base::TimeTicks; 17 using content::BrowserThread; 18 19 namespace { 20 21 // Negative numbers are never used as sequence numbers. We explicitly pick a 22 // negative number that is "so negative" that even when we add one (as is done 23 // when we generated the next sequence number) that it will still be negative. 24 // We have code that handles wrapping around on an overflow into negative 25 // territory. 26 const int kNeverUsableSequenceNumber = -2; 27 28 // This singleton instance should be started during the single threaded 29 // portion of main(). It initializes globals to provide support for all future 30 // calls. This object is created on the UI thread, and it is destroyed after 31 // all the other threads have gone away. As a result, it is ok to call it 32 // from the UI thread, or for about:profiler. 33 static chrome_browser_metrics::TrackingSynchronizer* g_tracking_synchronizer = 34 NULL; 35 36 } // anonymous namespace 37 38 namespace chrome_browser_metrics { 39 40 // The "RequestContext" structure describes an individual request received 41 // from the UI. All methods are accessible on UI thread. 42 class TrackingSynchronizer::RequestContext { 43 public: 44 // A map from sequence_number_ to the actual RequestContexts. 45 typedef std::map<int, RequestContext*> RequestContextMap; 46 47 RequestContext( 48 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object, 49 int sequence_number) 50 : callback_object_(callback_object), 51 sequence_number_(sequence_number), 52 received_process_group_count_(0), 53 processes_pending_(0) { 54 } 55 ~RequestContext() {} 56 57 void SetReceivedProcessGroupCount(bool done) { 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 59 received_process_group_count_ = done; 60 } 61 62 // Methods for book keeping of processes_pending_. 63 void IncrementProcessesPending() { 64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 65 ++processes_pending_; 66 } 67 68 void AddProcessesPending(int processes_pending) { 69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 70 processes_pending_ += processes_pending; 71 } 72 73 void DecrementProcessesPending() { 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 75 --processes_pending_; 76 } 77 78 // Records that we are waiting for one less tracking data from a process for 79 // the given sequence number. If |received_process_group_count_| and 80 // |processes_pending_| are zero, then delete the current object by calling 81 // Unregister. 82 void DeleteIfAllDone() { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 85 if (processes_pending_ <= 0 && received_process_group_count_) 86 RequestContext::Unregister(sequence_number_); 87 } 88 89 // Register |callback_object| in |outstanding_requests_| map for the given 90 // |sequence_number|. 91 static RequestContext* Register( 92 int sequence_number, 93 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { 94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 95 96 RequestContext* request = new RequestContext( 97 callback_object, sequence_number); 98 outstanding_requests_.Get()[sequence_number] = request; 99 100 return request; 101 } 102 103 // Find the |RequestContext| in |outstanding_requests_| map for the given 104 // |sequence_number|. 105 static RequestContext* GetRequestContext(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 NULL; 112 113 RequestContext* request = it->second; 114 DCHECK_EQ(sequence_number, request->sequence_number_); 115 return request; 116 } 117 118 // Delete the entry for the given |sequence_number| from 119 // |outstanding_requests_| map. This method is called when all changes have 120 // been acquired, or when the wait time expires (whichever is sooner). 121 static void Unregister(int sequence_number) { 122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 123 124 RequestContextMap::iterator it = 125 outstanding_requests_.Get().find(sequence_number); 126 if (it == outstanding_requests_.Get().end()) 127 return; 128 129 RequestContext* request = it->second; 130 DCHECK_EQ(sequence_number, request->sequence_number_); 131 bool received_process_group_count = request->received_process_group_count_; 132 int unresponsive_processes = request->processes_pending_; 133 134 if (request->callback_object_.get()) 135 request->callback_object_->FinishedReceivingProfilerData(); 136 137 delete request; 138 outstanding_requests_.Get().erase(it); 139 140 UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount", 141 received_process_group_count); 142 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding", 143 unresponsive_processes); 144 } 145 146 // Delete all the entries in |outstanding_requests_| map. 147 static void OnShutdown() { 148 // Just in case we have any pending tasks, clear them out. 149 while (!outstanding_requests_.Get().empty()) { 150 RequestContextMap::iterator it = outstanding_requests_.Get().begin(); 151 delete it->second; 152 outstanding_requests_.Get().erase(it); 153 } 154 } 155 156 // Requests are made to asynchronously send data to the |callback_object_|. 157 base::WeakPtr<TrackingSynchronizerObserver> callback_object_; 158 159 // The sequence number used by the most recent update request to contact all 160 // processes. 161 int sequence_number_; 162 163 // Indicates if we have received all pending processes count. 164 bool received_process_group_count_; 165 166 // The number of pending processes (browser, all renderer processes and 167 // browser child processes) that have not yet responded to requests. 168 int processes_pending_; 169 170 // Map of all outstanding RequestContexts, from sequence_number_ to 171 // RequestContext. 172 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_; 173 }; 174 175 // static 176 base::LazyInstance 177 <TrackingSynchronizer::RequestContext::RequestContextMap>::Leaky 178 TrackingSynchronizer::RequestContext::outstanding_requests_ = 179 LAZY_INSTANCE_INITIALIZER; 180 181 // TrackingSynchronizer methods and members. 182 183 TrackingSynchronizer::TrackingSynchronizer() 184 : last_used_sequence_number_(kNeverUsableSequenceNumber) { 185 DCHECK(!g_tracking_synchronizer); 186 g_tracking_synchronizer = this; 187 content::ProfilerController::GetInstance()->Register(this); 188 } 189 190 TrackingSynchronizer::~TrackingSynchronizer() { 191 content::ProfilerController::GetInstance()->Unregister(this); 192 193 // Just in case we have any pending tasks, clear them out. 194 RequestContext::OnShutdown(); 195 196 g_tracking_synchronizer = NULL; 197 } 198 199 // static 200 void TrackingSynchronizer::FetchProfilerDataAsynchronously( 201 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 204 if (!g_tracking_synchronizer) { 205 // System teardown is happening. 206 return; 207 } 208 209 int sequence_number = g_tracking_synchronizer->RegisterAndNotifyAllProcesses( 210 callback_object); 211 212 // Post a task that would be called after waiting for wait_time. This acts 213 // as a watchdog, to cancel the requests for non-responsive processes. 214 BrowserThread::PostDelayedTask( 215 BrowserThread::UI, FROM_HERE, 216 base::Bind(&RequestContext::Unregister, sequence_number), 217 base::TimeDelta::FromMinutes(1)); 218 } 219 220 void TrackingSynchronizer::OnPendingProcesses(int sequence_number, 221 int pending_processes, 222 bool end) { 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 225 RequestContext* request = RequestContext::GetRequestContext(sequence_number); 226 if (!request) 227 return; 228 request->AddProcessesPending(pending_processes); 229 request->SetReceivedProcessGroupCount(end); 230 request->DeleteIfAllDone(); 231 } 232 233 void TrackingSynchronizer::OnProfilerDataCollected( 234 int sequence_number, 235 const tracked_objects::ProcessDataSnapshot& profiler_data, 236 int process_type) { 237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 238 DecrementPendingProcessesAndSendData(sequence_number, profiler_data, 239 process_type); 240 } 241 242 int TrackingSynchronizer::RegisterAndNotifyAllProcesses( 243 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) { 244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 245 246 int sequence_number = GetNextAvailableSequenceNumber(); 247 248 RequestContext* request = 249 RequestContext::Register(sequence_number, callback_object); 250 251 // Increment pending process count for sending browser's profiler data. 252 request->IncrementProcessesPending(); 253 254 // Get profiler data from renderer and browser child processes. 255 content::ProfilerController::GetInstance()->GetProfilerData(sequence_number); 256 257 // Send profiler_data from browser process. 258 tracked_objects::ProcessDataSnapshot process_data; 259 tracked_objects::ThreadData::Snapshot(false, &process_data); 260 DecrementPendingProcessesAndSendData(sequence_number, process_data, 261 content::PROCESS_TYPE_BROWSER); 262 263 return sequence_number; 264 } 265 266 void TrackingSynchronizer::DecrementPendingProcessesAndSendData( 267 int sequence_number, 268 const tracked_objects::ProcessDataSnapshot& profiler_data, 269 int process_type) { 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 272 RequestContext* request = RequestContext::GetRequestContext(sequence_number); 273 if (!request) 274 return; 275 276 if (request->callback_object_.get()) { 277 request->callback_object_ 278 ->ReceivedProfilerData(profiler_data, process_type); 279 } 280 281 // Delete request if we have heard back from all child processes. 282 request->DecrementProcessesPending(); 283 request->DeleteIfAllDone(); 284 } 285 286 int TrackingSynchronizer::GetNextAvailableSequenceNumber() { 287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 288 289 ++last_used_sequence_number_; 290 291 // Watch out for wrapping to a negative number. 292 if (last_used_sequence_number_ < 0) 293 last_used_sequence_number_ = 1; 294 return last_used_sequence_number_; 295 } 296 297 } // namespace chrome_browser_metrics 298