/* * Copyright (C) 2016 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. */ WebInspector.CallingContextTree = class CallingContextTree extends WebInspector.Object { constructor(type) { super(); this._type = type || WebInspector.CallingContextTree.Type.TopDown; this.reset(); } // Public get type() { return this._type; } get totalNumberOfSamples() { return this._totalNumberOfSamples; } reset() { this._root = new WebInspector.CallingContextTreeNode(-1, -1, -1, "", null); this._totalNumberOfSamples = 0; } totalDurationInTimeRange(startTime, endTime) { return this._root.filteredTimestampsAndDuration(startTime, endTime).duration; } updateTreeWithStackTrace({timestamp, stackFrames}, duration) { this._totalNumberOfSamples++; let node = this._root; node.addTimestampAndExpressionLocation(timestamp, duration, null); switch (this._type) { case WebInspector.CallingContextTree.Type.TopDown: for (let i = stackFrames.length; i--; ) { let stackFrame = stackFrames[i]; node = node.findOrMakeChild(stackFrame); node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0); } break; case WebInspector.CallingContextTree.Type.BottomUp: for (let i = 0; i < stackFrames.length; ++i) { let stackFrame = stackFrames[i]; node = node.findOrMakeChild(stackFrame); node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0); } break; case WebInspector.CallingContextTree.Type.TopFunctionsTopDown: for (let i = stackFrames.length; i--; ) { node = this._root; for (let j = i + 1; j--; ) { let stackFrame = stackFrames[j]; node = node.findOrMakeChild(stackFrame); node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0); } } break; case WebInspector.CallingContextTree.Type.TopFunctionsBottomUp: for (let i = 0; i < stackFrames.length; i++) { node = this._root; for (let j = i; j < stackFrames.length; j++) { let stackFrame = stackFrames[j]; node = node.findOrMakeChild(stackFrame); node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0); } } break; default: console.assert(false, "This should not be reached."); break; } } toCPUProfilePayload(startTime, endTime) { let cpuProfile = {}; let roots = []; let numSamplesInTimeRange = this._root.filteredTimestampsAndDuration(startTime, endTime).timestamps.length; this._root.forEachChild((child) => { if (child.hasStackTraceInTimeRange(startTime, endTime)) roots.push(child.toCPUProfileNode(numSamplesInTimeRange, startTime, endTime)); }); cpuProfile.rootNodes = roots; return cpuProfile; } forEachChild(callback) { this._root.forEachChild(callback); } forEachNode(callback) { this._root.forEachNode(callback); } // Testing. static __test_makeTreeFromProtocolMessageObject(messageObject) { let tree = new WebInspector.CallingContextTree; let stackTraces = messageObject.params.samples.stackTraces; for (let i = 0; i < stackTraces.length; i++) tree.updateTreeWithStackTrace(stackTraces[i]); return tree; } __test_matchesStackTrace(stackTrace) { // StackTrace should have top frame first in the array and bottom frame last. // We don't look for a match that traces down the tree from the root; instead, // we match by looking at all the leafs, and matching while walking up the tree // towards the root. If we successfully make the walk, we've got a match that // suffices for a particular test. A successful match doesn't mean we actually // walk all the way up to the root; it just means we didn't fail while walking // in the direction of the root. let leaves = this.__test_buildLeafLinkedLists(); outer: for (let node of leaves) { for (let stackNode of stackTrace) { for (let propertyName of Object.getOwnPropertyNames(stackNode)) { if (stackNode[propertyName] !== node[propertyName]) continue outer; } node = node.parent; } return true; } return false; } __test_buildLeafLinkedLists() { let result = []; let parent = null; this._root.__test_buildLeafLinkedLists(parent, result); return result; } }; WebInspector.CallingContextTree.Type = { TopDown: Symbol("TopDown"), BottomUp: Symbol("BottomUp"), TopFunctionsTopDown: Symbol("TopFunctionsTopDown"), TopFunctionsBottomUp: Symbol("TopFunctionsBottomUp"), };