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 "content/browser/tracing/trace_controller_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/debug/trace_event.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "components/tracing/tracing_messages.h" | |
12 #include "content/browser/tracing/trace_message_filter.h" | |
13 #include "content/browser/tracing/trace_subscriber_stdio.h" | |
14 #include "content/common/child_process_messages.h" | |
15 #include "content/public/browser/browser_message_filter.h" | |
16 #include "content/public/common/content_switches.h" | |
17 | |
18 using base::debug::TraceLog; | |
19 | |
20 namespace content { | |
21 | |
22 namespace { | |
23 | |
24 base::LazyInstance<TraceControllerImpl>::Leaky g_controller = | |
25 LAZY_INSTANCE_INITIALIZER; | |
26 | |
27 } // namespace | |
28 | |
29 TraceController* TraceController::GetInstance() { | |
30 return TraceControllerImpl::GetInstance(); | |
31 } | |
32 | |
33 TraceControllerImpl::TraceControllerImpl() : | |
34 subscriber_(NULL), | |
35 pending_end_ack_count_(0), | |
36 pending_bpf_ack_count_(0), | |
37 maximum_bpf_(0.0f), | |
38 is_tracing_(false), | |
39 is_get_category_groups_(false), | |
40 category_filter_( | |
41 base::debug::CategoryFilter::kDefaultCategoryFilterString) { | |
42 TraceLog::GetInstance()->SetNotificationCallback( | |
43 base::Bind(&TraceControllerImpl::OnTraceNotification, | |
44 base::Unretained(this))); | |
45 } | |
46 | |
47 TraceControllerImpl::~TraceControllerImpl() { | |
48 // No need to SetNotificationCallback(nil) on the TraceLog since this is a | |
49 // Leaky instance. | |
50 NOTREACHED(); | |
51 } | |
52 | |
53 TraceControllerImpl* TraceControllerImpl::GetInstance() { | |
54 return g_controller.Pointer(); | |
55 } | |
56 | |
57 bool TraceControllerImpl::GetKnownCategoryGroupsAsync( | |
58 TraceSubscriber* subscriber) { | |
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
60 | |
61 // Known categories come back from child processes with the EndTracingAck | |
62 // message. So to get known categories, just begin and end tracing immediately | |
63 // afterwards. This will ping all the child processes for categories. | |
64 is_get_category_groups_ = true; | |
65 bool success = BeginTracing(subscriber, "*", | |
66 TraceLog::GetInstance()->trace_options()) && | |
67 EndTracingAsync(subscriber); | |
68 is_get_category_groups_ = success; | |
69 return success; | |
70 } | |
71 | |
72 bool TraceControllerImpl::BeginTracing(TraceSubscriber* subscriber, | |
73 const std::string& category_patterns, | |
74 base::debug::TraceLog::Options options) { | |
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
76 | |
77 if (!can_begin_tracing(subscriber)) | |
78 return false; | |
79 | |
80 #if defined(OS_ANDROID) | |
81 if (!is_get_category_groups_) | |
82 TraceLog::GetInstance()->AddClockSyncMetadataEvent(); | |
83 #endif | |
84 | |
85 // Enable tracing | |
86 TraceLog::GetInstance()->SetEnabled( | |
87 base::debug::CategoryFilter(category_patterns), options); | |
88 | |
89 OnTracingBegan(subscriber); | |
90 | |
91 return true; | |
92 } | |
93 | |
94 bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) { | |
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
96 | |
97 if (!can_end_tracing() || subscriber != subscriber_) | |
98 return false; | |
99 | |
100 // Disable local trace early to avoid traces during end-tracing process from | |
101 // interfering with the process. | |
102 TraceLog::GetInstance()->SetDisabled(); | |
103 | |
104 #if defined(OS_ANDROID) | |
105 if (!is_get_category_groups_) | |
106 TraceLog::GetInstance()->AddClockSyncMetadataEvent(); | |
107 #endif | |
108 | |
109 // There could be a case where there are no child processes and filters_ | |
110 // is empty. In that case we can immediately tell the subscriber that tracing | |
111 // has ended. To avoid recursive calls back to the subscriber, we will just | |
112 // use the existing asynchronous OnEndTracingAck code. | |
113 // Count myself (local trace) in pending_end_ack_count_, acked below. | |
114 pending_end_ack_count_ = filters_.size() + 1; | |
115 | |
116 // Handle special case of zero child processes. | |
117 if (pending_end_ack_count_ == 1) { | |
118 // Ack asynchronously now, because we don't have any children to wait for. | |
119 std::vector<std::string> category_groups; | |
120 TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); | |
121 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
122 base::Bind(&TraceControllerImpl::OnEndTracingAck, | |
123 base::Unretained(this), category_groups)); | |
124 } | |
125 | |
126 // Notify all child processes. | |
127 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { | |
128 it->get()->SendEndTracing(); | |
129 } | |
130 | |
131 return true; | |
132 } | |
133 | |
134 bool TraceControllerImpl::GetTraceBufferPercentFullAsync( | |
135 TraceSubscriber* subscriber) { | |
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
137 | |
138 if (!can_get_buffer_percent_full() || subscriber != subscriber_) | |
139 return false; | |
140 | |
141 maximum_bpf_ = 0.0f; | |
142 pending_bpf_ack_count_ = filters_.size() + 1; | |
143 | |
144 // Handle special case of zero child processes. | |
145 if (pending_bpf_ack_count_ == 1) { | |
146 // Ack asynchronously now, because we don't have any children to wait for. | |
147 float bpf = TraceLog::GetInstance()->GetBufferPercentFull(); | |
148 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
149 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, | |
150 base::Unretained(this), bpf)); | |
151 } | |
152 | |
153 // Message all child processes. | |
154 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { | |
155 it->get()->SendGetTraceBufferPercentFull(); | |
156 } | |
157 | |
158 return true; | |
159 } | |
160 | |
161 bool TraceControllerImpl::SetWatchEvent(TraceSubscriber* subscriber, | |
162 const std::string& category_name, | |
163 const std::string& event_name) { | |
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
165 if (subscriber != subscriber_) | |
166 return false; | |
167 | |
168 watch_category_ = category_name; | |
169 watch_name_ = event_name; | |
170 | |
171 TraceLog::GetInstance()->SetWatchEvent(category_name, event_name); | |
172 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) | |
173 it->get()->SendSetWatchEvent(category_name, event_name); | |
174 | |
175 return true; | |
176 } | |
177 | |
178 bool TraceControllerImpl::CancelWatchEvent(TraceSubscriber* subscriber) { | |
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
180 if (subscriber != subscriber_) | |
181 return false; | |
182 | |
183 watch_category_.clear(); | |
184 watch_name_.clear(); | |
185 | |
186 TraceLog::GetInstance()->CancelWatchEvent(); | |
187 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) | |
188 it->get()->SendCancelWatchEvent(); | |
189 | |
190 return true; | |
191 } | |
192 | |
193 void TraceControllerImpl::CancelSubscriber(TraceSubscriber* subscriber) { | |
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
195 | |
196 if (subscriber == subscriber_) { | |
197 subscriber_ = NULL; | |
198 // End tracing if necessary. | |
199 if (is_tracing_ && pending_end_ack_count_ == 0) | |
200 EndTracingAsync(NULL); | |
201 } | |
202 } | |
203 | |
204 void TraceControllerImpl::AddFilter(TraceMessageFilter* filter) { | |
205 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
206 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
207 base::Bind(&TraceControllerImpl::AddFilter, base::Unretained(this), | |
208 make_scoped_refptr(filter))); | |
209 return; | |
210 } | |
211 | |
212 filters_.insert(filter); | |
213 if (is_tracing_enabled()) { | |
214 std::string cf_str = category_filter_.ToString(); | |
215 filter->SendBeginTracing(cf_str, trace_options_); | |
216 if (!watch_category_.empty()) | |
217 filter->SendSetWatchEvent(watch_category_, watch_name_); | |
218 } | |
219 } | |
220 | |
221 void TraceControllerImpl::RemoveFilter(TraceMessageFilter* filter) { | |
222 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
223 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
224 base::Bind(&TraceControllerImpl::RemoveFilter, base::Unretained(this), | |
225 make_scoped_refptr(filter))); | |
226 return; | |
227 } | |
228 | |
229 filters_.erase(filter); | |
230 } | |
231 | |
232 void TraceControllerImpl::OnTracingBegan(TraceSubscriber* subscriber) { | |
233 is_tracing_ = true; | |
234 | |
235 subscriber_ = subscriber; | |
236 | |
237 category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter(); | |
238 trace_options_ = TraceLog::GetInstance()->trace_options(); | |
239 | |
240 // Notify all child processes. | |
241 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { | |
242 it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_); | |
243 } | |
244 } | |
245 | |
246 void TraceControllerImpl::OnEndTracingAck( | |
247 const std::vector<std::string>& known_category_groups) { | |
248 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
249 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
250 base::Bind(&TraceControllerImpl::OnEndTracingAck, | |
251 base::Unretained(this), known_category_groups)); | |
252 return; | |
253 } | |
254 | |
255 // Merge known_category_groups with known_category_groups_ | |
256 known_category_groups_.insert(known_category_groups.begin(), | |
257 known_category_groups.end()); | |
258 | |
259 if (pending_end_ack_count_ == 0) | |
260 return; | |
261 | |
262 | |
263 if (--pending_end_ack_count_ == 1) { | |
264 // All acks from subprocesses have been received. Now flush the local trace. | |
265 // During or after this call, our OnLocalTraceDataCollected will be | |
266 // called with the last of the local trace data. | |
267 TraceLog::GetInstance()->Flush( | |
268 base::Bind(&TraceControllerImpl::OnLocalTraceDataCollected, | |
269 base::Unretained(this))); | |
270 } | |
271 | |
272 if (pending_end_ack_count_ == 0) { | |
273 // All acks (including from the subprocesses and the local trace) have been | |
274 // received. | |
275 is_tracing_ = false; | |
276 | |
277 // Trigger callback if one is set. | |
278 if (subscriber_) { | |
279 if (is_get_category_groups_) | |
280 subscriber_->OnKnownCategoriesCollected(known_category_groups_); | |
281 else | |
282 subscriber_->OnEndTracingComplete(); | |
283 // Clear subscriber so that others can use TraceController. | |
284 subscriber_ = NULL; | |
285 } | |
286 | |
287 is_get_category_groups_ = false; | |
288 } | |
289 } | |
290 | |
291 void TraceControllerImpl::OnTraceDataCollected( | |
292 const scoped_refptr<base::RefCountedString>& events_str_ptr) { | |
293 // OnTraceDataCollected may be called from any browser thread, either by the | |
294 // local event trace system or from child processes via TraceMessageFilter. | |
295 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
296 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
297 base::Bind(&TraceControllerImpl::OnTraceDataCollected, | |
298 base::Unretained(this), events_str_ptr)); | |
299 return; | |
300 } | |
301 | |
302 // Drop trace events if we are just getting categories. | |
303 if (subscriber_ && !is_get_category_groups_) | |
304 subscriber_->OnTraceDataCollected(events_str_ptr); | |
305 } | |
306 | |
307 void TraceControllerImpl::OnLocalTraceDataCollected( | |
308 const scoped_refptr<base::RefCountedString>& events_str_ptr, | |
309 bool has_more_events) { | |
310 if (events_str_ptr->data().size()) | |
311 OnTraceDataCollected(events_str_ptr); | |
312 | |
313 if (!has_more_events) { | |
314 // Simulate an EndTrackingAck for the local trace. | |
315 std::vector<std::string> category_groups; | |
316 TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); | |
317 OnEndTracingAck(category_groups); | |
318 } | |
319 } | |
320 | |
321 void TraceControllerImpl::OnTraceNotification(int notification) { | |
322 // OnTraceNotification may be called from any browser thread, either by the | |
323 // local event trace system or from child processes via TraceMessageFilter. | |
324 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
325 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
326 base::Bind(&TraceControllerImpl::OnTraceNotification, | |
327 base::Unretained(this), notification)); | |
328 return; | |
329 } | |
330 | |
331 if (notification & base::debug::TraceLog::TRACE_BUFFER_FULL) { | |
332 // EndTracingAsync may return false if tracing is already in the process | |
333 // of being ended. That is ok. | |
334 EndTracingAsync(subscriber_); | |
335 } | |
336 if (notification & base::debug::TraceLog::EVENT_WATCH_NOTIFICATION) { | |
337 if (subscriber_) | |
338 subscriber_->OnEventWatchNotification(); | |
339 } | |
340 } | |
341 | |
342 void TraceControllerImpl::OnTraceBufferPercentFullReply(float percent_full) { | |
343 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
344 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
345 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, | |
346 base::Unretained(this), percent_full)); | |
347 return; | |
348 } | |
349 | |
350 if (pending_bpf_ack_count_ == 0) | |
351 return; | |
352 | |
353 maximum_bpf_ = (maximum_bpf_ > percent_full)? maximum_bpf_ : percent_full; | |
354 | |
355 if (--pending_bpf_ack_count_ == 0) { | |
356 // Trigger callback if one is set. | |
357 if (subscriber_) | |
358 subscriber_->OnTraceBufferPercentFullReply(maximum_bpf_); | |
359 } | |
360 | |
361 if (pending_bpf_ack_count_ == 1) { | |
362 // The last ack represents local trace, so we need to ack it now. Note that | |
363 // this code only executes if there were child processes. | |
364 float bpf = TraceLog::GetInstance()->GetBufferPercentFull(); | |
365 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
366 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply, | |
367 base::Unretained(this), bpf)); | |
368 } | |
369 } | |
370 | |
371 } // namespace content | |
OLD | NEW |