Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2012 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 "chrome/test/logging/win/file_logger.h"
      6 
      7 #include <windows.h>
      8 #include <guiddef.h>
      9 #include <objbase.h>
     10 
     11 #include <ios>
     12 
     13 #include "base/debug/trace_event_win.h"
     14 #include "base/files/file_path.h"
     15 #include "base/logging.h"
     16 #include "base/logging_win.h"
     17 #include "base/strings/string16.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "base/win/event_trace_consumer.h"
     20 #include "base/win/registry.h"
     21 
     22 namespace logging_win {
     23 
     24 namespace {
     25 
     26 const wchar_t kChromeTestSession[] = L"chrome_tests";
     27 
     28 // From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F}
     29 const GUID kChromeFrameProvider =
     30     { 0x562bfc3, 0x2550, 0x45b4,
     31         { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } };
     32 
     33 // From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7}
     34 const GUID kChromeTraceProviderName =
     35     { 0x7fe69228, 0x633e, 0x4f06,
     36         { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
     37 
     38 // {81729947-CD2A-49e6-8885-785429F339F5}
     39 const GUID kChromeTestsProvider =
     40     { 0x81729947, 0xcd2a, 0x49e6,
     41         { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } };
     42 
     43 // The configurations for the supported providers.  This must be in sync with
     44 // FileLogger::EventProviderBits.
     45 const struct {
     46   const GUID* provider_name;
     47   uint8 level;
     48   uint32 flags;
     49 } kProviders[] = {
     50   { &kChromeTraceProviderName, 255, 0 },
     51   { &kChromeFrameProvider, 255, 0 },
     52   { &kChromeTestsProvider, 255, 0 },
     53   { &base::debug::kChromeTraceProviderName, 255, 0 }
     54 };
     55 
     56 COMPILE_ASSERT((1 << arraysize(kProviders)) - 1 ==
     57                    FileLogger::kAllEventProviders,
     58                size_of_kProviders_is_inconsistent_with_kAllEventProviders);
     59 
     60 }  // namespace
     61 
     62 bool FileLogger::is_initialized_ = false;
     63 
     64 FileLogger::FileLogger()
     65     : event_provider_mask_() {
     66 }
     67 
     68 FileLogger::~FileLogger() {
     69   if (is_logging()) {
     70     LOG(ERROR)
     71       << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()";
     72     StopLogging();
     73   }
     74 
     75   is_initialized_ = false;
     76 }
     77 
     78 // Returns false if all providers could not be enabled.  A log message is
     79 // produced for each provider that could not be enabled.
     80 bool FileLogger::EnableProviders() {
     81   // Default to false if there's at least one provider.
     82   bool result = (event_provider_mask_ == 0);
     83 
     84   // Generate ETW log events for this test binary.  Log messages at and above
     85   // logging::GetMinLogLevel() will continue to go to stderr as well.  This
     86   // leads to double logging in case of test failures: each LOG statement at
     87   // or above the min level will go to stderr during test execution, and then
     88   // all events logged to the file session will be dumped again.  If this
     89   // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX)
     90   // here (stashing away the previous min log level to be restored in
     91   // DisableProviders) to suppress stderr logging during test execution.  Then
     92   // those events in the file that were logged at/above the old min level from
     93   // the test binary could be dumped to stderr if there were no failures.
     94   if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER)
     95     logging::LogEventProvider::Initialize(kChromeTestsProvider);
     96 
     97   HRESULT hr = S_OK;
     98   for (size_t i = 0; i < arraysize(kProviders); ++i) {
     99     if (event_provider_mask_ & (1 << i)) {
    100       hr = controller_.EnableProvider(*kProviders[i].provider_name,
    101                                       kProviders[i].level,
    102                                       kProviders[i].flags);
    103       if (FAILED(hr)) {
    104         LOG(ERROR) << "Failed to enable event provider " << i
    105                    << "; hr=" << std::hex << hr;
    106       } else {
    107         result = true;
    108       }
    109     }
    110   }
    111 
    112   return result;
    113 }
    114 
    115 void FileLogger::DisableProviders() {
    116   HRESULT hr = S_OK;
    117   for (size_t i = 0; i < arraysize(kProviders); ++i) {
    118     if (event_provider_mask_ & (1 << i)) {
    119       hr = controller_.DisableProvider(*kProviders[i].provider_name);
    120       LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider "
    121                                 << i << "; hr=" << std::hex << hr;
    122     }
    123   }
    124 
    125   if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER)
    126     logging::LogEventProvider::Uninitialize();
    127 }
    128 
    129 void FileLogger::Initialize() {
    130   Initialize(kAllEventProviders);
    131 }
    132 
    133 void FileLogger::Initialize(uint32 event_provider_mask) {
    134   CHECK(!is_initialized_);
    135 
    136   // Stop a previous session that wasn't shut down properly.
    137   base::win::EtwTraceProperties ignore;
    138   HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession,
    139                                                    &ignore);
    140   LOG_IF(ERROR, FAILED(hr) &&
    141              hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND))
    142       << "Failed to stop a previous trace session; hr=" << std::hex << hr;
    143 
    144   event_provider_mask_ = event_provider_mask;
    145 
    146   is_initialized_ = true;
    147 }
    148 
    149 bool FileLogger::StartLogging(const base::FilePath& log_file) {
    150   HRESULT hr =
    151       controller_.StartFileSession(kChromeTestSession,
    152                                    log_file.value().c_str(), false);
    153   if (SUCCEEDED(hr)) {
    154     // Ignore the return value here in the hopes that at least one provider was
    155     // enabled.
    156     if (!EnableProviders()) {
    157       LOG(ERROR) << "Failed to enable any provider.";
    158       controller_.Stop(NULL);
    159       return false;
    160     }
    161   } else {
    162     if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
    163       LOG(WARNING) << "Access denied while trying to start trace session. "
    164                       "This is expected when not running as an administrator.";
    165     } else {
    166       LOG(ERROR) << "Failed to start trace session to file " << log_file.value()
    167                  << "; hr=" << std::hex << hr;
    168     }
    169     return false;
    170   }
    171   return true;
    172 }
    173 
    174 void FileLogger::StopLogging() {
    175   HRESULT hr = S_OK;
    176 
    177   DisableProviders();
    178 
    179   hr = controller_.Flush(NULL);
    180   LOG_IF(ERROR, FAILED(hr))
    181       << "Failed to flush events; hr=" << std::hex << hr;
    182   hr = controller_.Stop(NULL);
    183   LOG_IF(ERROR, FAILED(hr))
    184       << "Failed to stop ETW session; hr=" << std::hex << hr;
    185 }
    186 
    187 }  // namespace logging_win
    188