1<!DOCTYPE html>
2<!--
3Copyright (c) 2015 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/extras/importer/jszip.html">
9<link rel="import" href="/tracing/importer/importer.html">
10<link rel="import" href="/tracing/model/model.html">
11
12<script>
13/**
14 * @fileoverview Blah.
15 */
16'use strict';
17
18tr.exportTo('tr.e.importer.ddms', function() {
19  var kPid = 0;
20  var kCategory = 'java';
21  var kMethodLutEndMarker = '\n*end\n';
22  var kThreadsStart = '\n*threads\n';
23  var kMethodsStart = '\n*methods\n';
24
25  var kTraceMethodEnter = 0x00;       // method entry
26  var kTraceMethodExit = 0x01;        // method exit
27  var kTraceUnroll = 0x02;            // method exited by exception unrolling
28  // 0x03 currently unused
29  var kTraceMethodActionMask = 0x03;  // two bits
30
31  var kTraceHeaderLength = 32;
32  var kTraceMagicValue = 0x574f4c53;
33  var kTraceVersionSingleClock = 2;
34  var kTraceVersionDualClock = 3;
35  var kTraceRecordSizeSingleClock = 10;  // using v2
36  var kTraceRecordSizeDualClock = 14;  // using v3 with two timestamps
37
38  function Reader(string_payload) {
39    this.position_ = 0;
40    this.data_ = JSZip.utils.transformTo('uint8array', string_payload);
41  }
42
43  Reader.prototype = {
44    __proto__: Object.prototype,
45
46    uint8: function() {
47      var result = this.data_[this.position_];
48      this.position_ += 1;
49      return result;
50    },
51
52    uint16: function() {
53      var result = 0;
54      result += this.uint8();
55      result += this.uint8() << 8;
56      return result;
57    },
58
59    uint32: function() {
60      var result = 0;
61      result += this.uint8();
62      result += this.uint8() << 8;
63      result += this.uint8() << 16;
64      result += this.uint8() << 24;
65      return result;
66    },
67
68    uint64: function() {
69      // Javascript isn't able to manage 64-bit numeric values.
70      var low = this.uint32();
71      var high = this.uint32();
72      var low_str = ('0000000' + low.toString(16)).substr(-8);
73      var high_str = ('0000000' + high.toString(16)).substr(-8);
74      var result = high_str + low_str;
75      return result;
76    },
77
78    seekTo: function(position) {
79      this.position_ = position;
80    },
81
82    hasMore: function() {
83      return this.position_ < this.data_.length;
84    }
85  };
86
87  /**
88   * Imports DDMS method tracing events into a specified model.
89   * @constructor
90   */
91  function DdmsImporter(model, data) {
92    this.importPriority = 3;
93    this.model_ = model;
94    this.data_ = data;
95  }
96
97  /**
98   * Guesses whether the provided events is from a DDMS method trace.
99   * @return {boolean} True when events is a DDMS method trace.
100   */
101  DdmsImporter.canImport = function(data) {
102    if (typeof(data) === 'string' || data instanceof String) {
103      var header = data.slice(0, 1000);
104      return header.startsWith('*version\n') &&
105        header.indexOf('\nvm=') >= 0 &&
106        header.indexOf(kThreadsStart) >= 0;
107    }
108    /* key bit */
109    return false;
110  };
111
112  DdmsImporter.prototype = {
113    __proto__: tr.importer.Importer.prototype,
114
115    get importerName() {
116      return 'DdmsImporter';
117    },
118
119    get model() {
120      return this.model_;
121    },
122
123    /**
124     * Imports the data in this.data_ into this.model_.
125     */
126    importEvents: function() {
127      var divider = this.data_.indexOf(kMethodLutEndMarker) +
128          kMethodLutEndMarker.length;
129      this.metadata_ = this.data_.slice(0, divider);
130      this.methods_ = {};
131      this.parseThreads();
132      this.parseMethods();
133
134      var traceReader = new Reader(this.data_.slice(divider));
135      var magic = traceReader.uint32();
136      if (magic != kTraceMagicValue) {
137        throw Error('Failed to match magic value');
138      }
139      this.version_ = traceReader.uint16();
140      if (this.version_ != kTraceVersionDualClock) {
141        throw Error('Unknown version');
142      }
143      var dataOffest = traceReader.uint16();
144      var startDateTime = traceReader.uint64();
145      var recordSize = traceReader.uint16();
146
147      traceReader.seekTo(dataOffest);
148
149      while (traceReader.hasMore()) {
150        this.parseTraceEntry(traceReader);
151      }
152    },
153
154    parseTraceEntry: function(reader) {
155      var tid = reader.uint16();
156      var methodPacked = reader.uint32();
157      var cpuSinceStart = reader.uint32();
158      var wallClockSinceStart = reader.uint32();
159      var method = methodPacked & ~kTraceMethodActionMask;
160      var action = methodPacked & kTraceMethodActionMask;
161      var thread = this.getTid(tid);
162      method = this.getMethodName(method);
163      if (action == kTraceMethodEnter) {
164        thread.sliceGroup.beginSlice(kCategory, method, wallClockSinceStart,
165            undefined, cpuSinceStart);
166      } else if (thread.sliceGroup.openSliceCount) {
167        thread.sliceGroup.endSlice(wallClockSinceStart, cpuSinceStart);
168      }
169    },
170
171    parseThreads: function() {
172      var threads = this.metadata_.slice(this.metadata_.indexOf(kThreadsStart) +
173          kThreadsStart.length);
174      threads = threads.slice(0, threads.indexOf('\n*'));
175      threads = threads.split('\n');
176      threads.forEach(this.parseThread.bind(this));
177    },
178
179    parseThread: function(thread_line) {
180      var tid = thread_line.slice(0, thread_line.indexOf('\t'));
181      var thread = this.getTid(parseInt(tid));
182      thread.name = thread_line.slice(thread_line.indexOf('\t') + 1);
183    },
184
185    getTid: function(tid) {
186      return this.model_.getOrCreateProcess(kPid)
187        .getOrCreateThread(tid);
188    },
189
190    parseMethods: function() {
191      var methods = this.metadata_.slice(this.metadata_.indexOf(kMethodsStart) +
192          kMethodsStart.length);
193      methods = methods.slice(0, methods.indexOf('\n*'));
194      methods = methods.split('\n');
195      methods.forEach(this.parseMethod.bind(this));
196    },
197
198    parseMethod: function(method_line) {
199      var data = method_line.split('\t');
200      var methodId = parseInt(data[0]);
201      var methodName = data[1] + '.' + data[2] + data[3];
202      this.addMethod(methodId, methodName);
203    },
204
205    addMethod: function(methodId, methodName) {
206      this.methods_[methodId] = methodName;
207    },
208
209    getMethodName: function(methodId) {
210      return this.methods_[methodId];
211    }
212  };
213
214  // Register the DdmsImporter to the Importer.
215  tr.importer.Importer.register(DdmsImporter);
216
217  return {
218    DdmsImporter: DdmsImporter
219  };
220});
221</script>
222