blob: 0bf806b0b3940a93e44f98fd0306b296e4445a7f [file] [log] [blame]
// Copyright 2013 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.
/**
* This class provides data access interface for dump file profiler.
* @constructor
*/
var Profiler = function(jsonData) {
this.jsonData_ = jsonData;
// Initialize template with templates information.
// TODO(junjianx): Make file path an argument.
this.template_ = jsonData.templates['l2'];
// Initialize selected category, and nothing selected at first.
this.selected_ = null;
// Trigger event.
this.callbacks_ = {};
};
/**
* Mimic Eventemitter in node. Add new listener for event.
* @param {string} event
* @param {Function} callback
*/
Profiler.prototype.addListener = function(event, callback) {
if (!this.callbacks_[event])
this.callbacks_[event] = $.Callbacks();
this.callbacks_[event].add(callback);
};
/**
* This function will emit the event.
* @param {string} event
*/
Profiler.prototype.emit = function(event) {
// Listeners should be able to receive arbitrary number of parameters.
var eventArguments = Array.prototype.slice.call(arguments, 1);
if (this.callbacks_[event])
this.callbacks_[event].fire.apply(this, eventArguments);
};
/**
* Remove listener from event.
* @param {string} event
* @param {Function} callback
*/
Profiler.prototype.removeListener = function(event, callback) {
if (this.callbacks_[event])
this.callbacks_[event].remove(callback);
};
/**
* Calcualte initial tree according default template.
*/
Profiler.prototype.initialize = function() {
this.tree_ = this.parseTemplate_();
this.emit('changed', this.tree_);
};
Profiler.prototype.accumulate_ = function(
template, snapshot, worldUnits, localUnits, nodePath, nodeName) {
var self = this;
var totalMemory = 0;
var worldName = template[0];
var breakdownName = template[1];
var categories = snapshot.worlds[worldName].breakdown[breakdownName];
// Make deep copy of localUnits.
var remainderUnits = localUnits.slice(0);
var node = {
name: nodeName || worldName + '-' + breakdownName,
nodePath: nodePath.slice(0),
children: []
};
Object.keys(categories).forEach(function(categoryName) {
var category = categories[categoryName];
if (category['hidden'] === true)
return;
// Accumulate categories.
var matchedUnits = intersection(category.units, localUnits);
var memory = matchedUnits.reduce(function(previous, current) {
return previous + worldUnits[worldName][current];
}, 0);
totalMemory += memory;
remainderUnits = difference(remainderUnits, matchedUnits);
// Handle subs options if exists.
var child = null;
if (!(categoryName in template[2])) {
// Calculate child for current category.
child = {
name: categoryName,
memory: memory
};
if ('subs' in category && category.subs.length)
node.subs = category.subs;
node.children.push(child);
} else {
// Calculate child recursively.
var subTemplate = template[2][categoryName];
var subWorldName = subTemplate[0];
var subNodePath = nodePath.slice(0).concat([categoryName, 2]);
var returnValue = null;
if (subWorldName === worldName) {
// If subs is in the same world, units should be filtered.
returnValue = self.accumulate_(subTemplate, snapshot, worldUnits,
matchedUnits, subNodePath, categoryName);
node.children.push(returnValue.node);
if (!returnValue.remainderUnits.length)
return;
var remainMemory =
returnValue.remainderUnits.reduce(function(previous, current) {
return previous + worldUnits[subWorldName][current];
}, 0);
node.children.push({
name: categoryName + '-remaining',
memory: remainMemory
});
} else {
// If subs is in different world, use all units in that world.
var subLocalUnits = Object.keys(worldUnits[subWorldName]);
subLocalUnits = subLocalUnits.map(function(unitID) {
return parseInt(unitID, 10);
});
returnValue = self.accumulate_(subTemplate, snapshot, worldUnits,
subLocalUnits, subNodePath, categoryName);
node.children.push(returnValue.node);
if (memory > returnValue.totalMemory) {
node.children.push({
name: categoryName + '-remaining',
memory: memory - returnValue.totalMemory
});
}
}
}
});
return {
node: node,
totalMemory: totalMemory,
remainderUnits: remainderUnits
};
};
Profiler.prototype.parseTemplate_ = function() {
var self = this;
return self.jsonData_.snapshots.map(function(snapshot) {
var worldUnits = {};
for (var worldName in snapshot.worlds) {
worldUnits[worldName] = {};
var units = snapshot.worlds[worldName].units;
for (var unitID in units)
worldUnits[worldName][unitID] = units[unitID][0];
}
var localUnits = Object.keys(worldUnits[self.template_[0]]);
localUnits = localUnits.map(function(unitID) {
return parseInt(unitID, 10);
});
var returnValue =
self.accumulate_(self.template_, snapshot, worldUnits, localUnits, [2]);
return returnValue.node;
});
};