3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="import" href="/tracing/importer/etw/eventtrace_parser.html">
9 <link rel="import" href="/tracing/importer/etw/process_parser.html">
10 <link rel="import" href="/tracing/importer/etw/thread_parser.html">
11 <link rel="import" href="/tvcm/base64.html">
12 <link rel="import" href="/tracing/trace_model.html">
13 <link rel="import" href="/tracing/importer/importer.html">
17 * @fileoverview Imports JSON file with the raw payloads from a Windows event
18 * trace into the Tracemodel. This format is outputted by Chrome running
19 * on a Windows system.
21 * This importer assumes the events arrived as a JSON file and the payloads are
22 * undecoded sequence of bytes in hex format string. The unit tests provide
23 * examples of the trace format.
25 * The format of the system trace is
28 * content: [ <events> ]
31 * where the <events> are dictionary values with fields.
34 * guid: "1234-...", // The unique GUID for the event.
35 * op: 12, // The opcode of the event.
36 * ver: 1, // The encoding version of the event.
37 * cpu: 0, // The cpu id on which the event was captured.
38 * ts: 1092, // The thread id on which the event was captured.
39 * payload: "aaaa" // A base64 encoded string of the raw payload.
42 * The payload is an undecoded version of the raw event sent by ETW.
43 * This importer uses specific parsers to decode recognized events.
44 * A parser need to register the recognized event by calling
45 * registerEventHandler(guid, opcode, handler). The parser is responsible to
46 * decode the payload and update the TraceModel.
48 * The payload formats are described there:
49 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa364085(v=vs.85).aspx
54 tvcm.exportTo('tracing.importer', function() {
55 var Importer = tracing.importer.Importer;
57 // GUID and opcode of a Thread DCStart event, as defined at the link above.
58 var kThreadGuid = '3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';
59 var kThreadDCStartOpcode = 3;
62 * Represents the raw bytes payload decoder.
66 this.payload_ = new DataView(new ArrayBuffer(256));
70 __proto__: Object.prototype,
72 reset: function(base64_payload) {
73 var decoded_size = tvcm.Base64.getDecodedBufferLength(base64_payload);
74 if (decoded_size > this.payload_.byteLength)
75 this.payload_ = new DataView(new ArrayBuffer(decoded_size));
77 tvcm.Base64.DecodeToTypedArray(base64_payload, this.payload_);
81 skip: function(length) {
82 this.position_ += length;
85 decodeUInt8: function() {
86 var result = this.payload_.getUint8(this.position_, true);
91 decodeUInt16: function() {
92 var result = this.payload_.getUint16(this.position_, true);
97 decodeUInt32: function() {
98 var result = this.payload_.getUint32(this.position_, true);
103 decodeUInt64ToString: function() {
104 // Javascript isn't able to manage 64-bit numeric values.
105 var low = this.decodeUInt32();
106 var high = this.decodeUInt32();
107 var low_str = ('0000000' + low.toString(16)).substr(-8);
108 var high_str = ('0000000' + high.toString(16)).substr(-8);
109 var result = high_str + low_str;
113 decodeInt8: function() {
114 var result = this.payload_.getInt8(this.position_, true);
119 decodeInt16: function() {
120 var result = this.payload_.getInt16(this.position_, true);
125 decodeInt32: function() {
126 var result = this.payload_.getInt32(this.position_, true);
131 decodeInt64ToString: function() {
132 // Javascript isn't able to manage 64-bit numeric values.
133 // Fallback to unsigned 64-bit hexa value.
134 return this.decodeUInt64ToString();
137 decodeUInteger: function(is64) {
139 return this.decodeUInt64ToString();
140 return this.decodeUInt32();
143 decodeString: function() {
146 var c = this.decodeUInt8();
149 str = str + String.fromCharCode(c);
153 decodeW16String: function() {
156 var c = this.decodeUInt16();
159 str = str + String.fromCharCode(c);
163 decodeFixedW16String: function(length) {
164 var old_position = this.position_;
166 for (var i = 0; i < length; i++) {
167 var c = this.decodeUInt16();
170 str = str + String.fromCharCode(c);
173 // Move the position after the fixed buffer (i.e. wchar[length]).
174 this.position_ = old_position + 2 * length;
178 decodeBytes: function(length) {
180 for (var i = 0; i < length; ++i) {
181 var c = this.decodeUInt8();
187 decodeSID: function(is64) {
188 // Decode the TOKEN_USER structure.
189 var pSid = this.decodeUInteger(is64);
190 var attributes = this.decodeUInt32();
196 // Decode the SID structure.
197 var revision = this.decodeUInt8();
198 var subAuthorityCount = this.decodeUInt8();
203 throw 'Invalid SID revision: could not decode the SID structure.';
205 var sid = this.decodeBytes(4 * subAuthorityCount);
209 attributes: attributes,
214 decodeSystemTime: function() {
215 // Decode the SystemTime structure.
216 var wYear = this.decodeInt16();
217 var wMonth = this.decodeInt16();
218 var wDayOfWeek = this.decodeInt16();
219 var wDay = this.decodeInt16();
220 var wHour = this.decodeInt16();
221 var wMinute = this.decodeInt16();
222 var wSecond = this.decodeInt16();
223 var wMilliseconds = this.decodeInt16();
227 wDayOfWeek: wDayOfWeek,
232 wMilliseconds: wMilliseconds
236 decodeTimeZoneInformation: function() {
237 // Decode the TimeZoneInformation structure.
238 var bias = this.decodeUInt32();
239 var standardName = this.decodeFixedW16String(32);
240 var standardDate = this.decodeSystemTime();
241 var standardBias = this.decodeUInt32();
242 var daylightName = this.decodeFixedW16String(32);
243 var daylightDate = this.decodeSystemTime();
244 var daylightBias = this.decodeUInt32();
247 standardName: standardName,
248 standardDate: standardDate,
249 standardBias: standardBias,
250 daylightName: daylightName,
251 daylightDate: daylightDate,
252 daylightBias: daylightBias
259 * Imports Windows ETW kernel events into a specified model.
262 function EtwImporter(model, events) {
263 this.importPriority = 3;
265 this.events_ = events;
267 this.decoder_ = new Decoder();
269 this.walltime_ = undefined;
270 this.ticks_ = undefined;
271 this.is64bit_ = undefined;
273 // A map of tids to their process pid. On Windows, the tid is global to
274 // the system and doesn't need to belong to a process. As many events
275 // only provide tid, this map allows to retrieve the parent process.
276 this.tidsToPid_ = {};
278 // Instantiate the parsers; this will register handlers for known events.
279 var constructors = tracing.importer.etw.Parser.getSubtypeConstructors();
280 for (var i = 0; i < constructors.length; ++i) {
281 var parser = constructors[i];
282 this.parsers_.push(new parser(this));
287 * Guesses whether the provided events is from a Windows ETW trace.
288 * The object must has a property named 'name' with the value 'ETW' and
289 * a property 'content' with all the undecoded events.
291 * @return {boolean} True when events is a Windows ETW array.
293 EtwImporter.canImport = function(events) {
294 if (!events.hasOwnProperty('name') ||
295 !events.hasOwnProperty('content') ||
296 events.name !== 'ETW') {
303 EtwImporter.prototype = {
304 __proto__: Importer.prototype,
310 createThreadIfNeeded: function(pid, tid) {
311 this.tidsToPid_[tid] = pid;
314 removeThreadIfPresent: function(tid) {
315 this.tidsToPid_[tid] = undefined;
318 getPidFromWindowsTid: function(tid) {
321 var pid = this.tidsToPid_[tid];
322 if (pid == undefined) {
323 // Kernel threads are not defined.
329 getThreadFromWindowsTid: function(tid) {
330 var pid = this.getPidFromWindowsTid(tid);
331 var process = this.model_.getProcess(pid);
334 return process.getThread(tid);
338 * Retrieve the Cpu for a given cpuNumber.
339 * @return {Cpu} A Cpu corresponding to the given cpuNumber.
341 getOrCreateCpu: function(cpuNumber) {
342 var cpu = this.model_.kernel.getOrCreateCpu(cpuNumber);
347 * Imports the data in this.events_ into this.model_.
349 importEvents: function(isSecondaryImport) {
350 this.events_.content.forEach(this.parseInfo.bind(this));
352 if (this.walltime_ == undefined || this.ticks_ == undefined)
353 throw Error('Cannot find clock sync information in the system trace.');
355 if (this.is64bit_ == undefined)
356 throw Error('Cannot determine pointer size of the system trace.');
358 this.events_.content.forEach(this.parseEvent.bind(this));
361 importTimestamp: function(timestamp) {
362 var ts = parseInt(timestamp, 16);
363 return (ts - this.walltime_ + this.ticks_) / 1000.;
366 parseInfo: function(event) {
367 // Retrieve clock sync information.
368 if (event.hasOwnProperty('guid') &&
369 event.hasOwnProperty('walltime') &&
370 event.hasOwnProperty('tick') &&
371 event.guid === 'ClockSync') {
372 this.walltime_ = parseInt(event.walltime, 16);
373 this.ticks_ = parseInt(event.tick, 16);
376 // Retrieve pointer size information from a Thread.DCStart event.
377 if (this.is64bit_ == undefined &&
378 event.hasOwnProperty('guid') &&
379 event.hasOwnProperty('op') &&
380 event.hasOwnProperty('ver') &&
381 event.hasOwnProperty('payload') &&
382 event.guid === kThreadGuid &&
383 event.op == kThreadDCStartOpcode) {
384 var decoded_size = tvcm.Base64.getDecodedBufferLength(event.payload);
386 if (event.ver == 1) {
387 if (decoded_size >= 52)
388 this.is64bit_ = true;
390 this.is64bit_ = false;
391 } else if (event.ver == 2) {
392 if (decoded_size >= 64)
393 this.is64bit_ = true;
395 this.is64bit_ = false;
396 } else if (event.ver == 3) {
397 if (decoded_size >= 60)
398 this.is64bit_ = true;
400 this.is64bit_ = false;
407 parseEvent: function(event) {
408 if (!event.hasOwnProperty('guid') ||
409 !event.hasOwnProperty('op') ||
410 !event.hasOwnProperty('ver') ||
411 !event.hasOwnProperty('cpu') ||
412 !event.hasOwnProperty('ts') ||
413 !event.hasOwnProperty('payload')) {
417 var timestamp = this.importTimestamp(event.ts);
419 // Create the event header.
425 timestamp: timestamp,
429 // Set the payload to decode.
430 var decoder = this.decoder_;
431 decoder.reset(event.payload);
433 // Retrieve the handler to decode the payload.
434 var handler = this.getEventHandler(header.guid, header.opcode);
438 if (!handler(header, decoder)) {
439 this.model_.importWarning({
441 message: 'Malformed ' + header.guid + ' event (' + text + ')'
450 * Registers a windows ETW event handler used by parseEvent().
452 registerEventHandler: function(guid, opcode, handler) {
453 if (this.handlers_[guid] == undefined)
454 this.handlers_[guid] = [];
455 this.handlers_[guid][opcode] = handler;
459 * Retrieves a registered event handler.
461 getEventHandler: function(guid, opcode) {
462 if (this.handlers_[guid] == undefined)
464 return this.handlers_[guid][opcode];
469 // Register the EtwImporter to the Importer.
470 tracing.TraceModel.registerImporter(EtwImporter);
473 EtwImporter: EtwImporter