1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/color_scheme.html">
9<link rel="import" href="/tracing/extras/importer/v8/codemap.html">
10<link rel="import" href="/tracing/extras/importer/v8/log_reader.html">
11<link rel="import" href="/tracing/importer/importer.html">
12<link rel="import" href="/tracing/model/model.html">
13<link rel="import" href="/tracing/model/slice.html">
14
15<script>
16
17'use strict';
18
19/**
20 * @fileoverview V8LogImporter imports v8.log files into the provided model.
21 */
22tr.exportTo('tr.e.importer.v8', function() {
23  var ColorScheme = tr.b.ColorScheme;
24
25  function V8LogImporter(model, eventData) {
26    this.importPriority = 3;
27    this.model_ = model;
28
29    this.logData_ = eventData;
30
31    this.code_map_ = new tr.e.importer.v8.CodeMap();
32    this.v8_timer_thread_ = undefined;
33    this.v8_thread_ = undefined;
34
35    this.root_stack_frame_ = new tr.model.StackFrame(
36        undefined, 'v8-root-stack-frame', 'v8-root-stack-frame', 0);
37
38    // We reconstruct the stack timeline from ticks.
39    this.v8_stack_timeline_ = new Array();
40  }
41
42  var kV8BinarySuffixes = ['/d8', '/libv8.so'];
43
44
45  var TimerEventDefaultArgs = {
46    'V8.Execute': { pause: false, no_execution: false},
47    'V8.External': { pause: false, no_execution: true},
48    'V8.CompileFullCode': { pause: true, no_execution: true},
49    'V8.RecompileSynchronous': { pause: true, no_execution: true},
50    'V8.RecompileParallel': { pause: false, no_execution: false},
51    'V8.CompileEval': { pause: true, no_execution: true},
52    'V8.Parse': { pause: true, no_execution: true},
53    'V8.PreParse': { pause: true, no_execution: true},
54    'V8.ParseLazy': { pause: true, no_execution: true},
55    'V8.GCScavenger': { pause: true, no_execution: true},
56    'V8.GCCompactor': { pause: true, no_execution: true},
57    'V8.GCContext': { pause: true, no_execution: true}
58  };
59
60  /**
61   * @return {boolean} Whether obj is a V8 log string.
62   */
63  V8LogImporter.canImport = function(eventData) {
64    if (typeof(eventData) !== 'string' && !(eventData instanceof String))
65      return false;
66
67    return eventData.substring(0, 11) == 'v8-version,' ||
68           eventData.substring(0, 12) == 'timer-event,' ||
69           eventData.substring(0, 5) == 'tick,' ||
70           eventData.substring(0, 15) == 'shared-library,' ||
71           eventData.substring(0, 9) == 'profiler,' ||
72           eventData.substring(0, 14) == 'code-creation,';
73  };
74
75  V8LogImporter.prototype = {
76
77    __proto__: tr.importer.Importer.prototype,
78
79    get importerName() {
80      return 'V8LogImporter';
81    },
82
83    processTimerEvent_: function(name, start, length) {
84      var args = TimerEventDefaultArgs[name];
85      if (args === undefined) return;
86      start /= 1000;  // Convert to milliseconds.
87      length /= 1000;
88      var colorId = ColorScheme.getColorIdForGeneralPurposeString(name);
89      var slice = new tr.model.Slice('v8', name, colorId, start,
90          args, length);
91      this.v8_timer_thread_.sliceGroup.pushSlice(slice);
92    },
93
94    processTimerEventStart_: function(name, start) {
95      var args = TimerEventDefaultArgs[name];
96      if (args === undefined) return;
97      start /= 1000;  // Convert to milliseconds.
98      this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args);
99    },
100
101    processTimerEventEnd_: function(name, end) {
102      end /= 1000;  // Convert to milliseconds.
103      this.v8_timer_thread_.sliceGroup.endSlice(end);
104    },
105
106    processCodeCreateEvent_: function(type, kind, address, size, name) {
107      var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(size, name);
108      code_entry.kind = kind;
109      this.code_map_.addCode(address, code_entry);
110    },
111
112    processCodeMoveEvent_: function(from, to) {
113      this.code_map_.moveCode(from, to);
114    },
115
116    processCodeDeleteEvent_: function(address) {
117      this.code_map_.deleteCode(address);
118    },
119
120    processSharedLibrary_: function(name, start, end) {
121      var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(
122          end - start, name);
123      code_entry.kind = -3;  // External code kind.
124      for (var i = 0; i < kV8BinarySuffixes.length; i++) {
125        var suffix = kV8BinarySuffixes[i];
126        if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
127          code_entry.kind = -1;  // V8 runtime code kind.
128          break;
129        }
130      }
131      this.code_map_.addLibrary(start, code_entry);
132    },
133
134    findCodeKind_: function(kind) {
135      for (name in CodeKinds) {
136        if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
137          return CodeKinds[name];
138        }
139      }
140    },
141
142    processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) {
143      start /= 1000;
144
145      function findChildWithEntryID(stackFrame, entryID) {
146        for (var i = 0; i < stackFrame.children.length; i++) {
147          if (stackFrame.children[i].entryID == entryID)
148            return stackFrame.children[i];
149        }
150        return undefined;
151      }
152
153      var lastStackFrame;
154      if (stack && stack.length) {
155
156        lastStackFrame = this.root_stack_frame_;
157        // v8 log stacks are inverted, leaf first and the root at the end.
158        stack = stack.reverse();
159        for (var i = 0; i < stack.length; i++) {
160          if (!stack[i])
161            break;
162          var entry = this.code_map_.findEntry(stack[i]);
163
164          var entryID = entry ? entry.id : 'Unknown';
165          var childFrame = findChildWithEntryID(lastStackFrame, entryID);
166          if (childFrame === undefined) {
167            var entryName = entry ? entry.name : 'Unknown';
168            lastStackFrame = new tr.model.StackFrame(
169                lastStackFrame, 'v8sf-' + tr.b.GUID.allocate(), entryName,
170                ColorScheme.getColorIdForGeneralPurposeString(entryName));
171            lastStackFrame.entryID = entryID;
172            this.model_.addStackFrame(lastStackFrame);
173          } else {
174            lastStackFrame = childFrame;
175          }
176        }
177      } else {
178        var pcEntry = this.code_map_.findEntry(pc);
179        var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown');
180        if (this.model_.stackFrames[pcEntryID] === undefined) {
181          var pcEntryName = pcEntry ? pcEntry.name : 'Unknown';
182          lastStackFrame = new tr.model.StackFrame(
183              undefined, pcEntryID, pcEntryName,
184              ColorScheme.getColorIdForGeneralPurposeString(pcEntryName));
185          this.model_.addStackFrame(lastStackFrame);
186        }
187        lastStackFrame = this.model_.stackFrames[pcEntryID];
188      }
189
190      var sample = new tr.model.Sample(
191          undefined, this.v8_thread_, 'V8 PC',
192          start, lastStackFrame, 1);
193      this.model_.samples.push(sample);
194    },
195
196    processDistortion_: function(distortion_in_picoseconds) {
197      distortion_per_entry = distortion_in_picoseconds / 1000000;
198    },
199
200    processPlotRange_: function(start, end) {
201      xrange_start_override = start;
202      xrange_end_override = end;
203    },
204
205    processV8Version_: function(major, minor, build, patch, candidate) {
206      // do nothing.
207    },
208
209    /**
210     * Walks through the events_ list and outputs the structures discovered to
211     * model_.
212     */
213    importEvents: function() {
214      var logreader = new tr.e.importer.v8.LogReader(
215          { 'timer-event' : {
216            parsers: [null, parseInt, parseInt],
217            processor: this.processTimerEvent_.bind(this)
218          },
219          'shared-library': {
220            parsers: [null, parseInt, parseInt],
221            processor: this.processSharedLibrary_.bind(this)
222          },
223          'timer-event-start' : {
224            parsers: [null, parseInt],
225            processor: this.processTimerEventStart_.bind(this)
226          },
227          'timer-event-end' : {
228            parsers: [null, parseInt],
229            processor: this.processTimerEventEnd_.bind(this)
230          },
231          'code-creation': {
232            parsers: [null, parseInt, parseInt, parseInt, null],
233            processor: this.processCodeCreateEvent_.bind(this)
234          },
235          'code-move': {
236            parsers: [parseInt, parseInt],
237            processor: this.processCodeMoveEvent_.bind(this)
238          },
239          'code-delete': {
240            parsers: [parseInt],
241            processor: this.processCodeDeleteEvent_.bind(this)
242          },
243          'tick': {
244            parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'],
245            processor: this.processTickEvent_.bind(this)
246          },
247          'distortion': {
248            parsers: [parseInt],
249            processor: this.processDistortion_.bind(this)
250          },
251          'plot-range': {
252            parsers: [parseInt, parseInt],
253            processor: this.processPlotRange_.bind(this)
254          },
255          'v8-version': {
256            parsers: [parseInt, parseInt, parseInt, parseInt, parseInt],
257            processor: this.processV8Version_.bind(this)
258          }
259          });
260
261      this.v8_timer_thread_ =
262          this.model_.getOrCreateProcess(-32).getOrCreateThread(1);
263      this.v8_timer_thread_.name = 'V8 Timers';
264      this.v8_thread_ =
265          this.model_.getOrCreateProcess(-32).getOrCreateThread(2);
266      this.v8_thread_.name = 'V8';
267
268      var lines = this.logData_.split('\n');
269      for (var i = 0; i < lines.length; i++) {
270        logreader.processLogLine(lines[i]);
271      }
272
273      // The processing of samples adds all the root stack frame to
274      // this.root_stack_frame_. But, we don't want that stack frame in the real
275      // model. So get rid of it.
276      this.root_stack_frame_.removeAllChildren();
277
278      function addSlices(slices, thread) {
279        for (var i = 0; i < slices.length; i++) {
280          var duration = slices[i].end - slices[i].start;
281          var slice = new tr.model.Slice('v8', slices[i].name,
282              ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),
283              slices[i].start, {}, duration);
284          thread.sliceGroup.pushSlice(slice);
285          addSlices(slices[i].children, thread);
286        }
287      }
288      addSlices(this.v8_stack_timeline_, this.v8_thread_);
289    }
290  };
291
292  tr.importer.Importer.register(V8LogImporter);
293
294  return {
295    V8LogImporter: V8LogImporter
296  };
297});
298</script>
299