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/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/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 logging_win { |
| 24 |
| 25 namespace { |
| 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 // The provider bits that require CHROME_ETW_LOGGING in the environment. |
| 62 const uint32 kChromeLogProviders = |
| 63 FileLogger::CHROME_LOG_PROVIDER | FileLogger::CHROME_FRAME_LOG_PROVIDER; |
| 64 const HKEY kEnvironmentRoot = HKEY_LOCAL_MACHINE; |
| 65 const wchar_t kEnvironmentKey[] = |
| 66 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; |
| 67 const wchar_t kEnvironment[] = L"Environment"; |
| 68 const unsigned int kBroadcastTimeoutMilliseconds = 2 * 1000; |
| 69 |
| 70 } // namespace |
| 71 |
| 72 // FileLogger::ScopedSystemEnvironmentVariable implementation. |
| 73 |
| 74 FileLogger::ScopedSystemEnvironmentVariable::ScopedSystemEnvironmentVariable( |
| 75 const string16& variable, |
| 76 const string16& value) { |
| 77 |
| 78 // Set the value in this process and its children. |
| 79 ::SetEnvironmentVariable(variable.c_str(), value.c_str()); |
| 80 |
| 81 // Set the value for the whole system and ask everyone to refresh. |
| 82 base::win::RegKey environment; |
| 83 LONG result = environment.Open(kEnvironmentRoot, kEnvironmentKey, |
| 84 KEY_QUERY_VALUE | KEY_SET_VALUE); |
| 85 if (result == ERROR_SUCCESS) { |
| 86 string16 old_value; |
| 87 // The actual value of the variable is insignificant in the eyes of Chrome. |
| 88 if (environment.ReadValue(variable.c_str(), |
| 89 &old_value) != ERROR_SUCCESS && |
| 90 environment.WriteValue(variable.c_str(), |
| 91 value.c_str()) == ERROR_SUCCESS) { |
| 92 environment.Close(); |
| 93 // Remember that this needs to be reversed in the dtor. |
| 94 variable_ = variable; |
| 95 NotifyOtherProcesses(); |
| 96 } |
| 97 } else { |
| 98 SetLastError(result); |
| 99 PLOG(ERROR) << "Failed to open HKLM to check/modify the system environment"; |
| 100 } |
| 101 } |
| 102 |
| 103 FileLogger::ScopedSystemEnvironmentVariable::~ScopedSystemEnvironmentVariable() |
| 104 { |
| 105 if (!variable_.empty()) { |
| 106 base::win::RegKey environment; |
| 107 if (environment.Open(kEnvironmentRoot, kEnvironmentKey, |
| 108 KEY_SET_VALUE) == ERROR_SUCCESS) { |
| 109 environment.DeleteValue(variable_.c_str()); |
| 110 environment.Close(); |
| 111 NotifyOtherProcesses(); |
| 112 } |
| 113 } |
| 114 } |
| 115 |
| 116 // static |
| 117 void FileLogger::ScopedSystemEnvironmentVariable::NotifyOtherProcesses() { |
| 118 // Announce to the system that a change has been made so that the shell and |
| 119 // other Windowsy bits pick it up; see |
| 120 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx. |
| 121 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, |
| 122 reinterpret_cast<LPARAM>(kEnvironment), SMTO_ABORTIFHUNG, |
| 123 kBroadcastTimeoutMilliseconds, NULL); |
| 124 } |
| 125 |
| 126 bool FileLogger::is_initialized_ = false; |
| 127 |
| 128 FileLogger::FileLogger() |
| 129 : event_provider_mask_() { |
| 130 } |
| 131 |
| 132 FileLogger::~FileLogger() { |
| 133 if (is_logging()) { |
| 134 LOG(ERROR) |
| 135 << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()"; |
| 136 StopLogging(); |
| 137 } |
| 138 |
| 139 is_initialized_ = false; |
| 140 } |
| 141 |
| 142 // Returns false if all providers could not be enabled. A log message is |
| 143 // produced for each provider that could not be enabled. |
| 144 bool FileLogger::EnableProviders() { |
| 145 // Default to false if there's at least one provider. |
| 146 bool result = (event_provider_mask_ == 0); |
| 147 |
| 148 // Generate ETW log events for this test binary. Log messages at and above |
| 149 // logging::GetMinLogLevel() will continue to go to stderr as well. This |
| 150 // leads to double logging in case of test failures: each LOG statement at |
| 151 // or above the min level will go to stderr during test execution, and then |
| 152 // all events logged to the file session will be dumped again. If this |
| 153 // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX) |
| 154 // here (stashing away the previous min log level to be restored in |
| 155 // DisableProviders) to suppress stderr logging during test execution. Then |
| 156 // those events in the file that were logged at/above the old min level from |
| 157 // the test binary could be dumped to stderr if there were no failures. |
| 158 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) |
| 159 logging::LogEventProvider::Initialize(kChromeTestsProvider); |
| 160 |
| 161 HRESULT hr = S_OK; |
| 162 for (size_t i = 0; i < arraysize(kProviders); ++i) { |
| 163 if (event_provider_mask_ & (1 << i)) { |
| 164 hr = controller_.EnableProvider(*kProviders[i].provider_name, |
| 165 kProviders[i].level, |
| 166 kProviders[i].flags); |
| 167 if (FAILED(hr)) { |
| 168 LOG(ERROR) << "Failed to enable event provider " << i |
| 169 << "; hr=" << std::hex << hr; |
| 170 } else { |
| 171 result = true; |
| 172 } |
| 173 } |
| 174 } |
| 175 |
| 176 return result; |
| 177 } |
| 178 |
| 179 void FileLogger::DisableProviders() { |
| 180 HRESULT hr = S_OK; |
| 181 for (size_t i = 0; i < arraysize(kProviders); ++i) { |
| 182 if (event_provider_mask_ & (1 << i)) { |
| 183 hr = controller_.DisableProvider(*kProviders[i].provider_name); |
| 184 LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider " |
| 185 << i << "; hr=" << std::hex << hr; |
| 186 } |
| 187 } |
| 188 |
| 189 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) |
| 190 logging::LogEventProvider::Uninitialize(); |
| 191 } |
| 192 |
| 193 void FileLogger::Initialize() { |
| 194 Initialize(kAllEventProviders); |
| 195 } |
| 196 |
| 197 void FileLogger::Initialize(uint32 event_provider_mask) { |
| 198 CHECK(!is_initialized_); |
| 199 |
| 200 // Set up CHROME_ETW_LOGGING in the environment if providers that require it |
| 201 // are enabled. |
| 202 if (event_provider_mask & kChromeLogProviders) { |
| 203 etw_logging_configurator_.reset(new ScopedSystemEnvironmentVariable( |
| 204 ASCIIToWide(env_vars::kEtwLogging), L"1")); |
| 205 } |
| 206 |
| 207 // Stop a previous session that wasn't shut down properly. |
| 208 base::win::EtwTraceProperties ignore; |
| 209 HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession, |
| 210 &ignore); |
| 211 LOG_IF(ERROR, FAILED(hr) && |
| 212 hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND)) |
| 213 << "Failed to stop a previous trace session; hr=" << std::hex << hr; |
| 214 |
| 215 event_provider_mask_ = event_provider_mask; |
| 216 |
| 217 is_initialized_ = true; |
| 218 } |
| 219 |
| 220 bool FileLogger::StartLogging(const FilePath& log_file) { |
| 221 HRESULT hr = |
| 222 controller_.StartFileSession(kChromeTestSession, |
| 223 log_file.value().c_str(), false); |
| 224 if (SUCCEEDED(hr)) { |
| 225 // Ignore the return value here in the hopes that at least one provider was |
| 226 // enabled. |
| 227 if (!EnableProviders()) { |
| 228 LOG(ERROR) << "Failed to enable any provider."; |
| 229 controller_.Stop(NULL); |
| 230 return false; |
| 231 } |
| 232 } else { |
| 233 if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) { |
| 234 LOG(WARNING) << "Access denied while trying to start trace session. " |
| 235 "This is expected when not running as an administrator."; |
| 236 } else { |
| 237 LOG(ERROR) << "Failed to start trace session to file " << log_file.value() |
| 238 << "; hr=" << std::hex << hr; |
| 239 } |
| 240 return false; |
| 241 } |
| 242 return true; |
| 243 } |
| 244 |
| 245 void FileLogger::StopLogging() { |
| 246 HRESULT hr = S_OK; |
| 247 |
| 248 DisableProviders(); |
| 249 |
| 250 hr = controller_.Flush(NULL); |
| 251 LOG_IF(ERROR, FAILED(hr)) |
| 252 << "Failed to flush events; hr=" << std::hex << hr; |
| 253 hr = controller_.Stop(NULL); |
| 254 LOG_IF(ERROR, FAILED(hr)) |
| 255 << "Failed to stop ETW session; hr=" << std::hex << hr; |
| 256 } |
| 257 |
| 258 } // namespace logging_win |
OLD | NEW |