// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/test/base/tracing.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/memory/singleton.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/timer/timer.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/tracing_controller.h" #include "content/public/test/test_utils.h" namespace { using content::BrowserThread; class InProcessTraceController { public: static InProcessTraceController* GetInstance() { return Singleton<InProcessTraceController>::get(); } InProcessTraceController() : is_waiting_on_watch_(false), watch_notification_count_(0) {} virtual ~InProcessTraceController() {} bool BeginTracing(const std::string& category_patterns) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); return content::TracingController::GetInstance()->EnableRecording( category_patterns, content::TracingController::DEFAULT_OPTIONS, content::TracingController::EnableRecordingDoneCallback()); } bool BeginTracingWithWatch(const std::string& category_patterns, const std::string& category_name, const std::string& event_name, int num_occurrences) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(num_occurrences > 0); watch_notification_count_ = num_occurrences; if (!content::TracingController::GetInstance()->SetWatchEvent( category_name, event_name, base::Bind(&InProcessTraceController::OnWatchEventMatched, base::Unretained(this)))) { return false; } if (!content::TracingController::GetInstance()->EnableRecording( category_patterns, content::TracingController::DEFAULT_OPTIONS, base::Bind(&InProcessTraceController::OnEnableTracingComplete, base::Unretained(this)))) { return false; } message_loop_runner_ = new content::MessageLoopRunner; message_loop_runner_->Run(); return true; } bool WaitForWatchEvent(base::TimeDelta timeout) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (watch_notification_count_ == 0) return true; if (timeout != base::TimeDelta()) { timer_.Start(FROM_HERE, timeout, this, &InProcessTraceController::Timeout); } is_waiting_on_watch_ = true; message_loop_runner_ = new content::MessageLoopRunner; message_loop_runner_->Run(); is_waiting_on_watch_ = false; return watch_notification_count_ == 0; } bool EndTracing(std::string* json_trace_output) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); using namespace base::debug; if (!content::TracingController::GetInstance()->DisableRecording( base::FilePath(), base::Bind(&InProcessTraceController::OnTraceDataCollected, base::Unretained(this), base::Unretained(json_trace_output)))) return false; // Wait for OnEndTracingComplete() to quit the message loop. message_loop_runner_ = new content::MessageLoopRunner; message_loop_runner_->Run(); // Watch notifications can occur during this method's message loop run, but // not after, so clear them here. watch_notification_count_ = 0; return true; } private: friend struct DefaultSingletonTraits<InProcessTraceController>; void OnEnableTracingComplete() { message_loop_runner_->Quit(); } void OnEndTracingComplete() { message_loop_runner_->Quit(); } void OnTraceDataCollected(std::string* json_trace_output, const base::FilePath& path) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&InProcessTraceController::ReadTraceData, base::Unretained(this), base::Unretained(json_trace_output), path)); } void ReadTraceData(std::string* json_trace_output, const base::FilePath& path) { json_trace_output->clear(); bool ok = base::ReadFileToString(path, json_trace_output); DCHECK(ok); base::DeleteFile(path, false); // The callers expect an array of trace events. const char* preamble = "{\"traceEvents\": "; const char* trailout = "}"; DCHECK(StartsWithASCII(*json_trace_output, preamble, true)); DCHECK(EndsWith(*json_trace_output, trailout, true)); json_trace_output->erase(0, strlen(preamble)); json_trace_output->erase(json_trace_output->end() - 1); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&InProcessTraceController::OnEndTracingComplete, base::Unretained(this))); } void OnWatchEventMatched() { if (watch_notification_count_ == 0) return; if (--watch_notification_count_ == 0) { timer_.Stop(); if (is_waiting_on_watch_) message_loop_runner_->Quit(); } } void Timeout() { DCHECK(is_waiting_on_watch_); message_loop_runner_->Quit(); } scoped_refptr<content::MessageLoopRunner> message_loop_runner_; base::OneShotTimer<InProcessTraceController> timer_; bool is_waiting_on_watch_; int watch_notification_count_; DISALLOW_COPY_AND_ASSIGN(InProcessTraceController); }; } // namespace namespace tracing { bool BeginTracing(const std::string& category_patterns) { return InProcessTraceController::GetInstance()->BeginTracing( category_patterns); } bool BeginTracingWithWatch(const std::string& category_patterns, const std::string& category_name, const std::string& event_name, int num_occurrences) { return InProcessTraceController::GetInstance()->BeginTracingWithWatch( category_patterns, category_name, event_name, num_occurrences); } bool WaitForWatchEvent(base::TimeDelta timeout) { return InProcessTraceController::GetInstance()->WaitForWatchEvent(timeout); } bool EndTracing(std::string* json_trace_output) { return InProcessTraceController::GetInstance()->EndTracing(json_trace_output); } } // namespace tracing