| <!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> |