335c8ad009
JavaScript is a dynamically typed language. But most code is written with fixed types in mind. When debugging JavaScript, it is helpful to know the types of variables and parameters at runtime. It is often hard to infer types for complex code. Type profiling provides this information at runtime. Node.js uses the inspector protocol. This CL allows Node.js users to access and analyse type profile for via Node modules or the in-procress api. Type Profile helps developers to analyze their code for correctness and performance. Design doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing Add `takeTypeProfile` to the inspector protocol. It returns a list of TypeProfileForScripts, which in turn contains the type profile for each function. We can use TypeProfile data to annotate JavaScript code. Sample script with data from TypeProfile: function f(/*Object, number, undefined*/a, /*Array, number, null*/b, /*boolean, Object, symbol*/c) { return 'bye'; /*string*/}; f({}, [], true); f(3, 2.3, {a: 42}); f(undefined, null, Symbol('hello'));/*string*/ Bug: v8:5933 Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I626bfb886b752f90b9c86cc6953601558b18b60d Reviewed-on: https://chromium-review.googlesource.com/508588 Commit-Queue: Franziska Hinkelmann <franzih@chromium.org> Reviewed-by: Pavel Feldman <pfeldman@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#47920}
266 lines
9.6 KiB
JavaScript
266 lines
9.6 KiB
JavaScript
// 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.
|
|
|
|
// Flags: --allow-natives-syntax --no-always-opt --opt
|
|
|
|
var source =
|
|
`
|
|
function fib(x) {
|
|
if (x < 2) return 1;
|
|
return fib(x-1) + fib(x-2);
|
|
}
|
|
function is_optimized(f) {
|
|
return (%GetOptimizationStatus(f) & 16) ? "optimized" : "unoptimized";
|
|
}
|
|
(function iife() {
|
|
return 1;
|
|
})();
|
|
fib(5);
|
|
`;
|
|
|
|
var break_source =
|
|
`
|
|
function g() {
|
|
debugger;
|
|
}
|
|
function f(x) {
|
|
if (x == 0) g();
|
|
else f(x - 1);
|
|
}
|
|
function h() {
|
|
g();
|
|
}
|
|
f(3);
|
|
`;
|
|
|
|
var nested =
|
|
`
|
|
var f = (function outer() {
|
|
function nested_0() {
|
|
return function nested_1() {
|
|
return function nested_2() {
|
|
return function nested_3() {}
|
|
}
|
|
}
|
|
}
|
|
function nested_4() {}
|
|
return nested_0();
|
|
})();
|
|
f()()();
|
|
`;
|
|
|
|
let {session, contextGroup, Protocol} = InspectorTest.start("Test collecting code coverage data with Profiler.collectCoverage.");
|
|
|
|
function ClearAndGC() {
|
|
return Protocol.Runtime.evaluate({ expression: "fib = g = f = h = is_optimized = null;" })
|
|
.then(GC);
|
|
}
|
|
|
|
function GC() {
|
|
return Protocol.HeapProfiler.collectGarbage();
|
|
}
|
|
|
|
function LogSorted(message) {
|
|
message.result.result.sort((a, b) => parseInt(a.scriptId) - parseInt(b.scriptId));
|
|
return InspectorTest.logMessage(message);
|
|
}
|
|
|
|
InspectorTest.runTestSuite([
|
|
function testPreciseCountBaseline(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(GC)
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({callCount: true, detailed: false}))
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testPreciseCountCoverage(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({callCount: true, detailed: false}))
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testPreciseCoverageFail(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(InspectorTest.logMessage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testBestEffortCoverage(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testBestEffortCoverageWithPreciseBinaryEnabled(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({detailed: false}))
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testBestEffortCoverageWithPreciseCountEnabled(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({callCount: true, detailed: false}))
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.getBestEffortCoverage)
|
|
.then(LogSorted)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testEnablePreciseCountCoverageAtPause(next)
|
|
{
|
|
function handleDebuggerPause() {
|
|
Protocol.Profiler.enable()
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({callCount: true, detailed: false}))
|
|
.then(Protocol.Debugger.resume)
|
|
}
|
|
Protocol.Debugger.enable();
|
|
Protocol.Debugger.oncePaused().then(handleDebuggerPause);
|
|
Protocol.Runtime.enable()
|
|
.then(() => Protocol.Runtime.compileScript({ expression: break_source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(Protocol.Debugger.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testPreciseBinaryCoverage(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({detailed: false}))
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(() => Protocol.Runtime.evaluate({ expression: "is_optimized(fib)" }))
|
|
.then(message => InspectorTest.logMessage(message))
|
|
.then(() => Protocol.Runtime.evaluate({ expression: "fib(20)" }))
|
|
.then(message => InspectorTest.logMessage(message))
|
|
.then(() => Protocol.Runtime.evaluate({ expression: "is_optimized(fib)" }))
|
|
.then(message => InspectorTest.logMessage(message))
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testPreciseEmptyScriptCoverageEntries(next)
|
|
{
|
|
// Enabling the debugger holds onto script objects even though its
|
|
// functions can be garbage collected. We would get empty ScriptCoverage
|
|
// entries unless we remove them.
|
|
Protocol.Debugger.enable()
|
|
.then(Protocol.Runtime.enable)
|
|
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(ClearAndGC)
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({detailed: false}))
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(Protocol.Debugger.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
function testPreciseCountCoveragePartial(next)
|
|
{
|
|
Protocol.Runtime.enable()
|
|
.then(Protocol.Profiler.enable)
|
|
.then(() => Protocol.Profiler.startPreciseCoverage({callCount: true, detailed: false}))
|
|
.then(() => Protocol.Runtime.compileScript({ expression: nested, sourceURL: arguments.callee.name, persistScript: true }))
|
|
.then((result) => Protocol.Runtime.runScript({ scriptId: result.result.scriptId }))
|
|
.then(InspectorTest.logMessage)
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(() => Protocol.Runtime.evaluate({ expression: "f()" }))
|
|
.then(Protocol.Profiler.takePreciseCoverage)
|
|
.then(LogSorted)
|
|
.then(Protocol.Profiler.stopPreciseCoverage)
|
|
.then(Protocol.Profiler.disable)
|
|
.then(Protocol.Runtime.disable)
|
|
.then(ClearAndGC)
|
|
.then(next);
|
|
},
|
|
]);
|