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