8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally
authorsundar
Thu, 14 Aug 2014 18:54:54 +0530
changeset 26065 d15adb218527
parent 26064 8c8604a11c57
child 26066 0665ee3822e0
8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally Reviewed-by: attila, jlaskey
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/test/script/trusted/JDK-8055107.js
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Aug 14 14:35:44 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Aug 14 18:54:54 2014 +0530
@@ -289,7 +289,7 @@
      * @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 @@
         }
 
         // 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 @@
      * 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;
-        }
-
-        final PrintWriter out = compiler.getScriptEnvironment().getErr();
-        out.println("[BLOCK in '" + ident + "']");
-        if (!block.printSymbols(out)) {
-            out.println("<no symbols>");
-        }
-        out.println();
+    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();
+        }
     }
 
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Aug 14 14:35:44 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Aug 14 18:54:54 2014 +0530
@@ -280,12 +280,12 @@
             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));
             }
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Aug 14 14:35:44 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Aug 14 18:54:54 2014 +0530
@@ -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 @@
     /** 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;
 
@@ -354,6 +387,41 @@
     }
 
     /**
+     * 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
      */
@@ -390,6 +458,38 @@
     }
 
     /**
+     * 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.
      */
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Thu Aug 14 14:35:44 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Thu Aug 14 18:54:54 2014 +0530
@@ -106,6 +106,8 @@
 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 @@
             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 @@
                                         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 @@
                 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;
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Thu Aug 14 14:35:44 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Thu Aug 14 18:54:54 2014 +0530
@@ -1104,11 +1104,11 @@
                 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));
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/trusted/JDK-8055107.js	Thu Aug 14 18:54:54 2014 +0530
@@ -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");
+}