| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |