Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1270)

Side by Side Diff: chrome/test/base/file_logger_win.cc

Issue 9584017: New test infrastructure for producing verbose logs in failing tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comment updates Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
9 #include <ios> // For std::hex
10
11 #include "base/logging.h"
12 #include "base/file_path.h"
13 #include "base/logging_win.h"
14 #include "base/string16.h"
15 #include "base/utf_string_conversions.h"
16 #include "base/win/event_trace_consumer.h"
17 #include "base/win/registry.h"
18 #include "chrome/common/env_vars.h"
19
20 namespace {
21
22 bool g_is_initialized = false;
23
24 const wchar_t kChromeTestSession[] = L"chrome_tests";
25
26 // From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F}
27 const GUID kChromeFrameProvider =
28 { 0x562bfc3, 0x2550, 0x45b4,
29 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } };
30
31 // From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7}
32 const GUID kChromeTraceProviderName =
33 { 0x7fe69228, 0x633e, 0x4f06,
34 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
35
36 // {81729947-CD2A-49e6-8885-785429F339F5}
37 const GUID kChromeTestsProvider =
38 { 0x81729947, 0xcd2a, 0x49e6,
39 { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } };
40
41 const GUID* const kDefaultProviders[] = {
42 &kChromeTraceProviderName,
43 &kChromeFrameProvider,
44 &kChromeTestsProvider,
45 };
46
47 const HKEY kEnvironmentRoot = HKEY_LOCAL_MACHINE;
robertshield 2012/03/02 22:37:41 I take it that this can only be used for tests run
grt (UTC plus 2) 2012/03/03 02:12:43 Yeah, firing up an ETW session requires admin, as
48 const wchar_t kEnvironmentKey[] =
49 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
50 const wchar_t kEnvironment[] = L"Environment";
51 unsigned int kBroadcastTimeoutMilliseconds = 2 * 1000;
52
53 // A helper class that dumps a log file to an output stream. Only one may be
54 // in use at a time.
55 class LogDumper {
56 public:
57 // Dump |log_file| to |out|.
58 static void DumpLog(const FilePath& log_file, std::ostream& out);
59
60 private:
61 // An implementation of a trace consumer that sends the message text of
62 // events to a given output stream.
63 class TraceConsumer : public base::win::EtwTraceConsumerBase<TraceConsumer> {
64 public:
65 explicit TraceConsumer(std::ostream& out);
66 ~TraceConsumer();
67
68 static void ProcessEvent(EVENT_TRACE* event);
69
70 private:
71 static std::ostream* out_;
72 DISALLOW_COPY_AND_ASSIGN(TraceConsumer);
73 };
74
75 DISALLOW_IMPLICIT_CONSTRUCTORS(LogDumper);
76 };
77
78 // static
79 std::ostream* LogDumper::TraceConsumer::out_ = NULL;
80
81 LogDumper::TraceConsumer::TraceConsumer(std::ostream& out) {
82 DCHECK(out_ == NULL);
83 out_ = &out;
84 }
85
86 LogDumper::TraceConsumer::~TraceConsumer() {
87 out_ = NULL;
88 }
89
90 // static
91 void LogDumper::TraceConsumer::ProcessEvent(EVENT_TRACE* event) {
92 // The payload for events generated with the ENABLE_LOG_MESSAGE_ONLY and
93 // ENABLE_DETAILED_LOG_MESSAGE flags is simply the message text.
94 *out_ << reinterpret_cast<const char*>(event->MofData);
95 }
96
97 // static
98 void LogDumper::DumpLog(const FilePath& log_file, std::ostream& out) {
99 TraceConsumer consumer(out);
100 HRESULT hr = S_OK;
101
102 hr = consumer.OpenFileSession(log_file.value().c_str());
103 if (FAILED(hr)) {
104 LOG(ERROR) << "Failed to open session for log file " << log_file.value()
105 << "; hr=" << std::hex << hr;
106 } else {
107 consumer.Consume();
108 consumer.Close();
109 out.flush();
110 }
111 }
112
113 } // namespace
114
115 FileLogger::FileLogger()
116 : added_chrome_etw_variable_(false) {
117 }
118
119 FileLogger::~FileLogger() {
120 if (!provider_names_.empty()) {
121 LOG(WARNING)
122 << __FUNCTION__ << " don't forget to call FileLogger::Uninitialize()";
123 Uninitialize();
124 }
125 }
126
127 void FileLogger::ConfigureChromeEtwLogging() {
128 const string16 chrome_etw_logging(ASCIIToWide(env_vars::kEtwLogging));
129
130 // Set the value in this process and its children.
131 ::SetEnvironmentVariable(chrome_etw_logging.c_str(), L"1");
132
133 // Set the value for the whole system and ask everyone to refresh.
134 base::win::RegKey environment;
135 if (environment.Open(kEnvironmentRoot, kEnvironmentKey,
136 KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS) {
137 string16 value;
138 if (environment.ReadValue(chrome_etw_logging.c_str(),
139 &value) != ERROR_SUCCESS &&
140 environment.WriteValue(chrome_etw_logging.c_str(),
141 L"1") == ERROR_SUCCESS) {
142 environment.Close();
143 added_chrome_etw_variable_ = true;
144 // Announce to the system that a change has been made so that the shell
145 // and other Windowsy bits pick it up; see
146 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx.
147 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
148 reinterpret_cast<LPARAM>(kEnvironment),
149 SMTO_ABORTIFHUNG,
150 kBroadcastTimeoutMilliseconds, NULL);
151 }
152 }
153 }
154
155 void FileLogger::RevertChromeEtwLogging() {
156 if (added_chrome_etw_variable_) {
157 base::win::RegKey environment;
158 if (environment.Open(kEnvironmentRoot, kEnvironmentKey,
159 KEY_SET_VALUE) == ERROR_SUCCESS) {
160 environment.DeleteValue(ASCIIToWide(env_vars::kEtwLogging).c_str());
161 environment.Close();
162 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
163 reinterpret_cast<LPARAM>(kEnvironment),
164 SMTO_ABORTIFHUNG,
165 kBroadcastTimeoutMilliseconds, NULL);
166 }
167 added_chrome_etw_variable_ = false;
168 }
169 }
170
171 bool FileLogger::EnableProviders() {
172 bool result = true;
173 HRESULT hr = S_OK;
174 const std::vector<GUID>::const_iterator end(provider_names_.end());
175 for (std::vector<GUID>::const_iterator scan(provider_names_.begin());
176 scan != end; ++scan) {
177 hr = controller_.EnableProvider(*scan, 255,
178 (logging::ENABLE_LOG_MESSAGE_ONLY |
179 logging::ENABLE_DETAILED_LOG_MESSAGE));
180 if (FAILED(hr)) {
181 result = false;
182 LOG(ERROR) << "Failed to enable event provider "
183 << scan - provider_names_.begin()
184 << "; hr=" << std::hex << hr;
185 }
186 }
187 return result;
188 }
189
190 void FileLogger::DisableProviders() {
191 HRESULT hr = S_OK;
192 const std::vector<GUID>::const_iterator end(provider_names_.end());
193 for (std::vector<GUID>::const_iterator scan(provider_names_.begin());
194 scan != end; ++scan) {
195 hr = controller_.DisableProvider(*scan);
196 LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider "
197 << scan - provider_names_.begin()
198 << "; hr=" << std::hex << hr;
199 }
200 }
201
202 void FileLogger::Initialize() {
203 Initialize(&kDefaultProviders[0], arraysize(kDefaultProviders));
204 }
205
206
207 void FileLogger::Initialize(const GUID* const* provider_names,
208 size_t provider_count) {
209 DCHECK(provider_names || provider_count == 0);
210 DCHECK(provider_names_.empty() && !added_chrome_etw_variable_);
211 CHECK(!g_is_initialized);
212
213 provider_names_.resize(provider_count);
214 for (size_t i = 0; i < provider_count; ++i) {
215 provider_names_[i] = *provider_names[i];
216 }
217
218 ConfigureChromeEtwLogging();
robertshield 2012/03/02 22:37:41 Make this return a value and log if it fails?
grt (UTC plus 2) 2012/03/03 02:12:43 I've added some logging to the implementation. WD
219
220 // Stop a previous session that wasn't shut down properly.
221 base::win::EtwTraceProperties ignore;
222 HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession,
223 &ignore);
224 LOG_IF(ERROR, FAILED(hr) &&
225 hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND))
226 << "Failed to stop a previous trace session; hr=" << std::hex << hr;
227
228 // Generate ETW log events for this test binary. Log messages at and above
229 // logging::GetMinLogLevel() will continue to go to stderr as well. This
230 // leads to double logging in case of test failures: each LOG statement at
231 // or above the min level will go to stderr during test execution, and then
232 // all events logged to the file session will be dumped again. If this
233 // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX)
234 // here (stashing away the previous min log level to be restored in
235 // Uninitialize) to suppress stderr logging during test execution. Then those
236 // events in the file that were logged at/above the old min level from the
237 // test binary could be dumped to stderr if there were no failures.
238 logging::LogEventProvider::Initialize(kChromeTestsProvider);
239
240 g_is_initialized = true;
241 }
242
243 void FileLogger::Uninitialize() {
244 if (is_logging()) {
245 LOG(ERROR)
246 << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()";
247 StopLogging();
248 }
249
250 logging::LogEventProvider::Uninitialize();
251
252 provider_names_.clear();
253
254 RevertChromeEtwLogging();
255
256 g_is_initialized = false;
257 }
258
259 bool FileLogger::StartLogging(const FilePath& log_file) {
260 HRESULT hr =
261 controller_.StartFileSession(kChromeTestSession,
262 log_file.value().c_str(), false);
263 if (SUCCEEDED(hr)) {
264 EnableProviders();
265 } else {
266 if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
267 LOG(WARNING) << "Access denied while trying to start trace session. "
268 "This is expected when not running as an administrator.";
269 } else {
270 LOG(ERROR) << "Failed to start trace session to file " << log_file.value()
271 << "; hr=" << std::hex << hr;
272 }
273 return false;
274 }
275 return true;
276 }
277
278 void FileLogger::StopLogging() {
279 HRESULT hr = S_OK;
280
281 DisableProviders();
282
283 hr = controller_.Flush(NULL);
284 LOG_IF(ERROR, FAILED(hr))
285 << "Failed to flush events; hr=" << std::hex << hr;
286 hr = controller_.Stop(NULL);
287 LOG_IF(ERROR, FAILED(hr))
288 << "Failed to stop ETW session; hr=" << std::hex << hr;
289 }
290
291 // static
292 void FileLogger::DumpLogFile(const FilePath& log_file, std::ostream& out) {
293 LogDumper::DumpLog(log_file, out);
294 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698