| Index: tools/profview/profile-utils.js
|
| diff --git a/tools/profview/profile-utils.js b/tools/profview/profile-utils.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e6fb58c5646a9dd173c1cba85531dc791cc76cb2
|
| --- /dev/null
|
| +++ b/tools/profview/profile-utils.js
|
| @@ -0,0 +1,278 @@
|
| +// Copyright 2017 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +"use strict"
|
| +
|
| +let codeKinds = [
|
| + "UNKNOWN",
|
| + "CPPCOMP",
|
| + "CPPGC",
|
| + "CPPEXT",
|
| + "CPP",
|
| + "LIB",
|
| + "IC",
|
| + "BC",
|
| + "STUB",
|
| + "BUILTIN",
|
| + "REGEXP",
|
| + "JSOPT",
|
| + "JSUNOPT"
|
| +];
|
| +
|
| +function resolveCodeKind(code) {
|
| + if (!code || !code.type) {
|
| + return "UNKNOWN";
|
| + } else if (code.type === "CPP") {
|
| + return "CPP";
|
| + } else if (code.type === "SHARED_LIB") {
|
| + return "LIB";
|
| + } else if (code.type === "CODE") {
|
| + if (code.kind === "LoadIC" ||
|
| + code.kind === "StoreIC" ||
|
| + code.kind === "KeyedStoreIC" ||
|
| + code.kind === "KeyedLoadIC" ||
|
| + code.kind === "LoadGlobalIC" ||
|
| + code.kind === "Handler") {
|
| + return "IC";
|
| + } else if (code.kind === "BytecodeHandler") {
|
| + return "BC";
|
| + } else if (code.kind === "Stub") {
|
| + return "STUB";
|
| + } else if (code.kind === "Builtin") {
|
| + return "BUILTIN";
|
| + } else if (code.kind === "RegExp") {
|
| + return "REGEXP";
|
| + }
|
| + console.log("Unknown CODE: '" + code.kind + "'.");
|
| + return "CODE";
|
| + } else if (code.type === "JS") {
|
| + if (code.kind === "Builtin") {
|
| + return "JSUNOPT";
|
| + } else if (code.kind === "Opt") {
|
| + return "JSOPT";
|
| + } else if (code.kind === "Unopt") {
|
| + return "JSUNOPT";
|
| + }
|
| + }
|
| + console.log("Unknown code type '" + type + "'.");
|
| +}
|
| +
|
| +function resolveCodeKindAndVmState(code, vmState) {
|
| + let kind = resolveCodeKind(code);
|
| + if (kind === "CPP") {
|
| + if (vmState === 1) {
|
| + kind = "CPPGC";
|
| + } else if (vmState === 2) {
|
| + kind = "CPPCOMP";
|
| + } else if (vmState === 4) {
|
| + kind = "CPPEXT";
|
| + }
|
| + }
|
| + return kind;
|
| +}
|
| +
|
| +function createNodeFromStackEntry(code) {
|
| + let name = code ? code.name : "UNKNOWN";
|
| +
|
| + return { name, type : resolveCodeKind(code),
|
| + children : [], ownTicks : 0, ticks : 0 };
|
| +}
|
| +
|
| +function addStackToTree(file, stack, tree, filter, ascending, start) {
|
| + if (start === undefined) {
|
| + start = ascending ? 0 : stack.length - 2;
|
| + }
|
| + tree.ticks++;
|
| + for (let i = start;
|
| + ascending ? (i < stack.length) : (i >= 0);
|
| + i += ascending ? 2 : -2) {
|
| + let codeId = stack[i];
|
| + let code = codeId >= 0 ? file.code[codeId] : undefined;
|
| + if (filter) {
|
| + let type = code ? code.type : undefined;
|
| + let kind = code ? code.kind : undefined;
|
| + if (!filter(type, kind)) continue;
|
| + }
|
| +
|
| + // For JavaScript function, pretend there is one instance of optimized
|
| + // function and one instance of unoptimized function per SFI.
|
| + let type = resolveCodeKind(code);
|
| + let childId;
|
| + if (type === "JSOPT") {
|
| + childId = code.func * 4 + 1;
|
| + } else if (type === "JSUNOPT") {
|
| + childId = code.func * 4 + 2;
|
| + } else {
|
| + childId = codeId * 4;
|
| + }
|
| + let child = tree.children[childId];
|
| + if (!child) {
|
| + child = createNodeFromStackEntry(code);
|
| + tree.children[childId] = child;
|
| + }
|
| + child.ticks++;
|
| + tree = child;
|
| + }
|
| + tree.ownTicks++;
|
| +}
|
| +
|
| +function createEmptyNode(name) {
|
| + return {
|
| + name : name,
|
| + type : "CAT",
|
| + children : [],
|
| + ownTicks : 0,
|
| + ticks : 0
|
| + };
|
| +}
|
| +
|
| +class PlainCallTreeProcessor {
|
| + constructor(filter, isBottomUp) {
|
| + this.filter = filter;
|
| + this.tree = createEmptyNode("root");
|
| + this.isBottomUp = isBottomUp;
|
| + }
|
| +
|
| + addStack(file, timestamp, vmState, stack) {
|
| + addStackToTree(file, stack, this.tree, this.filter, this.isBottomUp);
|
| + }
|
| +}
|
| +
|
| +class CategorizedCallTreeProcessor {
|
| + constructor(filter, isBottomUp) {
|
| + this.filter = filter;
|
| + let root = createEmptyNode("root");
|
| + let categories = {};
|
| + function addCategory(name, types) {
|
| + let n = createEmptyNode(name);
|
| + for (let i = 0; i < types.length; i++) {
|
| + categories[types[i]] = n;
|
| + }
|
| + root.children.push(n);
|
| + }
|
| + addCategory("JS Optimized", [ "JSOPT" ]);
|
| + addCategory("JS Unoptimized", [ "JSUNOPT", "BC" ]);
|
| + addCategory("IC", [ "IC" ]);
|
| + addCategory("RegExp", [ "REGEXP" ]);
|
| + addCategory("Other generated", [ "STUB", "BUILTIN" ]);
|
| + addCategory("C++", [ "CPP", "LIB" ]);
|
| + addCategory("C++/GC", [ "CPPGC" ]);
|
| + addCategory("C++/Compiler", [ "CPPCOMP" ]);
|
| + addCategory("C++/External", [ "CPPEXT" ]);
|
| + addCategory("Unknown", [ "UNKNOWN" ]);
|
| +
|
| + this.tree = root;
|
| + this.categories = categories;
|
| + this.isBottomUp = isBottomUp;
|
| + }
|
| +
|
| + addStack(file, timestamp, vmState, stack) {
|
| + if (stack.length === 0) return;
|
| + let codeId = stack[0];
|
| + let code = codeId >= 0 ? file.code[codeId] : undefined;
|
| + let kind = resolveCodeKindAndVmState(code, vmState);
|
| + let node = this.categories[kind];
|
| +
|
| + this.tree.ticks++;
|
| +
|
| + console.assert(node);
|
| +
|
| + addStackToTree(file, stack, node, this.filter, this.isBottomUp);
|
| + }
|
| +}
|
| +
|
| +class FunctionListTree {
|
| + constructor(filter) {
|
| + this.tree = { name : "root", children : [], ownTicks : 0, ticks : 0 };
|
| + this.codeVisited = [];
|
| + this.filter = filter;
|
| + }
|
| +
|
| + addStack(file, timestamp, vmState, stack) {
|
| + this.tree.ticks++;
|
| + let child = null;
|
| + for (let i = stack.length - 2; i >= 0; i -= 2) {
|
| + let codeId = stack[i];
|
| + if (codeId < 0 || this.codeVisited[codeId]) continue;
|
| +
|
| + let code = codeId >= 0 ? file.code[codeId] : undefined;
|
| + if (this.filter) {
|
| + let type = code ? code.type : undefined;
|
| + let kind = code ? code.kind : undefined;
|
| + if (!this.filter(type, kind)) continue;
|
| + }
|
| + child = this.tree.children[codeId];
|
| + if (!child) {
|
| + child = createNodeFromStackEntry(code);
|
| + this.tree.children[codeId] = child;
|
| + }
|
| + child.ticks++;
|
| + this.codeVisited[codeId] = true;
|
| + }
|
| + if (child) {
|
| + child.ownTicks++;
|
| + }
|
| +
|
| + for (let i = 0; i < stack.length; i += 2) {
|
| + let codeId = stack[i];
|
| + if (codeId >= 0) this.codeVisited[codeId] = false;
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +class CategorySampler {
|
| + constructor(file, bucketCount) {
|
| + this.bucketCount = bucketCount;
|
| +
|
| + this.firstTime = file.ticks[0].tm;
|
| + let lastTime = file.ticks[file.ticks.length - 1].tm;
|
| + this.step = (lastTime - this.firstTime) / bucketCount;
|
| +
|
| + this.buckets = [];
|
| + let bucket = {};
|
| + for (let i = 0; i < codeKinds.length; i++) {
|
| + bucket[codeKinds[i]] = 0;
|
| + }
|
| + for (let i = 0; i < bucketCount; i++) {
|
| + this.buckets.push(Object.assign({ total : 0 }, bucket));
|
| + }
|
| + }
|
| +
|
| + addStack(file, timestamp, vmState, stack) {
|
| + let i = Math.floor((timestamp - this.firstTime) / this.step);
|
| + if (i == this.buckets.length) i--;
|
| + console.assert(i >= 0 && i < this.buckets.length);
|
| +
|
| + let bucket = this.buckets[i];
|
| + bucket.total++;
|
| +
|
| + let codeId = (stack.length > 0) ? stack[0] : -1;
|
| + let code = codeId >= 0 ? file.code[codeId] : undefined;
|
| + let kind = resolveCodeKindAndVmState(code, vmState);
|
| + bucket[kind]++;
|
| + }
|
| +}
|
| +
|
| +// Generates a tree out of a ticks sequence.
|
| +// {file} is the JSON files with the ticks and code objects.
|
| +// {startTime}, {endTime} is the interval.
|
| +// {tree} is the processor of stacks.
|
| +function generateTree(
|
| + file, startTime, endTime, tree) {
|
| + let ticks = file.ticks;
|
| + let i = 0;
|
| + while (i < ticks.length && ticks[i].tm < startTime) {
|
| + i++;
|
| + }
|
| +
|
| + let tickCount = 0;
|
| + while (i < ticks.length && ticks[i].tm < endTime) {
|
| + tree.addStack(file, ticks[i].tm, ticks[i].vm, ticks[i].s);
|
| + i++;
|
| + tickCount++;
|
| + }
|
| +
|
| + return tickCount;
|
| +}
|
|
|