OLD | NEW |
(Empty) | |
| 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/base/file_logger_win.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/logging.h" |
| 15 #include "base/file_path.h" |
| 16 #include "base/logging_win.h" |
| 17 #include "base/string16.h" |
| 18 #include "base/utf_string_conversions.h" |
| 19 #include "base/win/event_trace_consumer.h" |
| 20 #include "base/win/registry.h" |
| 21 #include "chrome/common/env_vars.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 bool g_is_initialized = false; |
| 26 |
| 27 const wchar_t kChromeTestSession[] = L"chrome_tests"; |
| 28 |
| 29 // From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F} |
| 30 const GUID kChromeFrameProvider = |
| 31 { 0x562bfc3, 0x2550, 0x45b4, |
| 32 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } }; |
| 33 |
| 34 // From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7} |
| 35 const GUID kChromeTraceProviderName = |
| 36 { 0x7fe69228, 0x633e, 0x4f06, |
| 37 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; |
| 38 |
| 39 // {81729947-CD2A-49e6-8885-785429F339F5} |
| 40 const GUID kChromeTestsProvider = |
| 41 { 0x81729947, 0xcd2a, 0x49e6, |
| 42 { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } }; |
| 43 |
| 44 // The configurations for the supported providers. This must be in sync with |
| 45 // FileLogger::EventProviderBits. |
| 46 const struct { |
| 47 const GUID* provider_name; |
| 48 uint8 level; |
| 49 uint32 flags; |
| 50 } kProviders[] = { |
| 51 { &kChromeTraceProviderName, 255, 0 }, |
| 52 { &kChromeFrameProvider, 255, 0 }, |
| 53 { &kChromeTestsProvider, 255, 0 }, |
| 54 { &base::debug::kChromeTraceProviderName, 255, 0 } |
| 55 }; |
| 56 |
| 57 COMPILE_ASSERT((1 << arraysize(kProviders)) - 1 == |
| 58 FileLogger::kAllEventProviders, |
| 59 size_of_kProviders_is_inconsistent_with_kAllEventProviders); |
| 60 |
| 61 const HKEY kEnvironmentRoot = HKEY_LOCAL_MACHINE; |
| 62 const wchar_t kEnvironmentKey[] = |
| 63 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; |
| 64 const wchar_t kEnvironment[] = L"Environment"; |
| 65 unsigned int kBroadcastTimeoutMilliseconds = 2 * 1000; |
| 66 |
| 67 } // namespace |
| 68 |
| 69 FileLogger::FileLogger() |
| 70 : event_provider_mask_(), |
| 71 added_chrome_etw_variable_() { |
| 72 } |
| 73 |
| 74 FileLogger::~FileLogger() { |
| 75 if (event_provider_mask_) { |
| 76 LOG(WARNING) |
| 77 << __FUNCTION__ << " don't forget to call FileLogger::Uninitialize()"; |
| 78 Uninitialize(); |
| 79 } |
| 80 } |
| 81 |
| 82 void FileLogger::ConfigureChromeEtwLogging() { |
| 83 const string16 chrome_etw_logging(ASCIIToWide(env_vars::kEtwLogging)); |
| 84 |
| 85 // Set the value in this process and its children. |
| 86 ::SetEnvironmentVariable(chrome_etw_logging.c_str(), L"1"); |
| 87 |
| 88 // Set the value for the whole system and ask everyone to refresh. |
| 89 base::win::RegKey environment; |
| 90 LONG result = environment.Open(kEnvironmentRoot, kEnvironmentKey, |
| 91 KEY_QUERY_VALUE | KEY_SET_VALUE); |
| 92 if (result == ERROR_SUCCESS) { |
| 93 string16 value; |
| 94 // The actual value of the variable is insignificant in the eyes of Chrome. |
| 95 if (environment.ReadValue(chrome_etw_logging.c_str(), |
| 96 &value) != ERROR_SUCCESS && |
| 97 environment.WriteValue(chrome_etw_logging.c_str(), |
| 98 L"1") == ERROR_SUCCESS) { |
| 99 environment.Close(); |
| 100 added_chrome_etw_variable_ = true; |
| 101 // Announce to the system that a change has been made so that the shell |
| 102 // and other Windowsy bits pick it up; see |
| 103 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx. |
| 104 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, |
| 105 reinterpret_cast<LPARAM>(kEnvironment), |
| 106 SMTO_ABORTIFHUNG, |
| 107 kBroadcastTimeoutMilliseconds, NULL); |
| 108 } |
| 109 } else { |
| 110 SetLastError(result); |
| 111 PLOG(ERROR) << "Failed to open HKLM to check/modify the system environment"; |
| 112 } |
| 113 } |
| 114 |
| 115 void FileLogger::RevertChromeEtwLogging() { |
| 116 if (added_chrome_etw_variable_) { |
| 117 base::win::RegKey environment; |
| 118 if (environment.Open(kEnvironmentRoot, kEnvironmentKey, |
| 119 KEY_SET_VALUE) == ERROR_SUCCESS) { |
| 120 environment.DeleteValue(ASCIIToWide(env_vars::kEtwLogging).c_str()); |
| 121 environment.Close(); |
| 122 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, |
| 123 reinterpret_cast<LPARAM>(kEnvironment), |
| 124 SMTO_ABORTIFHUNG, |
| 125 kBroadcastTimeoutMilliseconds, NULL); |
| 126 } |
| 127 added_chrome_etw_variable_ = false; |
| 128 } |
| 129 } |
| 130 |
| 131 bool FileLogger::EnableProviders() { |
| 132 bool result = true; |
| 133 |
| 134 // Generate ETW log events for this test binary. Log messages at and above |
| 135 // logging::GetMinLogLevel() will continue to go to stderr as well. This |
| 136 // leads to double logging in case of test failures: each LOG statement at |
| 137 // or above the min level will go to stderr during test execution, and then |
| 138 // all events logged to the file session will be dumped again. If this |
| 139 // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX) |
| 140 // here (stashing away the previous min log level to be restored in |
| 141 // Uninitialize) to suppress stderr logging during test execution. Then those |
| 142 // events in the file that were logged at/above the old min level from the |
| 143 // test binary could be dumped to stderr if there were no failures. |
| 144 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) |
| 145 logging::LogEventProvider::Initialize(kChromeTestsProvider); |
| 146 |
| 147 HRESULT hr = S_OK; |
| 148 for (size_t i = 0; i < arraysize(kProviders); ++i) { |
| 149 if (event_provider_mask_ & (1 << i)) { |
| 150 hr = controller_.EnableProvider(*kProviders[i].provider_name, |
| 151 kProviders[i].level, |
| 152 kProviders[i].flags); |
| 153 if (FAILED(hr)) { |
| 154 result = false; |
| 155 LOG(ERROR) << "Failed to enable event provider " << i |
| 156 << "; hr=" << std::hex << hr; |
| 157 } |
| 158 } |
| 159 } |
| 160 |
| 161 return result; |
| 162 } |
| 163 |
| 164 void FileLogger::DisableProviders() { |
| 165 HRESULT hr = S_OK; |
| 166 for (size_t i = 0; i < arraysize(kProviders); ++i) { |
| 167 if (event_provider_mask_ & (1 << i)) { |
| 168 hr = controller_.DisableProvider(*kProviders[i].provider_name); |
| 169 LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider " |
| 170 << i << "; hr=" << std::hex << hr; |
| 171 } |
| 172 } |
| 173 |
| 174 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) |
| 175 logging::LogEventProvider::Uninitialize(); |
| 176 } |
| 177 |
| 178 void FileLogger::Initialize() { |
| 179 Initialize(kAllEventProviders); |
| 180 } |
| 181 |
| 182 void FileLogger::Initialize(uint32 event_provider_mask) { |
| 183 DCHECK(event_provider_mask_ == 0 && !added_chrome_etw_variable_); |
| 184 CHECK(!g_is_initialized); |
| 185 |
| 186 ConfigureChromeEtwLogging(); |
| 187 |
| 188 // Stop a previous session that wasn't shut down properly. |
| 189 base::win::EtwTraceProperties ignore; |
| 190 HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession, |
| 191 &ignore); |
| 192 LOG_IF(ERROR, FAILED(hr) && |
| 193 hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND)) |
| 194 << "Failed to stop a previous trace session; hr=" << std::hex << hr; |
| 195 |
| 196 event_provider_mask_ = event_provider_mask; |
| 197 |
| 198 g_is_initialized = true; |
| 199 } |
| 200 |
| 201 void FileLogger::Uninitialize() { |
| 202 if (is_logging()) { |
| 203 LOG(ERROR) |
| 204 << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()"; |
| 205 StopLogging(); |
| 206 } |
| 207 |
| 208 event_provider_mask_ = 0; |
| 209 |
| 210 RevertChromeEtwLogging(); |
| 211 |
| 212 g_is_initialized = false; |
| 213 } |
| 214 |
| 215 bool FileLogger::StartLogging(const FilePath& log_file) { |
| 216 HRESULT hr = |
| 217 controller_.StartFileSession(kChromeTestSession, |
| 218 log_file.value().c_str(), false); |
| 219 if (SUCCEEDED(hr)) { |
| 220 EnableProviders(); |
| 221 } else { |
| 222 if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) { |
| 223 LOG(WARNING) << "Access denied while trying to start trace session. " |
| 224 "This is expected when not running as an administrator."; |
| 225 } else { |
| 226 LOG(ERROR) << "Failed to start trace session to file " << log_file.value() |
| 227 << "; hr=" << std::hex << hr; |
| 228 } |
| 229 return false; |
| 230 } |
| 231 return true; |
| 232 } |
| 233 |
| 234 void FileLogger::StopLogging() { |
| 235 HRESULT hr = S_OK; |
| 236 |
| 237 DisableProviders(); |
| 238 |
| 239 hr = controller_.Flush(NULL); |
| 240 LOG_IF(ERROR, FAILED(hr)) |
| 241 << "Failed to flush events; hr=" << std::hex << hr; |
| 242 hr = controller_.Stop(NULL); |
| 243 LOG_IF(ERROR, FAILED(hr)) |
| 244 << "Failed to stop ETW session; hr=" << std::hex << hr; |
| 245 } |
OLD | NEW |