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 |