Home | History | Annotate | Download | only in browser
      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