blob: 53e4ea5cae05fb44b09f45c02cda83f42f08acf9 [file] [log] [blame]
[email protected]bcb6ee23d2013-02-03 04:06:401// 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"
[email protected]348fbaac2013-06-11 06:31:5110#include "base/strings/string_number_conversions.h"
[email protected]bcb6ee23d2013-02-03 04:06:4011#include "content/browser/tracing/trace_message_filter.h"
12#include "content/browser/tracing/trace_subscriber_stdio.h"
13#include "content/common/child_process_messages.h"
14#include "content/public/browser/browser_message_filter.h"
15#include "content/public/common/content_switches.h"
16
17using base::debug::TraceLog;
18
19namespace content {
20
21namespace {
22
23base::LazyInstance<TraceControllerImpl>::Leaky g_controller =
24 LAZY_INSTANCE_INITIALIZER;
25
26class AutoStopTraceSubscriberStdio : public TraceSubscriberStdio {
27 public:
[email protected]2dec8ec2013-02-07 19:20:3428 AutoStopTraceSubscriberStdio(const base::FilePath& file_path)
[email protected]28a015d82013-08-20 15:42:4629 : TraceSubscriberStdio(file_path,
30 FILE_TYPE_PROPERTY_LIST,
31 false) {}
[email protected]bcb6ee23d2013-02-03 04:06:4032
[email protected]28a015d82013-08-20 15:42:4633 static void EndStartupTrace(AutoStopTraceSubscriberStdio* subscriber) {
[email protected]bcb6ee23d2013-02-03 04:06:4034 if (!TraceControllerImpl::GetInstance()->EndTracingAsync(subscriber))
35 delete subscriber;
36 // else, the tracing will end asynchronously in OnEndTracingComplete().
37 }
38
[email protected]c3e35892013-02-12 02:08:0139 virtual void OnEndTracingComplete() OVERRIDE {
[email protected]bcb6ee23d2013-02-03 04:06:4040 TraceSubscriberStdio::OnEndTracingComplete();
41 delete this;
42 // TODO(joth): this would be the time to automatically open up
43 // chrome://tracing/ and load up the trace data collected.
44 }
45};
46
47} // namespace
48
49TraceController* TraceController::GetInstance() {
50 return TraceControllerImpl::GetInstance();
51}
52
53TraceControllerImpl::TraceControllerImpl() :
54 subscriber_(NULL),
55 pending_end_ack_count_(0),
56 pending_bpf_ack_count_(0),
57 maximum_bpf_(0.0f),
58 is_tracing_(false),
[email protected]d936677a2013-04-19 08:49:0359 is_get_category_groups_(false),
60 category_filter_(
61 base::debug::CategoryFilter::kDefaultCategoryFilterString) {
[email protected]bcb6ee23d2013-02-03 04:06:4062 TraceLog::GetInstance()->SetNotificationCallback(
63 base::Bind(&TraceControllerImpl::OnTraceNotification,
64 base::Unretained(this)));
65}
66
67TraceControllerImpl::~TraceControllerImpl() {
68 // No need to SetNotificationCallback(nil) on the TraceLog since this is a
69 // Leaky instance.
70 NOTREACHED();
71}
72
73TraceControllerImpl* TraceControllerImpl::GetInstance() {
74 return g_controller.Pointer();
75}
76
77void TraceControllerImpl::InitStartupTracing(const CommandLine& command_line) {
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2dec8ec2013-02-07 19:20:3479 base::FilePath trace_file = command_line.GetSwitchValuePath(
[email protected]bcb6ee23d2013-02-03 04:06:4080 switches::kTraceStartupFile);
81 // trace_file = "none" means that startup events will show up for the next
82 // begin/end tracing (via about:tracing or AutomationProxy::BeginTracing/
83 // EndTracing, for example).
[email protected]2dec8ec2013-02-07 19:20:3484 if (trace_file == base::FilePath().AppendASCII("none"))
[email protected]bcb6ee23d2013-02-03 04:06:4085 return;
86
87 if (trace_file.empty()) {
88 // Default to saving the startup trace into the current dir.
[email protected]2dec8ec2013-02-07 19:20:3489 trace_file = base::FilePath().AppendASCII("chrometrace.log");
[email protected]bcb6ee23d2013-02-03 04:06:4090 }
91 scoped_ptr<AutoStopTraceSubscriberStdio> subscriber(
92 new AutoStopTraceSubscriberStdio(trace_file));
93 DCHECK(can_begin_tracing(subscriber.get()));
94
95 std::string delay_str = command_line.GetSwitchValueASCII(
96 switches::kTraceStartupDuration);
97 int delay_secs = 5;
98 if (!delay_str.empty() && !base::StringToInt(delay_str, &delay_secs)) {
99 DLOG(WARNING) << "Could not parse --" << switches::kTraceStartupDuration
100 << "=" << delay_str << " defaulting to 5 (secs)";
101 delay_secs = 5;
102 }
103
104 OnTracingBegan(subscriber.get());
105 BrowserThread::PostDelayedTask(
106 BrowserThread::UI,
107 FROM_HERE,
108 base::Bind(&AutoStopTraceSubscriberStdio::EndStartupTrace,
109 base::Unretained(subscriber.release())),
110 base::TimeDelta::FromSeconds(delay_secs));
111}
112
[email protected]d936677a2013-04-19 08:49:03113bool TraceControllerImpl::GetKnownCategoryGroupsAsync(
114 TraceSubscriber* subscriber) {
[email protected]bcb6ee23d2013-02-03 04:06:40115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116
117 // Known categories come back from child processes with the EndTracingAck
118 // message. So to get known categories, just begin and end tracing immediately
119 // afterwards. This will ping all the child processes for categories.
[email protected]d936677a2013-04-19 08:49:03120 is_get_category_groups_ = true;
[email protected]dcc78372013-02-23 02:09:53121 bool success = BeginTracing(subscriber, "*",
122 TraceLog::GetInstance()->trace_options()) &&
[email protected]bcb6ee23d2013-02-03 04:06:40123 EndTracingAsync(subscriber);
[email protected]d936677a2013-04-19 08:49:03124 is_get_category_groups_ = success;
[email protected]bcb6ee23d2013-02-03 04:06:40125 return success;
126}
127
[email protected]bcb6ee23d2013-02-03 04:06:40128bool TraceControllerImpl::BeginTracing(TraceSubscriber* subscriber,
[email protected]d936677a2013-04-19 08:49:03129 const std::string& category_patterns,
[email protected]dcc78372013-02-23 02:09:53130 base::debug::TraceLog::Options options) {
[email protected]bcb6ee23d2013-02-03 04:06:40131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
132
133 if (!can_begin_tracing(subscriber))
134 return false;
135
136 // Enable tracing
[email protected]d936677a2013-04-19 08:49:03137 TraceLog::GetInstance()->SetEnabled(
138 base::debug::CategoryFilter(category_patterns), options);
[email protected]bcb6ee23d2013-02-03 04:06:40139
140 OnTracingBegan(subscriber);
141
142 return true;
143}
144
145bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147
148 if (!can_end_tracing() || subscriber != subscriber_)
149 return false;
150
151 // There could be a case where there are no child processes and filters_
152 // is empty. In that case we can immediately tell the subscriber that tracing
153 // has ended. To avoid recursive calls back to the subscriber, we will just
154 // use the existing asynchronous OnEndTracingAck code.
155 // Count myself (local trace) in pending_end_ack_count_, acked below.
156 pending_end_ack_count_ = filters_.size() + 1;
157
158 // Handle special case of zero child processes.
159 if (pending_end_ack_count_ == 1) {
160 // Ack asynchronously now, because we don't have any children to wait for.
[email protected]d936677a2013-04-19 08:49:03161 std::vector<std::string> category_groups;
162 TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
[email protected]bcb6ee23d2013-02-03 04:06:40163 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
164 base::Bind(&TraceControllerImpl::OnEndTracingAck,
[email protected]d936677a2013-04-19 08:49:03165 base::Unretained(this), category_groups));
[email protected]bcb6ee23d2013-02-03 04:06:40166 }
167
168 // Notify all child processes.
169 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
170 it->get()->SendEndTracing();
171 }
172
173 return true;
174}
175
176bool TraceControllerImpl::GetTraceBufferPercentFullAsync(
177 TraceSubscriber* subscriber) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179
180 if (!can_get_buffer_percent_full() || subscriber != subscriber_)
181 return false;
182
183 maximum_bpf_ = 0.0f;
184 pending_bpf_ack_count_ = filters_.size() + 1;
185
186 // Handle special case of zero child processes.
187 if (pending_bpf_ack_count_ == 1) {
188 // Ack asynchronously now, because we don't have any children to wait for.
189 float bpf = TraceLog::GetInstance()->GetBufferPercentFull();
190 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
191 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
192 base::Unretained(this), bpf));
193 }
194
195 // Message all child processes.
196 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
197 it->get()->SendGetTraceBufferPercentFull();
198 }
199
200 return true;
201}
202
203bool TraceControllerImpl::SetWatchEvent(TraceSubscriber* subscriber,
204 const std::string& category_name,
205 const std::string& event_name) {
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207 if (subscriber != subscriber_)
208 return false;
209
210 watch_category_ = category_name;
211 watch_name_ = event_name;
212
213 TraceLog::GetInstance()->SetWatchEvent(category_name, event_name);
214 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it)
215 it->get()->SendSetWatchEvent(category_name, event_name);
216
217 return true;
218}
219
220bool TraceControllerImpl::CancelWatchEvent(TraceSubscriber* subscriber) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 if (subscriber != subscriber_)
223 return false;
224
225 watch_category_.clear();
226 watch_name_.clear();
227
228 TraceLog::GetInstance()->CancelWatchEvent();
229 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it)
230 it->get()->SendCancelWatchEvent();
231
232 return true;
233}
234
235void TraceControllerImpl::CancelSubscriber(TraceSubscriber* subscriber) {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237
238 if (subscriber == subscriber_) {
239 subscriber_ = NULL;
240 // End tracing if necessary.
241 if (is_tracing_ && pending_end_ack_count_ == 0)
242 EndTracingAsync(NULL);
243 }
244}
245
246void TraceControllerImpl::AddFilter(TraceMessageFilter* filter) {
247 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
248 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
249 base::Bind(&TraceControllerImpl::AddFilter, base::Unretained(this),
250 make_scoped_refptr(filter)));
251 return;
252 }
253
254 filters_.insert(filter);
255 if (is_tracing_enabled()) {
[email protected]d936677a2013-04-19 08:49:03256 std::string cf_str = category_filter_.ToString();
257 filter->SendBeginTracing(cf_str, trace_options_);
[email protected]bcb6ee23d2013-02-03 04:06:40258 if (!watch_category_.empty())
259 filter->SendSetWatchEvent(watch_category_, watch_name_);
260 }
261}
262
263void TraceControllerImpl::RemoveFilter(TraceMessageFilter* filter) {
264 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
265 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
266 base::Bind(&TraceControllerImpl::RemoveFilter, base::Unretained(this),
267 make_scoped_refptr(filter)));
268 return;
269 }
270
271 filters_.erase(filter);
272}
273
274void TraceControllerImpl::OnTracingBegan(TraceSubscriber* subscriber) {
275 is_tracing_ = true;
276
277 subscriber_ = subscriber;
278
[email protected]d936677a2013-04-19 08:49:03279 category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter();
[email protected]dcc78372013-02-23 02:09:53280 trace_options_ = TraceLog::GetInstance()->trace_options();
281
[email protected]bcb6ee23d2013-02-03 04:06:40282 // Notify all child processes.
283 for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
[email protected]d936677a2013-04-19 08:49:03284 it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_);
[email protected]bcb6ee23d2013-02-03 04:06:40285 }
286}
287
288void TraceControllerImpl::OnEndTracingAck(
[email protected]d936677a2013-04-19 08:49:03289 const std::vector<std::string>& known_category_groups) {
[email protected]bcb6ee23d2013-02-03 04:06:40290 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
291 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
292 base::Bind(&TraceControllerImpl::OnEndTracingAck,
[email protected]d936677a2013-04-19 08:49:03293 base::Unretained(this), known_category_groups));
[email protected]bcb6ee23d2013-02-03 04:06:40294 return;
295 }
296
[email protected]d936677a2013-04-19 08:49:03297 // Merge known_category_groups with known_category_groups_
298 known_category_groups_.insert(known_category_groups.begin(),
299 known_category_groups.end());
[email protected]bcb6ee23d2013-02-03 04:06:40300
301 if (pending_end_ack_count_ == 0)
302 return;
303
304 if (--pending_end_ack_count_ == 0) {
305 // All acks have been received.
306 is_tracing_ = false;
307
308 // Disable local trace.
309 TraceLog::GetInstance()->SetDisabled();
310
311 // During this call, our OnTraceDataCollected will be
312 // called with the last of the local trace data. Since we are on the UI
313 // thread, the call to OnTraceDataCollected will be synchronous, so we can
314 // immediately call OnEndTracingComplete below.
315 TraceLog::GetInstance()->Flush(
316 base::Bind(&TraceControllerImpl::OnTraceDataCollected,
317 base::Unretained(this)));
318
319 // Trigger callback if one is set.
320 if (subscriber_) {
[email protected]d936677a2013-04-19 08:49:03321 if (is_get_category_groups_)
322 subscriber_->OnKnownCategoriesCollected(known_category_groups_);
[email protected]bcb6ee23d2013-02-03 04:06:40323 else
324 subscriber_->OnEndTracingComplete();
325 // Clear subscriber so that others can use TraceController.
326 subscriber_ = NULL;
327 }
328
[email protected]d936677a2013-04-19 08:49:03329 is_get_category_groups_ = false;
[email protected]bcb6ee23d2013-02-03 04:06:40330 }
331
332 if (pending_end_ack_count_ == 1) {
333 // The last ack represents local trace, so we need to ack it now. Note that
334 // this code only executes if there were child processes.
[email protected]d936677a2013-04-19 08:49:03335 std::vector<std::string> category_groups;
336 TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
[email protected]bcb6ee23d2013-02-03 04:06:40337 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
338 base::Bind(&TraceControllerImpl::OnEndTracingAck,
[email protected]d936677a2013-04-19 08:49:03339 base::Unretained(this), category_groups));
[email protected]bcb6ee23d2013-02-03 04:06:40340 }
341}
342
343void TraceControllerImpl::OnTraceDataCollected(
344 const scoped_refptr<base::RefCountedString>& events_str_ptr) {
345 // OnTraceDataCollected may be called from any browser thread, either by the
346 // local event trace system or from child processes via TraceMessageFilter.
347 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
348 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
349 base::Bind(&TraceControllerImpl::OnTraceDataCollected,
350 base::Unretained(this), events_str_ptr));
351 return;
352 }
353
354 // Drop trace events if we are just getting categories.
[email protected]d936677a2013-04-19 08:49:03355 if (subscriber_ && !is_get_category_groups_)
[email protected]bcb6ee23d2013-02-03 04:06:40356 subscriber_->OnTraceDataCollected(events_str_ptr);
357}
358
359void TraceControllerImpl::OnTraceNotification(int notification) {
360 // OnTraceNotification may be called from any browser thread, either by the
361 // local event trace system or from child processes via TraceMessageFilter.
362 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
363 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
364 base::Bind(&TraceControllerImpl::OnTraceNotification,
365 base::Unretained(this), notification));
366 return;
367 }
368
369 if (notification & base::debug::TraceLog::TRACE_BUFFER_FULL) {
370 // EndTracingAsync may return false if tracing is already in the process
371 // of being ended. That is ok.
372 EndTracingAsync(subscriber_);
373 }
374 if (notification & base::debug::TraceLog::EVENT_WATCH_NOTIFICATION) {
375 if (subscriber_)
376 subscriber_->OnEventWatchNotification();
377 }
378}
379
380void TraceControllerImpl::OnTraceBufferPercentFullReply(float percent_full) {
381 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
382 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
383 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
384 base::Unretained(this), percent_full));
385 return;
386 }
387
388 if (pending_bpf_ack_count_ == 0)
389 return;
390
391 maximum_bpf_ = (maximum_bpf_ > percent_full)? maximum_bpf_ : percent_full;
392
393 if (--pending_bpf_ack_count_ == 0) {
394 // Trigger callback if one is set.
395 if (subscriber_)
396 subscriber_->OnTraceBufferPercentFullReply(maximum_bpf_);
397 }
398
399 if (pending_bpf_ack_count_ == 1) {
400 // The last ack represents local trace, so we need to ack it now. Note that
401 // this code only executes if there were child processes.
402 float bpf = TraceLog::GetInstance()->GetBufferPercentFull();
403 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
404 base::Bind(&TraceControllerImpl::OnTraceBufferPercentFullReply,
405 base::Unretained(this), bpf));
406 }
407}
408
409} // namespace content