158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// found in the LICENSE file.
458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "content/browser/browser_shutdown_profile_dumper.h"
658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/base_switches.h"
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/command_line.h"
958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/debug/trace_event.h"
1058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/debug/trace_event_impl.h"
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/file_util.h"
1258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/files/file_path.h"
1358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/logging.h"
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/synchronization/waitable_event.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/threading/thread.h"
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/threading/thread_restrictions.h"
1758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "content/public/common/content_switches.h"
1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace content {
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrowserShutdownProfileDumper::BrowserShutdownProfileDumper()
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    : blocks_(0),
2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      dump_file_(NULL) {
2458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
2558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)BrowserShutdownProfileDumper::~BrowserShutdownProfileDumper() {
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  WriteTracesToDisc(GetFileName());
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrowserShutdownProfileDumper::WriteTracesToDisc(
3158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const base::FilePath& file_name) {
3258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Note: I have seen a usage of 0.000xx% when dumping - which fits easily.
3358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Since the tracer stops when the trace buffer is filled, we'd rather save
3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // what we have than nothing since we might see from the amount of events
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // that caused the problem.
3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DVLOG(1) << "Flushing shutdown traces to disc. The buffer is %" <<
3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      base::debug::TraceLog::GetInstance()->GetBufferPercentFull() <<
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      " full.";
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(!dump_file_);
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dump_file_ = base::OpenFile(file_name, "w+");
4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!IsFileValid()) {
4258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    LOG(ERROR) << "Failed to open performance trace file: " <<
4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        file_name.value();
4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
4558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  WriteString("{\"traceEvents\":");
4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  WriteString("[");
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
49d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // TraceLog::Flush() requires the calling thread to have a message loop.
50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // As the message loop of the current thread may have quit, start another
51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // thread for flushing the trace.
52d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::WaitableEvent flush_complete_event(false, false);
53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::Thread flush_thread("browser_shutdown_trace_event_flush");
54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  flush_thread.Start();
55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  flush_thread.message_loop()->PostTask(
56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      FROM_HERE,
57d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      base::Bind(&BrowserShutdownProfileDumper::EndTraceAndFlush,
58d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(this),
59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(&flush_complete_event)));
60d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
61d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  bool original_wait_allowed = base::ThreadRestrictions::SetWaitAllowed(true);
62d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  flush_complete_event.Wait();
63d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::ThreadRestrictions::SetWaitAllowed(original_wait_allowed);
64d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
65d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
66d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrowserShutdownProfileDumper::EndTraceAndFlush(
67d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    base::WaitableEvent* flush_complete_event) {
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::debug::TraceLog::GetInstance()->SetDisabled();
6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  base::debug::TraceLog::GetInstance()->Flush(
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      base::Bind(&BrowserShutdownProfileDumper::WriteTraceDataCollected,
71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(this),
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(flush_complete_event)));
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)base::FilePath BrowserShutdownProfileDumper::GetFileName() {
7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  base::FilePath trace_file =
7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      command_line.GetSwitchValuePath(switches::kTraceShutdownFile);
7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!trace_file.empty())
8158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return trace_file;
8258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Default to saving the startup trace into the current dir.
8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return base::FilePath().AppendASCII("chrometrace.log");
8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrowserShutdownProfileDumper::WriteTraceDataCollected(
88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    base::WaitableEvent* flush_complete_event,
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const scoped_refptr<base::RefCountedString>& events_str,
9058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    bool has_more_events) {
91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!IsFileValid()) {
92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    flush_complete_event->Signal();
9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (blocks_) {
9658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // Blocks are not comma separated. Beginning with the second block we
9758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // start therefore to add one in front of the previous block.
9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    WriteString(",");
9958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ++blocks_;
10158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  WriteString(events_str->data());
10258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
10358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!has_more_events) {
10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    WriteString("]");
10558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    WriteString("}");
10658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    CloseFile();
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    flush_complete_event->Signal();
10858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
10958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool BrowserShutdownProfileDumper::IsFileValid() {
11258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return dump_file_ && (ferror(dump_file_) == 0);
11358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
11458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrowserShutdownProfileDumper::WriteString(const std::string& string) {
11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  WriteChars(string.data(), string.size());
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrowserShutdownProfileDumper::WriteChars(const char* chars, size_t size) {
12058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!IsFileValid())
12158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  size_t written = fwrite(chars, 1, size, dump_file_);
12458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (written != size) {
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    LOG(ERROR) << "Error " << ferror(dump_file_) <<
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        " in fwrite() to trace file";
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    CloseFile();
12858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void BrowserShutdownProfileDumper::CloseFile() {
13258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!dump_file_)
13358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::CloseFile(dump_file_);
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  dump_file_ = NULL;
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace content
139