1 // Copyright 2013 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/browser_shutdown_profile_dumper.h" 6 7 #include "base/base_switches.h" 8 #include "base/command_line.h" 9 #include "base/debug/trace_event.h" 10 #include "base/debug/trace_event_impl.h" 11 #include "base/file_util.h" 12 #include "base/files/file_path.h" 13 #include "base/logging.h" 14 #include "base/synchronization/waitable_event.h" 15 #include "base/threading/thread.h" 16 #include "base/threading/thread_restrictions.h" 17 #include "content/public/common/content_switches.h" 18 19 namespace content { 20 21 BrowserShutdownProfileDumper::BrowserShutdownProfileDumper() 22 : blocks_(0), 23 dump_file_(NULL) { 24 } 25 26 BrowserShutdownProfileDumper::~BrowserShutdownProfileDumper() { 27 WriteTracesToDisc(GetFileName()); 28 } 29 30 void BrowserShutdownProfileDumper::WriteTracesToDisc( 31 const base::FilePath& file_name) { 32 // Note: I have seen a usage of 0.000xx% when dumping - which fits easily. 33 // Since the tracer stops when the trace buffer is filled, we'd rather save 34 // what we have than nothing since we might see from the amount of events 35 // that caused the problem. 36 DVLOG(1) << "Flushing shutdown traces to disc. The buffer is %" << 37 base::debug::TraceLog::GetInstance()->GetBufferPercentFull() << 38 " full."; 39 DCHECK(!dump_file_); 40 dump_file_ = base::OpenFile(file_name, "w+"); 41 if (!IsFileValid()) { 42 LOG(ERROR) << "Failed to open performance trace file: " << 43 file_name.value(); 44 return; 45 } 46 WriteString("{\"traceEvents\":"); 47 WriteString("["); 48 49 // TraceLog::Flush() requires the calling thread to have a message loop. 50 // As the message loop of the current thread may have quit, start another 51 // thread for flushing the trace. 52 base::WaitableEvent flush_complete_event(false, false); 53 base::Thread flush_thread("browser_shutdown_trace_event_flush"); 54 flush_thread.Start(); 55 flush_thread.message_loop()->PostTask( 56 FROM_HERE, 57 base::Bind(&BrowserShutdownProfileDumper::EndTraceAndFlush, 58 base::Unretained(this), 59 base::Unretained(&flush_complete_event))); 60 61 bool original_wait_allowed = base::ThreadRestrictions::SetWaitAllowed(true); 62 flush_complete_event.Wait(); 63 base::ThreadRestrictions::SetWaitAllowed(original_wait_allowed); 64 } 65 66 void BrowserShutdownProfileDumper::EndTraceAndFlush( 67 base::WaitableEvent* flush_complete_event) { 68 base::debug::TraceLog::GetInstance()->SetDisabled(); 69 base::debug::TraceLog::GetInstance()->Flush( 70 base::Bind(&BrowserShutdownProfileDumper::WriteTraceDataCollected, 71 base::Unretained(this), 72 base::Unretained(flush_complete_event))); 73 } 74 75 base::FilePath BrowserShutdownProfileDumper::GetFileName() { 76 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 77 base::FilePath trace_file = 78 command_line.GetSwitchValuePath(switches::kTraceShutdownFile); 79 80 if (!trace_file.empty()) 81 return trace_file; 82 83 // Default to saving the startup trace into the current dir. 84 return base::FilePath().AppendASCII("chrometrace.log"); 85 } 86 87 void BrowserShutdownProfileDumper::WriteTraceDataCollected( 88 base::WaitableEvent* flush_complete_event, 89 const scoped_refptr<base::RefCountedString>& events_str, 90 bool has_more_events) { 91 if (!IsFileValid()) { 92 flush_complete_event->Signal(); 93 return; 94 } 95 if (blocks_) { 96 // Blocks are not comma separated. Beginning with the second block we 97 // start therefore to add one in front of the previous block. 98 WriteString(","); 99 } 100 ++blocks_; 101 WriteString(events_str->data()); 102 103 if (!has_more_events) { 104 WriteString("]"); 105 WriteString("}"); 106 CloseFile(); 107 flush_complete_event->Signal(); 108 } 109 } 110 111 bool BrowserShutdownProfileDumper::IsFileValid() { 112 return dump_file_ && (ferror(dump_file_) == 0); 113 } 114 115 void BrowserShutdownProfileDumper::WriteString(const std::string& string) { 116 WriteChars(string.data(), string.size()); 117 } 118 119 void BrowserShutdownProfileDumper::WriteChars(const char* chars, size_t size) { 120 if (!IsFileValid()) 121 return; 122 123 size_t written = fwrite(chars, 1, size, dump_file_); 124 if (written != size) { 125 LOG(ERROR) << "Error " << ferror(dump_file_) << 126 " in fwrite() to trace file"; 127 CloseFile(); 128 } 129 } 130 131 void BrowserShutdownProfileDumper::CloseFile() { 132 if (!dump_file_) 133 return; 134 base::CloseFile(dump_file_); 135 dump_file_ = NULL; 136 } 137 138 } // namespace content 139