/** * Copyright 2014 SAP AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.aim.mainagent; import org.aim.description.restrictions.Restriction; import org.aim.logging.AIMLogger; import org.aim.logging.AIMLoggerFactory; import org.aim.mainagent.events.MonitorEventListener; import org.lpe.common.util.LpeNumericUtils; /** * Adapter for the JVMTI Event Agent. To use this agent, the CEventAgent has to * be started with the target application.<br> * * Before the agent can be used, the {@link CEventAgentAdapter#initialize() * initialize()} method has to be called. * * @author Henning Schulz * */ public final class CEventAgentAdapter { private static final AIMLogger LOGGER = AIMLoggerFactory.getLogger(CEventAgentAdapter.class); private static final String PACKAGE_AIM = "org.aim"; private static final String PACKAGE_LPE_COMMON = "org.lpe.common"; private static final String PACKAGE_JAVA = "java."; private static final String CLASS_NAME = "java.lang.Class"; private static final int GRANULARITY_PRECISION = 10; private static MonitorEventListener monitorListener; private static Restriction restriction; private static boolean initialized = false; private static boolean activated = false; private static int[] granularity = { 1, 1 }; private CEventAgentAdapter() { } /** * This method is called if a thread has to wait on a monitor and monitor * events are enabled.<br> * * <b>This methods implementation should avoid to provoke monitor waits as * they may lead to endless recursions!</b> * * @param thread * Thread which has to wait * @param monitor * Monitor on which {@code thread} has to wait * @param waitTime * timestamp in nanoseconds * * @see CEventAgentAdapter#enableMonitorEvents() enableMonitorEvents() * @see CEventAgentAdapter#disableMonitorEvents() disableMonitorEvents() */ public static void onMonitorWait(Thread thread, Object monitor, long waitTime) { if (monitorListener == null) { throw new IllegalStateException("No AIMEventListener specified!"); } if ((thread.getId() & (granularity[1] - 1)) >= granularity[0]) { return; } String className = monitor == null ? "null" : monitor.getClass().getName(); if (!restriction.isExcluded(className) || className.startsWith(CLASS_NAME)) { monitorListener.onMonitorWait(thread, monitor, waitTime); } } /** * This method is called if a thread had to wait on a monitor and entered * it. * * <b>This methods implementation should avoid to provoke monitor waits as * they may lead to endless recursions!</b> * * @param thread * Thread which had to wait * @param monitor * Monitor on which {@code thread} had to wait * @param enteredTime * timestamp in nanoseconds * * @see CEventAgentAdapter#enableMonitorEvents() enableMonitorEvents() * @see CEventAgentAdapter#disableMonitorEvents() disableMonitorEvents() */ public static void onMonitorEntered(Thread thread, Object monitor, long enteredTime) { if (monitorListener == null) { throw new IllegalStateException("No AIMEventListener specified!"); } if ((thread.getId() & (granularity[1] - 1)) >= granularity[0]) { return; } String className = monitor == null ? "null" : monitor.getClass().getName(); if (!restriction.isExcluded(className) || className.startsWith(CLASS_NAME)) { monitorListener.onMonitorEntered(thread, monitor, enteredTime); } } /** * Initializes the underlying JVMTI agent. * * @return {@code false}, if initialization failed, because the C agent * could not be found, or {@code true} otherwise. */ public static boolean initialize() { if (initialized) { LOGGER.warn("The C agent has already been initialized!"); } else { try { init(); } catch (UnsatisfiedLinkError e) { LOGGER.warn("The C agent could not be found!"); return false; } initialized = true; } return true; } /** * Sets the synchronizedListener. * * @param listener * synchronized listener to be set */ public static void setMonitorListener(MonitorEventListener listener) { monitorListener = listener; } /** * Sets the restriction. * * @param res * Restriction to be set */ public static void setRestriction(Restriction res) { restriction = res; } private static native void init(); /** * Enables listening to monitor events. * * @see CEventAgentAdapter#onMonitorWait(Thread, Object) * onMonitorWait(Thread, Object) * @see CEventAgentAdapter#onMonitorEntered(Thread, Object) * onMonitorEntered(Thread, Object) */ public static void enableMonitorEvents() { if (!initialized) { throw new RuntimeException("The C agent has to be initialized before using it!"); } if (activated) { LOGGER.warn("Synchronized listening has alredy been activated!"); } else { activateMonitorEvents(); activated = true; } } /** * Sets the granularity for monitor events. * * @param dGran * granularity for monitor events */ public static void setMonitorGranularity(double dGran) { granularity = LpeNumericUtils.getFractionFromDouble(dGran, 2, GRANULARITY_PRECISION); } private static native void activateMonitorEvents(); /** * Disables listening to monitor events. * * @see CEventAgentAdapter#onMonitorWait(Thread, Object) * onMonitorWait(Thread, Object) * @see CEventAgentAdapter#onMonitorEntered(Thread, Object) * onMonitorEntered(Thread, Object) */ public static void disableMonitorEvents() { if (!initialized) { throw new RuntimeException("The C agent has to be initialized before using it!"); } if (!activated) { LOGGER.warn("Synchronized listening has alredy been deactivated!"); } else { deactivateMonitorEvents(); monitorListener = null; activated = false; } } private static native void deactivateMonitorEvents(); /** * Prints a message without the ability of monitor waits. May be used for * logging in {@link CEventAgentAdapter#onMonitorWait(Thread, Object) * onMonitorWait(Thread, Object)} and * {@link CEventAgentAdapter#onMonitorEntered(Thread, Object) * onMonitorEntered(Thread, Object)}. * * @param message * Message to be printed */ private static native void printlnNonBlocking(String message); /** * Checks whether the C-Agent has been successfully initialized. * * @return true, if the CAgent has been initialized */ public static boolean isInitialized() { return initialized; } }