| // 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; |
| } |
| |
| LOG(INFO) << "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; |
| 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 |