--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java 2020-04-15 18:51:06.000000000 +0530 +++ /dev/null 2020-04-15 18:51:06.000000000 +0530 @@ -1,553 +0,0 @@ -/* - * Copyright (c) 2010, 2017, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.nashorn.internal.runtime.linker; - -import static jdk.nashorn.internal.lookup.Lookup.MH; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.LongAdder; -import jdk.dynalink.DynamicLinker; -import jdk.dynalink.linker.GuardedInvocation; -import jdk.dynalink.support.ChainedCallSite; -import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.Debug; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.options.Options; - - -/** - * Relinkable form of call site. - */ -public class LinkerCallSite extends ChainedCallSite { - /** Maximum number of arguments passed directly. */ - public static final int ARGLIMIT = 125; - - private static final String PROFILEFILE = Options.getStringProperty("nashorn.profilefile", "NashornProfile.txt"); - - private static final MethodHandle INCREASE_MISS_COUNTER = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "increaseMissCount", MH.type(Object.class, String.class, Object.class)); - - LinkerCallSite(final NashornCallSiteDescriptor descriptor) { - super(descriptor); - if (Context.DEBUG) { - LinkerCallSite.count.increment(); - } - } - - /** - * Construct a new linker call site. - * @param name Name of method. - * @param type Method type. - * @param flags Call site specific flags. - * @return New LinkerCallSite. - */ - static LinkerCallSite newLinkerCallSite(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int flags) { - final NashornCallSiteDescriptor desc = NashornCallSiteDescriptor.get(lookup, name, type, flags); - - if (desc.isProfile()) { - return ProfilingLinkerCallSite.newProfilingLinkerCallSite(desc); - } - - if (desc.isTrace()) { - return new TracingLinkerCallSite(desc); - } - - return new LinkerCallSite(desc); - } - - @Override - public String toString() { - return getDescriptor().toString(); - } - - /** - * Get the descriptor for this callsite - * @return a {@link NashornCallSiteDescriptor} - */ - public NashornCallSiteDescriptor getNashornDescriptor() { - return (NashornCallSiteDescriptor)getDescriptor(); - } - - @Override - public void relink(final GuardedInvocation invocation, final MethodHandle relink) { - super.relink(invocation, getDebuggingRelink(relink)); - } - - @Override - public void resetAndRelink(final GuardedInvocation invocation, final MethodHandle relink) { - super.resetAndRelink(invocation, getDebuggingRelink(relink)); - } - - private MethodHandle getDebuggingRelink(final MethodHandle relink) { - if (Context.DEBUG) { - return MH.filterArguments(relink, 0, getIncreaseMissCounter(relink.type().parameterType(0))); - } - return relink; - } - - private MethodHandle getIncreaseMissCounter(final Class type) { - final MethodHandle missCounterWithDesc = MH.bindTo(INCREASE_MISS_COUNTER, getDescriptor().getOperation() + " @ " + getScriptLocation()); - if (type == Object.class) { - return missCounterWithDesc; - } - return MH.asType(missCounterWithDesc, missCounterWithDesc.type().changeParameterType(0, type).changeReturnType(type)); - } - - private static String getScriptLocation() { - final StackTraceElement caller = DynamicLinker.getLinkedCallSiteLocation(); - return caller == null ? "unknown location" : (caller.getFileName() + ":" + caller.getLineNumber()); - } - - /** - * Instrumentation - increase the miss count when a callsite misses. Used as filter - * @param desc descriptor for table entry - * @param self self reference - * @return self reference - */ - public static Object increaseMissCount(final String desc, final Object self) { - missCount.increment(); - if (r.nextInt(100) < missSamplingPercentage) { - final AtomicInteger i = missCounts.get(desc); - if (i == null) { - missCounts.put(desc, new AtomicInteger(1)); - } else { - i.incrementAndGet(); - } - } - return self; - } - - /* - * Debugging call sites. - */ - - private static class ProfilingLinkerCallSite extends LinkerCallSite { - /** List of all profiled call sites. */ - private static LinkedList profileCallSites = null; - - /** Start time when entered at zero depth. */ - private long startTime; - - /** Depth of nested calls. */ - private int depth; - - /** Total time spent in this call site. */ - private long totalTime; - - /** Total number of times call site entered. */ - private long hitCount; - - private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - - private static final MethodHandle PROFILEENTRY = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileEntry", MH.type(Object.class, Object.class)); - private static final MethodHandle PROFILEEXIT = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileExit", MH.type(Object.class, Object.class)); - private static final MethodHandle PROFILEVOIDEXIT = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileVoidExit", MH.type(void.class)); - - /* - * Constructor - */ - - ProfilingLinkerCallSite(final NashornCallSiteDescriptor desc) { - super(desc); - } - - public static ProfilingLinkerCallSite newProfilingLinkerCallSite(final NashornCallSiteDescriptor desc) { - if (profileCallSites == null) { - profileCallSites = new LinkedList<>(); - - final Thread profileDumperThread = new Thread(new ProfileDumper()); - Runtime.getRuntime().addShutdownHook(profileDumperThread); - } - - final ProfilingLinkerCallSite callSite = new ProfilingLinkerCallSite(desc); - profileCallSites.add(callSite); - - return callSite; - } - - @Override - public void setTarget(final MethodHandle newTarget) { - final MethodType type = type(); - final boolean isVoid = type.returnType() == void.class; - final Class newSelfType = newTarget.type().parameterType(0); - - MethodHandle selfFilter = MH.bindTo(PROFILEENTRY, this); - if (newSelfType != Object.class) { - // new target uses a more precise 'self' type than Object.class. We need to - // convert the filter type. Note that the profileEntry method returns "self" - // argument "as is" and so the cast introduced will succeed for any type. - final MethodType selfFilterType = MethodType.methodType(newSelfType, newSelfType); - selfFilter = selfFilter.asType(selfFilterType); - } - - MethodHandle methodHandle = MH.filterArguments(newTarget, 0, selfFilter); - - if (isVoid) { - methodHandle = MH.filterReturnValue(methodHandle, MH.bindTo(PROFILEVOIDEXIT, this)); - } else { - final MethodType filter = MH.type(type.returnType(), type.returnType()); - methodHandle = MH.filterReturnValue(methodHandle, MH.asType(MH.bindTo(PROFILEEXIT, this), filter)); - } - - super.setTarget(methodHandle); - } - - /** - * Start the clock for a profile entry and increase depth - * @param self argument to filter - * @return preserved argument - */ - @SuppressWarnings("unused") - public Object profileEntry(final Object self) { - if (depth == 0) { - startTime = System.nanoTime(); - } - - depth++; - hitCount++; - - return self; - } - - /** - * Decrease depth and stop the clock for a profile entry - * @param result return value to filter - * @return preserved argument - */ - @SuppressWarnings("unused") - public Object profileExit(final Object result) { - depth--; - - if (depth == 0) { - totalTime += System.nanoTime() - startTime; - } - - return result; - } - - /** - * Decrease depth without return value filter - */ - @SuppressWarnings("unused") - public void profileVoidExit() { - depth--; - - if (depth == 0) { - totalTime += System.nanoTime() - startTime; - } - } - - static class ProfileDumper implements Runnable { - @Override - public void run() { - PrintWriter out = null; - boolean fileOutput = false; - - try { - try { - out = new PrintWriter(new FileOutputStream(PROFILEFILE)); - fileOutput = true; - } catch (final FileNotFoundException e) { - out = Context.getCurrentErr(); - } - - dump(out); - } finally { - if (out != null && fileOutput) { - out.close(); - } - } - } - - private static void dump(final PrintWriter out) { - int index = 0; - for (final ProfilingLinkerCallSite callSite : profileCallSites) { - out.println("" + (index++) + '\t' + - callSite.getDescriptor().getOperation() + '\t' + - callSite.totalTime + '\t' + - callSite.hitCount); - } - } - } - } - - /** - * Debug subclass for LinkerCallSite that allows tracing - */ - private static class TracingLinkerCallSite extends LinkerCallSite { - private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - - private static final MethodHandle TRACEOBJECT = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceObject", MH.type(Object.class, MethodHandle.class, Object[].class)); - private static final MethodHandle TRACEVOID = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceVoid", MH.type(void.class, MethodHandle.class, Object[].class)); - private static final MethodHandle TRACEMISS = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceMiss", MH.type(void.class, String.class, Object[].class)); - - TracingLinkerCallSite(final NashornCallSiteDescriptor desc) { - super(desc); - } - - @Override - public void setTarget(final MethodHandle newTarget) { - if (!getNashornDescriptor().isTraceEnterExit()) { - super.setTarget(newTarget); - return; - } - - final MethodType type = type(); - final boolean isVoid = type.returnType() == void.class; - - MethodHandle traceMethodHandle = isVoid ? TRACEVOID : TRACEOBJECT; - traceMethodHandle = MH.bindTo(traceMethodHandle, this); - traceMethodHandle = MH.bindTo(traceMethodHandle, newTarget); - traceMethodHandle = MH.asCollector(traceMethodHandle, Object[].class, type.parameterCount()); - traceMethodHandle = MH.asType(traceMethodHandle, type); - - super.setTarget(traceMethodHandle); - } - - @Override - public void initialize(final MethodHandle relinkAndInvoke) { - super.initialize(getFallbackLoggingRelink(relinkAndInvoke)); - } - - @Override - public void relink(final GuardedInvocation invocation, final MethodHandle relink) { - super.relink(invocation, getFallbackLoggingRelink(relink)); - } - - @Override - public void resetAndRelink(final GuardedInvocation invocation, final MethodHandle relink) { - super.resetAndRelink(invocation, getFallbackLoggingRelink(relink)); - } - - private MethodHandle getFallbackLoggingRelink(final MethodHandle relink) { - if (!getNashornDescriptor().isTraceMisses()) { - // If we aren't tracing misses, just return relink as-is - return relink; - } - final MethodType type = relink.type(); - return MH.foldArguments(relink, MH.asType(MH.asCollector(MH.insertArguments(TRACEMISS, 0, this, "MISS " + getScriptLocation() + " "), Object[].class, type.parameterCount()), type.changeReturnType(void.class))); - } - - private void printObject(final PrintWriter out, final Object arg) { - if (!getNashornDescriptor().isTraceObjects()) { - out.print((arg instanceof ScriptObject) ? "ScriptObject" : arg); - return; - } - - if (arg instanceof ScriptObject) { - final ScriptObject object = (ScriptObject)arg; - - boolean isFirst = true; - final Set keySet = object.keySet(); - - if (keySet.isEmpty()) { - out.print(ScriptRuntime.safeToString(arg)); - } else { - out.print("{ "); - - for (final Object key : keySet) { - if (!isFirst) { - out.print(", "); - } - - out.print(key); - out.print(":"); - - final Object value = object.get(key); - - if (value instanceof ScriptObject) { - out.print("..."); - } else { - printObject(out, value); - } - - isFirst = false; - } - - out.print(" }"); - } - } else { - out.print(ScriptRuntime.safeToString(arg)); - } - } - - private void tracePrint(final PrintWriter out, final String tag, final Object[] args, final Object result) { - //boolean isVoid = type().returnType() == void.class; - out.print(Debug.id(this) + " TAG " + tag); - out.print(getDescriptor().getOperation() + "("); - - if (args.length > 0) { - printObject(out, args[0]); - for (int i = 1; i < args.length; i++) { - final Object arg = args[i]; - out.print(", "); - - if (!(arg instanceof ScriptObject && ((ScriptObject)arg).isScope())) { - printObject(out, arg); - } else { - out.print("SCOPE"); - } - } - } - - out.print(")"); - - if (tag.equals("EXIT ")) { - out.print(" --> "); - printObject(out, result); - } - - out.println(); - } - - /** - * Trace event. Wrap an invocation with a return value - * - * @param mh invocation handle - * @param args arguments to call - * - * @return return value from invocation - * - * @throws Throwable if invocation fails or throws exception/error - */ - @SuppressWarnings("unused") - public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable { - final PrintWriter out = Context.getCurrentErr(); - tracePrint(out, "ENTER ", args, null); - final Object result = mh.invokeWithArguments(args); - tracePrint(out, "EXIT ", args, result); - - return result; - } - - /** - * Trace event. Wrap an invocation that returns void - * - * @param mh invocation handle - * @param args arguments to call - * - * @throws Throwable if invocation fails or throws exception/error - */ - @SuppressWarnings("unused") - public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable { - final PrintWriter out = Context.getCurrentErr(); - tracePrint(out, "ENTER ", args, null); - mh.invokeWithArguments(args); - tracePrint(out, "EXIT ", args, null); - } - - /** - * Tracer function that logs a callsite miss - * - * @param desc callsite descriptor string - * @param args arguments to function - * - * @throws Throwable if invocation fails or throws exception/error - */ - @SuppressWarnings("unused") - public void traceMiss(final String desc, final Object... args) throws Throwable { - tracePrint(Context.getCurrentErr(), desc, args, null); - } - } - - // counters updated in debug mode - private static LongAdder count; - private static final HashMap missCounts = new HashMap<>(); - private static LongAdder missCount; - private static final Random r = new Random(); - private static final int missSamplingPercentage = Options.getIntProperty("nashorn.tcs.miss.samplePercent", 1); - - static { - if (Context.DEBUG) { - count = new LongAdder(); - missCount = new LongAdder(); - } - } - - @Override - protected int getMaxChainLength() { - return 8; - } - - /** - * Get the callsite count - * @return the count - */ - public static long getCount() { - return count.longValue(); - } - - /** - * Get the callsite miss count - * @return the missCount - */ - public static long getMissCount() { - return missCount.longValue(); - } - - /** - * Get given miss sampling percentage for sampler. Default is 1%. Specified with -Dnashorn.tcs.miss.samplePercent=x - * @return miss sampling percentage - */ - public static int getMissSamplingPercentage() { - return missSamplingPercentage; - } - - /** - * Dump the miss counts collected so far to a given output stream - * @param out print stream - */ - public static void getMissCounts(final PrintWriter out) { - final ArrayList> entries = new ArrayList<>(missCounts.entrySet()); - - Collections.sort(entries, new Comparator>() { - @Override - public int compare(final Entry o1, final Entry o2) { - return o2.getValue().get() - o1.getValue().get(); - } - }); - - for (final Entry entry : entries) { - out.println(" " + entry.getKey() + "\t" + entry.getValue().get()); - } - } - -}