blob: c0243d8976be91867124a51b66bbdfc095625412 [file] [log] [blame]
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
function LeaksParserImpl(didParseLeaksCallback) {
this._didParseLeaksCallback = didParseLeaksCallback;
this._profile = this._createNode("top level");
}
LeaksParserImpl.prototype = {
addLeaksFile: function(leaksText) {
this._incorporateLeaks(this._parseLeaks(leaksText));
this._didParseLeaksCallback(this._profile);
},
_parseLeaks: function(text) {
var leaks = [];
var currentSize = 0;
text.split("\n").forEach(function(line) {
var match = /^Leak:.*\ssize=(\d+)\s/.exec(line);
if (match) {
currentSize = parseInt(match[1], 10);
return;
}
if (!/^\s+Call stack:/.test(line))
return;
// The first frame is not really a frame at all ("Call stack: thread 0xNNNNN:"), so we omit it.
leaks.push({ size: currentSize, stack: line.split(" | ").slice(1).map(function(str) { return str.trim(); }) });
currentSize = 0;
});
return leaks;
},
_createNode: function(functionName) {
var url;
var lineNumber;
var match = /(.*) (\S+):(\d+)$/.exec(functionName);
if (match) {
functionName = match[1];
// FIXME: It would be nice to be able to link straight to the right line in Trac, but to
// do that we'd have to have some way of translating from filenames to Trac URLs.
// <http://webkit.org/b/72509>
url = match[2];
lineNumber = match[3];
}
return {
functionName: functionName,
selfTime: 0,
totalTime: 0,
averageTime: 0,
numberOfCalls: 0,
children: [],
childrenByName: {},
callUID: functionName,
url: url,
lineNumber: lineNumber,
};
},
// This function creates a fake "profile" from a set of leak stacks. "selfTime" is the number of
// stacks in which this function was at the top (in theory, only functions like malloc should have a
// non-zero selfTime). "totalTime" is the number of stacks which contain this function (and thus is
// the number of leaks that occurred in or beneath this function).
// FIXME: This is expensive! Can we parallelize it?
_incorporateLeaks: function(leaks) {
var self = this;
leaks.forEach(function(leak) {
leak.stack.reduce(function(node, frame, index, array) {
var childNode;
if (frame in node.childrenByName)
childNode = node.childrenByName[frame];
else {
childNode = self._createNode(frame);
childNode.head = self._profile;
node.childrenByName[frame] = childNode;
node.children.push(childNode);
}
if (index === array.length - 1)
childNode.selfTime += leak.size;
childNode.totalTime += leak.size;
++childNode.numberOfCalls;
return childNode;
}, self._profile);
});
self._profile.totalTime = self._profile.children.reduce(function(sum, child) { return sum + child.totalTime; }, 0);
},
};