v8_log_importer.html revision 8d2b206a675ec20ea07100c35df34e65ee1e45e8
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    processTimerEvent_: function(name, start, length) {
80      var args = TimerEventDefaultArgs[name];
81      if (args === undefined) return;
82      start /= 1000;  // Convert to milliseconds.
83      length /= 1000;
84      var colorId = ColorScheme.getColorIdForGeneralPurposeString(name);
85      var slice = new tr.model.Slice('v8', name, colorId, start,
86          args, length);
87      this.v8_timer_thread_.sliceGroup.pushSlice(slice);
88    },
89
90    processTimerEventStart_: function(name, start) {
91      var args = TimerEventDefaultArgs[name];
92      if (args === undefined) return;
93      start /= 1000;  // Convert to milliseconds.
94      this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args);
95    },
96
97    processTimerEventEnd_: function(name, end) {
98      end /= 1000;  // Convert to milliseconds.
99      this.v8_timer_thread_.sliceGroup.endSlice(end);
100    },
101
102    processCodeCreateEvent_: function(type, kind, address, size, name) {
103      var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(size, name);
104      code_entry.kind = kind;
105      this.code_map_.addCode(address, code_entry);
106    },
107
108    processCodeMoveEvent_: function(from, to) {
109      this.code_map_.moveCode(from, to);
110    },
111
112    processCodeDeleteEvent_: function(address) {
113      this.code_map_.deleteCode(address);
114    },
115
116    processSharedLibrary_: function(name, start, end) {
117      var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(
118          end - start, name);
119      code_entry.kind = -3;  // External code kind.
120      for (var i = 0; i < kV8BinarySuffixes.length; i++) {
121        var suffix = kV8BinarySuffixes[i];
122        if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
123          code_entry.kind = -1;  // V8 runtime code kind.
124          break;
125        }
126      }
127      this.code_map_.addLibrary(start, code_entry);
128    },
129
130    findCodeKind_: function(kind) {
131      for (name in CodeKinds) {
132        if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
133          return CodeKinds[name];
134        }
135      }
136    },
137
138    processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) {
139      start /= 1000;
140
141      function findChildWithEntryID(stackFrame, entryID) {
142        for (var i = 0; i < stackFrame.children.length; i++) {
143          if (stackFrame.children[i].entryID == entryID)
144            return stackFrame.children[i];
145        }
146        return undefined;
147      }
148
149      var lastStackFrame;
150      if (stack && stack.length) {
151
152        lastStackFrame = this.root_stack_frame_;
153        // v8 log stacks are inverted, leaf first and the root at the end.
154        stack = stack.reverse();
155        for (var i = 0; i < stack.length; i++) {
156          if (!stack[i])
157            break;
158          var entry = this.code_map_.findEntry(stack[i]);
159
160          var entryID = entry ? entry.id : 'Unknown';
161          var childFrame = findChildWithEntryID(lastStackFrame, entryID);
162          if (childFrame === undefined) {
163            var entryName = entry ? entry.name : 'Unknown';
164            lastStackFrame = new tr.model.StackFrame(
165                lastStackFrame, 'v8sf-' + tr.b.GUID.allocate(), entryName,
166                ColorScheme.getColorIdForGeneralPurposeString(entryName));
167            lastStackFrame.entryID = entryID;
168            this.model_.addStackFrame(lastStackFrame);
169          } else {
170            lastStackFrame = childFrame;
171          }
172        }
173      } else {
174        var pcEntry = this.code_map_.findEntry(pc);
175        var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown');
176        if (this.model_.stackFrames[pcEntryID] === undefined) {
177          var pcEntryName = pcEntry ? pcEntry.name : 'Unknown';
178          lastStackFrame = new tr.model.StackFrame(
179              undefined, pcEntryID, pcEntryName,
180              ColorScheme.getColorIdForGeneralPurposeString(pcEntryName));
181          this.model_.addStackFrame(lastStackFrame);
182        }
183        lastStackFrame = this.model_.stackFrames[pcEntryID];
184      }
185
186      var sample = new tr.model.Sample(
187          undefined, this.v8_thread_, 'V8 PC',
188          start, lastStackFrame, 1);
189      this.model_.samples.push(sample);
190    },
191
192    processDistortion_: function(distortion_in_picoseconds) {
193      distortion_per_entry = distortion_in_picoseconds / 1000000;
194    },
195
196    processPlotRange_: function(start, end) {
197      xrange_start_override = start;
198      xrange_end_override = end;
199    },
200
201    processV8Version_: function(major, minor, build, patch, candidate) {
202      // do nothing.
203    },
204
205    /**
206     * Walks through the events_ list and outputs the structures discovered to
207     * model_.
208     */
209    importEvents: function() {
210      var logreader = new tr.e.importer.v8.LogReader(
211          { 'timer-event' : {
212            parsers: [null, parseInt, parseInt],
213            processor: this.processTimerEvent_.bind(this)
214          },
215          'shared-library': {
216            parsers: [null, parseInt, parseInt],
217            processor: this.processSharedLibrary_.bind(this)
218          },
219          'timer-event-start' : {
220            parsers: [null, parseInt],
221            processor: this.processTimerEventStart_.bind(this)
222          },
223          'timer-event-end' : {
224            parsers: [null, parseInt],
225            processor: this.processTimerEventEnd_.bind(this)
226          },
227          'code-creation': {
228            parsers: [null, parseInt, parseInt, parseInt, null],
229            processor: this.processCodeCreateEvent_.bind(this)
230          },
231          'code-move': {
232            parsers: [parseInt, parseInt],
233            processor: this.processCodeMoveEvent_.bind(this)
234          },
235          'code-delete': {
236            parsers: [parseInt],
237            processor: this.processCodeDeleteEvent_.bind(this)
238          },
239          'tick': {
240            parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'],
241            processor: this.processTickEvent_.bind(this)
242          },
243          'distortion': {
244            parsers: [parseInt],
245            processor: this.processDistortion_.bind(this)
246          },
247          'plot-range': {
248            parsers: [parseInt, parseInt],
249            processor: this.processPlotRange_.bind(this)
250          },
251          'v8-version': {
252            parsers: [parseInt, parseInt, parseInt, parseInt, parseInt],
253            processor: this.processV8Version_.bind(this)
254          }
255          });
256
257      this.v8_timer_thread_ =
258          this.model_.getOrCreateProcess(-32).getOrCreateThread(1);
259      this.v8_timer_thread_.name = 'V8 Timers';
260      this.v8_thread_ =
261          this.model_.getOrCreateProcess(-32).getOrCreateThread(2);
262      this.v8_thread_.name = 'V8';
263
264      var lines = this.logData_.split('\n');
265      for (var i = 0; i < lines.length; i++) {
266        logreader.processLogLine(lines[i]);
267      }
268
269      // The processing of samples adds all the root stack frame to
270      // this.root_stack_frame_. But, we don't want that stack frame in the real
271      // model. So get rid of it.
272      this.root_stack_frame_.removeAllChildren();
273
274      function addSlices(slices, thread) {
275        for (var i = 0; i < slices.length; i++) {
276          var duration = slices[i].end - slices[i].start;
277          var slice = new tr.model.Slice('v8', slices[i].name,
278              ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),
279              slices[i].start, {}, duration);
280          thread.sliceGroup.pushSlice(slice);
281          addSlices(slices[i].children, thread);
282        }
283      }
284      addSlices(this.v8_stack_timeline_, this.v8_thread_);
285    }
286  };
287
288  tr.importer.Importer.register(V8LogImporter);
289
290  return {
291    V8LogImporter: V8LogImporter
292  };
293});
294</script>
295