Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6991)

Unified Diff: chrome/test/base/file_logger_win.cc

Issue 9584017: New test infrastructure for producing verbose logs in failing tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: capture trace events as well Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/test/base/file_logger_win.cc
diff --git a/chrome/test/base/file_logger_win.cc b/chrome/test/base/file_logger_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..fc5e2a1f8c3fecb3792ac9603d5658b64b4f36f6
--- /dev/null
+++ b/chrome/test/base/file_logger_win.cc
@@ -0,0 +1,805 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/base/file_logger_win.h"
+
+#include <windows.h>
+#include <evntrace.h>
+#include <guiddef.h>
+#include <objbase.h> // for StringFromGUID2
robertshield 2012/03/05 19:20:54 any reason for the comment?
grt (UTC plus 2) 2012/03/06 03:24:57 Not really. Removed.
+
+#include <iomanip>
+#include <ios> // For std::hex
+
+#include "base/debug/trace_event_win.h"
+#include "base/logging.h"
+#include "base/file_path.h"
+#include "base/logging_win.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/event_trace_consumer.h"
+#include "base/win/registry.h"
+#include "chrome/common/env_vars.h"
+
+namespace {
+
+bool g_is_initialized = false;
+
+const wchar_t kChromeTestSession[] = L"chrome_tests";
+
+// From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F}
+const GUID kChromeFrameProvider =
+ { 0x562bfc3, 0x2550, 0x45b4,
+ { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } };
+
+// From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7}
+const GUID kChromeTraceProviderName =
+ { 0x7fe69228, 0x633e, 0x4f06,
+ { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
+
+// {81729947-CD2A-49e6-8885-785429F339F5}
+const GUID kChromeTestsProvider =
+ { 0x81729947, 0xcd2a, 0x49e6,
+ { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } };
+
+// The configurations for the supported providers. This must be in sync with
+// FileLogger::EventProviderBits.
+const struct {
+ const GUID* provider_name;
+ uint8 level;
+ uint32 flags;
+} kProviders[] = {
+ { &kChromeTraceProviderName, 255, 0 },
+ { &kChromeFrameProvider, 255, 0 },
+ { &kChromeTestsProvider, 255, 0 },
+ { &base::debug::kChromeTraceProviderName, 255, 0 }
+};
robertshield 2012/03/05 19:20:54 COMPILE_ASSERT on the arraysize vs the enum in the
grt (UTC plus 2) 2012/03/06 03:24:57 Kinda done. I checked it against the bitfield of
+
+const HKEY kEnvironmentRoot = HKEY_LOCAL_MACHINE;
+const wchar_t kEnvironmentKey[] =
+ L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
+const wchar_t kEnvironment[] = L"Environment";
+unsigned int kBroadcastTimeoutMilliseconds = 2 * 1000;
+
+
+// Writes a timestamp.
+void WriteTimeStamp(LARGE_INTEGER time_stamp, std::ostream& out) {
robertshield 2012/03/05 19:20:54 don't use non-const references
grt (UTC plus 2) 2012/03/06 03:24:57 In the case of streams, I think it makes more sens
+ // Get a nice SYSTEMTIME from the time_stamp.
+ FILETIME event_time = {};
+ event_time.dwLowDateTime = time_stamp.LowPart;
+ event_time.dwHighDateTime = time_stamp.HighPart;
+ SYSTEMTIME system_time = {};
+ FileTimeToSystemTime(&event_time, &system_time);
+
+ // SYSTEMTIME only has millisecond accuracy. Do some conversions and math to
+ // get the sub-second time (number of 100ns ticks).
+ system_time.wMilliseconds = 0;
+ FILETIME coarse_event_time = {};
+ SystemTimeToFileTime(&system_time, &coarse_event_time);
+ ULARGE_INTEGER coarse_value;
+ coarse_value.HighPart = coarse_event_time.dwHighDateTime;
+ coarse_value.LowPart = coarse_event_time.dwLowDateTime;
+ coarse_value.QuadPart = time_stamp.QuadPart - coarse_value.QuadPart;
robertshield 2012/03/05 19:20:54 consider mentioning that ULARGE_INTEGER is a union
grt (UTC plus 2) 2012/03/06 03:24:57 Switched to using base/time.h's facilities, giving
+ DCHECK_EQ(coarse_value.HighPart, static_cast<DWORD>(0));
+ DCHECK_LT(coarse_value.LowPart, static_cast<DWORD>(10000000));
+
+ out << std::setfill('0')
+ << std::setw(2) << system_time.wMonth
+ << std::setw(2) << system_time.wDay
+ << '/'
+ << std::setw(2) << system_time.wHour
+ << std::setw(2) << system_time.wMinute
+ << std::setw(2) << system_time.wSecond
+ << '.'
+ << std::setw(7) << coarse_value.LowPart;
+}
+
+// Converts an ETW level created by logging::LogEventProvider back to its
+// corresponding severity.
erikwright (departed) 2012/03/05 19:55:30 I suspect this corresponds to an inverse switch st
+logging::LogSeverity EventLevelToSeverity(uint8 level) {
+ switch (level) {
+ case TRACE_LEVEL_NONE:
+ NOTREACHED();
+ case TRACE_LEVEL_FATAL:
+ return logging::LOG_FATAL;
+ case TRACE_LEVEL_ERROR:
+ return logging::LOG_ERROR;
+ case TRACE_LEVEL_WARNING:
+ return logging::LOG_WARNING;
+ case TRACE_LEVEL_INFORMATION:
+ return logging::LOG_INFO;
+ default:
+ // Trace levels above information correspond to negative severity levels,
+ // which are used for VLOG verbosity levels.
+ return TRACE_LEVEL_INFORMATION - level;
+ }
+}
+
+// Writes a severity level to the stream a la LogMessage::Init.
+void WriteSeverity(logging::LogSeverity severity, std::ostream& out) {
erikwright (departed) 2012/03/05 19:55:30 This is how base/logging.h does it: http://code.g
+ switch (severity) {
+ case logging::LOG_INFO:
+ out << "INFO";
+ break;
+ case logging::LOG_WARNING:
+ out << "WARNING";
+ break;
+ case logging::LOG_ERROR:
+ out << "ERROR";
+ break;
+ case logging::LOG_ERROR_REPORT:
+ out << "ERROR_REPORT";
+ break;
+ case logging::LOG_FATAL:
+ out << "FATAL";
+ break;
+ default:
+ if (severity < 0)
+ out << "VERBOSE" << -severity;
+ else
+ NOTREACHED();
+ break;
+ }
+}
+
+// A helper class that dumps a log file to an output stream. Only one may be
+// in use at a time.
+class LogDumper {
+ public:
+ // Dump |log_file| to |out|.
+ static void DumpLog(const FilePath& log_file, std::ostream& out);
+
+ private:
+ // An implementation of a trace consumer that delegates to a given (at
+ // compile-time) event processing function.
+ template<void (*ProcessEventFn)(EVENT_TRACE*)>
+ class TraceConsumer
+ : public base::win::EtwTraceConsumerBase<TraceConsumer<ProcessEventFn> > {
robertshield 2012/03/05 19:20:54 template abuse alert.. template abuse alert.. It
robertshield 2012/03/05 19:57:07 Taking a look at the implementation of EtwTraceCon
grt (UTC plus 2) 2012/03/06 03:24:57 Thanks. I didn't like the other obvious options (
+ public:
+ TraceConsumer() { }
+ static void ProcessEvent(EVENT_TRACE* event) { (*ProcessEventFn)(event); }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TraceConsumer);
+ };
+
+ // A parser for Mof data found in an EVENT_TRACE object as formatted by
erikwright (departed) 2012/03/05 19:55:30 I find it shocking that this needs to be written b
+ // Chromium-related classes.
+ class MofDataParser {
+ public:
+ explicit MofDataParser(EVENT_TRACE* event);
+ bool ReadDWORD(DWORD* value) {
+ return ReadPrimitive(value);
+ }
+ bool ReadInt(int* value) {
+ return ReadPrimitive(value);
+ }
+ bool ReadVoidPointer(void** value) {
+ return ReadPrimitive(value);
+ }
+ bool ReadVoidPointerArray(DWORD size, void* const** values) {
+ return ReadPointerArray(size, values);
+ }
+ // Reads an arbitrary data structure within the data.
+ template<typename T> bool ReadStructure(const T** value) {
+ if (length_ < sizeof(**value))
+ return false;
+ *value = reinterpret_cast<const T*>(scan_);
+ Advance(sizeof(**value));
+ return true;
+ }
+ // Reads a null-terminated string from the data. A trailing newline, if
+ // present, is stripped.
+ bool ReadString(base::StringPiece* value);
+ bool empty() { return length_ == 0; }
+
+ private:
+ void Advance(size_t num_bytes) {
+ scan_ += num_bytes;
+ length_ -= num_bytes;
+ }
+ template<typename T> bool ReadPrimitive(T* value) {
+ if (length_ < sizeof(*value))
+ return false;
+ *value = *reinterpret_cast<const T*>(scan_);
+ Advance(sizeof(*value));
+ return true;
+ }
+ template<typename T> bool ReadPointerArray(DWORD size, T* const** values) {
+ if (length_ < sizeof(*values) * size)
+ return false;
+ *values = reinterpret_cast<T* const*>(scan_);
+ Advance(sizeof(*values) * size);
+ return true;
+ }
+
+ const uint8* scan_;
+ uint32 length_;
+ };
+
+ // An interface for handling Mof data.
+ class MofData {
+ public:
+ virtual ~MofData();
+
+ // Parses the data in |event|, returning true on success; false otherwise.
+ virtual bool Initialize(EVENT_TRACE* event) = 0;
+
+ // Writes the event level.
+ virtual bool WriteLevel(uint8 level, std::ostream& out) = 0;
+
+ // Writes context information.
+ virtual void WriteContext(std::ostream& out) = 0;
+
+ // Writes the meat of the data.
+ virtual void WriteData(std::ostream& out) = 0;
+
+ protected:
+ MofData();
+ };
+
+ // A MofData implementation for events created by Chromium's LogEventProvider
+ // when the ENABLE_LOG_MESSAGE_ONLY flag is set.
+ class LogMessageData : public MofData {
+ public:
+ LogMessageData();
+ virtual ~LogMessageData();
+ virtual bool Initialize(EVENT_TRACE* event) OVERRIDE;
+ virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE;
+ virtual void WriteContext(std::ostream& out) OVERRIDE;
+ virtual void WriteData(std::ostream& out) OVERRIDE;
+
+ private:
+ base::StringPiece message_;
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+ };
+
+ // A MofData implementation for events created by Chromium's LogEventProvider
+ // when the ENABLE_LOG_MESSAGE_ONLY flag is not set.
+ class LogMessageFullData : public MofData {
+ public:
+ LogMessageFullData();
+ virtual ~LogMessageFullData();
+ virtual bool Initialize(EVENT_TRACE* event) OVERRIDE;
+ virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE;
+ virtual void WriteContext(std::ostream& out) OVERRIDE;
+ virtual void WriteData(std::ostream& out) OVERRIDE;
+
+ private:
+ DWORD stack_depth_;
+ void *const * backtrace_;
+ int line_;
+ base::StringPiece file_;
+ base::StringPiece message_;
+ DISALLOW_COPY_AND_ASSIGN(LogMessageFullData);
+ };
+
+ // A MofData implementation for events created by Chromium's
+ // TraceEventETWProvider.
+ class TraceEventData : public MofData {
+ public:
+ TraceEventData();
+ virtual ~TraceEventData();
+ virtual bool Initialize(EVENT_TRACE* event) OVERRIDE;
+ virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE;
+ virtual void WriteContext(std::ostream& out) OVERRIDE;
+ virtual void WriteData(std::ostream& out) OVERRIDE;
+
+ private:
+ const char* type_;
+ base::StringPiece name_;
+ void* id_;
+ base::StringPiece extra_;
+ DWORD stack_depth_;
+ void *const * backtrace_;
+ DISALLOW_COPY_AND_ASSIGN(TraceEventData);
+ };
+
+ // A MofData implementation for the TRACE_LOGFILE_HEADER event at the start of
+ // log files.
+ class LogfileHeaderData : public MofData {
+ public:
+ LogfileHeaderData();
+ virtual ~LogfileHeaderData();
+ virtual bool Initialize(EVENT_TRACE* event) OVERRIDE;
+ virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE;
+ virtual void WriteContext(std::ostream& out) OVERRIDE;
+ virtual void WriteData(std::ostream& out) OVERRIDE;
+
+ private:
+ const TRACE_LOGFILE_HEADER* header_;
+ DISALLOW_COPY_AND_ASSIGN(LogfileHeaderData);
+ };
+
+ // A pointer to a function that creates a new instance of some MofData
+ // implementation.
+ typedef MofData* (*MofDataFactoryFn)();
+
+ // Returns a new instance of a MofData implementation.
+ template<class MofDataType> static MofData* CreateMofData() {
+ return new MofDataType();
+ }
+
+ explicit LogDumper(std::ostream& out);
+ ~LogDumper();
+
+ // Returns a factory function to create the MofData implementation
+ // corresponding to an EVENT_TRACE instance of the given event class and type.
+ static MofDataFactoryFn GetMofDataFactory(const GUID& event_class,
+ uint8 type);
+
+ // Delegates to DumpEvent() of the current LogDumper instance.
+ static void ProcessEvent(EVENT_TRACE* event);
+
+ // Dumps the event.
+ void DumpEvent(EVENT_TRACE* event);
+
+ // Consumes all events in |log_file|, dumping them to the output stream.
+ void Consume(const FilePath& log_file);
+
+ // The current instance.
+ static LogDumper* instance_;
+ std::ostream* out_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogDumper);
+};
+
+// LogDumper::MofDataParser implementation
+
+LogDumper::MofDataParser::MofDataParser(EVENT_TRACE* event)
+ : scan_(reinterpret_cast<CONST uint8*>(event->MofData)),
+ length_(event->MofLength) {
+}
+
+bool LogDumper::MofDataParser::ReadString(base::StringPiece* value) {
+ const uint8* str_scan = scan_;
+ const uint8* const str_end = str_scan + length_;
+ while (str_scan < str_end && *str_scan != 0)
+ ++str_scan;
+ if (str_scan == str_end)
+ return false;
+ size_t string_length = str_scan - scan_;
+ bool has_trailing_newline = (string_length > 0 && str_scan[-1] == '\n');
+ value->set(reinterpret_cast<const char*>(scan_),
+ has_trailing_newline ? string_length - 1 : string_length);
+ Advance(string_length + 1);
+ return true;
+}
+
+// LogDumper::MofData implementation
+
+LogDumper::MofData::MofData() {
+}
+
+LogDumper::MofData::~MofData() {
+}
+
+// LogDumper::LogMessageData implementation
+
+LogDumper::LogMessageData::LogMessageData() {
+}
+
+LogDumper::LogMessageData::~LogMessageData() {
+}
+
+bool LogDumper::LogMessageData::Initialize(EVENT_TRACE* event) {
+ MofDataParser parser(event);
+ return (parser.ReadString(&message_) &&
+ parser.empty());
+}
+
+bool LogDumper::LogMessageData::WriteLevel(uint8 level, std::ostream& out) {
+ WriteSeverity(EventLevelToSeverity(level), out);
+ return true;
+}
+
+void LogDumper::LogMessageData::WriteContext(std::ostream& out) {
+}
+
+void LogDumper::LogMessageData::WriteData(std::ostream& out) {
+ out << message_;
+}
+
+// LogDumper::LogMessageFullData implementation
+
+LogDumper::LogMessageFullData::LogMessageFullData()
+ : stack_depth_(),
+ backtrace_(),
+ line_() {
+}
+
+LogDumper::LogMessageFullData::~LogMessageFullData() {
+}
+
+bool LogDumper::LogMessageFullData::Initialize(EVENT_TRACE* event) {
+ MofDataParser parser(event);
+ return (parser.ReadDWORD(&stack_depth_) &&
+ parser.ReadVoidPointerArray(stack_depth_, &backtrace_) &&
+ parser.ReadInt(&line_) &&
+ parser.ReadString(&file_) &&
+ parser.ReadString(&message_) &&
+ parser.empty());
+}
+
+bool LogDumper::LogMessageFullData::WriteLevel(uint8 level, std::ostream& out) {
+ WriteSeverity(EventLevelToSeverity(level), out);
+ return true;
+}
+
+void LogDumper::LogMessageFullData::WriteContext(std::ostream& out) {
+ // Shamelessly borrowed from base/logging.cc LogMessage::Init()
+ base::StringPiece filename(file_);
+ size_t last_slash_pos = filename.find_last_of("\\/");
+ if (last_slash_pos != base::StringPiece::npos)
+ filename.remove_prefix(last_slash_pos + 1);
+
+ out << filename << '(' << line_ << ')';
+}
+
+void LogDumper::LogMessageFullData::WriteData(std::ostream& out) {
+ out << message_;
+}
+
+// LogDumper::TraceEventData implementation
+
+LogDumper::TraceEventData::TraceEventData()
+ : type_(),
+ id_(),
+ stack_depth_(),
+ backtrace_() {
+}
+
+LogDumper::TraceEventData::~TraceEventData() {
+}
+
+bool LogDumper::TraceEventData::Initialize(EVENT_TRACE* event) {
+ switch (event->Header.Class.Type) {
+ case base::debug::kTraceEventTypeBegin:
+ type_ = "BEGIN";
+ break;
+ case base::debug::kTraceEventTypeEnd:
+ type_ = "END";
+ break;
+ case base::debug::kTraceEventTypeInstant:
+ type_ = "INSTANT";
+ break;
+ default:
+ NOTREACHED();
+ type_ = "";
+ break;
+ }
+ MofDataParser parser(event);
+ return (parser.ReadString(&name_) &&
+ parser.ReadVoidPointer(&id_) &&
+ parser.ReadString(&extra_) &&
+ (parser.empty() ||
+ parser.ReadDWORD(&stack_depth_) &&
+ parser.ReadVoidPointerArray(stack_depth_, &backtrace_) &&
+ parser.empty()));
+}
+
+void LogDumper::TraceEventData::WriteContext(std::ostream& out) {
+}
+
+bool LogDumper::TraceEventData::WriteLevel(uint8 level, std::ostream& out) {
+ out << type_;
+ return true;
+}
+
+void LogDumper::TraceEventData::WriteData(std::ostream& out) {
+ out << name_ << " " << extra_;
+}
+
+// LogDumper::LogfileHeaderData implementation
+
+LogDumper::LogfileHeaderData::LogfileHeaderData()
+ : header_() {
+}
+
+LogDumper::LogfileHeaderData::~LogfileHeaderData() {
+}
+
+bool LogDumper::LogfileHeaderData::Initialize(EVENT_TRACE* event) {
+ MofDataParser parser(event);
+ return parser.ReadStructure(&header_);
+}
+
+void LogDumper::LogfileHeaderData::WriteContext(std::ostream& out) {
+}
+
+bool LogDumper::LogfileHeaderData::WriteLevel(uint8 level, std::ostream& out) {
+ return false;
+}
+
+void LogDumper::LogfileHeaderData::WriteData(std::ostream& out) {
+ out << "Log captured from Windows "
+ << static_cast<int>(header_->VersionDetail.MajorVersion) << '.'
+ << static_cast<int>(header_->VersionDetail.MinorVersion) << '.'
+ << static_cast<int>(header_->VersionDetail.SubVersion) << '.'
+ << static_cast<int>(header_->VersionDetail.SubMinorVersion)
+ << ". " << header_->EventsLost << " events lost, "
+ << header_->BuffersLost << " buffers lost.";
+}
+
+// LogDumper implementation
+
+// static
+LogDumper* LogDumper::instance_ = NULL;
+
+LogDumper::LogDumper(std::ostream& out) : out_(&out) {
+ CHECK(instance_ == NULL);
+ instance_ = this;
+}
+
+LogDumper::~LogDumper() {
+ CHECK_EQ(instance_, this);
+ instance_ = NULL;
+}
+
+// static
+LogDumper::MofDataFactoryFn LogDumper::GetMofDataFactory(
+ const GUID& event_class,
+ uint8 type) {
robertshield 2012/03/05 19:20:54 IMO this is needlessly complicated. Returning a fa
grt (UTC plus 2) 2012/03/06 03:24:57 No, I agree wholeheartedly. This was left behind
+ if (IsEqualGUID(event_class, logging::kLogEventId)) {
+ if (type == logging::LOG_MESSAGE)
+ return &CreateMofData<LogMessageData>;
+ else if (type == logging::LOG_MESSAGE_FULL)
+ return &CreateMofData<LogMessageFullData>;
+ } else if (IsEqualGUID(event_class, base::debug::kTraceEventClass32)) {
+ return &CreateMofData<TraceEventData>;
+ } else if (IsEqualGUID(event_class, EventTraceGuid)) {
+ return &CreateMofData<LogfileHeaderData>;
+ }
+ return NULL;
+}
+
+// static
+void LogDumper::ProcessEvent(EVENT_TRACE* event) {
+ if (instance_ != NULL)
+ instance_->DumpEvent(event);
+}
+
+void LogDumper::DumpEvent(EVENT_TRACE* event) {
+ scoped_ptr<MofData> payload;
+ MofDataFactoryFn factory_fn = GetMofDataFactory(event->Header.Guid,
+ event->Header.Class.Type);
+
+ if (factory_fn) {
+ payload.reset((*factory_fn)());
+ if (!payload->Initialize(event))
+ payload.reset();
+ }
+
+ *out_ << '[' << event->Header.ProcessId << ':'
+ << event->Header.ThreadId << ':';
+ WriteTimeStamp(event->Header.TimeStamp, *out_);
+ *out_ << ':';
+ if (!payload.get() || !payload->WriteLevel(event->Header.Class.Level, *out_))
+ *out_ << "level" << static_cast<int>(event->Header.Class.Level);
+ if (payload.get()) {
+ *out_ << ':';
+ payload->WriteContext(*out_);
+ }
+ *out_ << "] ";
+ if (payload.get()) {
+ payload->WriteData(*out_);
+ } else {
+ wchar_t guid[64];
+ StringFromGUID2(event->Header.Guid, &guid[0], arraysize(guid));
+ if (factory_fn)
+ *out_ << "parse error";
+ else
+ *out_ << "unsupported event";
+ *out_ << " (class=" << guid << ", type="
+ << static_cast<int>(event->Header.Class.Type) << ")";
+ }
+
+ *out_ << std::endl;
+}
+
+void LogDumper::Consume(const FilePath& log_file) {
+ TraceConsumer<&ProcessEvent> consumer;
+ HRESULT hr = S_OK;
+
+ hr = consumer.OpenFileSession(log_file.value().c_str());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to open session for log file " << log_file.value()
+ << "; hr=" << std::hex << hr;
+ } else {
+ consumer.Consume();
+ consumer.Close();
+ out_->flush();
+ }
+}
+
+// static
+void LogDumper::DumpLog(const FilePath& log_file, std::ostream& out) {
+ LogDumper(out).Consume(log_file);
+}
+
+} // namespace
+
+FileLogger::FileLogger()
+ : event_provider_mask_(),
+ added_chrome_etw_variable_() {
+}
+
+FileLogger::~FileLogger() {
+ if (event_provider_mask_) {
+ LOG(WARNING)
+ << __FUNCTION__ << " don't forget to call FileLogger::Uninitialize()";
+ Uninitialize();
+ }
+}
+
+void FileLogger::ConfigureChromeEtwLogging() {
+ const string16 chrome_etw_logging(ASCIIToWide(env_vars::kEtwLogging));
+
+ // Set the value in this process and its children.
+ ::SetEnvironmentVariable(chrome_etw_logging.c_str(), L"1");
+
+ // Set the value for the whole system and ask everyone to refresh.
+ base::win::RegKey environment;
+ LONG result = environment.Open(kEnvironmentRoot, kEnvironmentKey,
+ KEY_QUERY_VALUE | KEY_SET_VALUE);
+ if (result == ERROR_SUCCESS) {
+ string16 value;
+ // The actual value of the variable is insignificant in the eyes of Chrome.
+ if (environment.ReadValue(chrome_etw_logging.c_str(),
+ &value) != ERROR_SUCCESS &&
+ environment.WriteValue(chrome_etw_logging.c_str(),
+ L"1") == ERROR_SUCCESS) {
+ environment.Close();
+ added_chrome_etw_variable_ = true;
+ // Announce to the system that a change has been made so that the shell
+ // and other Windowsy bits pick it up; see
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx.
+ SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
+ reinterpret_cast<LPARAM>(kEnvironment),
+ SMTO_ABORTIFHUNG,
+ kBroadcastTimeoutMilliseconds, NULL);
+ }
+ } else {
+ SetLastError(result);
+ PLOG(ERROR) << "Failed to open HKLM to check/modify the system environment";
+ }
+}
+
+void FileLogger::RevertChromeEtwLogging() {
+ if (added_chrome_etw_variable_) {
+ base::win::RegKey environment;
+ if (environment.Open(kEnvironmentRoot, kEnvironmentKey,
+ KEY_SET_VALUE) == ERROR_SUCCESS) {
+ environment.DeleteValue(ASCIIToWide(env_vars::kEtwLogging).c_str());
+ environment.Close();
+ SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
+ reinterpret_cast<LPARAM>(kEnvironment),
+ SMTO_ABORTIFHUNG,
+ kBroadcastTimeoutMilliseconds, NULL);
+ }
+ added_chrome_etw_variable_ = false;
+ }
+}
+
+bool FileLogger::EnableProviders() {
+ bool result = true;
+
+ // Generate ETW log events for this test binary. Log messages at and above
+ // logging::GetMinLogLevel() will continue to go to stderr as well. This
+ // leads to double logging in case of test failures: each LOG statement at
+ // or above the min level will go to stderr during test execution, and then
+ // all events logged to the file session will be dumped again. If this
+ // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX)
+ // here (stashing away the previous min log level to be restored in
+ // Uninitialize) to suppress stderr logging during test execution. Then those
+ // events in the file that were logged at/above the old min level from the
+ // test binary could be dumped to stderr if there were no failures.
+ if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER)
+ logging::LogEventProvider::Initialize(kChromeTestsProvider);
+
+ HRESULT hr = S_OK;
+ for (size_t i = 0; i < arraysize(kProviders); ++i) {
+ if (event_provider_mask_ & (1 << i)) {
+ hr = controller_.EnableProvider(*kProviders[i].provider_name,
+ kProviders[i].level,
+ kProviders[i].flags);
+ if (FAILED(hr)) {
+ result = false;
+ LOG(ERROR) << "Failed to enable event provider " << i
+ << "; hr=" << std::hex << hr;
+ }
+ }
+ }
+
+ return result;
+}
+
+void FileLogger::DisableProviders() {
+ HRESULT hr = S_OK;
+ for (size_t i = 0; i < arraysize(kProviders); ++i) {
+ if (event_provider_mask_ & (1 << i)) {
+ hr = controller_.DisableProvider(*kProviders[i].provider_name);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider "
+ << i << "; hr=" << std::hex << hr;
+ }
+ }
+
+ if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER)
+ logging::LogEventProvider::Uninitialize();
+}
+
+void FileLogger::Initialize() {
+ Initialize(kAllEventProviders);
+}
+
+void FileLogger::Initialize(uint32 event_provider_mask) {
+ DCHECK(event_provider_mask_ == 0 && !added_chrome_etw_variable_);
+ CHECK(!g_is_initialized);
+
+ ConfigureChromeEtwLogging();
+
+ // Stop a previous session that wasn't shut down properly.
+ base::win::EtwTraceProperties ignore;
+ HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession,
+ &ignore);
+ LOG_IF(ERROR, FAILED(hr) &&
+ hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND))
+ << "Failed to stop a previous trace session; hr=" << std::hex << hr;
+
+ event_provider_mask_ = event_provider_mask;
+
+ g_is_initialized = true;
+}
+
+void FileLogger::Uninitialize() {
+ if (is_logging()) {
+ LOG(ERROR)
+ << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()";
+ StopLogging();
+ }
+
+ event_provider_mask_ = 0;
+
+ RevertChromeEtwLogging();
+
+ g_is_initialized = false;
+}
+
+bool FileLogger::StartLogging(const FilePath& log_file) {
+ HRESULT hr =
+ controller_.StartFileSession(kChromeTestSession,
+ log_file.value().c_str(), false);
+ if (SUCCEEDED(hr)) {
+ EnableProviders();
+ } else {
+ if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
+ LOG(WARNING) << "Access denied while trying to start trace session. "
+ "This is expected when not running as an administrator.";
+ } else {
+ LOG(ERROR) << "Failed to start trace session to file " << log_file.value()
+ << "; hr=" << std::hex << hr;
+ }
+ return false;
+ }
+ return true;
+}
+
+void FileLogger::StopLogging() {
+ HRESULT hr = S_OK;
+
+ DisableProviders();
+
+ hr = controller_.Flush(NULL);
+ LOG_IF(ERROR, FAILED(hr))
+ << "Failed to flush events; hr=" << std::hex << hr;
+ hr = controller_.Stop(NULL);
+ LOG_IF(ERROR, FAILED(hr))
+ << "Failed to stop ETW session; hr=" << std::hex << hr;
+}
+
+// static
+void FileLogger::DumpLogFile(const FilePath& log_file, std::ostream& out) {
+ LogDumper::DumpLog(log_file, out);
+}

Powered by Google App Engine
This is Rietveld 408576698