blob: 637bd473345eff22fa290fb7623324eda139bdb4 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/extras/importer/jszip.html">
<link rel="import" href="/model/model.html">
<link rel="import" href="/importer/importer.html">
<script>
/**
* @fileoverview Blah.
*/
'use strict';
tr.exportTo('tr.e.importer.ddms', function() {
var Importer = tr.importer.Importer;
var kPid = 0;
var kCategory = 'java';
var kMethodLutEndMarker = '\n*end\n';
var kThreadsStart = '\n*threads\n';
var kMethodsStart = '\n*methods\n';
var kTraceMethodEnter = 0x00; // method entry
var kTraceMethodExit = 0x01; // method exit
var kTraceUnroll = 0x02; // method exited by exception unrolling
// 0x03 currently unused
var kTraceMethodActionMask = 0x03; // two bits
var kTraceHeaderLength = 32;
var kTraceMagicValue = 0x574f4c53;
var kTraceVersionSingleClock = 2;
var kTraceVersionDualClock = 3;
var kTraceRecordSizeSingleClock = 10; // using v2
var kTraceRecordSizeDualClock = 14; // using v3 with two timestamps
function Reader(string_payload) {
this.position_ = 0;
this.data_ = JSZip.utils.transformTo('uint8array', string_payload);
}
Reader.prototype = {
__proto__: Object.prototype,
uint8: function() {
var result = this.data_[this.position_];
this.position_ += 1;
return result;
},
uint16: function() {
var result = 0;
result += this.uint8();
result += this.uint8() << 8;
return result;
},
uint32: function() {
var result = 0;
result += this.uint8();
result += this.uint8() << 8;
result += this.uint8() << 16;
result += this.uint8() << 24;
return result;
},
uint64: function() {
// Javascript isn't able to manage 64-bit numeric values.
var low = this.uint32();
var high = this.uint32();
var low_str = ('0000000' + low.toString(16)).substr(-8);
var high_str = ('0000000' + high.toString(16)).substr(-8);
var result = high_str + low_str;
return result;
},
seekTo: function(position) {
this.position_ = position;
},
hasMore: function() {
return this.position_ < this.data_.length;
}
};
/**
* Imports DDMS method tracing events into a specified model.
* @constructor
*/
function DdmsImporter(model, data) {
this.importPriority = 3;
this.model_ = model;
this.data_ = data;
}
/**
* Guesses whether the provided events is from a DDMS method trace.
* @return {boolean} True when events is a DDMS method trace.
*/
DdmsImporter.canImport = function(data) {
if (typeof(data) === 'string' || data instanceof String) {
var header = data.slice(0, 1000);
return header.startsWith('*version\n') &&
header.indexOf('\nvm=') >= 0 &&
header.indexOf(kThreadsStart) >= 0;
}
/* key bit */
return false;
};
DdmsImporter.prototype = {
__proto__: Importer.prototype,
get model() {
return this.model_;
},
/**
* Imports the data in this.data_ into this.model_.
*/
importEvents: function(isSecondaryImport) {
var divider = this.data_.indexOf(kMethodLutEndMarker) +
kMethodLutEndMarker.length;
this.metadata_ = this.data_.slice(0, divider);
this.methods_ = {};
this.parseThreads();
this.parseMethods();
var traceReader = new Reader(this.data_.slice(divider));
var magic = traceReader.uint32();
if (magic != kTraceMagicValue) {
throw Error('Failed to match magic value');
}
this.version_ = traceReader.uint16();
if (this.version_ != kTraceVersionDualClock) {
throw Error('Unknown version');
}
var dataOffest = traceReader.uint16();
var startDateTime = traceReader.uint64();
var recordSize = traceReader.uint16();
traceReader.seekTo(dataOffest);
while (traceReader.hasMore()) {
this.parseTraceEntry(traceReader);
}
},
parseTraceEntry: function(reader) {
var tid = reader.uint16();
var methodPacked = reader.uint32();
var cpuSinceStart = reader.uint32();
var wallClockSinceStart = reader.uint32();
var method = methodPacked & ~kTraceMethodActionMask;
var action = methodPacked & kTraceMethodActionMask;
var thread = this.getTid(tid);
method = this.getMethodName(method);
if (action == kTraceMethodEnter) {
thread.sliceGroup.beginSlice(kCategory, method, wallClockSinceStart,
undefined, cpuSinceStart);
} else if (thread.sliceGroup.openSliceCount) {
thread.sliceGroup.endSlice(wallClockSinceStart, cpuSinceStart);
}
},
parseThreads: function() {
var threads = this.metadata_.slice(this.metadata_.indexOf(kThreadsStart) +
kThreadsStart.length);
threads = threads.slice(0, threads.indexOf('\n*'));
threads = threads.split('\n');
threads.forEach(this.parseThread.bind(this));
},
parseThread: function(thread_line) {
var tid = thread_line.slice(0, thread_line.indexOf('\t'));
var thread = this.getTid(parseInt(tid));
thread.name = thread_line.slice(thread_line.indexOf('\t') + 1);
},
getTid: function(tid) {
return this.model_.getOrCreateProcess(kPid)
.getOrCreateThread(tid);
},
parseMethods: function() {
var methods = this.metadata_.slice(this.metadata_.indexOf(kMethodsStart) +
kMethodsStart.length);
methods = methods.slice(0, methods.indexOf('\n*'));
methods = methods.split('\n');
methods.forEach(this.parseMethod.bind(this));
},
parseMethod: function(method_line) {
var data = method_line.split('\t');
var methodId = parseInt(data[0]);
var methodName = data[1] + '.' + data[2] + data[3];
this.addMethod(methodId, methodName);
},
addMethod: function(methodId, methodName) {
this.methods_[methodId] = methodName;
},
getMethodName: function(methodId) {
return this.methods_[methodId];
}
};
// Register the DdmsImporter to the Importer.
tr.importer.Importer.register(DdmsImporter);
return {
DdmsImporter: DdmsImporter
};
});
</script>