/* * Copyright 2008-2010 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.btrace; import java.lang.management.ManagementFactory; import static java.lang.management.ManagementFactory.*; import java.lang.instrument.Instrumentation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.LinkedBlockingQueue; import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.btrace.aggregation.Aggregation; import com.sun.btrace.aggregation.AggregationKey; import com.sun.btrace.aggregation.AggregationFunction; import com.sun.btrace.annotations.OnError; import com.sun.btrace.annotations.OnExit; import com.sun.btrace.annotations.OnTimer; import com.sun.btrace.annotations.OnEvent; import com.sun.btrace.annotations.OnLowMemory; import com.sun.btrace.comm.Command; import com.sun.btrace.comm.ErrorCommand; import com.sun.btrace.comm.EventCommand; import com.sun.btrace.comm.ExitCommand; import com.sun.btrace.comm.MessageCommand; import com.sun.btrace.comm.NumberDataCommand; import com.sun.btrace.comm.NumberMapDataCommand; import com.sun.btrace.comm.StringMapDataCommand; import com.sun.btrace.comm.GridDataCommand; import com.sun.btrace.profiling.MethodInvocationProfiler; import java.lang.management.GarbageCollectorMXBean; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.ObjectOutputStream; import java.lang.management.LockInfo; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryNotificationInfo; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.lang.management.MonitorInfo; import java.lang.management.RuntimeMXBean; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; import javax.management.ListenerNotFoundException; import javax.management.MBeanServer; import javax.management.Notification; import javax.management.NotificationEmitter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import sun.misc.Perf; import sun.misc.Unsafe; import sun.reflect.Reflection; import sun.security.action.GetPropertyAction; /** * Helper class used by BTrace built-in functions and * also acts runtime "manager" for a specific BTrace client * and sends Commands to the CommandListener passed. * * @author A. Sundararajan * @author Christian Glencross (aggregation support) * @author Joachim Skeie (GC MBean support, advanced Deque manipulation) * @author KLynch */ public final class BTraceRuntime { // we need Unsafe to load BTrace class bytes as // bootstrap class private static final Unsafe unsafe = Unsafe.getUnsafe(); private static Properties dotWriterProps; // a dummy BTraceRuntime instance private static BTraceRuntime dummy; private static BTraceRuntime NULL; // are we running with DTrace support enabled? private static boolean dtraceEnabled; private static final boolean messageTimestamp = false; private static final String LINE_SEPARATOR; final private static Thread samplerThread; volatile public static long TIMESTAMP = 0L; static { dummy = new BTraceRuntime(); NULL = new BTraceRuntime(); LINE_SEPARATOR = System.getProperty("line.separator"); if (Boolean.getBoolean("btrace.timer.sampled")) { final long interval = Long.parseLong(System.getProperty("btrace.timer.sampled.interval", "500")); long time = System.nanoTime(); for(int i=0;i<1000;i++) { unsafe.park(false, interval); } time = System.nanoTime() - time; final long step = (long)(time / 1000); samplerThread = new Thread(new Runnable() { public void run() { while (true) { unsafe.park(false, interval); TIMESTAMP+=step; } } }, "BTrace Sampled Timer"); samplerThread.setDaemon(true); samplerThread.start(); } else { samplerThread = null; } } private static ThreadEnteredMap map = new ThreadEnteredMap(NULL); // BTraceRuntime against BTrace class name private static Map<String, BTraceRuntime> runtimes = Collections.synchronizedMap(new HashMap<String, BTraceRuntime>()); // jvmstat related stuff // to read and write perf counters private static volatile Perf perf; // interface to read perf counters of this process private static volatile PerfReader perfReader; // performance counters created by this client private static Map<String, ByteBuffer> counters = new HashMap<String, ByteBuffer>(); // Few MBeans used to implement certain built-in functions private static volatile HotSpotDiagnosticMXBean hotspotMBean; private static volatile MemoryMXBean memoryMBean; private static volatile RuntimeMXBean runtimeMBean; private static volatile ThreadMXBean threadMBean; private static volatile List<GarbageCollectorMXBean> gcBeanList; private static volatile List<MemoryPoolMXBean> memPoolList; // bytecode generator that generates Runnable implementations private static RunnableGenerator runnableGenerator; // Per-client state starts here. // current thread's exception private ThreadLocal<Throwable> currentException = new ThreadLocal<Throwable>(); // "command line" args supplied by client private String[] args; // whether current runtime has been disabled? private volatile boolean disabled; // Class object of the BTrace class [of this client] private String className; // BTrace Class object corresponding to this client private Class clazz; // does the client have exit action? private Method exitHandler; // does the client have exception handler action? private Method exceptionHandler; // array of timer callback methods private Method[] timerHandlers; // map of client event handling methods private Map<String, Method> eventHandlers; // low memory handlers private Map<String, Method> lowMemHandlers; // timer to run profile provider actions private volatile Timer timer; // executer to run low memory handlers private volatile ExecutorService threadPool; // Memory MBean listener private volatile NotificationListener memoryListener; // Command queue for the client private volatile LinkedBlockingQueue<Command> queue; private static class SpeculativeQueueManager { // maximum number of speculative buffers private static final int MAX_SPECULATIVE_BUFFERS = Short.MAX_VALUE; // per buffer message limit private static final int MAX_SPECULATIVE_MSG_LIMIT = Short.MAX_VALUE; // next speculative buffer id private int nextSpeculationId; // speculative buffers map private ConcurrentHashMap<Integer, LinkedBlockingQueue<Command>> speculativeQueues; // per thread current speculative buffer id private ThreadLocal<Integer> currentSpeculationId; SpeculativeQueueManager() { speculativeQueues = new ConcurrentHashMap<Integer, LinkedBlockingQueue<Command>>(); currentSpeculationId = new ThreadLocal<Integer>(); } void clear() { speculativeQueues.clear(); speculativeQueues = null; currentSpeculationId.remove(); currentSpeculationId = null; } int speculation() { int nextId = getNextSpeculationId(); if (nextId != -1) { speculativeQueues.put(nextId, new LinkedBlockingQueue<Command>(MAX_SPECULATIVE_MSG_LIMIT)); } return nextId; } boolean send(Command cmd) { Integer curId = currentSpeculationId.get(); if ((curId != null) && (cmd.getType() != Command.EXIT)) { LinkedBlockingQueue<Command> sb = speculativeQueues.get(curId); if (sb != null) { try { sb.add(cmd); } catch (IllegalStateException ise) { sb.clear(); sb.add(new MessageCommand("speculative buffer overflow: " + curId)); } return true; } } return false; } void speculate(int id) { validateId(id); currentSpeculationId.set(id); } void commit(int id, LinkedBlockingQueue<Command> result) { validateId(id); currentSpeculationId.set(null); LinkedBlockingQueue<Command> sb = speculativeQueues.get(id); if (sb != null) { result.addAll(sb); sb.clear(); } } void discard(int id) { validateId(id); currentSpeculationId.set(null); speculativeQueues.get(id).clear(); } // -- Internals only below this point private synchronized int getNextSpeculationId() { if (nextSpeculationId == MAX_SPECULATIVE_BUFFERS) { return -1; } return nextSpeculationId++; } private void validateId(int id) { if (! speculativeQueues.containsKey(id)) { throw new RuntimeException("invalid speculative buffer id: " + id); } } } // per client speculative buffer manager private volatile SpeculativeQueueManager specQueueManager; // background thread that sends Commands to the handler private volatile Thread cmdThread; // CommandListener that receives the Commands private volatile CommandListener cmdListener; private Instrumentation instrumentation; private BTraceRuntime() { } public BTraceRuntime(final String className, String[] args, final CommandListener cmdListener, Instrumentation inst) { this.args = args; this.queue = new LinkedBlockingQueue<Command>(); this.specQueueManager = new SpeculativeQueueManager(); this.cmdListener = cmdListener; this.className = className; this.instrumentation = inst; runtimes.put(className, this); this.cmdThread = new Thread(new Runnable() { public void run() { try { BTraceRuntime.enter(); while (true) { Command cmd = queue.take(); cmdListener.onCommand(cmd); if (cmd.getType() == Command.EXIT) { return; } } } catch (InterruptedException ignored) { } catch (IOException ignored) { } finally { runtimes.put(className, NULL); queue.clear(); specQueueManager.clear(); BTraceRuntime.leave(); disabled = true; } } }); cmdThread.setDaemon(true); cmdThread.start(); } public static boolean classNameExists(String name) { return runtimes.containsKey(name); } public static void init(PerfReader perfRead, RunnableGenerator runGen) { // Class caller = Reflection.getCallerClass(2); // if (! caller.getName().equals("com.sun.btrace.agent.Client")) { // // workaround for "Reflection.getCallerClass(int)" problem // // in JDK7u25 - requiring one additional frame to get the caller // if (caller.getName().equals("com.sun.btrace.BTraceRuntime")) { // caller = Reflection.getCallerClass(3); // } // if (! caller.getName().equals("com.sun.btrace.agent.Client")) { // throw new SecurityException("unsafe init"); // } // } perfReader = perfRead; runnableGenerator = runGen; loadLibrary(perfRead.getClass().getClassLoader()); } public Class defineClass(byte[] code) { checkAccess(); return defineClassImpl(code, true); } public Class defineClass(byte[] code, boolean mustBeBootstrap) { checkAccess(); return defineClassImpl(code, mustBeBootstrap); } private void checkAccess() { Class caller = Reflection.getCallerClass(2); if (! caller.getName().equals("com.sun.btrace.agent.Client")) { // workaround for "Reflection.getCallerClass(int)" problem // in JDK7u25 - requiring one additional frame to get the caller if (caller.getName().equals("com.sun.btrace.BTraceRuntime")) { caller = Reflection.getCallerClass(3); } if (! caller.getName().equals("com.sun.btrace.agent.Client")) { // throw new SecurityException("unsafe defineClass"); } } } /** * Enter method is called by every probed method just * before the probe actions start. */ public static boolean enter(BTraceRuntime current) { if (current.disabled) return false; return map.enter(current); // // check we have entered already or disabled // if (current.disabled || (tls.get() != null)) { // return false; // } else { // tls.set(current); // return true; // } } public static boolean enter() { return enter(dummy); } /** * Leave method is called by every probed method just * before the probe actions end (and actual probed * method continues). */ public static void leave() { map.exit(); // tls.remove(); } /** * start method is called by every BTrace (preprocesed) class * just at the end of it's class initializer. */ public static void start() { BTraceRuntime current = getCurrent(); if (current != null) { current.startImpl(); } } public void handleExit(int exitCode) { exitImpl(exitCode); } public void handleEvent(EventCommand ecmd) { if (eventHandlers != null) { String event = ecmd.getEvent(); Method eventHandler = eventHandlers.get(event); if (eventHandler != null) { // BTraceRuntime oldRuntime = tls.get(); BTraceRuntime oldRuntime = (BTraceRuntime)map.get(); leave(); try { eventHandler.invoke(null, (Object[])null); } catch (Throwable ignored) { } finally { if (oldRuntime != null) { enter(oldRuntime); } } } } } /** * One instance of BTraceRuntime is created per-client. * This forClass method creates it. Class passed is the * preprocessed BTrace program of the client. */ public static BTraceRuntime forClass(Class cl) { BTraceRuntime runtime = runtimes.get(cl.getName()); runtime.init(cl); return runtime; } /** * Utility to create a new ThreadLocal object. Called * by preprocessed BTrace class to create ThreadLocal * for each @TLS variable. * @param initValue Initial value. * This value must be either a boxed primitive or {@linkplain Cloneable}. * In case a {@linkplain Cloneable} value is provided the value is never used directly * - instead, a new clone of the value is created per thread. */ public static ThreadLocal newThreadLocal( final Object initValue) { return new ThreadLocal() { @Override protected Object initialValue() { if (initValue == null) return initValue; if (initValue instanceof Cloneable) { try { Class clz = initValue.getClass(); Method m = clz.getDeclaredMethod("clone"); m.setAccessible(true); return m.invoke(initValue); } catch (Exception e) { e.printStackTrace(); return null; } } return initValue; } }; } // The following constants are copied from VM code // for jvmstat. // perf counter variability - we always variable variability private static final int V_Variable = 3; // perf counter units private static final int V_None = 1; private static final int V_String = 5; private static final int PERF_STRING_LIMIT = 256; /** * Utility to create a new jvmstat perf counter. Called * by preprocessed BTrace class to create perf counter * for each @Export variable. */ public static void newPerfCounter(String name, String desc, Object value) { Perf perf = getPerf(); char tc = desc.charAt(0); switch (tc) { case 'C': case 'Z': case 'B': case 'S': case 'I': case 'J': case 'F': case 'D': { long initValue = (value != null)? ((Number)value).longValue() : 0L; ByteBuffer b = perf.createLong(name, V_Variable, V_None, initValue); b.order(ByteOrder.nativeOrder()); counters.put(name, b); } break; case '[': break; case 'L': { if (desc.equals("Ljava/lang/String;")) { byte[] buf; if (value != null) { buf = getStringBytes((String)value); } else { buf = new byte[PERF_STRING_LIMIT]; buf[0] = '\0'; } ByteBuffer b = perf.createByteArray(name, V_Variable, V_String, buf, buf.length); counters.put(name, b); } } break; } } /** * Return the value of integer perf. counter of given name. */ public static int getPerfInt(String name) { return (int) getPerfLong(name); } /** * Write the value of integer perf. counter of given name. */ public static void putPerfInt(int value, String name) { long l = (long)value; putPerfLong(l, name); } /** * Return the value of float perf. counter of given name. */ public static float getPerfFloat(String name) { int val = getPerfInt(name); return Float.intBitsToFloat(val); } /** * Write the value of float perf. counter of given name. */ public static void putPerfFloat(float value, String name) { int i = Float.floatToRawIntBits(value); putPerfInt(i, name); } /** * Return the value of long perf. counter of given name. */ public static long getPerfLong(String name) { ByteBuffer b = counters.get(name); synchronized(b) { long l = b.getLong(); b.rewind(); return l; } } /** * Write the value of float perf. counter of given name. */ public static void putPerfLong(long value, String name) { ByteBuffer b = counters.get(name); synchronized (b) { b.putLong(value); b.rewind(); } } /** * Return the value of double perf. counter of given name. */ public static double getPerfDouble(String name) { long val = getPerfLong(name); return Double.longBitsToDouble(val); } /** * write the value of double perf. counter of given name. */ public static void putPerfDouble(double value, String name) { long l = Double.doubleToRawLongBits(value); putPerfLong(l, name); } /** * Return the value of String perf. counter of given name. */ public static String getPerfString(String name) { ByteBuffer b = counters.get(name); byte[] buf = new byte[b.limit()]; byte t = (byte)0; int i = 0; synchronized (b) { while ((t = b.get()) != '\0') { buf[i++] = t; } b.rewind(); } try { return new String(buf, 0, i, "UTF-8"); } catch (java.io.UnsupportedEncodingException e) { // ignore, UTF-8 encoding is always known } return ""; } /** * Write the value of float perf. counter of given name. */ public static void putPerfString(String value, String name) { ByteBuffer b = counters.get(name); byte[] v = getStringBytes(value); synchronized (b) { b.put(v); b.rewind(); } } /** * Handles exception from BTrace probe actions. */ public static void handleException(Throwable th) { BTraceRuntime current = getCurrent(); if (current != null) { current.handleExceptionImpl(th); } else { th.printStackTrace(); } } // package-private interface to BTraceUtils class. static int speculation() { BTraceRuntime current = getCurrent(); return current.specQueueManager.speculation(); } static void speculate(int id) { BTraceRuntime current = getCurrent(); current.specQueueManager.speculate(id); } static void discard(int id) { BTraceRuntime current = getCurrent(); current.specQueueManager.discard(id); } static void commit(int id) { BTraceRuntime current = getCurrent(); current.specQueueManager.commit(id, current.queue); } /** * Indicates whether two given objects are "equal to" one another. * For bootstrap classes, returns the result of calling Object.equals() * override. For non-bootstrap classes, the reference identity comparison * is done. * * @param obj1 first object to compare equality * @param obj2 second object to compare equality * @return <code>true</code> if the given objects are equal; * <code>false</code> otherwise. */ static boolean compare(Object obj1, Object obj2) { if (obj1 instanceof String) { return obj1.equals(obj2); } else if (obj1.getClass().getClassLoader() == null) { if (obj2 == null || obj2.getClass().getClassLoader() == null) { return obj1.equals(obj2); } // else fall through.. } return obj1 == obj2; } // BTrace map functions static <K, V> Map<K, V> newHashMap() { return new BTraceMap(new HashMap<K, V>()); } static <K, V> Map<K, V> newWeakMap() { return new BTraceMap(new WeakHashMap<K, V>()); } static <V> Deque<V> newDeque() { return new BTraceDeque<V>(new ArrayDeque<V>()); } static Appendable newStringBuilder(boolean threadSafe) { return threadSafe ? new StringBuffer() : new StringBuilder(); } static Appendable newStringBuilder() { return newStringBuilder(false); } static <E> int size(Collection<E> coll) { if (coll instanceof BTraceCollection || coll.getClass().getClassLoader() == null) { return coll.size(); } else { throw new IllegalArgumentException(); } } public static <E> boolean isEmpty(Collection<E> coll) { if (coll instanceof BTraceCollection || coll.getClass().getClassLoader() == null) { return coll.isEmpty(); } else { throw new IllegalArgumentException(); } } static <E> boolean contains(Collection<E> coll, Object obj) { if (coll instanceof BTraceCollection || coll.getClass().getClassLoader() == null) { for (E e : coll) { if (compare(e, obj)) { return true; } } return false; } else { throw new IllegalArgumentException(); } } static <E >Object[] toArray(Collection<E> collection) { if (collection == null) { return new Object[0]; } else { return collection.toArray(); } } static <K, V> V get(Map<K, V> map, Object key) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { return map.get(key); } else { throw new IllegalArgumentException(); } } static <K, V> boolean containsKey(Map<K, V> map, Object key) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { return map.containsKey(key); } else { throw new IllegalArgumentException(); } } static <K, V> boolean containsValue(Map<K, V> map, Object value) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { return map.containsValue(value); } else { throw new IllegalArgumentException(); } } static <K, V> V put(Map<K, V> map, K key, V value) { if (map instanceof BTraceMap) { return map.put(key, value); } else { throw new IllegalArgumentException("not a btrace map"); } } static <K, V> V remove(Map<K, V> map, Object key) { if (map instanceof BTraceMap) { return map.remove(key); } else { throw new IllegalArgumentException("not a btrace map"); } } static <K, V> void clear(Map<K, V> map) { if (map instanceof BTraceMap) { map.clear(); } else { throw new IllegalArgumentException("not a btrace map"); } } static <K, V> int size(Map<K, V> map) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { return map.size(); } else { throw new IllegalArgumentException(); } } static <K, V> boolean isEmpty(Map<K, V> map) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { return map.isEmpty(); } else { throw new IllegalArgumentException(); } } static <K,V> void putAll(Map<K,V> src, Map<K,V> dst) { dst.putAll(src); } static <K, V> void copy(Map<K,V> src, Map<K,V> dst) { dst.clear(); dst.putAll(src); } static void printMap(Map map) { if (map instanceof BTraceMap || map.getClass().getClassLoader() == null) { synchronized(map) { Map<String, String> m = new HashMap<String, String>(); Set<Map.Entry<Object, Object>> entries = map.entrySet(); for (Map.Entry<Object, Object> e : entries) { m.put(BTraceUtils.Strings.str(e.getKey()), BTraceUtils.Strings.str(e.getValue())); } printStringMap(null, m); } } else { print(BTraceUtils.Strings.str(map)); } } public static <V> void push(Deque<V> queue, V value) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { queue.push(value); } else { throw new IllegalArgumentException(); } } public static <V> void addLast(Deque<V> queue, V value) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { queue.addLast(value); } else { throw new IllegalArgumentException(); } } public static <V> V peekFirst(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.peekFirst(); } else { throw new IllegalArgumentException(); } } public static <V> V peekLast(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.peekLast(); } else { throw new IllegalArgumentException(); } } public static <V> V removeLast(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.removeLast(); } else { throw new IllegalArgumentException(); } } public static <V> V removeFirst(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.removeFirst(); } else { throw new IllegalArgumentException(); } } public static <V> V poll(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.poll(); } else { throw new IllegalArgumentException(); } } public static <V> V peek(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { return queue.peek(); } else { throw new IllegalArgumentException(); } } public static <V> void clear(Deque<V> queue) { if (queue instanceof BTraceDeque || queue.getClass().getClassLoader() == null) { queue.clear(); } else { throw new IllegalArgumentException(); } } public static Appendable append(Appendable buffer, String strToAppend) { try { if (buffer != null && strToAppend != null) { return buffer.append(strToAppend); } else { throw new IllegalArgumentException(); } } catch (IOException e) { throw new IllegalArgumentException(e); } } public static int length(Appendable buffer) { if (buffer != null && buffer instanceof CharSequence) { return ((CharSequence)buffer).length(); } else { throw new IllegalArgumentException(); } } static void printNumber(String name, Number value) { getCurrent().send(new NumberDataCommand(name, value)); } static void printNumberMap(String name, Map<String, ? extends Number> data) { getCurrent().send(new NumberMapDataCommand(name, data)); } static void printStringMap(String name, Map<String, String> data) { getCurrent().send(new StringMapDataCommand(name, data)); } // BTrace exit built-in function static void exit(int exitCode) { BTraceRuntime runtime = getCurrent(); if (runtime != null) { Throwable th = runtime.currentException.get(); if (! (th instanceof ExitException)) { runtime.currentException.set(null); } throw new ExitException(exitCode); } } public static void retransform(String runtimeName, Class<?> clazz) { try { BTraceRuntime rt = runtimes.get(runtimeName); if (rt != null && rt.instrumentation.isModifiableClass(clazz)) { rt.instrumentation.retransformClasses(clazz); } } catch (Exception e) { e.printStackTrace(); } } static long sizeof(Object obj) { BTraceRuntime runtime = getCurrent(); return runtime.instrumentation.getObjectSize(obj); } // BTrace command line argument functions static int $length() { BTraceRuntime runtime = getCurrent(); return runtime.args == null? 0 : runtime.args.length; } static String $(int n) { BTraceRuntime runtime = getCurrent(); if (runtime.args == null) { return null; } else { if (n >= 0 && n < runtime.args.length) { return runtime.args[n]; } else { return null; } } } private final static class BTraceAtomicInteger extends AtomicInteger { BTraceAtomicInteger(int initVal) { super(initVal); } } static AtomicInteger newAtomicInteger(int initVal) { return new BTraceAtomicInteger(initVal); } static int get(AtomicInteger ai) { if (ai instanceof BTraceAtomicInteger || ai.getClass().getClassLoader() == null) { return ai.get(); } else { throw new IllegalArgumentException(); } } static void set(AtomicInteger ai, int i) { if (ai instanceof BTraceAtomicInteger) { ai.set(i); } else { throw new IllegalArgumentException(); } } static void lazySet(AtomicInteger ai, int i) { if (ai instanceof BTraceAtomicInteger) { ai.lazySet(i); } else { throw new IllegalArgumentException(); } } static boolean compareAndSet(AtomicInteger ai, int i, int j) { if (ai instanceof BTraceAtomicInteger) { return ai.compareAndSet(i, j); } else { throw new IllegalArgumentException(); } } static boolean weakCompareAndSet(AtomicInteger ai, int i, int j) { if (ai instanceof BTraceAtomicInteger) { return ai.weakCompareAndSet(i, j); } else { throw new IllegalArgumentException(); } } static int getAndIncrement(AtomicInteger ai) { if (ai instanceof BTraceAtomicInteger) { return ai.getAndIncrement(); } else { throw new IllegalArgumentException(); } } static int getAndDecrement(AtomicInteger ai) { if (ai instanceof BTraceAtomicInteger) { return ai.getAndDecrement(); } else { throw new IllegalArgumentException(); } } static int incrementAndGet(AtomicInteger ai) { if (ai instanceof BTraceAtomicInteger) { return ai.incrementAndGet(); } else { throw new IllegalArgumentException(); } } static int decrementAndGet(AtomicInteger ai) { if (ai instanceof BTraceAtomicInteger) { return ai.decrementAndGet(); } else { throw new IllegalArgumentException(); } } static int getAndAdd(AtomicInteger ai, int i) { if (ai instanceof BTraceAtomicInteger) { return ai.getAndAdd(i); } else { throw new IllegalArgumentException(); } } static int addAndGet(AtomicInteger ai, int i) { if (ai instanceof BTraceAtomicInteger) { return ai.addAndGet(i); } else { throw new IllegalArgumentException(); } } static int getAndSet(AtomicInteger ai, int i) { if (ai instanceof BTraceAtomicInteger) { return ai.getAndSet(i); } else { throw new IllegalArgumentException(); } } private final static class BTraceAtomicLong extends AtomicLong { BTraceAtomicLong(long initVal) { super(initVal); } } static AtomicLong newAtomicLong(long initVal) { return new BTraceAtomicLong(initVal); } static long get(AtomicLong al) { if (al instanceof BTraceAtomicLong || al.getClass().getClassLoader() == null) { return al.get(); } else { throw new IllegalArgumentException(); } } static void set(AtomicLong al, long i) { if (al instanceof BTraceAtomicLong) { al.set(i); } else { throw new IllegalArgumentException(); } } static void lazySet(AtomicLong al, long i) { if (al instanceof BTraceAtomicLong) { al.lazySet(i); } else { throw new IllegalArgumentException(); } } static boolean compareAndSet(AtomicLong al, long i, long j) { if (al instanceof BTraceAtomicLong) { return al.compareAndSet(i, j); } else { throw new IllegalArgumentException(); } } static boolean weakCompareAndSet(AtomicLong al, long i, long j) { if (al instanceof BTraceAtomicLong) { return al.weakCompareAndSet(i, j); } else { throw new IllegalArgumentException(); } } static long getAndIncrement(AtomicLong al) { if (al instanceof BTraceAtomicLong) { return al.getAndIncrement(); } else { throw new IllegalArgumentException(); } } static long getAndDecrement(AtomicLong al) { if (al instanceof BTraceAtomicLong) { return al.getAndDecrement(); } else { throw new IllegalArgumentException(); } } static long incrementAndGet(AtomicLong al) { if (al instanceof BTraceAtomicLong) { return al.incrementAndGet(); } else { throw new IllegalArgumentException(); } } static long decrementAndGet(AtomicLong al) { if (al instanceof BTraceAtomicLong) { return al.decrementAndGet(); } else { throw new IllegalArgumentException(); } } static long getAndAdd(AtomicLong al, long i) { if (al instanceof BTraceAtomicLong) { return al.getAndAdd(i); } else { throw new IllegalArgumentException(); } } static long addAndGet(AtomicLong al, long i) { if (al instanceof BTraceAtomicLong) { return al.addAndGet(i); } else { throw new IllegalArgumentException(); } } static long getAndSet(AtomicLong al, long i) { if (al instanceof BTraceAtomicLong) { return al.getAndSet(i); } else { throw new IllegalArgumentException(); } } // BTrace perf counter reading functions static int perfInt(String name) { return getPerfReader().perfInt(name); } static long perfLong(String name) { return getPerfReader().perfLong(name); } static String perfString(String name) { return getPerfReader().perfString(name); } // stack trace functions private static String stackTraceAllStr(int numFrames, boolean printWarning) { Set<Map.Entry<Thread, StackTraceElement[]>> traces = Thread.getAllStackTraces().entrySet(); StringBuilder buf = new StringBuilder(); for (Map.Entry<Thread, StackTraceElement[]> t : traces) { buf.append(t.getKey().toString()); buf.append(LINE_SEPARATOR); buf.append(LINE_SEPARATOR); StackTraceElement[] st = t.getValue(); buf.append(stackTraceStr("\t", st, 0, numFrames, printWarning)); buf.append(LINE_SEPARATOR); } return buf.toString(); } static String stackTraceAllStr(int numFrames) { return stackTraceAllStr(numFrames, false); } static void stackTraceAll(int numFrames) { getCurrent().send(stackTraceAllStr(numFrames, true)); } static String stackTraceStr(StackTraceElement[] st, int start, int numFrames) { return stackTraceStr(null, st, start, numFrames, false); } static String stackTraceStr(String prefix, StackTraceElement[] st, int start, int numFrames) { return stackTraceStr(prefix, st, start, numFrames, false); } private static String stackTraceStr(String prefix, StackTraceElement[] st, int start, int numFrames, boolean printWarning) { start = start > 0 ? start : 0; numFrames = numFrames > 0 ? numFrames : st.length - start; int limit = start + numFrames; limit = limit <= st.length ? limit : st.length; if (prefix == null) { prefix = ""; } StringBuilder buf = new StringBuilder(); for (int i = start; i < limit; i++) { if (prefix != null) buf.append(prefix); buf.append(st[i].toString()); buf.append(LINE_SEPARATOR); } if (printWarning && limit < st.length) { if (prefix != null) buf.append(prefix); buf.append(st.length - limit); buf.append(" more frame(s) ..."); buf.append(LINE_SEPARATOR); } return buf.toString(); } static void stackTrace(StackTraceElement[] st, int start, int numFrames) { stackTrace(null, st, start, numFrames); } static void stackTrace(String prefix, StackTraceElement[] st, int start, int numFrames) { getCurrent().send(stackTraceStr(prefix, st, start, numFrames, true)); } // print/println functions static void print(String str) { getCurrent().send(str); } static void println(String str) { getCurrent().send(str + LINE_SEPARATOR); } static void println() { getCurrent().send(LINE_SEPARATOR); } static String property(String name) { return AccessController.doPrivileged( new GetPropertyAction(name)); } static Properties properties() { return AccessController.doPrivileged( new PrivilegedAction<Properties>() { public Properties run() { return System.getProperties(); } } ); } static String getenv(final String name) { return AccessController.doPrivileged( new PrivilegedAction<String>() { public String run() { return System.getenv(name); } } ); } static Map<String, String> getenv() { return AccessController.doPrivileged( new PrivilegedAction<Map<String, String>>() { public Map<String, String> run() { return System.getenv(); } } ); } static MemoryUsage heapUsage() { initMemoryMBean(); return memoryMBean.getHeapMemoryUsage(); } static MemoryUsage nonHeapUsage() { initMemoryMBean(); return memoryMBean.getNonHeapMemoryUsage(); } static long finalizationCount() { initMemoryMBean(); return memoryMBean.getObjectPendingFinalizationCount(); } static long vmStartTime() { initRuntimeMBean(); return runtimeMBean.getStartTime(); } static long vmUptime() { initRuntimeMBean(); return runtimeMBean.getUptime(); } static List<String> getInputArguments() { initRuntimeMBean(); return runtimeMBean.getInputArguments(); } static String getVmVersion() { initRuntimeMBean(); return runtimeMBean.getVmVersion(); } static boolean isBootClassPathSupported() { initRuntimeMBean(); return runtimeMBean.isBootClassPathSupported(); } static String getBootClassPath() { initRuntimeMBean(); return runtimeMBean.getBootClassPath(); } static long getThreadCount() { initThreadMBean(); return threadMBean.getThreadCount(); } static long getPeakThreadCount() { initThreadMBean(); return threadMBean.getPeakThreadCount(); } static long getTotalStartedThreadCount() { initThreadMBean(); return threadMBean.getTotalStartedThreadCount(); } static long getDaemonThreadCount() { initThreadMBean(); return threadMBean.getDaemonThreadCount(); } static long getCurrentThreadCpuTime() { initThreadMBean(); threadMBean.setThreadCpuTimeEnabled(true); return threadMBean.getCurrentThreadCpuTime(); } static long getCurrentThreadUserTime() { initThreadMBean(); threadMBean.setThreadCpuTimeEnabled(true); return threadMBean.getCurrentThreadUserTime(); } static void dumpHeap(String fileName, boolean live) { initHotspotMBean(); try { String name = resolveFileName(fileName); hotspotMBean.dumpHeap(name, live); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } static long getTotalGcTime() { initGarbageCollectionBeans(); long totalGcTime = 0; for (GarbageCollectorMXBean gcBean : gcBeanList) { totalGcTime += gcBean.getCollectionTime(); } return totalGcTime; } static String getMemoryPoolUsage(String poolFormat) { if (poolFormat == null) { poolFormat = "%1$s;%2$d;%3$d;%4$d;%5$d"; } Object[][] poolOutput = new Object[memPoolList.size()][5]; StringBuilder membuffer = new StringBuilder(); for (int i = 0; i < memPoolList.size(); i++) { MemoryPoolMXBean memPool = memPoolList.get(i); poolOutput[i][0] = memPool.getName(); poolOutput[i][1] = new Long(memPool.getUsage().getMax()); poolOutput[i][2] = new Long(memPool.getUsage().getUsed()); poolOutput[i][3] = new Long(memPool.getUsage().getCommitted()); poolOutput[i][4] = new Long(memPool.getUsage().getInit()); } for (Object[] memPoolOutput : poolOutput) { membuffer.append(String.format(poolFormat, memPoolOutput)).append("\n"); } return membuffer.toString(); } static void serialize(Object obj, String fileName) { try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(resolveFileName(fileName))); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.close(); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } static String toXML(Object obj) { try { return XMLSerializer.toXML(obj); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } static void writeXML(Object obj, String fileName) { try { BufferedWriter bw = new BufferedWriter( new FileWriter(resolveFileName(fileName))); XMLSerializer.write(obj, bw); bw.close(); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } private synchronized static void initDOTWriterProps() { if (dotWriterProps == null) { dotWriterProps = new Properties(); InputStream is = BTraceRuntime.class.getResourceAsStream("resources/btrace.dotwriter.properties"); if (is != null) { try { dotWriterProps.load(is); } catch (IOException ioExp) { ioExp.printStackTrace(); } } try { String home = System.getProperty("user.home"); File file = new File(home, "btrace.dotwriter.properties"); if (file.exists() && file.isFile()) { is = new BufferedInputStream(new FileInputStream(file)); if (is != null) { dotWriterProps.load(is); } } } catch (Exception exp) { exp.printStackTrace(); } } } static void writeDOT(Object obj, String fileName) { DOTWriter writer = new DOTWriter(resolveFileName(fileName)); initDOTWriterProps(); writer.customize(dotWriterProps); writer.addNode(null, obj); writer.close(); } private static String INDENT = " "; static void deadlocks(boolean stackTrace) { initThreadMBean(); if (threadMBean.isSynchronizerUsageSupported()) { long[] tids = threadMBean.findDeadlockedThreads(); if (tids != null && tids.length > 0) { ThreadInfo[] infos = threadMBean.getThreadInfo(tids, true, true); StringBuilder sb = new StringBuilder(); for (ThreadInfo ti : infos) { sb.append("\"" + ti.getThreadName() + "\"" + " Id=" + ti.getThreadId() + " in " + ti.getThreadState()); if (ti.getLockName() != null) { sb.append(" on lock=" + ti.getLockName()); } if (ti.isSuspended()) { sb.append(" (suspended)"); } if (ti.isInNative()) { sb.append(" (running in native)"); } if (ti.getLockOwnerName() != null) { sb.append(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId()); sb.append(LINE_SEPARATOR); } if (stackTrace) { // print stack trace with locks StackTraceElement[] stacktrace = ti.getStackTrace(); MonitorInfo[] monitors = ti.getLockedMonitors(); for (int i = 0; i < stacktrace.length; i++) { StackTraceElement ste = stacktrace[i]; sb.append(INDENT + "at " + ste.toString()); sb.append(LINE_SEPARATOR); for (MonitorInfo mi : monitors) { if (mi.getLockedStackDepth() == i) { sb.append(INDENT + " - locked " + mi); sb.append(LINE_SEPARATOR); } } } sb.append(LINE_SEPARATOR); } LockInfo[] locks = ti.getLockedSynchronizers(); sb.append(INDENT + "Locked synchronizers: count = " + locks.length); sb.append(LINE_SEPARATOR); for (LockInfo li : locks) { sb.append(INDENT + " - " + li); sb.append(LINE_SEPARATOR); } sb.append(LINE_SEPARATOR); } getCurrent().send(sb.toString()); } } } static int dtraceProbe(String s1, String s2, int i1, int i2) { if (dtraceEnabled) { return dtraceProbe0(s1, s2, i1, i2); } else { return 0; } } // BTrace aggregation support static Aggregation newAggregation(AggregationFunction type) { return new Aggregation(type); } static AggregationKey newAggregationKey(Object... elements) { return new AggregationKey(elements); } static void addToAggregation(Aggregation aggregation, long value) { aggregation.add(value); } static void addToAggregation(Aggregation aggregation, AggregationKey key, long value) { aggregation.add(key, value); } static void clearAggregation(Aggregation aggregation) { aggregation.clear(); } static void truncateAggregation(Aggregation aggregation, int count) { aggregation.truncate(count); } static void printAggregation(String name, Aggregation aggregation) { getCurrent().send(new GridDataCommand(name, aggregation.getData())); } static void printSnapshot(String name, Profiler.Snapshot snapshot) { getCurrent().send(new GridDataCommand(name, snapshot.getGridData())); } /** * Prints profiling snapshot using the provided format * @param name The name of the aggregation to be used in the textual output * @param snapshot The snapshot to print * @param format The format to use. It mimics {@linkplain String#format(java.lang.String, java.lang.Object[]) } behaviour * with the addition of the ability to address the key title as a 0-indexed item * @see String#format(java.lang.String, java.lang.Object[]) */ static void printSnapshot(String name, Profiler.Snapshot snapshot, String format) { getCurrent().send(new GridDataCommand(name, snapshot.getGridData(), format)); } /** * Precondition: Only values from the first Aggregation are printed. If the subsequent aggregations have * values for keys which the first aggregation does not have, these rows are ignored. * @param name * @param format * @param aggregationArray */ static void printAggregation(String name, String format, Aggregation[] aggregationArray) { if (aggregationArray.length > 1 && aggregationArray[0].getKeyData().size() > 1) { int aggregationDataSize = aggregationArray[0].getKeyData().get(0).getElements().length + aggregationArray.length; List<Object[]> aggregationData = new ArrayList<Object[]>(); //Iterate through all keys in the first Aggregation and build up an array of aggregationData for (AggregationKey aggKey : aggregationArray[0].getKeyData()) { int aggDataIndex = 0; Object[] currAggregationData = new Object[aggregationDataSize]; //Add the key to the from of the current aggregation Data for (Object obj : aggKey.getElements()) { currAggregationData[aggDataIndex] = obj; aggDataIndex++; } for (Aggregation agg : aggregationArray) { currAggregationData[aggDataIndex] = agg.getValueForKey(aggKey); aggDataIndex++; } aggregationData.add(currAggregationData); } getCurrent().send(new GridDataCommand(name, aggregationData, format)); } } /** * Prints aggregation using the provided format * @param name The name of the aggregation to be used in the textual output * @param aggregation The aggregation to print * @param format The format to use. It mimics {@linkplain String#format(java.lang.String, java.lang.Object[]) } behaviour * with the addition of the ability to address the key title as a 0-indexed item * @see String#format(java.lang.String, java.lang.Object[]) */ static void printAggregation(String name, Aggregation aggregation, String format) { getCurrent().send(new GridDataCommand(name, aggregation.getData(), format)); } // profiling related methods /** * @see BTraceUtils.Profiling#newProfiler() */ static Profiler newProfiler() { return new MethodInvocationProfiler(600); } /** * @see BTraceUtils.Profiling#newProfiler(int) */ static Profiler newProfiler(int expectedMethodCnt) { return new MethodInvocationProfiler(expectedMethodCnt); } /** * @see BTraceUtils.Profiling#recordEntry(com.sun.btrace.Profiler, java.lang.String) */ static void recordEntry(Profiler profiler, String methodName) { profiler.recordEntry(methodName); } /** * @see BTraceUtils.Profiling#recordExit(com.sun.btrace.Profiler, java.lang.String, long) */ static void recordExit(Profiler profiler, String methodName, long duration) { profiler.recordExit(methodName, duration); } /** * @see BTraceUtils.Profiling#snapshot(com.sun.btrace.Profiler) */ static Profiler.Snapshot snapshot(Profiler profiler) { return profiler.snapshot(); } /** * @see BTraceUtils.Profiling#snapshotAndReset(com.sun.btrace.Profiler) */ static Profiler.Snapshot snapshotAndReset(Profiler profiler) { return profiler.snapshot(true); } static void resetProfiler(Profiler profiler) { profiler.reset(); } // private methods below this point // raise DTrace USDT probe private static native int dtraceProbe0(String s1, String s2, int i1, int i2); private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; /** * Get the current thread BTraceRuntime instance * if there is one. */ private static BTraceRuntime getCurrent() { // BTraceRuntime current = tls.get(); BTraceRuntime current = (BTraceRuntime)map.get(); assert current != null : "BTraceRuntime is null!"; return current; } private void initThreadPool() { if (threadPool == null) { synchronized (this) { if (threadPool == null) { threadPool = Executors.newFixedThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread th = new Thread(r); th.setDaemon(true); return th; } }); } } } } private static void initHotspotMBean() { if (hotspotMBean == null) { synchronized (BTraceRuntime.class) { if (hotspotMBean == null) { hotspotMBean = getHotspotMBean(); } } } } private static HotSpotDiagnosticMXBean getHotspotMBean() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<HotSpotDiagnosticMXBean>() { public HotSpotDiagnosticMXBean run() throws Exception { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); Set<ObjectName> s = server.queryNames(new ObjectName(HOTSPOT_BEAN_NAME), null); Iterator<ObjectName> itr = s.iterator(); if (itr.hasNext()) { ObjectName name = itr.next(); HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(server, name.toString(), HotSpotDiagnosticMXBean.class); return bean; } else { return null; } } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static void initMemoryMBean() { if (memoryMBean == null) { synchronized (BTraceRuntime.class) { if (memoryMBean == null) { memoryMBean = getMemoryMBean(); } } } } private void initMemoryListener() { initThreadPool(); memoryListener = new NotificationListener() { public void handleNotification(Notification notif, Object handback) { String notifType = notif.getType(); if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) { CompositeData cd = (CompositeData) notif.getUserData(); final MemoryNotificationInfo info = MemoryNotificationInfo.from(cd); String name = info.getPoolName(); final Method handler = lowMemHandlers.get(name); if (handler != null) { threadPool.submit(new Runnable() { public void run() { try { if (handler.getParameterTypes().length == 1) { handler.invoke(null, info.getUsage()); } else { handler.invoke(null, (Object[])null); } } catch (Throwable th) { } } }); } } } }; } private static MemoryMXBean getMemoryMBean() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<MemoryMXBean>() { public MemoryMXBean run() throws Exception { return ManagementFactory.getMemoryMXBean(); } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static void initRuntimeMBean() { if (runtimeMBean == null) { synchronized (BTraceRuntime.class) { if (runtimeMBean == null) { runtimeMBean = getRuntimeMBean(); } } } } private static RuntimeMXBean getRuntimeMBean() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<RuntimeMXBean>() { public RuntimeMXBean run() throws Exception { return ManagementFactory.getRuntimeMXBean(); } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static void initThreadMBean() { if (threadMBean == null) { synchronized (BTraceRuntime.class) { if (threadMBean == null) { threadMBean = getThreadMBean(); } } } } private static ThreadMXBean getThreadMBean() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<ThreadMXBean>() { public ThreadMXBean run() throws Exception { return ManagementFactory.getThreadMXBean(); } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static List<MemoryPoolMXBean> getMemoryPoolMXBeans() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<List<MemoryPoolMXBean>>() { public List<MemoryPoolMXBean> run() throws Exception { return ManagementFactory.getMemoryPoolMXBeans(); } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static List<GarbageCollectorMXBean> getGarbageCollectionMBeans() { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<List<GarbageCollectorMXBean>>() { public List<GarbageCollectorMXBean> run() throws Exception { return ManagementFactory.getGarbageCollectorMXBeans(); } }); } catch (Exception exp) { throw new UnsupportedOperationException(exp); } } private static void initGarbageCollectionBeans() { if (gcBeanList == null) { synchronized (BTraceRuntime.class) { if (gcBeanList == null) { gcBeanList = getGarbageCollectionMBeans(); } } } } private static void initMemoryPoolList() { if (memPoolList == null) { synchronized (BTraceRuntime.class) { if (memPoolList == null) { memPoolList = getMemoryPoolMXBeans(); } } } } private static PerfReader getPerfReader() { if (perfReader == null) { throw new UnsupportedOperationException(); } return perfReader; } private static RunnableGenerator getRunnableGenerator() { return runnableGenerator; } private void send(String msg) { send(new MessageCommand(messageTimestamp? System.nanoTime() : 0L, msg)); } public void send(Command cmd) { try { boolean speculated = specQueueManager.send(cmd); if (! speculated) { queue.put(cmd); } } catch (InterruptedException ie) { ie.printStackTrace(); } } private void handleExceptionImpl(Throwable th) { if (currentException.get() != null) { return; } leave(); currentException.set(th); try { if (th instanceof ExitException) { exitImpl(((ExitException)th).exitCode()); } else { if (exceptionHandler != null) { try { exceptionHandler.invoke(null, th); } catch (Throwable ignored) { } } else { try { // Do not call send(Command). Exception messages should not // go to speculative buffers! queue.put(new ErrorCommand(th)); } catch (InterruptedException ie) { ie.printStackTrace(); } } } } finally { currentException.set(null); } } private void startImpl() { if (timerHandlers != null && timerHandlers.length != 0) { timer = new Timer(true); RunnableGenerator gen = getRunnableGenerator(); Runnable[] runnables = new Runnable[timerHandlers.length]; if (gen != null) { generateRunnables(gen, runnables); } else { wrapToRunnables(runnables); } for (int index = 0; index < timerHandlers.length; index++) { Method m = timerHandlers[index]; OnTimer tp = m.getAnnotation(OnTimer.class); long period = tp.value(); final Runnable r = runnables[index]; timer.schedule(new TimerTask() { public void run() { r.run(); } }, period, period); } } if (! lowMemHandlers.isEmpty()) { initMemoryMBean(); initMemoryListener(); NotificationEmitter emitter = (NotificationEmitter) memoryMBean; emitter.addNotificationListener(memoryListener, null, null); } leave(); } private void generateRunnables(RunnableGenerator gen, Runnable[] runnables) { final MemoryClassLoader loader = AccessController.doPrivileged( new PrivilegedAction<MemoryClassLoader>() { public MemoryClassLoader run() { return new MemoryClassLoader(clazz.getClassLoader()); } }); for (int index = 0; index < timerHandlers.length; index++) { Method m = timerHandlers[index]; try { final String className = "com/sun/btrace/BTraceRunnable$" + index; final byte[] buf = gen.generate(m, className); Class cls = AccessController.doPrivileged( new PrivilegedExceptionAction<Class>() { public Class run() throws Exception { return loader.loadClass(className.replace('/', '.'), buf); } }); runnables[index] = (Runnable) cls.newInstance(); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } } private void wrapToRunnables(Runnable[] runnables) { for (int index = 0; index < timerHandlers.length; index++) { final Method m = timerHandlers[index]; runnables[index] = new Runnable() { public void run() { try { m.invoke(null, (Object[])null); } catch (Throwable th) { } } }; } } private synchronized void exitImpl(int exitCode) { if (exitHandler != null) { try { exitHandler.invoke(null, exitCode); } catch (Throwable ignored) { } } disabled = true; if (timer != null) { timer.cancel(); } if (memoryListener != null && memoryMBean != null) { NotificationEmitter emitter = (NotificationEmitter) memoryMBean; try { emitter.removeNotificationListener(memoryListener); } catch (ListenerNotFoundException lnfe) {} } if (threadPool != null) { threadPool.shutdownNow(); } send(new ExitCommand(exitCode)); } private static Perf getPerf() { if (perf == null) { synchronized(BTraceRuntime.class) { if (perf == null) { perf = (Perf) AccessController.doPrivileged(new Perf.GetPerfAction()); } } } return perf; } private static byte[] getStringBytes(String value) { byte[] v = null; try { v = value.getBytes("UTF-8"); } catch (java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } byte[] v1 = new byte[v.length+1]; System.arraycopy(v, 0, v1, 0, v.length); v1[v.length] = '\0'; return v1; } private Class defineClassImpl(byte[] code, boolean mustBeBootstrap) { ClassLoader loader = null; if (! mustBeBootstrap) { loader = new ClassLoader(null) {}; } Class cl = unsafe.defineClass(className, code, 0, code.length, loader, null); unsafe.ensureClassInitialized(cl); return cl; } private void init(Class cl) { if (this.clazz != null) { return; } this.clazz = cl; List<Method> timersList = new ArrayList<Method>(); this.eventHandlers = new HashMap<String, Method>(); this.lowMemHandlers = new HashMap<String, Method>(); Method[] methods = clazz.getMethods(); for (Method m : methods) { int modifiers = m.getModifiers(); if (! Modifier.isStatic(modifiers)) { continue; } OnEvent oev = m.getAnnotation(OnEvent.class); if (oev != null && m.getParameterTypes().length == 0) { eventHandlers.put(oev.value(), m); } OnError oer = m.getAnnotation(OnError.class); if (oer != null) { Class[] argTypes = m.getParameterTypes(); if (argTypes.length == 1 && argTypes[0] == Throwable.class) { this.exceptionHandler = m; } } OnExit oex = m.getAnnotation(OnExit.class); if (oex != null) { Class[] argTypes = m.getParameterTypes(); if (argTypes.length == 1 && argTypes[0] == int.class) { this.exitHandler = m; } } OnTimer ot = m.getAnnotation(OnTimer.class); if (ot != null && m.getParameterTypes().length == 0) { timersList.add(m); } OnLowMemory olm = m.getAnnotation(OnLowMemory.class); if (olm != null) { Class[] argTypes = m.getParameterTypes(); if ((argTypes.length == 0) || (argTypes.length == 1 && argTypes[0] == MemoryUsage.class)) { lowMemHandlers.put(olm.pool(), m); } } } initMemoryPoolList(); for (MemoryPoolMXBean mpoolBean : memPoolList) { String name = mpoolBean.getName(); if (lowMemHandlers.containsKey(name)) { Method m = lowMemHandlers.get(name); OnLowMemory olm = m.getAnnotation(OnLowMemory.class); if (mpoolBean.isUsageThresholdSupported()) { mpoolBean.setUsageThreshold(olm.threshold()); } } } timerHandlers = new Method[timersList.size()]; timersList.toArray(timerHandlers); BTraceMBean.registerMBean(clazz); } private static String resolveFileName(String name) { if (name.indexOf(File.separatorChar) != -1) { return name; // throw new IllegalArgumentException("directories are not allowed"); } StringBuilder buf = new StringBuilder(); buf.append('.'); buf.append(File.separatorChar); BTraceRuntime runtime = getCurrent(); buf.append("btrace"); if (runtime.args != null && runtime.args.length > 0) { buf.append(runtime.args[0]); } buf.append(File.separatorChar); buf.append(runtime.className); new File(buf.toString()).mkdirs(); buf.append(File.separatorChar); buf.append(name); return buf.toString(); } private static void loadLibrary(final ClassLoader cl) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { loadBTraceLibrary(cl); return null; } }); } private static void loadBTraceLibrary(final ClassLoader loader) { boolean isSolaris = System.getProperty("os.name").equals("SunOS"); if (isSolaris) { try { System.loadLibrary("btrace"); dtraceEnabled = true; } catch (LinkageError le) { if (loader == null || loader.getResource("com/sun/btrace") == null) { System.err.println("cannot load libbtrace.so, will miss DTrace probes from BTrace"); return; } String path = loader.getResource("com/sun/btrace").toString(); path = path.substring(0, path.indexOf("!")); path = path.substring("jar:".length(), path.lastIndexOf('/')); String cpu = System.getProperty("os.arch"); if (cpu.equals("x86")) { cpu = "i386"; } path += "/" + cpu + "/libbtrace.so"; try { path = new File(new URI(path)).getAbsolutePath(); } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new RuntimeException(e); } try { System.load(path); dtraceEnabled = true; } catch (LinkageError le1) { System.err.println("cannot load libbtrace.so, will miss DTrace probes from BTrace"); } } } } }