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 <evntrace.h> | |
9 #include <guiddef.h> | |
10 #include <objbase.h> // for StringFromGUID2 | |
robertshield
2012/03/05 19:20:54
any reason for the comment?
grt (UTC plus 2)
2012/03/06 03:24:57
Not really. Removed.
| |
11 | |
12 #include <iomanip> | |
13 #include <ios> // For std::hex | |
14 | |
15 #include "base/debug/trace_event_win.h" | |
16 #include "base/logging.h" | |
17 #include "base/file_path.h" | |
18 #include "base/logging_win.h" | |
19 #include "base/memory/scoped_ptr.h" | |
20 #include "base/string_piece.h" | |
21 #include "base/string16.h" | |
22 #include "base/utf_string_conversions.h" | |
23 #include "base/win/event_trace_consumer.h" | |
24 #include "base/win/registry.h" | |
25 #include "chrome/common/env_vars.h" | |
26 | |
27 namespace { | |
28 | |
29 bool g_is_initialized = false; | |
30 | |
31 const wchar_t kChromeTestSession[] = L"chrome_tests"; | |
32 | |
33 // From chrome_tab.cc: {0562BFC3-2550-45b4-BD8E-A310583D3A6F} | |
34 const GUID kChromeFrameProvider = | |
35 { 0x562bfc3, 0x2550, 0x45b4, | |
36 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } }; | |
37 | |
38 // From chrome/common/logging_chrome.cc: {7FE69228-633E-4f06-80C1-527FEA23E3A7} | |
39 const GUID kChromeTraceProviderName = | |
40 { 0x7fe69228, 0x633e, 0x4f06, | |
41 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; | |
42 | |
43 // {81729947-CD2A-49e6-8885-785429F339F5} | |
44 const GUID kChromeTestsProvider = | |
45 { 0x81729947, 0xcd2a, 0x49e6, | |
46 { 0x88, 0x85, 0x78, 0x54, 0x29, 0xf3, 0x39, 0xf5 } }; | |
47 | |
48 // The configurations for the supported providers. This must be in sync with | |
49 // FileLogger::EventProviderBits. | |
50 const struct { | |
51 const GUID* provider_name; | |
52 uint8 level; | |
53 uint32 flags; | |
54 } kProviders[] = { | |
55 { &kChromeTraceProviderName, 255, 0 }, | |
56 { &kChromeFrameProvider, 255, 0 }, | |
57 { &kChromeTestsProvider, 255, 0 }, | |
58 { &base::debug::kChromeTraceProviderName, 255, 0 } | |
59 }; | |
robertshield
2012/03/05 19:20:54
COMPILE_ASSERT on the arraysize vs the enum in the
grt (UTC plus 2)
2012/03/06 03:24:57
Kinda done. I checked it against the bitfield of
| |
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 | |
68 // Writes a timestamp. | |
69 void WriteTimeStamp(LARGE_INTEGER time_stamp, std::ostream& out) { | |
robertshield
2012/03/05 19:20:54
don't use non-const references
grt (UTC plus 2)
2012/03/06 03:24:57
In the case of streams, I think it makes more sens
| |
70 // Get a nice SYSTEMTIME from the time_stamp. | |
71 FILETIME event_time = {}; | |
72 event_time.dwLowDateTime = time_stamp.LowPart; | |
73 event_time.dwHighDateTime = time_stamp.HighPart; | |
74 SYSTEMTIME system_time = {}; | |
75 FileTimeToSystemTime(&event_time, &system_time); | |
76 | |
77 // SYSTEMTIME only has millisecond accuracy. Do some conversions and math to | |
78 // get the sub-second time (number of 100ns ticks). | |
79 system_time.wMilliseconds = 0; | |
80 FILETIME coarse_event_time = {}; | |
81 SystemTimeToFileTime(&system_time, &coarse_event_time); | |
82 ULARGE_INTEGER coarse_value; | |
83 coarse_value.HighPart = coarse_event_time.dwHighDateTime; | |
84 coarse_value.LowPart = coarse_event_time.dwLowDateTime; | |
85 coarse_value.QuadPart = time_stamp.QuadPart - coarse_value.QuadPart; | |
robertshield
2012/03/05 19:20:54
consider mentioning that ULARGE_INTEGER is a union
grt (UTC plus 2)
2012/03/06 03:24:57
Switched to using base/time.h's facilities, giving
| |
86 DCHECK_EQ(coarse_value.HighPart, static_cast<DWORD>(0)); | |
87 DCHECK_LT(coarse_value.LowPart, static_cast<DWORD>(10000000)); | |
88 | |
89 out << std::setfill('0') | |
90 << std::setw(2) << system_time.wMonth | |
91 << std::setw(2) << system_time.wDay | |
92 << '/' | |
93 << std::setw(2) << system_time.wHour | |
94 << std::setw(2) << system_time.wMinute | |
95 << std::setw(2) << system_time.wSecond | |
96 << '.' | |
97 << std::setw(7) << coarse_value.LowPart; | |
98 } | |
99 | |
100 // Converts an ETW level created by logging::LogEventProvider back to its | |
101 // corresponding severity. | |
erikwright (departed)
2012/03/05 19:55:30
I suspect this corresponds to an inverse switch st
| |
102 logging::LogSeverity EventLevelToSeverity(uint8 level) { | |
103 switch (level) { | |
104 case TRACE_LEVEL_NONE: | |
105 NOTREACHED(); | |
106 case TRACE_LEVEL_FATAL: | |
107 return logging::LOG_FATAL; | |
108 case TRACE_LEVEL_ERROR: | |
109 return logging::LOG_ERROR; | |
110 case TRACE_LEVEL_WARNING: | |
111 return logging::LOG_WARNING; | |
112 case TRACE_LEVEL_INFORMATION: | |
113 return logging::LOG_INFO; | |
114 default: | |
115 // Trace levels above information correspond to negative severity levels, | |
116 // which are used for VLOG verbosity levels. | |
117 return TRACE_LEVEL_INFORMATION - level; | |
118 } | |
119 } | |
120 | |
121 // Writes a severity level to the stream a la LogMessage::Init. | |
122 void WriteSeverity(logging::LogSeverity severity, std::ostream& out) { | |
erikwright (departed)
2012/03/05 19:55:30
This is how base/logging.h does it:
http://code.g
| |
123 switch (severity) { | |
124 case logging::LOG_INFO: | |
125 out << "INFO"; | |
126 break; | |
127 case logging::LOG_WARNING: | |
128 out << "WARNING"; | |
129 break; | |
130 case logging::LOG_ERROR: | |
131 out << "ERROR"; | |
132 break; | |
133 case logging::LOG_ERROR_REPORT: | |
134 out << "ERROR_REPORT"; | |
135 break; | |
136 case logging::LOG_FATAL: | |
137 out << "FATAL"; | |
138 break; | |
139 default: | |
140 if (severity < 0) | |
141 out << "VERBOSE" << -severity; | |
142 else | |
143 NOTREACHED(); | |
144 break; | |
145 } | |
146 } | |
147 | |
148 // A helper class that dumps a log file to an output stream. Only one may be | |
149 // in use at a time. | |
150 class LogDumper { | |
151 public: | |
152 // Dump |log_file| to |out|. | |
153 static void DumpLog(const FilePath& log_file, std::ostream& out); | |
154 | |
155 private: | |
156 // An implementation of a trace consumer that delegates to a given (at | |
157 // compile-time) event processing function. | |
158 template<void (*ProcessEventFn)(EVENT_TRACE*)> | |
159 class TraceConsumer | |
160 : public base::win::EtwTraceConsumerBase<TraceConsumer<ProcessEventFn> > { | |
robertshield
2012/03/05 19:20:54
template abuse alert.. template abuse alert..
It
robertshield
2012/03/05 19:57:07
Taking a look at the implementation of EtwTraceCon
grt (UTC plus 2)
2012/03/06 03:24:57
Thanks. I didn't like the other obvious options (
| |
161 public: | |
162 TraceConsumer() { } | |
163 static void ProcessEvent(EVENT_TRACE* event) { (*ProcessEventFn)(event); } | |
164 private: | |
165 DISALLOW_COPY_AND_ASSIGN(TraceConsumer); | |
166 }; | |
167 | |
168 // A parser for Mof data found in an EVENT_TRACE object as formatted by | |
erikwright (departed)
2012/03/05 19:55:30
I find it shocking that this needs to be written b
| |
169 // Chromium-related classes. | |
170 class MofDataParser { | |
171 public: | |
172 explicit MofDataParser(EVENT_TRACE* event); | |
173 bool ReadDWORD(DWORD* value) { | |
174 return ReadPrimitive(value); | |
175 } | |
176 bool ReadInt(int* value) { | |
177 return ReadPrimitive(value); | |
178 } | |
179 bool ReadVoidPointer(void** value) { | |
180 return ReadPrimitive(value); | |
181 } | |
182 bool ReadVoidPointerArray(DWORD size, void* const** values) { | |
183 return ReadPointerArray(size, values); | |
184 } | |
185 // Reads an arbitrary data structure within the data. | |
186 template<typename T> bool ReadStructure(const T** value) { | |
187 if (length_ < sizeof(**value)) | |
188 return false; | |
189 *value = reinterpret_cast<const T*>(scan_); | |
190 Advance(sizeof(**value)); | |
191 return true; | |
192 } | |
193 // Reads a null-terminated string from the data. A trailing newline, if | |
194 // present, is stripped. | |
195 bool ReadString(base::StringPiece* value); | |
196 bool empty() { return length_ == 0; } | |
197 | |
198 private: | |
199 void Advance(size_t num_bytes) { | |
200 scan_ += num_bytes; | |
201 length_ -= num_bytes; | |
202 } | |
203 template<typename T> bool ReadPrimitive(T* value) { | |
204 if (length_ < sizeof(*value)) | |
205 return false; | |
206 *value = *reinterpret_cast<const T*>(scan_); | |
207 Advance(sizeof(*value)); | |
208 return true; | |
209 } | |
210 template<typename T> bool ReadPointerArray(DWORD size, T* const** values) { | |
211 if (length_ < sizeof(*values) * size) | |
212 return false; | |
213 *values = reinterpret_cast<T* const*>(scan_); | |
214 Advance(sizeof(*values) * size); | |
215 return true; | |
216 } | |
217 | |
218 const uint8* scan_; | |
219 uint32 length_; | |
220 }; | |
221 | |
222 // An interface for handling Mof data. | |
223 class MofData { | |
224 public: | |
225 virtual ~MofData(); | |
226 | |
227 // Parses the data in |event|, returning true on success; false otherwise. | |
228 virtual bool Initialize(EVENT_TRACE* event) = 0; | |
229 | |
230 // Writes the event level. | |
231 virtual bool WriteLevel(uint8 level, std::ostream& out) = 0; | |
232 | |
233 // Writes context information. | |
234 virtual void WriteContext(std::ostream& out) = 0; | |
235 | |
236 // Writes the meat of the data. | |
237 virtual void WriteData(std::ostream& out) = 0; | |
238 | |
239 protected: | |
240 MofData(); | |
241 }; | |
242 | |
243 // A MofData implementation for events created by Chromium's LogEventProvider | |
244 // when the ENABLE_LOG_MESSAGE_ONLY flag is set. | |
245 class LogMessageData : public MofData { | |
246 public: | |
247 LogMessageData(); | |
248 virtual ~LogMessageData(); | |
249 virtual bool Initialize(EVENT_TRACE* event) OVERRIDE; | |
250 virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE; | |
251 virtual void WriteContext(std::ostream& out) OVERRIDE; | |
252 virtual void WriteData(std::ostream& out) OVERRIDE; | |
253 | |
254 private: | |
255 base::StringPiece message_; | |
256 DISALLOW_COPY_AND_ASSIGN(LogMessageData); | |
257 }; | |
258 | |
259 // A MofData implementation for events created by Chromium's LogEventProvider | |
260 // when the ENABLE_LOG_MESSAGE_ONLY flag is not set. | |
261 class LogMessageFullData : public MofData { | |
262 public: | |
263 LogMessageFullData(); | |
264 virtual ~LogMessageFullData(); | |
265 virtual bool Initialize(EVENT_TRACE* event) OVERRIDE; | |
266 virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE; | |
267 virtual void WriteContext(std::ostream& out) OVERRIDE; | |
268 virtual void WriteData(std::ostream& out) OVERRIDE; | |
269 | |
270 private: | |
271 DWORD stack_depth_; | |
272 void *const * backtrace_; | |
273 int line_; | |
274 base::StringPiece file_; | |
275 base::StringPiece message_; | |
276 DISALLOW_COPY_AND_ASSIGN(LogMessageFullData); | |
277 }; | |
278 | |
279 // A MofData implementation for events created by Chromium's | |
280 // TraceEventETWProvider. | |
281 class TraceEventData : public MofData { | |
282 public: | |
283 TraceEventData(); | |
284 virtual ~TraceEventData(); | |
285 virtual bool Initialize(EVENT_TRACE* event) OVERRIDE; | |
286 virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE; | |
287 virtual void WriteContext(std::ostream& out) OVERRIDE; | |
288 virtual void WriteData(std::ostream& out) OVERRIDE; | |
289 | |
290 private: | |
291 const char* type_; | |
292 base::StringPiece name_; | |
293 void* id_; | |
294 base::StringPiece extra_; | |
295 DWORD stack_depth_; | |
296 void *const * backtrace_; | |
297 DISALLOW_COPY_AND_ASSIGN(TraceEventData); | |
298 }; | |
299 | |
300 // A MofData implementation for the TRACE_LOGFILE_HEADER event at the start of | |
301 // log files. | |
302 class LogfileHeaderData : public MofData { | |
303 public: | |
304 LogfileHeaderData(); | |
305 virtual ~LogfileHeaderData(); | |
306 virtual bool Initialize(EVENT_TRACE* event) OVERRIDE; | |
307 virtual bool WriteLevel(uint8 level, std::ostream& out) OVERRIDE; | |
308 virtual void WriteContext(std::ostream& out) OVERRIDE; | |
309 virtual void WriteData(std::ostream& out) OVERRIDE; | |
310 | |
311 private: | |
312 const TRACE_LOGFILE_HEADER* header_; | |
313 DISALLOW_COPY_AND_ASSIGN(LogfileHeaderData); | |
314 }; | |
315 | |
316 // A pointer to a function that creates a new instance of some MofData | |
317 // implementation. | |
318 typedef MofData* (*MofDataFactoryFn)(); | |
319 | |
320 // Returns a new instance of a MofData implementation. | |
321 template<class MofDataType> static MofData* CreateMofData() { | |
322 return new MofDataType(); | |
323 } | |
324 | |
325 explicit LogDumper(std::ostream& out); | |
326 ~LogDumper(); | |
327 | |
328 // Returns a factory function to create the MofData implementation | |
329 // corresponding to an EVENT_TRACE instance of the given event class and type. | |
330 static MofDataFactoryFn GetMofDataFactory(const GUID& event_class, | |
331 uint8 type); | |
332 | |
333 // Delegates to DumpEvent() of the current LogDumper instance. | |
334 static void ProcessEvent(EVENT_TRACE* event); | |
335 | |
336 // Dumps the event. | |
337 void DumpEvent(EVENT_TRACE* event); | |
338 | |
339 // Consumes all events in |log_file|, dumping them to the output stream. | |
340 void Consume(const FilePath& log_file); | |
341 | |
342 // The current instance. | |
343 static LogDumper* instance_; | |
344 std::ostream* out_; | |
345 | |
346 DISALLOW_COPY_AND_ASSIGN(LogDumper); | |
347 }; | |
348 | |
349 // LogDumper::MofDataParser implementation | |
350 | |
351 LogDumper::MofDataParser::MofDataParser(EVENT_TRACE* event) | |
352 : scan_(reinterpret_cast<CONST uint8*>(event->MofData)), | |
353 length_(event->MofLength) { | |
354 } | |
355 | |
356 bool LogDumper::MofDataParser::ReadString(base::StringPiece* value) { | |
357 const uint8* str_scan = scan_; | |
358 const uint8* const str_end = str_scan + length_; | |
359 while (str_scan < str_end && *str_scan != 0) | |
360 ++str_scan; | |
361 if (str_scan == str_end) | |
362 return false; | |
363 size_t string_length = str_scan - scan_; | |
364 bool has_trailing_newline = (string_length > 0 && str_scan[-1] == '\n'); | |
365 value->set(reinterpret_cast<const char*>(scan_), | |
366 has_trailing_newline ? string_length - 1 : string_length); | |
367 Advance(string_length + 1); | |
368 return true; | |
369 } | |
370 | |
371 // LogDumper::MofData implementation | |
372 | |
373 LogDumper::MofData::MofData() { | |
374 } | |
375 | |
376 LogDumper::MofData::~MofData() { | |
377 } | |
378 | |
379 // LogDumper::LogMessageData implementation | |
380 | |
381 LogDumper::LogMessageData::LogMessageData() { | |
382 } | |
383 | |
384 LogDumper::LogMessageData::~LogMessageData() { | |
385 } | |
386 | |
387 bool LogDumper::LogMessageData::Initialize(EVENT_TRACE* event) { | |
388 MofDataParser parser(event); | |
389 return (parser.ReadString(&message_) && | |
390 parser.empty()); | |
391 } | |
392 | |
393 bool LogDumper::LogMessageData::WriteLevel(uint8 level, std::ostream& out) { | |
394 WriteSeverity(EventLevelToSeverity(level), out); | |
395 return true; | |
396 } | |
397 | |
398 void LogDumper::LogMessageData::WriteContext(std::ostream& out) { | |
399 } | |
400 | |
401 void LogDumper::LogMessageData::WriteData(std::ostream& out) { | |
402 out << message_; | |
403 } | |
404 | |
405 // LogDumper::LogMessageFullData implementation | |
406 | |
407 LogDumper::LogMessageFullData::LogMessageFullData() | |
408 : stack_depth_(), | |
409 backtrace_(), | |
410 line_() { | |
411 } | |
412 | |
413 LogDumper::LogMessageFullData::~LogMessageFullData() { | |
414 } | |
415 | |
416 bool LogDumper::LogMessageFullData::Initialize(EVENT_TRACE* event) { | |
417 MofDataParser parser(event); | |
418 return (parser.ReadDWORD(&stack_depth_) && | |
419 parser.ReadVoidPointerArray(stack_depth_, &backtrace_) && | |
420 parser.ReadInt(&line_) && | |
421 parser.ReadString(&file_) && | |
422 parser.ReadString(&message_) && | |
423 parser.empty()); | |
424 } | |
425 | |
426 bool LogDumper::LogMessageFullData::WriteLevel(uint8 level, std::ostream& out) { | |
427 WriteSeverity(EventLevelToSeverity(level), out); | |
428 return true; | |
429 } | |
430 | |
431 void LogDumper::LogMessageFullData::WriteContext(std::ostream& out) { | |
432 // Shamelessly borrowed from base/logging.cc LogMessage::Init() | |
433 base::StringPiece filename(file_); | |
434 size_t last_slash_pos = filename.find_last_of("\\/"); | |
435 if (last_slash_pos != base::StringPiece::npos) | |
436 filename.remove_prefix(last_slash_pos + 1); | |
437 | |
438 out << filename << '(' << line_ << ')'; | |
439 } | |
440 | |
441 void LogDumper::LogMessageFullData::WriteData(std::ostream& out) { | |
442 out << message_; | |
443 } | |
444 | |
445 // LogDumper::TraceEventData implementation | |
446 | |
447 LogDumper::TraceEventData::TraceEventData() | |
448 : type_(), | |
449 id_(), | |
450 stack_depth_(), | |
451 backtrace_() { | |
452 } | |
453 | |
454 LogDumper::TraceEventData::~TraceEventData() { | |
455 } | |
456 | |
457 bool LogDumper::TraceEventData::Initialize(EVENT_TRACE* event) { | |
458 switch (event->Header.Class.Type) { | |
459 case base::debug::kTraceEventTypeBegin: | |
460 type_ = "BEGIN"; | |
461 break; | |
462 case base::debug::kTraceEventTypeEnd: | |
463 type_ = "END"; | |
464 break; | |
465 case base::debug::kTraceEventTypeInstant: | |
466 type_ = "INSTANT"; | |
467 break; | |
468 default: | |
469 NOTREACHED(); | |
470 type_ = ""; | |
471 break; | |
472 } | |
473 MofDataParser parser(event); | |
474 return (parser.ReadString(&name_) && | |
475 parser.ReadVoidPointer(&id_) && | |
476 parser.ReadString(&extra_) && | |
477 (parser.empty() || | |
478 parser.ReadDWORD(&stack_depth_) && | |
479 parser.ReadVoidPointerArray(stack_depth_, &backtrace_) && | |
480 parser.empty())); | |
481 } | |
482 | |
483 void LogDumper::TraceEventData::WriteContext(std::ostream& out) { | |
484 } | |
485 | |
486 bool LogDumper::TraceEventData::WriteLevel(uint8 level, std::ostream& out) { | |
487 out << type_; | |
488 return true; | |
489 } | |
490 | |
491 void LogDumper::TraceEventData::WriteData(std::ostream& out) { | |
492 out << name_ << " " << extra_; | |
493 } | |
494 | |
495 // LogDumper::LogfileHeaderData implementation | |
496 | |
497 LogDumper::LogfileHeaderData::LogfileHeaderData() | |
498 : header_() { | |
499 } | |
500 | |
501 LogDumper::LogfileHeaderData::~LogfileHeaderData() { | |
502 } | |
503 | |
504 bool LogDumper::LogfileHeaderData::Initialize(EVENT_TRACE* event) { | |
505 MofDataParser parser(event); | |
506 return parser.ReadStructure(&header_); | |
507 } | |
508 | |
509 void LogDumper::LogfileHeaderData::WriteContext(std::ostream& out) { | |
510 } | |
511 | |
512 bool LogDumper::LogfileHeaderData::WriteLevel(uint8 level, std::ostream& out) { | |
513 return false; | |
514 } | |
515 | |
516 void LogDumper::LogfileHeaderData::WriteData(std::ostream& out) { | |
517 out << "Log captured from Windows " | |
518 << static_cast<int>(header_->VersionDetail.MajorVersion) << '.' | |
519 << static_cast<int>(header_->VersionDetail.MinorVersion) << '.' | |
520 << static_cast<int>(header_->VersionDetail.SubVersion) << '.' | |
521 << static_cast<int>(header_->VersionDetail.SubMinorVersion) | |
522 << ". " << header_->EventsLost << " events lost, " | |
523 << header_->BuffersLost << " buffers lost."; | |
524 } | |
525 | |
526 // LogDumper implementation | |
527 | |
528 // static | |
529 LogDumper* LogDumper::instance_ = NULL; | |
530 | |
531 LogDumper::LogDumper(std::ostream& out) : out_(&out) { | |
532 CHECK(instance_ == NULL); | |
533 instance_ = this; | |
534 } | |
535 | |
536 LogDumper::~LogDumper() { | |
537 CHECK_EQ(instance_, this); | |
538 instance_ = NULL; | |
539 } | |
540 | |
541 // static | |
542 LogDumper::MofDataFactoryFn LogDumper::GetMofDataFactory( | |
543 const GUID& event_class, | |
544 uint8 type) { | |
robertshield
2012/03/05 19:20:54
IMO this is needlessly complicated. Returning a fa
grt (UTC plus 2)
2012/03/06 03:24:57
No, I agree wholeheartedly. This was left behind
| |
545 if (IsEqualGUID(event_class, logging::kLogEventId)) { | |
546 if (type == logging::LOG_MESSAGE) | |
547 return &CreateMofData<LogMessageData>; | |
548 else if (type == logging::LOG_MESSAGE_FULL) | |
549 return &CreateMofData<LogMessageFullData>; | |
550 } else if (IsEqualGUID(event_class, base::debug::kTraceEventClass32)) { | |
551 return &CreateMofData<TraceEventData>; | |
552 } else if (IsEqualGUID(event_class, EventTraceGuid)) { | |
553 return &CreateMofData<LogfileHeaderData>; | |
554 } | |
555 return NULL; | |
556 } | |
557 | |
558 // static | |
559 void LogDumper::ProcessEvent(EVENT_TRACE* event) { | |
560 if (instance_ != NULL) | |
561 instance_->DumpEvent(event); | |
562 } | |
563 | |
564 void LogDumper::DumpEvent(EVENT_TRACE* event) { | |
565 scoped_ptr<MofData> payload; | |
566 MofDataFactoryFn factory_fn = GetMofDataFactory(event->Header.Guid, | |
567 event->Header.Class.Type); | |
568 | |
569 if (factory_fn) { | |
570 payload.reset((*factory_fn)()); | |
571 if (!payload->Initialize(event)) | |
572 payload.reset(); | |
573 } | |
574 | |
575 *out_ << '[' << event->Header.ProcessId << ':' | |
576 << event->Header.ThreadId << ':'; | |
577 WriteTimeStamp(event->Header.TimeStamp, *out_); | |
578 *out_ << ':'; | |
579 if (!payload.get() || !payload->WriteLevel(event->Header.Class.Level, *out_)) | |
580 *out_ << "level" << static_cast<int>(event->Header.Class.Level); | |
581 if (payload.get()) { | |
582 *out_ << ':'; | |
583 payload->WriteContext(*out_); | |
584 } | |
585 *out_ << "] "; | |
586 if (payload.get()) { | |
587 payload->WriteData(*out_); | |
588 } else { | |
589 wchar_t guid[64]; | |
590 StringFromGUID2(event->Header.Guid, &guid[0], arraysize(guid)); | |
591 if (factory_fn) | |
592 *out_ << "parse error"; | |
593 else | |
594 *out_ << "unsupported event"; | |
595 *out_ << " (class=" << guid << ", type=" | |
596 << static_cast<int>(event->Header.Class.Type) << ")"; | |
597 } | |
598 | |
599 *out_ << std::endl; | |
600 } | |
601 | |
602 void LogDumper::Consume(const FilePath& log_file) { | |
603 TraceConsumer<&ProcessEvent> consumer; | |
604 HRESULT hr = S_OK; | |
605 | |
606 hr = consumer.OpenFileSession(log_file.value().c_str()); | |
607 if (FAILED(hr)) { | |
608 LOG(ERROR) << "Failed to open session for log file " << log_file.value() | |
609 << "; hr=" << std::hex << hr; | |
610 } else { | |
611 consumer.Consume(); | |
612 consumer.Close(); | |
613 out_->flush(); | |
614 } | |
615 } | |
616 | |
617 // static | |
618 void LogDumper::DumpLog(const FilePath& log_file, std::ostream& out) { | |
619 LogDumper(out).Consume(log_file); | |
620 } | |
621 | |
622 } // namespace | |
623 | |
624 FileLogger::FileLogger() | |
625 : event_provider_mask_(), | |
626 added_chrome_etw_variable_() { | |
627 } | |
628 | |
629 FileLogger::~FileLogger() { | |
630 if (event_provider_mask_) { | |
631 LOG(WARNING) | |
632 << __FUNCTION__ << " don't forget to call FileLogger::Uninitialize()"; | |
633 Uninitialize(); | |
634 } | |
635 } | |
636 | |
637 void FileLogger::ConfigureChromeEtwLogging() { | |
638 const string16 chrome_etw_logging(ASCIIToWide(env_vars::kEtwLogging)); | |
639 | |
640 // Set the value in this process and its children. | |
641 ::SetEnvironmentVariable(chrome_etw_logging.c_str(), L"1"); | |
642 | |
643 // Set the value for the whole system and ask everyone to refresh. | |
644 base::win::RegKey environment; | |
645 LONG result = environment.Open(kEnvironmentRoot, kEnvironmentKey, | |
646 KEY_QUERY_VALUE | KEY_SET_VALUE); | |
647 if (result == ERROR_SUCCESS) { | |
648 string16 value; | |
649 // The actual value of the variable is insignificant in the eyes of Chrome. | |
650 if (environment.ReadValue(chrome_etw_logging.c_str(), | |
651 &value) != ERROR_SUCCESS && | |
652 environment.WriteValue(chrome_etw_logging.c_str(), | |
653 L"1") == ERROR_SUCCESS) { | |
654 environment.Close(); | |
655 added_chrome_etw_variable_ = true; | |
656 // Announce to the system that a change has been made so that the shell | |
657 // and other Windowsy bits pick it up; see | |
658 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx. | |
659 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, | |
660 reinterpret_cast<LPARAM>(kEnvironment), | |
661 SMTO_ABORTIFHUNG, | |
662 kBroadcastTimeoutMilliseconds, NULL); | |
663 } | |
664 } else { | |
665 SetLastError(result); | |
666 PLOG(ERROR) << "Failed to open HKLM to check/modify the system environment"; | |
667 } | |
668 } | |
669 | |
670 void FileLogger::RevertChromeEtwLogging() { | |
671 if (added_chrome_etw_variable_) { | |
672 base::win::RegKey environment; | |
673 if (environment.Open(kEnvironmentRoot, kEnvironmentKey, | |
674 KEY_SET_VALUE) == ERROR_SUCCESS) { | |
675 environment.DeleteValue(ASCIIToWide(env_vars::kEtwLogging).c_str()); | |
676 environment.Close(); | |
677 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, | |
678 reinterpret_cast<LPARAM>(kEnvironment), | |
679 SMTO_ABORTIFHUNG, | |
680 kBroadcastTimeoutMilliseconds, NULL); | |
681 } | |
682 added_chrome_etw_variable_ = false; | |
683 } | |
684 } | |
685 | |
686 bool FileLogger::EnableProviders() { | |
687 bool result = true; | |
688 | |
689 // Generate ETW log events for this test binary. Log messages at and above | |
690 // logging::GetMinLogLevel() will continue to go to stderr as well. This | |
691 // leads to double logging in case of test failures: each LOG statement at | |
692 // or above the min level will go to stderr during test execution, and then | |
693 // all events logged to the file session will be dumped again. If this | |
694 // turns out to be an issue, one could call logging::SetMinLogLevel(INT_MAX) | |
695 // here (stashing away the previous min log level to be restored in | |
696 // Uninitialize) to suppress stderr logging during test execution. Then those | |
697 // events in the file that were logged at/above the old min level from the | |
698 // test binary could be dumped to stderr if there were no failures. | |
699 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) | |
700 logging::LogEventProvider::Initialize(kChromeTestsProvider); | |
701 | |
702 HRESULT hr = S_OK; | |
703 for (size_t i = 0; i < arraysize(kProviders); ++i) { | |
704 if (event_provider_mask_ & (1 << i)) { | |
705 hr = controller_.EnableProvider(*kProviders[i].provider_name, | |
706 kProviders[i].level, | |
707 kProviders[i].flags); | |
708 if (FAILED(hr)) { | |
709 result = false; | |
710 LOG(ERROR) << "Failed to enable event provider " << i | |
711 << "; hr=" << std::hex << hr; | |
712 } | |
713 } | |
714 } | |
715 | |
716 return result; | |
717 } | |
718 | |
719 void FileLogger::DisableProviders() { | |
720 HRESULT hr = S_OK; | |
721 for (size_t i = 0; i < arraysize(kProviders); ++i) { | |
722 if (event_provider_mask_ & (1 << i)) { | |
723 hr = controller_.DisableProvider(*kProviders[i].provider_name); | |
724 LOG_IF(ERROR, FAILED(hr)) << "Failed to disable event provider " | |
725 << i << "; hr=" << std::hex << hr; | |
726 } | |
727 } | |
728 | |
729 if (event_provider_mask_ & CHROME_TESTS_LOG_PROVIDER) | |
730 logging::LogEventProvider::Uninitialize(); | |
731 } | |
732 | |
733 void FileLogger::Initialize() { | |
734 Initialize(kAllEventProviders); | |
735 } | |
736 | |
737 void FileLogger::Initialize(uint32 event_provider_mask) { | |
738 DCHECK(event_provider_mask_ == 0 && !added_chrome_etw_variable_); | |
739 CHECK(!g_is_initialized); | |
740 | |
741 ConfigureChromeEtwLogging(); | |
742 | |
743 // Stop a previous session that wasn't shut down properly. | |
744 base::win::EtwTraceProperties ignore; | |
745 HRESULT hr = base::win::EtwTraceController::Stop(kChromeTestSession, | |
746 &ignore); | |
747 LOG_IF(ERROR, FAILED(hr) && | |
748 hr != HRESULT_FROM_WIN32(ERROR_WMI_INSTANCE_NOT_FOUND)) | |
749 << "Failed to stop a previous trace session; hr=" << std::hex << hr; | |
750 | |
751 event_provider_mask_ = event_provider_mask; | |
752 | |
753 g_is_initialized = true; | |
754 } | |
755 | |
756 void FileLogger::Uninitialize() { | |
757 if (is_logging()) { | |
758 LOG(ERROR) | |
759 << __FUNCTION__ << " don't forget to call FileLogger::StopLogging()"; | |
760 StopLogging(); | |
761 } | |
762 | |
763 event_provider_mask_ = 0; | |
764 | |
765 RevertChromeEtwLogging(); | |
766 | |
767 g_is_initialized = false; | |
768 } | |
769 | |
770 bool FileLogger::StartLogging(const FilePath& log_file) { | |
771 HRESULT hr = | |
772 controller_.StartFileSession(kChromeTestSession, | |
773 log_file.value().c_str(), false); | |
774 if (SUCCEEDED(hr)) { | |
775 EnableProviders(); | |
776 } else { | |
777 if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) { | |
778 LOG(WARNING) << "Access denied while trying to start trace session. " | |
779 "This is expected when not running as an administrator."; | |
780 } else { | |
781 LOG(ERROR) << "Failed to start trace session to file " << log_file.value() | |
782 << "; hr=" << std::hex << hr; | |
783 } | |
784 return false; | |
785 } | |
786 return true; | |
787 } | |
788 | |
789 void FileLogger::StopLogging() { | |
790 HRESULT hr = S_OK; | |
791 | |
792 DisableProviders(); | |
793 | |
794 hr = controller_.Flush(NULL); | |
795 LOG_IF(ERROR, FAILED(hr)) | |
796 << "Failed to flush events; hr=" << std::hex << hr; | |
797 hr = controller_.Stop(NULL); | |
798 LOG_IF(ERROR, FAILED(hr)) | |
799 << "Failed to stop ETW session; hr=" << std::hex << hr; | |
800 } | |
801 | |
802 // static | |
803 void FileLogger::DumpLogFile(const FilePath& log_file, std::ostream& out) { | |
804 LogDumper::DumpLog(log_file, out); | |
805 } | |
OLD | NEW |