|
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/json/json_reader.h" | |
8 #include "base/json/json_writer.h" | |
9 #include "base/logging.h" | |
10 #include "base/metrics/histogram.h" | |
11 #include "base/threading/thread.h" | |
12 #include "base/tracked_objects.h" | |
13 #include "chrome/common/chrome_constants.h" | |
14 #include "chrome/common/render_messages.h" | |
15 #include "content/browser/renderer_host/render_process_host.h" | |
16 #include "content/public/browser/browser_thread.h" | |
17 | |
18 using base::TimeTicks; | |
19 | |
20 namespace chrome_browser_metrics { | |
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 static const int kNeverUsableSequenceNumber = -2; | |
28 | |
29 TrackingSynchronizer::TrackingSynchronizer() | |
30 : last_used_sequence_number_(kNeverUsableSequenceNumber) { | |
jar (doing other things)
2011/11/03 06:23:15
nit: Indent the colon for an initializer list 4 sp
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
31 DCHECK(tracking_synchronizer_ == NULL); | |
32 tracking_synchronizer_ = this; | |
33 } | |
34 | |
35 TrackingSynchronizer::~TrackingSynchronizer() { | |
36 // Just in case we have any pending tasks, clear them out. | |
37 while (!outstanding_requests_.empty()) { | |
38 RequestContextMap::iterator it = outstanding_requests_.begin(); | |
39 delete it->second; | |
40 outstanding_requests_.erase(it); | |
41 } | |
42 | |
43 tracking_synchronizer_ = NULL; | |
44 } | |
45 | |
46 // static | |
47 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { | |
48 DCHECK(tracking_synchronizer_ != NULL); | |
49 return tracking_synchronizer_; | |
jar (doing other things)
2011/11/03 06:23:15
You might want to DCHECK() that we're on the UI th
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
50 } | |
51 | |
52 // static | |
53 // Used for testing purposes only. | |
54 // TODO(rtenneti): DELETE it. | |
55 void TrackingSynchronizer::FetchTrackingDataSynchronously(std::string* output) { | |
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
57 | |
58 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); | |
59 if (!current_synchronizer) | |
60 return; | |
61 | |
62 scoped_refptr<CallbackObject> callback_object(new CallbackObject); | |
63 | |
64 // Set the tracking status for testing purposes. | |
65 bool enable = tracked_objects::ThreadData::tracking_status(); | |
66 SetTrackingStatus(enable); | |
67 | |
68 // Get the data for testing purposes. | |
69 FetchTrackingDataAsynchronously(callback_object); | |
70 | |
71 if (!callback_object->GetValue()) | |
72 return; | |
73 | |
74 // Send whatever data we have for testing purposes. | |
75 base::JSONWriter::Write(callback_object->GetValue(), false, output); | |
76 } | |
77 | |
78 // static | |
79 void TrackingSynchronizer::FetchTrackingDataAsynchronously( | |
80 scoped_refptr<CallbackObject> callback_object) { | |
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
82 DCHECK(callback_object != NULL); | |
83 | |
84 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); | |
85 | |
86 if (current_synchronizer == NULL) { | |
87 // System teardown is happening. | |
88 return; | |
89 } | |
90 | |
91 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses( | |
92 callback_object); | |
93 | |
94 // Post a task that would be called after waiting for wait_time. This acts | |
95 // as a watchdog, to cancel the requests for non-responsive processes. | |
96 BrowserThread::PostDelayedTask( | |
97 BrowserThread::UI, FROM_HERE, | |
98 NewRunnableMethod( | |
99 current_synchronizer, | |
100 &TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback, | |
101 sequence_number), | |
102 60000); | |
103 } | |
104 | |
105 // static | |
106 void TrackingSynchronizer::SetTrackingStatus(bool enable) { | |
107 // To iterate over all processes, or to send messages to the hosts, we need | |
108 // to be on the UI thread. | |
109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
110 | |
111 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); | |
112 !it.IsAtEnd(); it.Advance()) { | |
113 RenderProcessHost* render_process_host = it.GetCurrentValue(); | |
114 DCHECK(render_process_host); | |
115 // Ignore processes that don't have a connection, such as crashed tabs. | |
116 if (!render_process_host->HasConnection()) | |
117 continue; | |
118 | |
119 render_process_host->Send(new ChromeViewMsg_SetTrackingStatus(enable)); | |
120 } | |
121 } | |
122 | |
123 // static | |
124 void TrackingSynchronizer::IsTrackingEnabled(int process_id) { | |
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
126 // To find the process, or to send messages to the hosts, we need to be on the | |
127 // UI thread. | |
128 BrowserThread::PostTask( | |
129 BrowserThread::UI, FROM_HERE, | |
130 NewRunnableFunction( | |
131 &TrackingSynchronizer::SetTrackingStatusInProcess, process_id)); | |
132 } | |
133 | |
134 // static | |
135 void TrackingSynchronizer::SetTrackingStatusInProcess(int process_id) { | |
136 // To find the process, or to send messages to the hosts, we need to be on the | |
137 // UI thread. | |
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
139 | |
140 bool enable = tracked_objects::ThreadData::tracking_status(); | |
141 | |
142 RenderProcessHost* process = RenderProcessHost::FromID(process_id); | |
143 // Ignore processes that don't have a connection, such as crashed tabs. | |
144 if (!process || !process->HasConnection()) | |
145 return; | |
146 process->Send(new ChromeViewMsg_SetTrackingStatus(enable)); | |
147 } | |
148 | |
149 // static | |
150 void TrackingSynchronizer::DeserializeTrackingList( | |
151 int sequence_number, | |
152 const std::string& tracking_data, | |
153 ChildProcessInfo::ProcessType process_type) { | |
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
155 | |
156 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); | |
jar (doing other things)
2011/11/03 06:23:15
I think we need to bounce over to the UI thread be
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
157 if (current_synchronizer == NULL) | |
158 return; | |
159 | |
160 base::Value* value = | |
161 base::JSONReader().JsonToValue(tracking_data, false, true); | |
162 DCHECK(value->GetType() == base::Value::TYPE_DICTIONARY); | |
163 base::DictionaryValue* dictionary_value = | |
164 static_cast<DictionaryValue*>(value); | |
165 dictionary_value->SetString( | |
166 "process_type", ChildProcessInfo::GetTypeNameInEnglish(process_type)); | |
167 | |
168 // Send back the tracking data we have received from a process. | |
169 BrowserThread::PostTask( | |
170 BrowserThread::UI, FROM_HERE, | |
171 NewRunnableMethod( | |
172 current_synchronizer, | |
173 &TrackingSynchronizer::DecrementPendingProcessesAndSendData, | |
174 sequence_number, | |
175 dictionary_value)); | |
176 } | |
177 | |
178 int TrackingSynchronizer::RegisterAndNotifyAllProcesses( | |
179 scoped_refptr<CallbackObject> callback_object) { | |
180 // To iterate over all processes, or to send messages to the hosts, we need | |
181 // to be on the UI thread. | |
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
183 | |
184 int sequence_number = GetNextAvailableSequenceNumber(); | |
185 | |
186 // Initialize processes_pending with one because we are going to send | |
187 // browser's ThreadData. | |
188 RequestContext* request = new RequestContext( | |
189 callback_object, sequence_number, 1, TimeTicks::Now()); | |
190 outstanding_requests_[sequence_number] = request; | |
191 | |
192 DCHECK_GT(request->processes_pending_, 0); | |
193 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); | |
194 !it.IsAtEnd(); it.Advance()) { | |
195 RenderProcessHost* render_process_host = it.GetCurrentValue(); | |
196 DCHECK(render_process_host); | |
197 // Ignore processes that don't have a connection, such as crashed tabs. | |
198 if (!render_process_host->HasConnection()) | |
199 continue; | |
200 | |
201 ++request->processes_pending_; | |
202 if (!render_process_host->Send( | |
203 new ChromeViewMsg_GetRendererTrackedData(sequence_number))) { | |
204 DecrementPendingProcesses(sequence_number); | |
205 } | |
206 } | |
207 | |
208 // Get the ThreadData for the browser process and send it back. | |
209 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); | |
210 const std::string process_type = | |
211 ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS); | |
212 value->SetString("process_type", process_type); | |
213 value->SetInteger("process_id", base::GetCurrentProcId()); | |
214 DCHECK_GT(request->processes_pending_, 0); | |
215 DecrementPendingProcessesAndSendData(sequence_number, value); | |
216 | |
217 return sequence_number; | |
218 } | |
219 | |
220 void TrackingSynchronizer::DecrementPendingProcessesAndSendData( | |
221 int sequence_number, | |
222 base::DictionaryValue* value) { | |
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
224 | |
225 RequestContext* request = NULL; | |
226 bool completed = false; | |
227 | |
228 RequestContextMap::iterator it = | |
229 outstanding_requests_.find(sequence_number); | |
230 if (it == outstanding_requests_.end()) | |
231 return; | |
jar (doing other things)
2011/11/03 06:23:15
This will probably leak value.
Probably delete it
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
232 | |
233 request = it->second; | |
jar (doing other things)
2011/11/03 06:23:15
nit: Move declarations on lines 225 down to here (
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
234 | |
235 DCHECK(sequence_number == request->sequence_number_); | |
236 if (--request->processes_pending_ <= 0) | |
237 completed = true; | |
238 | |
239 if (value) { | |
240 // Transfers ownership of |value| to |callback_object_|. | |
241 request->callback_object_->SendData(value); | |
242 } | |
jar (doing other things)
2011/11/03 06:23:15
probably:
else {
delete value;
}
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
243 | |
244 if (completed) | |
245 ForceTrackingSynchronizationDoneCallback(sequence_number); | |
246 } | |
247 | |
248 void TrackingSynchronizer::DecrementPendingProcesses(int sequence_number) { | |
249 DecrementPendingProcessesAndSendData(sequence_number, NULL); | |
250 } | |
251 | |
252 void TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback( | |
253 int sequence_number) { | |
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
255 | |
256 TimeTicks started; | |
257 int unresponsive_processes; | |
258 RequestContextMap::iterator it = | |
259 outstanding_requests_.find(sequence_number); | |
260 if (it == outstanding_requests_.end()) | |
261 return; | |
262 | |
263 RequestContext* request = it->second; | |
264 | |
265 DCHECK(sequence_number == request->sequence_number_); | |
266 | |
267 started = request->request_start_time_; | |
jar (doing other things)
2011/11/03 06:23:15
Do we need this anymore? Probably delete declarat
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
268 unresponsive_processes = request->processes_pending_; | |
269 | |
270 delete it->second; | |
271 outstanding_requests_.erase(it); | |
272 | |
273 UMA_HISTOGRAM_COUNTS("Tracking.ProcessNotRespondingAsynchronous", | |
274 unresponsive_processes); | |
275 } | |
276 | |
277 int TrackingSynchronizer::GetNextAvailableSequenceNumber() { | |
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
279 | |
280 ++last_used_sequence_number_; | |
281 | |
282 static const int kReservedSequenceNumber = 0; | |
jar (doing other things)
2011/11/03 06:23:15
Do you anticipate a need for this?
ramant (doing other things)
2011/11/03 21:51:39
Done.
| |
283 | |
284 // Watch out for wrapping to a negative number. | |
285 if (last_used_sequence_number_ < 0) { | |
286 // Bypass the reserved number, which is used when a process spontaneously | |
287 // decides to send some tracking data. | |
288 last_used_sequence_number_ = kReservedSequenceNumber + 1; | |
289 } | |
290 DCHECK_NE(last_used_sequence_number_, kReservedSequenceNumber); | |
291 return last_used_sequence_number_; | |
292 } | |
293 | |
294 // static | |
295 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; | |
296 | |
297 } // namespace chrome_browser_metrics | |
OLD | NEW |