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