#include "base/bind.h"
#include "base/debug/trace_event.h"
-#include "base/file_util.h"
+#include "base/files/file_util.h"
#include "base/json/string_escape.h"
+#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/tracing/trace_message_filter.h"
#include "content/browser/tracing/tracing_ui.h"
#endif
using base::debug::TraceLog;
+using base::debug::TraceOptions;
+using base::debug::CategoryFilter;
namespace content {
base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
LAZY_INSTANCE_INITIALIZER;
-} // namespace
-
-TracingController* TracingController::GetInstance() {
- return TracingControllerImpl::GetInstance();
-}
-
-class TracingControllerImpl::ResultFile {
+class FileTraceDataSink : public TracingController::TraceDataSink {
public:
- explicit ResultFile(const base::FilePath& path);
- void Write(const scoped_refptr<base::RefCountedString>& events_str_ptr) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&TracingControllerImpl::ResultFile::WriteTask,
- base::Unretained(this), events_str_ptr));
+ explicit FileTraceDataSink(const base::FilePath& trace_file_path,
+ const base::Closure& callback)
+ : file_path_(trace_file_path),
+ completion_callback_(callback),
+ file_(NULL) {}
+
+ void AddTraceChunk(const std::string& chunk) override {
+ std::string tmp = chunk;
+ scoped_refptr<base::RefCountedString> chunk_ptr =
+ base::RefCountedString::TakeString(&tmp);
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ &FileTraceDataSink::AddTraceChunkOnFileThread, this, chunk_ptr));
}
- void Close(const base::Closure& callback) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&TracingControllerImpl::ResultFile::CloseTask,
- base::Unretained(this), callback));
+ void SetSystemTrace(const std::string& data) override {
+ system_trace_ = data;
}
- void WriteSystemTrace(
- const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ void Close() override {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
- base::Bind(&TracingControllerImpl::ResultFile::WriteSystemTraceTask,
- base::Unretained(this), events_str_ptr));
+ base::Bind(&FileTraceDataSink::CloseOnFileThread, this));
}
- const base::FilePath& path() const { return path_; }
-
private:
- void OpenTask();
- void WriteTask(const scoped_refptr<base::RefCountedString>& events_str_ptr);
- void WriteSystemTraceTask(
- const scoped_refptr<base::RefCountedString>& events_str_ptr);
- void CloseTask(const base::Closure& callback);
+ ~FileTraceDataSink() override { DCHECK(file_ == NULL); }
+
+ void AddTraceChunkOnFileThread(
+ const scoped_refptr<base::RefCountedString> chunk) {
+ if (file_ != NULL)
+ fputc(',', file_);
+ else if (!OpenFileIfNeededOnFileThread())
+ return;
+ ignore_result(fwrite(chunk->data().c_str(), strlen(chunk->data().c_str()),
+ 1, file_));
+ }
+
+ bool OpenFileIfNeededOnFileThread() {
+ if (file_ != NULL)
+ return true;
+ file_ = base::OpenFile(file_path_, "w");
+ if (file_ == NULL) {
+ LOG(ERROR) << "Failed to open " << file_path_.value();
+ return false;
+ }
+ const char preamble[] = "{\"traceEvents\": [";
+ ignore_result(fwrite(preamble, strlen(preamble), 1, file_));
+ return true;
+ }
+
+ void CloseOnFileThread() {
+ if (OpenFileIfNeededOnFileThread()) {
+ fputc(']', file_);
+ if (!system_trace_.empty()) {
+ const char systemTraceEvents[] = ",\"systemTraceEvents\": ";
+ ignore_result(fwrite(systemTraceEvents, strlen(systemTraceEvents),
+ 1, file_));
+ ignore_result(fwrite(system_trace_.c_str(),
+ strlen(system_trace_.c_str()), 1, file_));
+ }
+ fputc('}', file_);
+ base::CloseFile(file_);
+ file_ = NULL;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&FileTraceDataSink::FinalizeOnUIThread, this));
+ }
+
+ void FinalizeOnUIThread() { completion_callback_.Run(); }
+ base::FilePath file_path_;
+ base::Closure completion_callback_;
FILE* file_;
- base::FilePath path_;
- bool has_at_least_one_result_;
- scoped_refptr<base::RefCountedString> system_trace_;
+ std::string system_trace_;
- DISALLOW_COPY_AND_ASSIGN(ResultFile);
+ DISALLOW_COPY_AND_ASSIGN(FileTraceDataSink);
};
-TracingControllerImpl::ResultFile::ResultFile(const base::FilePath& path)
- : file_(NULL),
- path_(path),
- has_at_least_one_result_(false) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&TracingControllerImpl::ResultFile::OpenTask,
- base::Unretained(this)));
-}
-
-void TracingControllerImpl::ResultFile::OpenTask() {
- if (path_.empty())
- base::CreateTemporaryFile(&path_);
- file_ = base::OpenFile(path_, "w");
- if (!file_) {
- LOG(ERROR) << "Failed to open " << path_.value();
- return;
- }
- const char* preamble = "{\"traceEvents\": [";
- size_t written = fwrite(preamble, strlen(preamble), 1, file_);
- DCHECK(written == 1);
-}
+class StringTraceDataSink : public TracingController::TraceDataSink {
+ public:
+ typedef base::Callback<void(base::RefCountedString*)> CompletionCallback;
-void TracingControllerImpl::ResultFile::WriteTask(
- const scoped_refptr<base::RefCountedString>& events_str_ptr) {
- if (!file_ || !events_str_ptr->data().size())
- return;
+ explicit StringTraceDataSink(CompletionCallback callback)
+ : completion_callback_(callback) {}
- // If there is already a result in the file, then put a comma
- // before the next batch of results.
- if (has_at_least_one_result_) {
- size_t written = fwrite(",", 1, 1, file_);
- DCHECK(written == 1);
+ // TracingController::TraceDataSink implementation
+ void AddTraceChunk(const std::string& chunk) override {
+ if (!trace_.empty())
+ trace_ += ",";
+ trace_ += chunk;
}
- has_at_least_one_result_ = true;
- size_t written = fwrite(events_str_ptr->data().c_str(),
- events_str_ptr->data().size(), 1,
- file_);
- DCHECK(written == 1);
-}
-
-void TracingControllerImpl::ResultFile::WriteSystemTraceTask(
- const scoped_refptr<base::RefCountedString>& events_str_ptr) {
- system_trace_ = events_str_ptr;
-}
-
-void TracingControllerImpl::ResultFile::CloseTask(
- const base::Closure& callback) {
- if (!file_)
- return;
-
- const char* trailevents = "]";
- size_t written = fwrite(trailevents, strlen(trailevents), 1, file_);
- DCHECK(written == 1);
+ void SetSystemTrace(const std::string& data) override {
+ system_trace_ = data;
+ }
+ void Close() override {
+ std::string result = "{\"traceEvents\":[" + trace_ + "]";
+ if (!system_trace_.empty())
+ result += ",\"systemTraceEvents\": " + system_trace_;
+ result += "}";
- if (system_trace_) {
-#if defined(OS_WIN)
- // The Windows kernel events are kept into a JSon format stored as string
- // and must not be escaped.
- std::string json_string = system_trace_->data();
-#else
- std::string json_string = base::GetQuotedJSONString(system_trace_->data());
-#endif
+ scoped_refptr<base::RefCountedString> str =
+ base::RefCountedString::TakeString(&result);
+ completion_callback_.Run(str.get());
+ }
- const char* systemTraceHead = ",\n\"systemTraceEvents\": ";
- written = fwrite(systemTraceHead, strlen(systemTraceHead), 1, file_);
- DCHECK(written == 1);
+ private:
+ ~StringTraceDataSink() override {}
- written = fwrite(json_string.data(), json_string.size(), 1, file_);
- DCHECK(written == 1);
+ std::string trace_;
+ std::string system_trace_;
+ CompletionCallback completion_callback_;
- system_trace_ = NULL;
- }
+ DISALLOW_COPY_AND_ASSIGN(StringTraceDataSink);
+};
- const char* trailout = "}";
- written = fwrite(trailout, strlen(trailout), 1, file_);
- DCHECK(written == 1);
- base::CloseFile(file_);
- file_ = NULL;
+} // namespace
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
+TracingController* TracingController::GetInstance() {
+ return TracingControllerImpl::GetInstance();
}
-
TracingControllerImpl::TracingControllerImpl() :
pending_disable_recording_ack_count_(0),
pending_capture_monitoring_snapshot_ack_count_(0),
// message. So to get known categories, just begin and end tracing immediately
// afterwards. This will ping all the child processes for categories.
pending_get_categories_done_callback_ = callback;
- if (!EnableRecording("*", TracingController::Options(),
- EnableRecordingDoneCallback())) {
+ if (!EnableRecording(
+ CategoryFilter("*"), TraceOptions(), EnableRecordingDoneCallback())) {
pending_get_categories_done_callback_.Reset();
return false;
}
- bool ok = DisableRecording(base::FilePath(), TracingFileResultCallback());
+ bool ok = DisableRecording(NULL);
DCHECK(ok);
return true;
}
void TracingControllerImpl::SetEnabledOnFileThread(
- const std::string& category_filter,
+ const CategoryFilter& category_filter,
int mode,
- int trace_options,
+ const TraceOptions& trace_options,
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
TraceLog::GetInstance()->SetEnabled(
- base::debug::CategoryFilter(category_filter),
- static_cast<TraceLog::Mode>(mode),
- static_cast<TraceLog::Options>(trace_options));
+ category_filter, static_cast<TraceLog::Mode>(mode), trace_options);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}
}
bool TracingControllerImpl::EnableRecording(
- const std::string& category_filter,
- TracingController::Options options,
+ const CategoryFilter& category_filter,
+ const TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
- options_ = options;
- int trace_options = (options & RECORD_CONTINUOUSLY) ?
- TraceLog::RECORD_CONTINUOUSLY : TraceLog::RECORD_UNTIL_FULL;
- if (options & ENABLE_SAMPLING) {
- trace_options |= TraceLog::ENABLE_SAMPLING;
- }
+ trace_options_ = trace_options;
- if (options & ENABLE_SYSTRACE) {
+ if (trace_options.enable_systrace) {
#if defined(OS_CHROMEOS)
DCHECK(!is_system_tracing_);
chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
}
void TracingControllerImpl::OnEnableRecordingDone(
- const std::string& category_filter,
- int trace_options,
+ const CategoryFilter& category_filter,
+ const TraceOptions& trace_options,
const EnableRecordingDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
- it->get()->SendBeginTracing(category_filter,
- static_cast<TraceLog::Options>(trace_options));
+ it->get()->SendBeginTracing(category_filter, trace_options);
}
if (!callback.is_null())
}
bool TracingControllerImpl::DisableRecording(
- const base::FilePath& result_file_path,
- const TracingFileResultCallback& callback) {
+ const scoped_refptr<TraceDataSink>& trace_data_sink) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!can_disable_recording())
return false;
- options_ = TracingController::Options();
+ trace_data_sink_ = trace_data_sink;
+ trace_options_ = TraceOptions();
// Disable local trace early to avoid traces during end-tracing process from
// interfering with the process.
- base::Closure on_disable_recording_done_callback =
- base::Bind(&TracingControllerImpl::OnDisableRecordingDone,
- base::Unretained(this),
- result_file_path, callback);
+ base::Closure on_disable_recording_done_callback = base::Bind(
+ &TracingControllerImpl::OnDisableRecordingDone, base::Unretained(this));
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
base::Unretained(this),
return true;
}
-void TracingControllerImpl::OnDisableRecordingDone(
- const base::FilePath& result_file_path,
- const TracingFileResultCallback& callback) {
+void TracingControllerImpl::OnDisableRecordingDone() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- pending_disable_recording_done_callback_ = callback;
-
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
- if (!callback.is_null() || !result_file_path.empty())
- result_file_.reset(new ResultFile(result_file_path));
-
// Count myself (local trace) in pending_disable_recording_ack_count_,
// acked below.
pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1;
pending_disable_recording_filters_ = trace_message_filters_;
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ if (is_system_tracing_) {
+ // Disable system tracing.
+ is_system_tracing_ = false;
+ ++pending_disable_recording_ack_count_;
+
+#if defined(OS_CHROMEOS)
+ scoped_refptr<base::TaskRunner> task_runner =
+ BrowserThread::GetBlockingPool();
+ chromeos::DBusThreadManager::Get()
+ ->GetDebugDaemonClient()
+ ->RequestStopSystemTracing(
+ task_runner,
+ base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
+ base::Unretained(this)));
+#elif defined(OS_WIN)
+ EtwSystemEventConsumer::GetInstance()->StopSystemTracing(
+ base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
+ base::Unretained(this)));
+#endif
+ }
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
+
// Handle special case of zero child processes by immediately flushing the
// trace log. Once the flush has completed the caller will be notified that
// tracing has ended.
}
bool TracingControllerImpl::EnableMonitoring(
- const std::string& category_filter,
- TracingController::Options options,
+ const CategoryFilter& category_filter,
+ const TraceOptions& trace_options,
const EnableMonitoringDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
- options_ = options;
- int trace_options = 0;
- if (options & ENABLE_SAMPLING)
- trace_options |= TraceLog::ENABLE_SAMPLING;
+ trace_options_ = trace_options;
base::Closure on_enable_monitoring_done_callback =
base::Bind(&TracingControllerImpl::OnEnableMonitoringDone,
}
void TracingControllerImpl::OnEnableMonitoringDone(
- const std::string& category_filter,
- int trace_options,
+ const CategoryFilter& category_filter,
+ const TraceOptions& trace_options,
const EnableMonitoringDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Notify all child processes.
for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
- it->get()->SendEnableMonitoring(category_filter,
- static_cast<TraceLog::Options>(trace_options));
+ it->get()->SendEnableMonitoring(category_filter, trace_options);
}
if (!callback.is_null())
if (!can_disable_monitoring())
return false;
- options_ = TracingController::Options();
+ trace_options_ = TraceOptions();
base::Closure on_disable_monitoring_done_callback =
base::Bind(&TracingControllerImpl::OnDisableMonitoringDone,
base::Unretained(this), callback);
return true;
}
+scoped_refptr<TracingController::TraceDataSink>
+TracingController::CreateStringSink(
+ const base::Callback<void(base::RefCountedString*)>& callback) {
+ return new StringTraceDataSink(callback);
+}
+
+scoped_refptr<TracingController::TraceDataSink>
+TracingController::CreateFileSink(const base::FilePath& file_path,
+ const base::Closure& callback) {
+ return new FileTraceDataSink(file_path, callback);
+}
+
void TracingControllerImpl::OnDisableMonitoringDone(
const DisableMonitoringDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
it != trace_message_filters_.end(); ++it) {
it->get()->SendDisableMonitoring();
}
-
if (!callback.is_null())
callback.Run();
}
void TracingControllerImpl::GetMonitoringStatus(
bool* out_enabled,
- std::string* out_category_filter,
- TracingController::Options* out_options) {
+ CategoryFilter* out_category_filter,
+ TraceOptions* out_trace_options) {
*out_enabled = is_monitoring_;
- *out_category_filter =
- TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString();
- *out_options = options_;
+ *out_category_filter = TraceLog::GetInstance()->GetCurrentCategoryFilter();
+ *out_trace_options = trace_options_;
}
bool TracingControllerImpl::CaptureMonitoringSnapshot(
- const base::FilePath& result_file_path,
- const TracingFileResultCallback& callback) {
+ const scoped_refptr<TraceDataSink>& monitoring_data_sink) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!can_disable_monitoring())
return false;
- if (callback.is_null() && result_file_path.empty())
+ if (!monitoring_data_sink.get())
return false;
- pending_capture_monitoring_snapshot_done_callback_ = callback;
- monitoring_snapshot_file_.reset(new ResultFile(result_file_path));
+ monitoring_data_sink_ = monitoring_data_sink;
// Count myself in pending_capture_monitoring_snapshot_ack_count_,
// acked below.
}
if (can_disable_recording()) {
trace_message_filter->SendBeginTracing(
- TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(),
- TraceLog::GetInstance()->trace_options());
+ TraceLog::GetInstance()->GetCurrentCategoryFilter(),
+ TraceLog::GetInstance()->GetCurrentTraceOptions());
}
if (can_disable_monitoring()) {
trace_message_filter->SendEnableMonitoring(
- TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(),
- TraceLog::GetInstance()->trace_options());
+ TraceLog::GetInstance()->GetCurrentCategoryFilter(),
+ TraceLog::GetInstance()->GetCurrentTraceOptions());
}
}
if (pending_disable_recording_ack_count_ != 0)
return;
- OnDisableRecordingComplete();
-}
-
-void TracingControllerImpl::OnDisableRecordingComplete() {
// All acks (including from the subprocesses and the local trace) have been
// received.
is_recording_ = false;
-#if defined(OS_CHROMEOS)
- if (is_system_tracing_) {
- // Disable system tracing.
- is_system_tracing_ = false;
-
- // Disable system tracing now that the local trace has shutdown.
- // This must be done last because we potentially need to push event
- // records into the system event log for synchronizing system event
- // timestamps with chrome event timestamps--and since the system event
- // log is a ring-buffer (on linux) adding them at the end is the only
- // way we're confident we'll have them in the final result.
- chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
- RequestStopSystemTracing(
- base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
- base::Unretained(this)));
- return;
- }
-#elif defined(OS_WIN)
- if (is_system_tracing_) {
- // Disable system tracing.
- is_system_tracing_ = false;
-
-
- // Stop kernel tracing and flush events.
- EtwSystemEventConsumer::GetInstance()->StopSystemTracing(
- base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
- base::Unretained(this)));
- return;
- }
-#endif
-
// Trigger callback if one is set.
if (!pending_get_categories_done_callback_.is_null()) {
pending_get_categories_done_callback_.Run(known_category_groups_);
pending_get_categories_done_callback_.Reset();
- } else if (result_file_) {
- result_file_->Close(
- base::Bind(&TracingControllerImpl::OnResultFileClosed,
- base::Unretained(this)));
+ } else if (trace_data_sink_.get()) {
+ trace_data_sink_->Close();
+ trace_data_sink_ = NULL;
}
}
-void TracingControllerImpl::OnResultFileClosed() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (!result_file_)
- return;
-
- if (!pending_disable_recording_done_callback_.is_null()) {
- pending_disable_recording_done_callback_.Run(result_file_->path());
- pending_disable_recording_done_callback_.Reset();
- }
- result_file_.reset();
-}
-
#if defined(OS_CHROMEOS) || defined(OS_WIN)
void TracingControllerImpl::OnEndSystemTracingAcked(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (result_file_)
- result_file_->WriteSystemTrace(events_str_ptr);
-
+ if (trace_data_sink_.get()) {
+#if defined(OS_WIN)
+ // The Windows kernel events are kept into a JSon format stored as string
+ // and must not be escaped.
+ std::string json_string = events_str_ptr->data();
+#else
+ std::string json_string =
+ base::GetQuotedJSONString(events_str_ptr->data());
+#endif
+ trace_data_sink_->SetSystemTrace(json_string);
+ }
DCHECK(!is_system_tracing_);
- OnDisableRecordingComplete();
+ std::vector<std::string> category_groups;
+ OnDisableRecordingAcked(NULL, category_groups);
}
#endif
if (pending_capture_monitoring_snapshot_ack_count_ != 0)
return;
- if (monitoring_snapshot_file_) {
- monitoring_snapshot_file_->Close(
- base::Bind(&TracingControllerImpl::OnMonitoringSnapshotFileClosed,
- base::Unretained(this)));
- }
-}
-
-void TracingControllerImpl::OnMonitoringSnapshotFileClosed() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (!monitoring_snapshot_file_)
- return;
-
- if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) {
- pending_capture_monitoring_snapshot_done_callback_.Run(
- monitoring_snapshot_file_->path());
- pending_capture_monitoring_snapshot_done_callback_.Reset();
+ if (monitoring_data_sink_.get()) {
+ monitoring_data_sink_->Close();
+ monitoring_data_sink_ = NULL;
}
- monitoring_snapshot_file_.reset();
}
void TracingControllerImpl::OnTraceDataCollected(
return;
}
- if (result_file_)
- result_file_->Write(events_str_ptr);
+ if (trace_data_sink_.get())
+ trace_data_sink_->AddTraceChunk(events_str_ptr->data());
}
void TracingControllerImpl::OnMonitoringTraceDataCollected(
return;
}
- if (monitoring_snapshot_file_)
- monitoring_snapshot_file_->Write(events_str_ptr);
+ if (monitoring_data_sink_.get())
+ monitoring_data_sink_->AddTraceChunk(events_str_ptr->data());
}
void TracingControllerImpl::OnLocalTraceDataCollected(