Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / importer / etw_importer.html
1 <!DOCTYPE html>
2 <!--
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.
6 -->
7
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="/tracing/trace_model.html">
12 <link rel="import" href="/tracing/importer/importer.html">
13 <link rel="import" href="/base/base64.html">
14
15 <script>
16 /**
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.
20  *
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.
24  *
25  * The format of the system trace is
26  *     {
27  *       name: 'ETW',
28  *       content: [ <events> ]
29  *     }
30   *
31  * where the <events> are dictionary values with fields.
32  *
33  *     {
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.
40  *     }
41  *
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.
47  *
48  * The payload formats are described there:
49  *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa364085(v=vs.85).aspx
50  *
51  */
52 'use strict';
53
54 tv.exportTo('tracing.importer', function() {
55   var Importer = tracing.importer.Importer;
56
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;
60
61   /**
62    * Represents the raw bytes payload decoder.
63    * @constructor
64    */
65   function Decoder() {
66     this.payload_ = new DataView(new ArrayBuffer(256));
67   };
68
69   Decoder.prototype = {
70     __proto__: Object.prototype,
71
72     reset: function(base64_payload) {
73       var decoded_size = tv.Base64.getDecodedBufferLength(base64_payload);
74       if (decoded_size > this.payload_.byteLength)
75         this.payload_ = new DataView(new ArrayBuffer(decoded_size));
76
77       tv.Base64.DecodeToTypedArray(base64_payload, this.payload_);
78       this.position_ = 0;
79     },
80
81     skip: function(length) {
82       this.position_ += length;
83     },
84
85     decodeUInt8: function() {
86       var result = this.payload_.getUint8(this.position_, true);
87       this.position_ += 1;
88       return result;
89     },
90
91     decodeUInt16: function() {
92       var result = this.payload_.getUint16(this.position_, true);
93       this.position_ += 2;
94       return result;
95     },
96
97     decodeUInt32: function() {
98       var result = this.payload_.getUint32(this.position_, true);
99       this.position_ += 4;
100       return result;
101     },
102
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;
110       return result;
111     },
112
113     decodeInt8: function() {
114       var result = this.payload_.getInt8(this.position_, true);
115       this.position_ += 1;
116       return result;
117     },
118
119     decodeInt16: function() {
120       var result = this.payload_.getInt16(this.position_, true);
121       this.position_ += 2;
122       return result;
123     },
124
125     decodeInt32: function() {
126       var result = this.payload_.getInt32(this.position_, true);
127       this.position_ += 4;
128       return result;
129     },
130
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();
135     },
136
137     decodeUInteger: function(is64) {
138       if (is64)
139         return this.decodeUInt64ToString();
140       return this.decodeUInt32();
141     },
142
143     decodeString: function() {
144       var str = '';
145       while (true) {
146         var c = this.decodeUInt8();
147         if (!c)
148           return str;
149         str = str + String.fromCharCode(c);
150       }
151     },
152
153     decodeW16String: function() {
154       var str = '';
155       while (true) {
156         var c = this.decodeUInt16();
157         if (!c)
158           return str;
159         str = str + String.fromCharCode(c);
160       }
161     },
162
163     decodeFixedW16String: function(length) {
164       var old_position = this.position_;
165       var str = '';
166       for (var i = 0; i < length; i++) {
167         var c = this.decodeUInt16();
168         if (!c)
169           break;
170         str = str + String.fromCharCode(c);
171       }
172
173       // Move the position after the fixed buffer (i.e. wchar[length]).
174       this.position_ = old_position + 2 * length;
175       return str;
176     },
177
178     decodeBytes: function(length) {
179       var bytes = [];
180       for (var i = 0; i < length; ++i) {
181         var c = this.decodeUInt8();
182         bytes.push(c);
183       }
184       return bytes;
185     },
186
187     decodeSID: function(is64) {
188       // Decode the TOKEN_USER structure.
189       var pSid = this.decodeUInteger(is64);
190       var attributes = this.decodeUInt32();
191
192       // Skip padding.
193       if (is64)
194         this.decodeUInt32();
195
196       // Decode the SID structure.
197       var revision = this.decodeUInt8();
198       var subAuthorityCount = this.decodeUInt8();
199       this.decodeUInt16();
200       this.decodeUInt32();
201
202       if (revision != 1)
203         throw 'Invalid SID revision: could not decode the SID structure.';
204
205       var sid = this.decodeBytes(4 * subAuthorityCount);
206
207       return {
208         pSid: pSid,
209         attributes: attributes,
210         sid: sid
211       };
212     },
213
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();
224       return {
225         wYear: wYear,
226         wMonth: wMonth,
227         wDayOfWeek: wDayOfWeek,
228         wDay: wDay,
229         wHour: wHour,
230         wMinute: wMinute,
231         wSecond: wSecond,
232         wMilliseconds: wMilliseconds
233       };
234     },
235
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();
245       return {
246         bias: bias,
247         standardName: standardName,
248         standardDate: standardDate,
249         standardBias: standardBias,
250         daylightName: daylightName,
251         daylightDate: daylightDate,
252         daylightBias: daylightBias
253       };
254     }
255
256   };
257
258   /**
259    * Imports Windows ETW kernel events into a specified model.
260    * @constructor
261    */
262   function EtwImporter(model, events) {
263     this.importPriority = 3;
264     this.model_ = model;
265     this.events_ = events;
266     this.handlers_ = {};
267     this.decoder_ = new Decoder();
268     this.parsers_ = [];
269     this.walltime_ = undefined;
270     this.ticks_ = undefined;
271     this.is64bit_ = undefined;
272
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_ = {};
277
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));
283     }
284   }
285
286   /**
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.
290    *
291    * @return {boolean} True when events is a Windows ETW array.
292    */
293   EtwImporter.canImport = function(events) {
294     if (!events.hasOwnProperty('name') ||
295         !events.hasOwnProperty('content') ||
296         events.name !== 'ETW') {
297       return false;
298     }
299
300     return true;
301   };
302
303   EtwImporter.prototype = {
304     __proto__: Importer.prototype,
305
306     get model() {
307       return this.model_;
308     },
309
310     createThreadIfNeeded: function(pid, tid) {
311       this.tidsToPid_[tid] = pid;
312     },
313
314     removeThreadIfPresent: function(tid) {
315       this.tidsToPid_[tid] = undefined;
316     },
317
318     getPidFromWindowsTid: function(tid) {
319       if (tid == 0)
320         return 0;
321       var pid = this.tidsToPid_[tid];
322       if (pid == undefined) {
323         // Kernel threads are not defined.
324         return 0;
325       }
326       return pid;
327     },
328
329     getThreadFromWindowsTid: function(tid) {
330       var pid = this.getPidFromWindowsTid(tid);
331       var process = this.model_.getProcess(pid);
332       if (!process)
333         return undefined;
334       return process.getThread(tid);
335     },
336
337     /*
338      * Retrieve the Cpu for a given cpuNumber.
339      * @return {Cpu} A Cpu corresponding to the given cpuNumber.
340      */
341     getOrCreateCpu: function(cpuNumber) {
342       var cpu = this.model_.kernel.getOrCreateCpu(cpuNumber);
343       return cpu;
344     },
345
346     /**
347      * Imports the data in this.events_ into this.model_.
348      */
349     importEvents: function(isSecondaryImport) {
350       this.events_.content.forEach(this.parseInfo.bind(this));
351
352       if (this.walltime_ == undefined || this.ticks_ == undefined)
353         throw Error('Cannot find clock sync information in the system trace.');
354
355       if (this.is64bit_ == undefined)
356         throw Error('Cannot determine pointer size of the system trace.');
357
358       this.events_.content.forEach(this.parseEvent.bind(this));
359     },
360
361     importTimestamp: function(timestamp) {
362       var ts = parseInt(timestamp, 16);
363       return (ts - this.walltime_ + this.ticks_) / 1000.;
364     },
365
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);
374       }
375
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 = tv.Base64.getDecodedBufferLength(event.payload);
385
386         if (event.ver == 1) {
387           if (decoded_size >= 52)
388             this.is64bit_ = true;
389           else
390             this.is64bit_ = false;
391         } else if (event.ver == 2) {
392           if (decoded_size >= 64)
393             this.is64bit_ = true;
394           else
395             this.is64bit_ = false;
396         } else if (event.ver == 3) {
397           if (decoded_size >= 60)
398             this.is64bit_ = true;
399           else
400             this.is64bit_ = false;
401         }
402       }
403
404       return true;
405     },
406
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')) {
414         return false;
415       }
416
417       var timestamp = this.importTimestamp(event.ts);
418
419       // Create the event header.
420       var header = {
421         guid: event.guid,
422         opcode: event.op,
423         version: event.ver,
424         cpu: event.cpu,
425         timestamp: timestamp,
426         is64: this.is64bit_
427       };
428
429       // Set the payload to decode.
430       var decoder = this.decoder_;
431       decoder.reset(event.payload);
432
433       // Retrieve the handler to decode the payload.
434       var handler = this.getEventHandler(header.guid, header.opcode);
435       if (!handler)
436         return false;
437
438       if (!handler(header, decoder)) {
439         this.model_.importWarning({
440           type: 'parse_error',
441           message: 'Malformed ' + header.guid + ' event (' + text + ')'
442         });
443         return false;
444       }
445
446       return true;
447     },
448
449     /**
450      * Registers a windows ETW event handler used by parseEvent().
451      */
452     registerEventHandler: function(guid, opcode, handler) {
453       if (this.handlers_[guid] == undefined)
454         this.handlers_[guid] = [];
455       this.handlers_[guid][opcode] = handler;
456     },
457
458     /**
459      * Retrieves a registered event handler.
460      */
461     getEventHandler: function(guid, opcode) {
462       if (this.handlers_[guid] == undefined)
463         return undefined;
464       return this.handlers_[guid][opcode];
465     }
466
467   };
468
469   // Register the EtwImporter to the Importer.
470   tracing.TraceModel.registerImporter(EtwImporter);
471
472   return {
473     EtwImporter: EtwImporter
474   };
475 });
476 </script>