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 const wchar_t kChromeTestSession[] = L"chrome_tests"; | |
26 | |
27 // From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F} | |
28 const GUID kChromeFrameProvider = | |
29 { 0x562bfc3, 0x2550, 0x45b4, | |
30 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } }; | |
31 | |
32 // From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7} | |
33 const GUID kChromeTraceProviderName = | |
34 { 0x7fe69228, 0x633e, 0x4f06, | |
35 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; | |
36 | |
37 // {81729947-CD2A-49e6-8885-785429F339F5} | |
38 const GUID kChromeTestsProvider = | |
39 { 0x81729947, 0xcd2a, 0x49e6, | |
40 { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } }; | |
41 | |
42 // The configurations for the supported providers. This must be in sync with | |
43 // FileLogger::EventProviderBits. | |
44 const struct { | |
45 const GUID* provider_name; | |
46 uint8 level; | |
47 uint32 flags; | |
48 } kProviders[] = { | |
49 { &kChromeTraceProviderName, 255, 0 }, | |
50 { &kChromeFrameProvider, 255, 0 }, | |
51 { &kChromeTestsProvider, 255, 0 }, | |
52 { &base::debug::kChromeTraceProviderName, 255, 0 } | |
53 }; | |
54 | |
55 COMPILE_ASSERT((1 << arraysize(kProviders)) - 1 == | |
56 FileLogger::kAllEventProviders, | |
57 size_of_kProviders_is_inconsistent_with_kAllEventProviders); | |
58 | |
59 const HKEY kEnvironmentRoot = HKEY_LOCAL_MACHINE; | |
60 const wchar_t kEnvironmentKey[] = | |
61 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; | |
62 const wchar_t kEnvironment[] = L"Environment"; | |
63 const unsigned int kBroadcastTimeoutMilliseconds = 2 * 1000; | |
Paweł Hajdan Jr.
2012/03/09 18:12:12
Why not base/test/test_timeouts?
grt (UTC plus 2)
2012/03/09 20:38:58
The closest timeout that's already defined there (
| |
64 | |
65 } // namespace | |
66 | |
67 bool FileLogger::is_initialized_ = false; | |
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(!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 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 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 |