LinkerCallSite.java revision 1416:a750a66640e0
1258057Sbr/*
2258057Sbr * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3258057Sbr * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4258057Sbr *
5258057Sbr * This code is free software; you can redistribute it and/or modify it
6258057Sbr * under the terms of the GNU General Public License version 2 only, as
7258057Sbr * published by the Free Software Foundation.  Oracle designates this
8258057Sbr * particular file as subject to the "Classpath" exception as provided
9258057Sbr * by Oracle in the LICENSE file that accompanied this code.
10258057Sbr *
11258057Sbr * This code is distributed in the hope that it will be useful, but WITHOUT
12258057Sbr * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13258057Sbr * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14258057Sbr * version 2 for more details (a copy is included in the LICENSE file that
15258057Sbr * accompanied this code).
16258057Sbr *
17258057Sbr * You should have received a copy of the GNU General Public License version
18258057Sbr * 2 along with this work; if not, write to the Free Software Foundation,
19258057Sbr * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20258057Sbr *
21258057Sbr * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22258057Sbr * or visit www.oracle.com if you need additional information or have any
23258057Sbr * questions.
24258057Sbr */
25258057Sbr
26258057Sbrpackage jdk.nashorn.internal.runtime.linker;
27258057Sbr
28258057Sbrimport static jdk.nashorn.internal.lookup.Lookup.MH;
29258057Sbr
30258057Sbrimport java.io.FileNotFoundException;
31258057Sbrimport java.io.FileOutputStream;
32258057Sbrimport java.io.PrintWriter;
33258057Sbrimport java.lang.invoke.MethodHandle;
34258057Sbrimport java.lang.invoke.MethodHandles;
35258057Sbrimport java.lang.invoke.MethodType;
36258057Sbrimport java.util.ArrayList;
37258057Sbrimport java.util.Collections;
38258057Sbrimport java.util.Comparator;
39258057Sbrimport java.util.HashMap;
40258057Sbrimport java.util.LinkedList;
41258057Sbrimport java.util.Map;
42258057Sbrimport java.util.Map.Entry;
43258057Sbrimport java.util.Random;
44258057Sbrimport java.util.Set;
45258057Sbrimport java.util.concurrent.atomic.AtomicInteger;
46258057Sbrimport java.util.concurrent.atomic.LongAdder;
47258057Sbrimport jdk.internal.dynalink.ChainedCallSite;
48258057Sbrimport jdk.internal.dynalink.DynamicLinker;
49258057Sbrimport jdk.internal.dynalink.linker.GuardedInvocation;
50258057Sbrimport jdk.nashorn.internal.runtime.Context;
51258057Sbrimport jdk.nashorn.internal.runtime.Debug;
52258057Sbrimport jdk.nashorn.internal.runtime.ScriptObject;
53258057Sbrimport jdk.nashorn.internal.runtime.ScriptRuntime;
54258057Sbrimport jdk.nashorn.internal.runtime.options.Options;
55258057Sbr
56258057Sbr
57258057Sbr/**
58258057Sbr * Relinkable form of call site.
59258057Sbr */
60258057Sbrpublic class LinkerCallSite extends ChainedCallSite {
61258057Sbr    /** Maximum number of arguments passed directly. */
62258057Sbr    public static final int ARGLIMIT = 250;
63258057Sbr
64258057Sbr    private static final String PROFILEFILE = Options.getStringProperty("nashorn.profilefile", "NashornProfile.txt");
65258057Sbr
66258057Sbr    private static final MethodHandle INCREASE_MISS_COUNTER = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "increaseMissCount", MH.type(Object.class, String.class, Object.class));
67258057Sbr    private static final MethodHandle ON_CATCH_INVALIDATION = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "onCatchInvalidation", MH.type(ChainedCallSite.class, LinkerCallSite.class));
68258057Sbr
69258057Sbr    private int catchInvalidations;
70258057Sbr
71258057Sbr    LinkerCallSite(final NashornCallSiteDescriptor descriptor) {
72258057Sbr        super(descriptor);
73258057Sbr        if (Context.DEBUG) {
74258057Sbr            LinkerCallSite.count.increment();
75258057Sbr        }
76258057Sbr    }
77258057Sbr
78258057Sbr    @Override
79258057Sbr    protected MethodHandle getPruneCatches() {
80258057Sbr        return MH.filterArguments(super.getPruneCatches(), 0, ON_CATCH_INVALIDATION);
81258057Sbr    }
82258057Sbr
83258057Sbr    /**
84258057Sbr     * Action to perform when a catch guard around a callsite triggers. Increases
85258057Sbr     * catch invalidation counter
86258057Sbr     * @param callSite callsite
87258057Sbr     * @return the callsite, so this can be used as argument filter
88258057Sbr     */
89258057Sbr    @SuppressWarnings("unused")
90258057Sbr    private static ChainedCallSite onCatchInvalidation(final LinkerCallSite callSite) {
91258057Sbr        ++callSite.catchInvalidations;
92258057Sbr        return callSite;
93258057Sbr    }
94258057Sbr
95258057Sbr    /**
96258057Sbr     * Get the number of catch invalidations that have happened at this call site so far
97258057Sbr     * @param callSiteToken call site token, unique to the callsite.
98258057Sbr     * @return number of catch invalidations, i.e. thrown exceptions caught by the linker
99258057Sbr     */
100258057Sbr    public static int getCatchInvalidationCount(final Object callSiteToken) {
101258057Sbr        if (callSiteToken instanceof LinkerCallSite) {
102258057Sbr            return ((LinkerCallSite)callSiteToken).catchInvalidations;
103258057Sbr        }
104258057Sbr        return 0;
105258057Sbr    }
106258057Sbr    /**
107258057Sbr     * Construct a new linker call site.
108258057Sbr     * @param name     Name of method.
109258057Sbr     * @param type     Method type.
110258057Sbr     * @param flags    Call site specific flags.
111258057Sbr     * @return New LinkerCallSite.
112258057Sbr     */
113258057Sbr    static LinkerCallSite newLinkerCallSite(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int flags) {
114258057Sbr        final NashornCallSiteDescriptor desc = NashornCallSiteDescriptor.get(lookup, name, type, flags);
115258057Sbr
116258057Sbr        if (desc.isProfile()) {
117258057Sbr            return ProfilingLinkerCallSite.newProfilingLinkerCallSite(desc);
118258057Sbr        }
119258057Sbr
120258057Sbr        if (desc.isTrace()) {
121258057Sbr            return new TracingLinkerCallSite(desc);
122258057Sbr        }
123258057Sbr
124258057Sbr        return new LinkerCallSite(desc);
125258057Sbr    }
126258057Sbr
127258057Sbr    @Override
128258057Sbr    public String toString() {
129258057Sbr        return getDescriptor().toString();
130258057Sbr    }
131258057Sbr
132258057Sbr    /**
133258057Sbr     * Get the descriptor for this callsite
134258057Sbr     * @return a {@link NashornCallSiteDescriptor}
135258057Sbr     */
136258057Sbr    public NashornCallSiteDescriptor getNashornDescriptor() {
137258057Sbr        return (NashornCallSiteDescriptor)getDescriptor();
138258057Sbr    }
139258057Sbr
140258057Sbr    @Override
141258057Sbr    public void relink(final GuardedInvocation invocation, final MethodHandle relink) {
142258057Sbr        super.relink(invocation, getDebuggingRelink(relink));
143258057Sbr    }
144258057Sbr
145258057Sbr    @Override
146258057Sbr    public void resetAndRelink(final GuardedInvocation invocation, final MethodHandle relink) {
147258057Sbr        super.resetAndRelink(invocation, getDebuggingRelink(relink));
148258057Sbr    }
149258057Sbr
150258057Sbr    private MethodHandle getDebuggingRelink(final MethodHandle relink) {
151258057Sbr        if (Context.DEBUG) {
152258057Sbr            return MH.filterArguments(relink, 0, getIncreaseMissCounter(relink.type().parameterType(0)));
153258057Sbr        }
154258057Sbr        return relink;
155258057Sbr    }
156258057Sbr
157258057Sbr    private MethodHandle getIncreaseMissCounter(final Class<?> type) {
158258057Sbr        final MethodHandle missCounterWithDesc = MH.bindTo(INCREASE_MISS_COUNTER, getDescriptor().getName() + " @ " + getScriptLocation());
159258057Sbr        if (type == Object.class) {
160258057Sbr            return missCounterWithDesc;
161258057Sbr        }
162258057Sbr        return MH.asType(missCounterWithDesc, missCounterWithDesc.type().changeParameterType(0, type).changeReturnType(type));
163258057Sbr    }
164258057Sbr
165258057Sbr    private static String getScriptLocation() {
166258057Sbr        final StackTraceElement caller = DynamicLinker.getLinkedCallSiteLocation();
167258057Sbr        return caller == null ? "unknown location" : (caller.getFileName() + ":" + caller.getLineNumber());
168258057Sbr    }
169258057Sbr
170258057Sbr    /**
171258057Sbr     * Instrumentation - increase the miss count when a callsite misses. Used as filter
172258057Sbr     * @param desc descriptor for table entry
173261410Sian     * @param self self reference
174261410Sian     * @return self reference
175261410Sian     */
176258057Sbr    public static Object increaseMissCount(final String desc, final Object self) {
177258057Sbr        missCount.increment();
178258057Sbr        if (r.nextInt(100) < missSamplingPercentage) {
179258057Sbr            final AtomicInteger i = missCounts.get(desc);
180258057Sbr            if (i == null) {
181258057Sbr                missCounts.put(desc, new AtomicInteger(1));
182258057Sbr            } else {
183258057Sbr                i.incrementAndGet();
184258057Sbr            }
185258057Sbr        }
186258057Sbr        return self;
187258057Sbr    }
188258057Sbr
189258057Sbr    /*
190258057Sbr     * Debugging call sites.
191258057Sbr     */
192258057Sbr
193258057Sbr    private static class ProfilingLinkerCallSite extends LinkerCallSite {
194258057Sbr        /** List of all profiled call sites. */
195258057Sbr        private static LinkedList<ProfilingLinkerCallSite> profileCallSites = null;
196258057Sbr
197258057Sbr        /** Start time when entered at zero depth. */
198258057Sbr        private long startTime;
199258057Sbr
200258057Sbr        /** Depth of nested calls. */
201258057Sbr        private int depth;
202258057Sbr
203258057Sbr        /** Total time spent in this call site. */
204258057Sbr        private long totalTime;
205258057Sbr
206258057Sbr        /** Total number of times call site entered. */
207258057Sbr        private long hitCount;
208258057Sbr
209258057Sbr        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
210258057Sbr
211258057Sbr        private static final MethodHandle PROFILEENTRY    = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileEntry",    MH.type(Object.class, Object.class));
212258057Sbr        private static final MethodHandle PROFILEEXIT     = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileExit",     MH.type(Object.class, Object.class));
213258057Sbr        private static final MethodHandle PROFILEVOIDEXIT = MH.findVirtual(LOOKUP, ProfilingLinkerCallSite.class, "profileVoidExit", MH.type(void.class));
214258057Sbr
215258057Sbr        /*
216258057Sbr         * Constructor
217258057Sbr         */
218258057Sbr
219258057Sbr        ProfilingLinkerCallSite(final NashornCallSiteDescriptor desc) {
220258057Sbr           super(desc);
221258057Sbr        }
222258057Sbr
223258057Sbr        public static ProfilingLinkerCallSite newProfilingLinkerCallSite(final NashornCallSiteDescriptor desc) {
224258057Sbr            if (profileCallSites == null) {
225258057Sbr                profileCallSites = new LinkedList<>();
226258057Sbr
227258057Sbr                final Thread profileDumperThread = new Thread(new ProfileDumper());
228258057Sbr                Runtime.getRuntime().addShutdownHook(profileDumperThread);
229258057Sbr            }
230258057Sbr
231258057Sbr            final ProfilingLinkerCallSite callSite = new ProfilingLinkerCallSite(desc);
232258057Sbr            profileCallSites.add(callSite);
233258057Sbr
234258057Sbr            return callSite;
235258057Sbr        }
236258057Sbr
237258057Sbr        @Override
238258057Sbr        public void setTarget(final MethodHandle newTarget) {
239258057Sbr            final MethodType type   = type();
240258057Sbr            final boolean    isVoid = type.returnType() == void.class;
241258057Sbr
242258057Sbr            MethodHandle methodHandle = MH.filterArguments(newTarget, 0, MH.bindTo(PROFILEENTRY, this));
243258057Sbr
244258057Sbr            if (isVoid) {
245258057Sbr                methodHandle = MH.filterReturnValue(methodHandle, MH.bindTo(PROFILEVOIDEXIT, this));
246258057Sbr            } else {
247258057Sbr                final MethodType filter = MH.type(type.returnType(), type.returnType());
248258057Sbr                methodHandle = MH.filterReturnValue(methodHandle, MH.asType(MH.bindTo(PROFILEEXIT, this), filter));
249258057Sbr            }
250258057Sbr
251258057Sbr            super.setTarget(methodHandle);
252258057Sbr        }
253258057Sbr
254258057Sbr        /**
255258057Sbr         * Start the clock for a profile entry and increase depth
256258057Sbr         * @param self argument to filter
257258057Sbr         * @return preserved argument
258258057Sbr         */
259258057Sbr        @SuppressWarnings("unused")
260258057Sbr        public Object profileEntry(final Object self) {
261258057Sbr            if (depth == 0) {
262258057Sbr                startTime = System.nanoTime();
263258057Sbr            }
264258057Sbr
265258057Sbr            depth++;
266258057Sbr            hitCount++;
267258057Sbr
268258057Sbr            return self;
269258057Sbr        }
270258057Sbr
271258057Sbr        /**
272258057Sbr         * Decrease depth and stop the clock for a profile entry
273258057Sbr         * @param result return value to filter
274258057Sbr         * @return preserved argument
275258057Sbr         */
276258057Sbr        @SuppressWarnings("unused")
277258057Sbr        public Object profileExit(final Object result) {
278258057Sbr            depth--;
279258057Sbr
280258057Sbr            if (depth == 0) {
281258057Sbr                totalTime += System.nanoTime() - startTime;
282258057Sbr            }
283258057Sbr
284258057Sbr            return result;
285258057Sbr        }
286258057Sbr
287258057Sbr        /**
288258057Sbr         * Decrease depth without return value filter
289258057Sbr         */
290258057Sbr        @SuppressWarnings("unused")
291258057Sbr        public void profileVoidExit() {
292258057Sbr            depth--;
293258057Sbr
294258057Sbr            if (depth == 0) {
295258057Sbr                totalTime += System.nanoTime() - startTime;
296258057Sbr            }
297258057Sbr        }
298258057Sbr
299258057Sbr        static class ProfileDumper implements Runnable {
300258057Sbr            @Override
301258057Sbr            public void run() {
302258057Sbr                PrintWriter out    = null;
303258057Sbr                boolean fileOutput = false;
304258057Sbr
305258057Sbr                try {
306258057Sbr                    try {
307258057Sbr                        out = new PrintWriter(new FileOutputStream(PROFILEFILE));
308258057Sbr                        fileOutput = true;
309258057Sbr                    } catch (final FileNotFoundException e) {
310258057Sbr                        out = Context.getCurrentErr();
311258057Sbr                    }
312258057Sbr
313258057Sbr                    dump(out);
314258057Sbr                } finally {
315258057Sbr                    if (out != null && fileOutput) {
316258057Sbr                        out.close();
317258057Sbr                    }
318258057Sbr                }
319258057Sbr            }
320258057Sbr
321258057Sbr            private static void dump(final PrintWriter out) {
322258057Sbr                int index = 0;
323258057Sbr                for (final ProfilingLinkerCallSite callSite : profileCallSites) {
324258057Sbr                   out.println("" + (index++) + '\t' +
325258057Sbr                                  callSite.getDescriptor().getName() + '\t' +
326258057Sbr                                  callSite.totalTime + '\t' +
327258057Sbr                                  callSite.hitCount);
328258057Sbr                }
329258057Sbr            }
330258057Sbr        }
331258057Sbr    }
332258057Sbr
333258057Sbr    /**
334258057Sbr     * Debug subclass for LinkerCallSite that allows tracing
335258057Sbr     */
336258057Sbr    private static class TracingLinkerCallSite extends LinkerCallSite {
337258057Sbr        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
338258057Sbr
339258057Sbr        private static final MethodHandle TRACEOBJECT = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceObject", MH.type(Object.class, MethodHandle.class, Object[].class));
340258057Sbr        private static final MethodHandle TRACEVOID   = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceVoid", MH.type(void.class, MethodHandle.class, Object[].class));
341258057Sbr        private static final MethodHandle TRACEMISS   = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceMiss", MH.type(void.class, String.class, Object[].class));
342258057Sbr
343258057Sbr        TracingLinkerCallSite(final NashornCallSiteDescriptor desc) {
344258057Sbr           super(desc);
345258057Sbr        }
346258057Sbr
347258057Sbr        @Override
348258057Sbr        public void setTarget(final MethodHandle newTarget) {
349258057Sbr            if (!getNashornDescriptor().isTraceEnterExit()) {
350258057Sbr                super.setTarget(newTarget);
351258057Sbr                return;
352258057Sbr            }
353258057Sbr
354258057Sbr            final MethodType type = type();
355258057Sbr            final boolean isVoid = type.returnType() == void.class;
356258057Sbr
357258057Sbr            MethodHandle traceMethodHandle = isVoid ? TRACEVOID : TRACEOBJECT;
358258057Sbr            traceMethodHandle = MH.bindTo(traceMethodHandle, this);
359258057Sbr            traceMethodHandle = MH.bindTo(traceMethodHandle, newTarget);
360258057Sbr            traceMethodHandle = MH.asCollector(traceMethodHandle, Object[].class, type.parameterCount());
361258057Sbr            traceMethodHandle = MH.asType(traceMethodHandle, type);
362258057Sbr
363258057Sbr            super.setTarget(traceMethodHandle);
364258057Sbr        }
365258057Sbr
366258057Sbr        @Override
367258057Sbr        public void initialize(final MethodHandle relinkAndInvoke) {
368258057Sbr            super.initialize(getFallbackLoggingRelink(relinkAndInvoke));
369258057Sbr        }
370258057Sbr
371258057Sbr        @Override
372258057Sbr        public void relink(final GuardedInvocation invocation, final MethodHandle relink) {
373258057Sbr            super.relink(invocation, getFallbackLoggingRelink(relink));
374258057Sbr        }
375258057Sbr
376258057Sbr        @Override
377258057Sbr        public void resetAndRelink(final GuardedInvocation invocation, final MethodHandle relink) {
378258057Sbr            super.resetAndRelink(invocation, getFallbackLoggingRelink(relink));
379258057Sbr        }
380258057Sbr
381258057Sbr        private MethodHandle getFallbackLoggingRelink(final MethodHandle relink) {
382258057Sbr            if (!getNashornDescriptor().isTraceMisses()) {
383258057Sbr                // If we aren't tracing misses, just return relink as-is
384258057Sbr                return relink;
385258057Sbr            }
386258057Sbr            final MethodType type = relink.type();
387258057Sbr            return MH.foldArguments(relink, MH.asType(MH.asCollector(MH.insertArguments(TRACEMISS, 0, this, "MISS " + getScriptLocation() + " "), Object[].class, type.parameterCount()), type.changeReturnType(void.class)));
388258057Sbr        }
389258057Sbr
390258057Sbr        private void printObject(final PrintWriter out, final Object arg) {
391258057Sbr            if (!getNashornDescriptor().isTraceObjects()) {
392258057Sbr                out.print((arg instanceof ScriptObject) ? "ScriptObject" : arg);
393258057Sbr                return;
394258057Sbr            }
395258057Sbr
396258057Sbr            if (arg instanceof ScriptObject) {
397258057Sbr                final ScriptObject object = (ScriptObject)arg;
398258057Sbr
399258057Sbr                boolean isFirst = true;
400258057Sbr                final Set<Object> keySet = object.keySet();
401258057Sbr
402258057Sbr                if (keySet.isEmpty()) {
403258057Sbr                    out.print(ScriptRuntime.safeToString(arg));
404258057Sbr                } else {
405258057Sbr                    out.print("{ ");
406258057Sbr
407258057Sbr                    for (final Object key : keySet) {
408258057Sbr                        if (!isFirst) {
409258057Sbr                            out.print(", ");
410258057Sbr                        }
411258057Sbr
412258057Sbr                        out.print(key);
413258057Sbr                        out.print(":");
414258057Sbr
415258057Sbr                        final Object value = object.get(key);
416258057Sbr
417258057Sbr                        if (value instanceof ScriptObject) {
418258057Sbr                            out.print("...");
419258057Sbr                        } else {
420258057Sbr                            printObject(out, value);
421258057Sbr                        }
422258057Sbr
423258057Sbr                        isFirst = false;
424258057Sbr                    }
425258057Sbr
426258057Sbr                    out.print(" }");
427258057Sbr                }
428258057Sbr             } else {
429258057Sbr                out.print(ScriptRuntime.safeToString(arg));
430258057Sbr            }
431258057Sbr        }
432258057Sbr
433258057Sbr        private void tracePrint(final PrintWriter out, final String tag, final Object[] args, final Object result) {
434258057Sbr            //boolean isVoid = type().returnType() == void.class;
435258057Sbr            out.print(Debug.id(this) + " TAG " + tag);
436258057Sbr            out.print(getDescriptor().getName() + "(");
437258057Sbr
438258057Sbr            if (args.length > 0) {
439258057Sbr                printObject(out, args[0]);
440258057Sbr                for (int i = 1; i < args.length; i++) {
441258057Sbr                    final Object arg = args[i];
442258057Sbr                    out.print(", ");
443258057Sbr
444258057Sbr                    if (!(arg instanceof ScriptObject && ((ScriptObject)arg).isScope())) {
445258057Sbr                        printObject(out, arg);
446258057Sbr                    } else {
447258057Sbr                        out.print("SCOPE");
448258057Sbr                    }
449258057Sbr                }
450258057Sbr            }
451258057Sbr
452258057Sbr            out.print(")");
453258057Sbr
454258057Sbr            if (tag.equals("EXIT  ")) {
455258057Sbr                out.print(" --> ");
456258057Sbr                printObject(out, result);
457258057Sbr            }
458258057Sbr
459258057Sbr            out.println();
460258057Sbr        }
461258057Sbr
462258057Sbr        /**
463258057Sbr         * Trace event. Wrap an invocation with a return value
464258057Sbr         *
465258057Sbr         * @param mh     invocation handle
466258057Sbr         * @param args   arguments to call
467258057Sbr         *
468258057Sbr         * @return return value from invocation
469258057Sbr         *
470258057Sbr         * @throws Throwable if invocation fails or throws exception/error
471258057Sbr         */
472258057Sbr        @SuppressWarnings("unused")
473258057Sbr        public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable {
474258057Sbr            final PrintWriter out = Context.getCurrentErr();
475258057Sbr            tracePrint(out, "ENTER ", args, null);
476258057Sbr            final Object result = mh.invokeWithArguments(args);
477258057Sbr            tracePrint(out, "EXIT  ", args, result);
478258057Sbr
479258057Sbr            return result;
480258057Sbr        }
481258057Sbr
482258057Sbr        /**
483258057Sbr         * Trace event. Wrap an invocation that returns void
484258057Sbr         *
485258057Sbr         * @param mh     invocation handle
486258057Sbr         * @param args   arguments to call
487258057Sbr         *
488258057Sbr         * @throws Throwable if invocation fails or throws exception/error
489258057Sbr         */
490258057Sbr        @SuppressWarnings("unused")
491258057Sbr        public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable {
492258057Sbr            final PrintWriter out = Context.getCurrentErr();
493258057Sbr            tracePrint(out, "ENTER ", args, null);
494258057Sbr            mh.invokeWithArguments(args);
495258057Sbr            tracePrint(out, "EXIT  ", args, null);
496258057Sbr        }
497258057Sbr
498258057Sbr        /**
499258057Sbr         * Tracer function that logs a callsite miss
500258057Sbr         *
501258057Sbr         * @param desc callsite descriptor string
502258057Sbr         * @param args arguments to function
503258057Sbr         *
504258057Sbr         * @throws Throwable if invocation fails or throws exception/error
505258057Sbr         */
506258057Sbr        @SuppressWarnings("unused")
507258057Sbr        public void traceMiss(final String desc, final Object... args) throws Throwable {
508258057Sbr            tracePrint(Context.getCurrentErr(), desc, args, null);
509258057Sbr        }
510258057Sbr    }
511258057Sbr
512258057Sbr    // counters updated in debug mode
513258057Sbr    private static LongAdder count;
514258057Sbr    private static final HashMap<String, AtomicInteger> missCounts = new HashMap<>();
515258057Sbr    private static LongAdder missCount;
516258057Sbr    private static final Random r = new Random();
517258057Sbr    private static final int missSamplingPercentage = Options.getIntProperty("nashorn.tcs.miss.samplePercent", 1);
518258057Sbr
519258057Sbr    static {
520258057Sbr        if (Context.DEBUG) {
521258057Sbr            count = new LongAdder();
522258057Sbr            missCount = new LongAdder();
523258057Sbr        }
524258057Sbr    }
525258057Sbr
526258057Sbr    @Override
527    protected int getMaxChainLength() {
528        return 8;
529    }
530
531    /**
532     * Get the callsite count
533     * @return the count
534     */
535    public static long getCount() {
536        return count.longValue();
537    }
538
539    /**
540     * Get the callsite miss count
541     * @return the missCount
542     */
543    public static long getMissCount() {
544        return missCount.longValue();
545    }
546
547    /**
548     * Get given miss sampling percentage for sampler. Default is 1%. Specified with -Dnashorn.tcs.miss.samplePercent=x
549     * @return miss sampling percentage
550     */
551    public static int getMissSamplingPercentage() {
552        return missSamplingPercentage;
553    }
554
555    /**
556     * Dump the miss counts collected so far to a given output stream
557     * @param out print stream
558     */
559    public static void getMissCounts(final PrintWriter out) {
560        final ArrayList<Entry<String, AtomicInteger>> entries = new ArrayList<>(missCounts.entrySet());
561
562        Collections.sort(entries, new Comparator<Map.Entry<String, AtomicInteger>>() {
563            @Override
564            public int compare(final Entry<String, AtomicInteger> o1, final Entry<String, AtomicInteger> o2) {
565                return o2.getValue().get() - o1.getValue().get();
566            }
567        });
568
569        for (final Entry<String, AtomicInteger> entry : entries) {
570            out.println("  " + entry.getKey() + "\t" + entry.getValue().get());
571        }
572    }
573
574}
575