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
19namespace content {
20
21BrowserShutdownProfileDumper::BrowserShutdownProfileDumper(
22    const base::FilePath& dump_file_name)
23    : dump_file_name_(dump_file_name),
24      blocks_(0),
25      dump_file_(NULL) {
26}
27
28BrowserShutdownProfileDumper::~BrowserShutdownProfileDumper() {
29  WriteTracesToDisc();
30}
31
32void 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
67void 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
77base::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
90void 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
114bool BrowserShutdownProfileDumper::IsFileValid() {
115  return dump_file_ && (ferror(dump_file_) == 0);
116}
117
118void BrowserShutdownProfileDumper::WriteString(const std::string& string) {
119  WriteChars(string.data(), string.size());
120}
121
122void 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
135void BrowserShutdownProfileDumper::CloseFile() {
136  if (!dump_file_)
137    return;
138  base::CloseFile(dump_file_);
139  dump_file_ = NULL;
140}
141
142}  // namespace content
143