8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally

Reviewed-by: attila, jlaskey
This commit is contained in:
Athijegannathan Sundararajan 2014-08-14 18:54:54 +05:30
parent dbb42efd61
commit 62e846549d
6 changed files with 315 additions and 18 deletions

View File

@ -289,7 +289,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @return the correct flags for a call site in the current function
*/
int getCallSiteFlags() {
return lc.getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
return lc.getCurrentFunction().getCallSiteFlags() | callSiteFlags;
}
/**
@ -1764,7 +1764,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
// Debugging: print symbols? @see --print-symbols flag
printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
printSymbols(block, function, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
}
/**
@ -4106,19 +4106,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* Debug code used to print symbols
*
* @param block the block we are in
* @param function the function we are in
* @param ident identifier for block or function where applicable
*/
private void printSymbols(final Block block, final String ident) {
if (!compiler.getScriptEnvironment()._print_symbols) {
return;
private void printSymbols(final Block block, final FunctionNode function, final String ident) {
if (compiler.getScriptEnvironment()._print_symbols || function.getFlag(FunctionNode.IS_PRINT_SYMBOLS)) {
final PrintWriter out = compiler.getScriptEnvironment().getErr();
out.println("[BLOCK in '" + ident + "']");
if (!block.printSymbols(out)) {
out.println("<no symbols>");
}
out.println();
}
final PrintWriter out = compiler.getScriptEnvironment().getErr();
out.println("[BLOCK in '" + ident + "']");
if (!block.printSymbols(out)) {
out.println("<no symbols>");
}
out.println();
}

View File

@ -280,12 +280,12 @@ enum CompilationPhase {
final PrintWriter err = senv.getErr();
//TODO separate phase for the debug printouts for abstraction and clarity
if (senv._print_lower_ast) {
if (senv._print_lower_ast || fn.getFlag(FunctionNode.IS_PRINT_LOWER_AST)) {
err.println("Lower AST for: " + quote(newFunctionNode.getName()));
err.println(new ASTWriter(newFunctionNode));
}
if (senv._print_lower_parse) {
if (senv._print_lower_parse || fn.getFlag(FunctionNode.IS_PRINT_LOWER_PARSE)) {
err.println("Lower AST for: " + quote(newFunctionNode.getName()));
err.println(new PrintVisitor(newFunctionNode));
}

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.*;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
@ -228,6 +230,37 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Is this declared in a dynamic context */
public static final int IN_DYNAMIC_CONTEXT = 1 << 17;
/**
* The following flags are derived from directive comments within this function.
* Note that even IS_STRICT is one such flag but that requires special handling.
*/
// parser, lower debugging this function
public static final int IS_PRINT_PARSE = 1 << 18;
public static final int IS_PRINT_LOWER_PARSE = 1 << 19;
public static final int IS_PRINT_AST = 1 << 20;
public static final int IS_PRINT_LOWER_AST = 1 << 21;
public static final int IS_PRINT_SYMBOLS = 1 << 22;
/** profile callsites in this function? */
public static final int IS_PROFILE = 1 << 23;
// callsite tracing, profiling within this function
/** trace callsite enterexit in this function? */
public static final int IS_TRACE_ENTEREXIT = 1 << 24;
/** trace callsite misses in this function? */
public static final int IS_TRACE_MISSES = 1 << 25;
/** trace callsite values in this function? */
public static final int IS_TRACE_VALUES = 1 << 26;
/** extension callsite flags mask */
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST |
IS_PRINT_SYMBOLS | IS_PROFILE | IS_TRACE_ENTEREXIT |
IS_TRACE_MISSES | IS_TRACE_VALUES;
/** Does this function or any nested functions contain an eval? */
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
@ -353,6 +386,41 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return Node.accept(visitor, parameters);
}
/**
* Get additional callsite flags to be used specific to this function.
*
* @return callsite flags
*/
public int getCallSiteFlags() {
int callsiteFlags = 0;
if (getFlag(IS_STRICT)) {
callsiteFlags |= CALLSITE_STRICT;
}
// quick check for extension callsite flags turned on by directives.
if ((flags & EXTENSION_CALLSITE_FLAGS) == 0) {
return callsiteFlags;
}
if (getFlag(IS_PROFILE)) {
callsiteFlags |= CALLSITE_PROFILE;
}
if (getFlag(IS_TRACE_MISSES)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_MISSES;
}
if (getFlag(IS_TRACE_VALUES)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT | CALLSITE_TRACE_VALUES;
}
if (getFlag(IS_TRACE_ENTEREXIT)) {
callsiteFlags |= CALLSITE_TRACE | CALLSITE_TRACE_ENTEREXIT;
}
return callsiteFlags;
}
/**
* Get the source for this function
* @return the source
@ -389,6 +457,38 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
return explicitURL != null ? explicitURL : source.getName();
}
/**
* Function to parse nashorn per-function extension directive comments.
*
* @param directive nashorn extension directive string
* @return integer flag for the given directive.
*/
public static int getDirectiveFlag(final String directive) {
switch (directive) {
case "nashorn callsite trace enterexit":
return IS_TRACE_ENTEREXIT;
case "nashorn callsite trace misses":
return IS_TRACE_MISSES;
case "nashorn callsite trace objects":
return IS_TRACE_VALUES;
case "nashorn callsite profile":
return IS_PROFILE;
case "nashorn print parse":
return IS_PRINT_PARSE;
case "nashorn print lower parse":
return IS_PRINT_LOWER_PARSE;
case "nashorn print ast":
return IS_PRINT_AST;
case "nashorn print lower ast":
return IS_PRINT_LOWER_AST;
case "nashorn print symbols":
return IS_PRINT_SYMBOLS;
default:
// unknown/unsupported directive
return 0;
}
}
/**
* Returns the line number.
* @return the line number.

View File

@ -106,6 +106,8 @@ import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
@ -346,9 +348,10 @@ public class Parser extends AbstractParser implements Loggable {
expect(EOF);
function.setFinish(source.getLength() - 1);
function = restoreFunctionNode(function, token); //commit code
function = function.setBody(lc, function.getBody().setNeedsScope(lc));
printAST(function);
return function;
} catch (final Exception e) {
handleParseException(e);
@ -800,6 +803,12 @@ loop:
verifyStrictIdent(param, "function parameter");
}
}
} else if (Context.DEBUG) {
final int flag = FunctionNode.getDirectiveFlag(directive);
if (flag != 0) {
final FunctionNode function = lc.getCurrentFunction();
lc.setFlag(function, flag);
}
}
}
}
@ -2809,14 +2818,24 @@ loop:
lastToken = token;
expect(RBRACE);
functionNode.setFinish(finish);
}
} finally {
functionNode = restoreFunctionNode(functionNode, lastToken);
}
printAST(functionNode);
return functionNode;
}
private void printAST(final FunctionNode functionNode) {
if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
env.getErr().println(new ASTWriter(functionNode));
}
if (functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
env.getErr().println(new PrintVisitor(functionNode, true, false));
}
}
private void addFunctionDeclarations(final FunctionNode functionNode) {
assert lc.peek() == lc.getFunctionBody(functionNode);
VarNode lastDecl = null;

View File

@ -1104,11 +1104,11 @@ public final class Context {
return null;
}
if (env._print_ast) {
if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
getErr().println(new ASTWriter(functionNode));
}
if (env._print_parse) {
if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
getErr().println(new PrintVisitor(functionNode, true, false));
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally
*
* @test
* @option -Dnashorn.debug=true
* @option -scripting
* @run
* @fork
*/
function runScriptEngine(code) {
var imports = new JavaImporter(
java.io, java.lang, java.util, javax.script);
with(imports) {
var m = new ScriptEngineManager();
// get current System.err
var oldErr = System.err;
var baos = new ByteArrayOutputStream();
var newErr = new PrintStream(baos);
try {
// set new standard err
System.setErr(newErr);
var engine = m.getEngineByName("nashorn");
engine.eval(code);
newErr.flush();
return new java.lang.String(baos.toByteArray());
} finally {
// restore System.err to old value
System.setErr(oldErr);
}
}
}
// nashorn callsite trace enterexit
var str = runScriptEngine(<<CODE
function func() {
"nashorn callsite trace enterexit";
k();
}
function k() {
var x = "hello";
}
func();
CODE);
if (!str.contains(" ENTER ")) {
fail("expected 'ENTER' in trace mode output");
}
if (!str.contains(" EXIT ")) {
fail("expected 'EXIT' in trace mode output");
}
// nashorn callsite trace objects
var str = runScriptEngine(<<CODE
"nashorn callsite trace objects";
function func(x) {
}
func("hello");
CODE);
if (!str.contains(" ENTER ")) {
fail("expected 'ENTER' in trace mode output");
}
if (!str.contains(" EXIT ")) {
fail("expected 'EXIT' in trace mode output");
}
if (!str.contains("hello")) {
fail("expected argument to be traced in trace objects mode");
}
// nashorn callsite trace misses
str = runScriptEngine(<<CODE
function f() {
"nashorn callsite trace misses";
k();
}
function k() {}
f();
CODE);
if (!str.contains(" MISS ")) {
fail("expected callsite MISS trace messages");
}
// nashorn print lower ast
str = runScriptEngine(<<CODE
function foo() {
"nashorn print lower ast";
var x = 'hello';
}
foo();
CODE);
if (!str.contains("Lower AST for: 'foo'") ||
!str.contains("nashorn print lower ast")) {
fail("expected Lower AST to be printed for 'foo'");
}
// nashorn print ast
str = runScriptEngine(<<CODE
function foo() {
"nashorn print ast";
}
CODE);
if (!str.contains("[function ") ||
!str.contains("nashorn print ast")) {
fail("expected AST to be printed");
}
// nashorn print symbols
str = runScriptEngine(<<CODE
function bar(a) {
"nashorn print symbols";
if (a) print(a);
}
bar();
CODE)
if (!str.contains("[BLOCK in 'Function bar']")) {
fail("expected symbols to be printed for 'bar'");
}
// nashorn print parse
str = runScriptEngine(<<CODE
"nashorn print parse";
function func() {}
CODE);
if (!str.contains("function func") ||
!str.contains("nashorn print parse")) {
fail("expected nashorn print parse output");
}
// nashorn print lower parse
str = runScriptEngine(<<CODE
"nashorn print lower parse";
function func() {}
func()
CODE);
if (!str.contains("function {U%}func") ||
!str.contains("nashorn print lower parse")) {
fail("expected nashorn print lower parse output");
}