Chromium Code Reviews| Index: content/browser/browser_main_runner.cc |
| diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc |
| index f97318a690657892c4676b1b1d454f9dd75f8a1b..5a0b0a1f5db76d1bb9bac20eaa48409b0788e725 100644 |
| --- a/content/browser/browser_main_runner.cc |
| +++ b/content/browser/browser_main_runner.cc |
| @@ -8,6 +8,9 @@ |
| #include "base/base_switches.h" |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| +#include "base/debug/trace_event_impl.h" |
| +#include "base/file_util.h" |
| +#include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/statistics_recorder.h" |
| @@ -25,6 +28,124 @@ |
| bool g_exited_main_message_loop = false; |
| +namespace { |
| + |
| +// This class is intended to initiate tracing of the shutdown procedure as well |
| +// as dumping the resulting traces to a file before the browser process exits. |
| +// It will save the file either into the command line passed |
| +// "--trace-shutdown-file=<name>" parameter - or - to "chrometrace.log". |
| +// Use the class with a scoped_ptr to automatically write the dump when the |
| +// object gets destroyed. |
| +// Note that we cannot use the asynchronous file writer since the |
| +// |SequencedWorkerPool| will get killed in the shutdown process. |
| +class BrowserShutdownProfilerAndDumper { |
|
James Cook
2013/08/30 22:54:02
I think this class is big enough to go in a separa
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
Done.
|
| + public: |
| + BrowserShutdownProfilerAndDumper() : blocks_(0), dump_file_(0) { |
| + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| + base::debug::CategoryFilter category_filter( |
| + command_line.GetSwitchValueASCII(switches::kTraceShutdown)); |
| + base::debug::TraceLog::GetInstance()->SetEnabled( |
| + category_filter, |
| + base::debug::TraceLog::RECORD_UNTIL_FULL); |
| + } |
| + |
| + virtual ~BrowserShutdownProfilerAndDumper() { |
| + WriteTracesToDisc(GetFileName()); |
| + } |
| + |
| + private: |
| + // Writes all traces which happened to disc. |
| + void WriteTracesToDisc(base::FilePath file_name) { |
| + LOG(ERROR) << "Flushing shutdown traces to disc. The buffer is %" << |
|
James Cook
2013/08/30 22:54:02
Could this be a VLOG? Or is this slow enough that
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
I did never run VLOG yet, but since you asked for
|
| + base::debug::TraceLog::GetInstance()->GetBufferPercentFull() << |
| + " full."; |
| + DCHECK(!dump_file_); |
| + dump_file_ = file_util::OpenFile(file_name, "w+"); |
| + if (!IsFileValid()) { |
| + LOG(ERROR) << "Failed to open performance trace file: " << |
| + file_name.value(); |
| + return; |
| + } |
| + WriteString("{\"traceEvents\":"); |
| + WriteString("["); |
| + |
| + base::debug::TraceLog::GetInstance()->Flush( |
| + base::Bind(&BrowserShutdownProfilerAndDumper::WriteTraceDataCollected, |
| + base::Unretained(this))); |
| + |
| + WriteString("]"); |
| + WriteString("}"); |
| + CloseFile(); |
| + } |
| + |
| + // Returns the file name where we should save the trace dump to. |
| + base::FilePath GetFileName() { |
| + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| + base::FilePath trace_file = |
| + command_line.GetSwitchValuePath(switches::kTraceShutdownFile); |
| + |
| + if (!trace_file.empty()) |
| + return trace_file; |
| + |
| + // Default to saving the startup trace into the current dir. |
| + return base::FilePath().AppendASCII("chrometrace.log"); |
|
James Cook
2013/08/30 22:54:02
FYI - We usually use the suffix ".json" for trace
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
I used the same default name as for the startup ca
|
| + } |
| + |
| + // The callback for the |TraceLog::Flush| function. It saves all traces to |
| + // disc. |
| + void WriteTraceDataCollected( |
| + const scoped_refptr<base::RefCountedString>& events_str) { |
| + if (!IsFileValid()) |
| + return; |
| + if (blocks_) { |
| + // Blocks are not comma separated. Beginning with the second block we |
| + // start therefore to add one in front of the previous block. |
| + WriteString(","); |
| + } |
| + blocks_++; |
| + WriteString(events_str->data()); |
| + } |
| + |
| + // Returns true if the dump file is valid. |
| + bool IsFileValid() { |
| + return dump_file_ && (0 == ferror(dump_file_)); |
|
James Cook
2013/08/30 22:54:02
ferror(dump_file_) == 0
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
Done.
|
| + } |
| + |
| + // Writes a string to the dump file. |
| + void WriteString(const std::string& string) { |
| + WriteChars(string.data(), string.size()); |
| + |
|
James Cook
2013/08/30 22:54:02
no blank line
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
Done.
|
| + } |
| + |
| + // Write a buffer to the dump file. |
| + void WriteChars(const char* chars, size_t size) { |
| + if (!IsFileValid()) |
| + return; |
| + |
| + size_t written = fwrite(chars, 1, size, dump_file_); |
| + if (written != size) { |
| + LOG(ERROR) << "Error " << ferror(dump_file_) << |
| + " in fwrite() to trace file"; |
| + CloseFile(); |
| + } |
| + } |
| + |
| + // Closes the dump file. |
| + void CloseFile() { |
| + if (!IsFileValid()) |
| + return; |
| + fclose(dump_file_); |
|
James Cook
2013/08/30 22:54:02
Use file_util::CloseFile()
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
Done.
|
| + dump_file_ = 0; |
| + } |
| + |
| + // The number of blocks we have already written. |
| + int blocks_; |
| + // For dumping the content to disc. |
| + FILE* dump_file_; |
| +}; |
| + |
| +} |
| + |
| namespace content { |
| class BrowserMainRunnerImpl : public BrowserMainRunner { |
| @@ -122,22 +243,32 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { |
| } |
| virtual void Shutdown() OVERRIDE { |
| - DCHECK(initialization_started_); |
| - DCHECK(!is_shutdown_); |
| - g_exited_main_message_loop = true; |
| - |
| - main_loop_->ShutdownThreadsAndCleanUp(); |
| - |
| - ui::ShutdownInputMethod(); |
| -#if defined(OS_WIN) |
| - ole_initializer_.reset(NULL); |
| -#endif |
| - |
| - main_loop_.reset(NULL); |
| - |
| - notification_service_.reset(NULL); |
| - |
| - is_shutdown_ = true; |
| + // Enable shutdown tracing automatically upon shutdown if requested. |
| + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| + // When the object gets destroyed the traces will get written to disc. |
| + scoped_ptr<BrowserShutdownProfilerAndDumper> profiler; |
| + if (command_line.HasSwitch(switches::kTraceShutdown)) |
| + profiler.reset(new BrowserShutdownProfilerAndDumper()); |
| + { |
|
James Cook
2013/08/30 22:54:02
Put a blank line above this, or move the comment f
Mr4D (OOO till 08-26)
2013/08/31 02:03:44
Did that already in the following patch. Done.
|
| + // The trace event has to stay between profiler creation and destruction. |
| + TRACE_EVENT0("shutdown", "BrowserMainRunner"); |
| + DCHECK(initialization_started_); |
| + DCHECK(!is_shutdown_); |
| + g_exited_main_message_loop = true; |
| + |
| + main_loop_->ShutdownThreadsAndCleanUp(); |
| + |
| + ui::ShutdownInputMethod(); |
| + #if defined(OS_WIN) |
| + ole_initializer_.reset(NULL); |
| + #endif |
| + |
| + main_loop_.reset(NULL); |
| + |
| + notification_service_.reset(NULL); |
| + |
| + is_shutdown_ = true; |
| + } |
| } |
| protected: |