Chromium Code Reviews| 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 | |
| 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} | |
|
erikwright (departed)
2012/03/02 20:47:59
I would argue against declaring these here, and ag
grt (UTC plus 2)
2012/03/02 21:22:43
My argument is that these are the only three provi
erikwright (departed)
2012/03/03 02:04:56
I'll explain my reasoning, but leave it at that. I
grt (UTC plus 2)
2012/03/03 02:31:59
Understood. Duplicating them here made me a bit u
| |
| 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; | |
| 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); | |
|
robertshield
2012/03/02 22:37:41
Is MofData guaranteed to be null terminated or sho
grt (UTC plus 2)
2012/03/03 02:12:43
The terminator is always added by loggin_win.cc.
| |
| 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"); | |
|
erikwright (departed)
2012/03/02 20:47:59
Should this also be reverted? If not, why not?
grt (UTC plus 2)
2012/03/02 21:22:43
It's more work. The change won't persist beyond t
| |
| 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 && | |
|
robertshield
2012/03/02 22:37:41
do you need to check value here?
grt (UTC plus 2)
2012/03/03 02:12:43
Nope, Chrome doesn't care. I've added a comment.
| |
| 140 environment.WriteValue(chrome_etw_logging.c_str(), | |
| 141 L"1") == ERROR_SUCCESS) { | |
| 142 environment.Close(); | |
| 143 added_chrome_etw_variable_ = true; | |
| 144 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, | |
|
erikwright (departed)
2012/03/02 20:47:59
I would appreciate further comments as to how this
grt (UTC plus 2)
2012/03/02 21:22:43
Comment added. It's a standard Windows thing (htt
robertshield
2012/03/02 22:37:41
Did you test this against IE8/9 in protected mode?
erikwright (departed)
2012/03/03 02:04:56
So I gather that this class can only catch log mes
grt (UTC plus 2)
2012/03/03 02:12:43
I've tested against IE8, but I didn't do anything
grt (UTC plus 2)
2012/03/03 02:31:59
Not necessarily. If you already had that var in y
robertshield
2012/03/05 19:20:53
I just meant with IE8/9 in multi-process mode (the
| |
| 145 reinterpret_cast<LPARAM>(kEnvironment), | |
| 146 SMTO_ABORTIFHUNG, | |
| 147 kBroadcastTimeoutMilliseconds, NULL); | |
| 148 } | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 void FileLogger::RevertChromeEtwLogging() { | |
| 153 if (added_chrome_etw_variable_) { | |
| 154 base::win::RegKey environment; | |
| 155 if (environment.Open(kEnvironmentRoot, kEnvironmentKey, | |
| 156 KEY_SET_VALUE) == ERROR_SUCCESS) { | |
| 157 environment.DeleteValue(ASCIIToWide(env_vars::kEtwLogging).c_str()); | |
| 158 environment.Close(); | |
| 159 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, | |
| 160 reinterpret_cast<LPARAM>(kEnvironment), | |
| 161 SMTO_ABORTIFHUNG, | |
| 162 kBroadcastTimeoutMilliseconds, NULL); | |
| 163 } | |
| 164 added_chrome_etw_variable_ = false; | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 bool FileLogger::EnableProviders() { | |
| 169 bool result = true; | |
| 170 HRESULT hr = S_OK; | |
| 171 const std::vector<GUID>::const_iterator end(provider_names_.end()); | |
| 172 for (std::vector<GUID>::const_iterator scan(provider_names_.begin()); | |
| 173 scan != end; ++scan) { | |
|
erikwright (departed)
2012/03/02 20:47:59
Is it actually considered costly to just inline th
grt (UTC plus 2)
2012/03/02 21:22:43
For a release build, no. For a debug build it'll
| |
| 174 hr = controller_.EnableProvider(*scan, 255, | |
| 175 (logging::ENABLE_LOG_MESSAGE_ONLY | | |
| 176 logging::ENABLE_DETAILED_LOG_MESSAGE)); | |
| 177 if (FAILED(hr)) { | |
| 178 result = false; | |
| 179 LOG(ERROR) << "Failed to enable event provider " | |
| 180 << scan - provider_names_.begin() | |
| 181 << "; hr=" << std::hex << hr; | |
| 182 } | |
| 183 } | |
| 184 return result; | |
| 185 } | |
| 186 | |
| 187 void FileLogger::DisableProviders() { | |
| 188 HRESULT hr = S_OK; | |
| 189 const std::vector<GUID>::const_iterator end(provider_names_.end()); | |
| 190 for (std::vector<GUID>::const_iterator scan(provider_names_.begin()); | |
| 191 scan != end; ++scan) { | |
| 192 hr = controller_.DisableProvider(*scan); | |
| 193 LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider " | |
| 194 << scan - provider_names_.begin() | |
| 195 << "; hr=" << std::hex << hr; | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 void FileLogger::Initialize() { | |
| 200 Initialize(&kDefaultProviders[0], arraysize(kDefaultProviders)); | |
| 201 } | |
| 202 | |
| 203 | |
| 204 void FileLogger::Initialize(const GUID* const* provider_names, | |
| 205 size_t provider_count) { | |
| 206 DCHECK(provider_names || provider_count == 0); | |
| 207 DCHECK(provider_names_.empty() && !added_chrome_etw_variable_); | |
| 208 CHECK(!g_is_initialized); | |
|
robertshield
2012/03/02 22:37:41
If these are initialized on different threads, you
grt (UTC plus 2)
2012/03/03 02:12:43
The documentation for the class says only one inst
| |
| 209 | |
| 210 provider_names_.resize(provider_count); | |
| 211 for (size_t i = 0; i < provider_count; ++i) { | |
| 212 provider_names_[i] = *provider_names[i]; | |
| 213 } | |
| 214 | |
| 215 ConfigureChromeEtwLogging(); | |
| 216 | |
| 217 // Stop a previous session that wasn't shut down properly. | |
| 218 base::win::EtwTraceProperties ignore; | |
| 219 HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession, | |
| 220 &ignore); | |
| 221 LOG_IF(ERROR, FAILED(hr) && | |
| 222 hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND)) | |
| 223 << "Failed to stop a previous trace session; hr=" << std::hex << hr; | |
| 224 | |
| 225 // Generate ETW log events for this test binary. Log messages at and above | |
| 226 // logging::GetMinLogLevel() will continue to go to stderr as well. This | |
| 227 // leads to double logging in case of test failures: each LOG statement at | |
| 228 // or above the min level will go to stderr during test execution, and then | |
| 229 // all events logged to the file session will be dumped again. If this | |
| 230 // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX) | |
| 231 // here (stashing away the previous min log level to be restored in | |
| 232 // Uninitialize) to suppress stderr logging during test execution. Then those | |
| 233 // events in the file that were logged at/above the old min level from the | |
| 234 // test binary could be dumped to stderr if there were no failures. | |
| 235 logging::LogEventProvider::Initialize(kChromeTestsProvider); | |
| 236 | |
| 237 g_is_initialized = true; | |
| 238 } | |
| 239 | |
| 240 void FileLogger::Uninitialize() { | |
| 241 if (is_logging()) { | |
| 242 LOG(ERROR) | |
| 243 << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()"; | |
| 244 StopLogging(); | |
| 245 } | |
| 246 | |
| 247 logging::LogEventProvider::Uninitialize(); | |
| 248 | |
| 249 provider_names_.clear(); | |
| 250 | |
| 251 RevertChromeEtwLogging(); | |
| 252 | |
| 253 g_is_initialized = false; | |
| 254 } | |
| 255 | |
| 256 bool FileLogger::StartLogging(const FilePath& log_file) { | |
| 257 HRESULT hr = | |
| 258 controller_.StartFileSession(kChromeTestSession, | |
| 259 log_file.value().c_str(), false); | |
| 260 if (SUCCEEDED(hr)) { | |
| 261 EnableProviders(); | |
| 262 } else { | |
| 263 LOG(ERROR) << "Failed to start trace session to file " << log_file.value() | |
| 264 << "; hr=" << std::hex << hr; | |
| 265 return false; | |
| 266 } | |
| 267 return true; | |
| 268 } | |
| 269 | |
| 270 void FileLogger::StopLogging() { | |
| 271 HRESULT hr = S_OK; | |
| 272 | |
| 273 DisableProviders(); | |
| 274 | |
| 275 hr = controller_.Flush(NULL); | |
| 276 LOG_IF(ERROR, FAILED(hr)) | |
| 277 << "Failed to flush events; hr=" << std::hex << hr; | |
| 278 hr = controller_.Stop(NULL); | |
| 279 LOG_IF(ERROR, FAILED(hr)) | |
| 280 << "Failed to stop ETW session; hr=" << std::hex << hr; | |
| 281 } | |
| 282 | |
| 283 // static | |
| 284 void FileLogger::DumpLogFile(const FilePath& log_file, std::ostream& out) { | |
| 285 LogDumper::DumpLog(log_file, out); | |
| 286 } | |
| OLD | NEW |