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