| // 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/logging/win/file_logger.h" |
| |
| #include <limits.h> |
| #include <windows.h> |
| #include <guiddef.h> |
| #include <objbase.h> |
| #include <stddef.h> |
| |
| #include <ios> |
| |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/logging_win.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/event_trace_consumer.h" |
| #include "base/win/registry.h" |
| |
| namespace logging_win { |
| |
| namespace { |
| |
| 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_t level; |
| uint32_t flags; |
| } kProviders[] = { |
| { &kChromeTraceProviderName, 255, 0 }, |
| { &kChromeFrameProvider, 255, 0 }, |
| { &kChromeTestsProvider, 255, 0 }, |
| }; |
| |
| static_assert((1 << base::size(kProviders)) - 1 == |
| FileLogger::kAllEventProviders, |
| "size of kProviders is inconsistent with kAllEventProviders"); |
| |
| } // namespace |
| |
| bool FileLogger::is_initialized_ = false; |
| |
| FileLogger::FileLogger() |
| : event_provider_mask_() { |
| } |
| |
| FileLogger::~FileLogger() { |
| if (is_logging()) { |
| LOG(ERROR) |
| << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()"; |
| StopLogging(); |
| } |
| |
| is_initialized_ = false; |
| } |
| |
| // Returns false if all providers could not be enabled. A log message is |
| // produced for each provider that could not be enabled. |
| bool FileLogger::EnableProviders() { |
| // Default to false if there's at least one provider. |
| bool result = (event_provider_mask_ == 0); |
| |
| // 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 |
| // DisableProviders) 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 < base::size(kProviders); ++i) { |
| if (event_provider_mask_ & (1 << i)) { |
| hr = controller_.EnableProvider(*kProviders[i].provider_name, |
| kProviders[i].level, |
| kProviders[i].flags); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to enable event provider " << i |
| << "; hr=" << std::hex << hr; |
| } else { |
| result = true; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| void FileLogger::DisableProviders() { |
| HRESULT hr = S_OK; |
| for (size_t i = 0; i < base::size(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_t event_provider_mask) { |
| CHECK(!is_initialized_); |
| |
| // 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; |
| |
| is_initialized_ = true; |
| } |
| |
| bool FileLogger::StartLogging(const base::FilePath& log_file) { |
| HRESULT hr = |
| controller_.StartFileSession(kChromeTestSession, |
| log_file.value().c_str(), false); |
| if (SUCCEEDED(hr)) { |
| // Ignore the return value here in the hopes that at least one provider was |
| // enabled. |
| if (!EnableProviders()) { |
| LOG(ERROR) << "Failed to enable any provider."; |
| controller_.Stop(NULL); |
| return false; |
| } |
| } 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; |
| } |
| |
| } // namespace logging_win |