Index: content/browser/tracing/trace_subscriber_stdio.cc |
diff --git a/content/browser/tracing/trace_subscriber_stdio.cc b/content/browser/tracing/trace_subscriber_stdio.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..da816609391b34dd76c659b9e49fc15571216634 |
--- /dev/null |
+++ b/content/browser/tracing/trace_subscriber_stdio.cc |
@@ -0,0 +1,201 @@ |
+// 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 "content/browser/tracing/trace_subscriber_stdio.h" |
+ |
+#include "base/bind.h" |
+#include "base/debug/trace_event.h" |
+#include "base/file_util.h" |
+#include "base/logging.h" |
+#include "base/threading/sequenced_worker_pool.h" |
+#include "content/public/browser/browser_thread.h" |
+ |
+namespace content { |
+ |
+// All method calls on this class are done on a SequencedWorkerPool thread. |
+class TraceSubscriberStdio::TraceSubscriberStdioWorker |
+ : public base::RefCountedThreadSafe<TraceSubscriberStdioWorker> { |
+ public: |
+ TraceSubscriberStdioWorker(const base::FilePath& path, |
+ FileType file_type, |
+ bool has_system_trace) |
+ : path_(path), |
+ file_type_(file_type), |
+ has_system_trace_(has_system_trace), |
+ file_(0), |
+ needs_comma_(false), |
+ wrote_trace_(false), |
+ has_pending_system_trace_(false), |
+ wrote_system_trace_(false) {} |
+ |
+ void OnTraceStart() { |
+ DCHECK(!file_); |
+ file_ = file_util::OpenFile(path_, "w+"); |
+ if (!IsValid()) { |
+ LOG(ERROR) << "Failed to open performance trace file: " << path_.value(); |
+ return; |
+ } |
+ |
+ VLOG(0) << "Logging performance trace to file: " << path_.value(); |
+ if (file_type_ == FILE_TYPE_PROPERTY_LIST) |
+ WriteString("{\"traceEvents\":"); |
+ WriteString("["); |
+ } |
+ |
+ void OnTraceData(const scoped_refptr<base::RefCountedString>& data_ptr) { |
+ if (!IsValid()) |
+ return; |
+ DCHECK(!data_ptr->data().empty()); |
+ if (needs_comma_) |
+ WriteString(","); |
+ WriteString(data_ptr->data()); |
+ needs_comma_ = true; |
+ } |
+ |
+ void OnSystemTraceData( |
+ const scoped_refptr<base::RefCountedString>& data_ptr) { |
+ if (wrote_trace_) { |
+ WriteSystemTrace(data_ptr); |
+ End(); |
+ } else { |
+ pending_system_trace_ = data_ptr; |
+ has_pending_system_trace_ = true; |
+ } |
+ } |
+ |
+ void OnTraceEnd() { |
+ if (!IsValid()) |
+ return; |
+ WriteString("]"); |
+ |
+ wrote_trace_ = true; |
+ |
+ if (!has_system_trace_ || wrote_system_trace_) { |
+ End(); |
+ return; |
+ } |
+ |
+ WriteString(","); |
+ if (has_pending_system_trace_) { |
+ WriteSystemTrace(pending_system_trace_); |
+ End(); |
+ } |
+ } |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<TraceSubscriberStdioWorker>; |
+ |
+ ~TraceSubscriberStdioWorker() { |
+ CloseFile(); |
+ } |
+ |
+ bool IsValid() const { |
+ return file_ && (0 == ferror(file_)); |
+ } |
+ |
+ void CloseFile() { |
+ if (file_) { |
+ fclose(file_); |
+ file_ = 0; |
+ |
+ } |
+ } |
+ |
+ void End() { |
+ if (file_type_ == FILE_TYPE_PROPERTY_LIST) |
+ WriteString("}"); |
+ CloseFile(); |
+ } |
+ |
+ void WriteSystemTrace(const scoped_refptr<base::RefCountedString>& data_ptr) { |
+ // Newlines need to be replaced with the string "\n" to be parsed correctly. |
+ // Double quotes need to be replaced with the string "\"". |
+ // System logs are ASCII. |
+ const std::string& data = data_ptr->data(); |
+ const char* chars = data.c_str(); |
+ WriteString("\"systemTraceEvents\":\""); |
+ size_t old_index = 0; |
+ for (size_t new_index = data.find_first_of("\n\""); |
+ std::string::npos != new_index; |
+ old_index = new_index + 1, |
+ new_index = data.find_first_of("\n\"", old_index)) { |
+ WriteChars(chars + old_index, new_index - old_index); |
+ if (chars[new_index] == '\n') |
+ WriteChars("\\n", 2); |
+ else |
+ WriteChars("\\\"", 2); |
+ } |
+ WriteChars(chars + old_index, data.size() - old_index); |
+ WriteString("\""); |
+ wrote_system_trace_ = true; |
+ } |
+ |
+ void WriteChars(const char* output_chars, size_t size) { |
+ if (size == 0) |
+ return; |
+ |
+ if (IsValid()) { |
+ size_t written = fwrite(output_chars, 1, size, file_); |
+ if (written != size) { |
+ LOG(ERROR) << "Error " << ferror(file_) << " in fwrite() to trace file"; |
+ CloseFile(); |
+ } |
+ } |
+ } |
+ |
+ void WriteString(const std::string& output_str) { |
+ WriteChars(output_str.data(), output_str.size()); |
+ } |
+ |
+ base::FilePath path_; |
+ const FileType file_type_; |
+ const bool has_system_trace_; |
+ FILE* file_; |
+ bool needs_comma_; |
+ bool wrote_trace_; |
+ bool has_pending_system_trace_; |
+ bool wrote_system_trace_; |
+ scoped_refptr<base::RefCountedString> pending_system_trace_; |
+ DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdioWorker); |
+}; |
+ |
+TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path, |
+ FileType file_type, |
+ bool has_system_trace) |
+ : worker_(new TraceSubscriberStdioWorker(path, |
+ file_type, |
+ has_system_trace)) { |
+ if (has_system_trace) |
+ CHECK_EQ(FILE_TYPE_PROPERTY_LIST, file_type); |
+ BrowserThread::PostBlockingPoolSequencedTask( |
+ __FILE__, FROM_HERE, |
+ base::Bind(&TraceSubscriberStdioWorker::OnTraceStart, worker_)); |
+} |
+ |
+TraceSubscriberStdio::~TraceSubscriberStdio() { |
+} |
+ |
+void TraceSubscriberStdio::OnEndTracingComplete() { |
+ BrowserThread::PostBlockingPoolSequencedTask( |
+ __FILE__, FROM_HERE, |
+ base::Bind(&TraceSubscriberStdioWorker::OnTraceEnd, worker_)); |
+} |
+ |
+void TraceSubscriberStdio::OnTraceDataCollected( |
+ const scoped_refptr<base::RefCountedString>& data_ptr) { |
+ BrowserThread::PostBlockingPoolSequencedTask( |
+ __FILE__, FROM_HERE, |
+ base::Bind(&TraceSubscriberStdioWorker::OnTraceData, worker_, data_ptr)); |
+} |
+ |
+void TraceSubscriberStdio::OnEndSystemTracing( |
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) { |
+ BrowserThread::PostBlockingPoolSequencedTask( |
+ __FILE__, FROM_HERE, |
+ base::Bind(&TraceSubscriberStdioWorker::OnSystemTraceData, |
+ worker_, |
+ events_str_ptr)); |
+} |
+ |
+} // namespace content |