Path: blob/master/src/java.instrument/share/native/libinstrument/JPLISAgent.c
41149 views
/*1* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* Copyright 2003 Wily Technology, Inc.27*/2829#include <jni.h>30#include <jvm.h>31#include <jvmti.h>32#include <stdlib.h>33#include <string.h>34#include "JPLISAgent.h"35#include "JPLISAssert.h"36#include "Utilities.h"37#include "Reentrancy.h"38#include "JavaExceptions.h"3940#include "EncodingSupport.h"41#include "FileSystemSupport.h" /* For MAXPATHLEN & uintptr_t */4243#include "sun_instrument_InstrumentationImpl.h"4445/*46* The JPLISAgent manages the initialization all of the Java programming language Agents.47* It also supports the native method bridge between the JPLIS and the JVMTI.48* It maintains a single JVMTI Env that all JPL agents share.49* It parses command line requests and creates individual Java agents.50*/515253/*54* private prototypes55*/5657/* Allocates an unformatted JPLIS agent data structure. Returns NULL if allocation fails. */58JPLISAgent *59allocateJPLISAgent(jvmtiEnv * jvmtiEnv);6061/* Initializes an already-allocated JPLIS agent data structure. */62JPLISInitializationError63initializeJPLISAgent( JPLISAgent * agent,64JavaVM * vm,65jvmtiEnv * jvmtienv);66/* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;67* in normal usage the JPLIS agent lives forever68*/69void70deallocateJPLISAgent( jvmtiEnv * jvmtienv,71JPLISAgent * agent);7273/* Does one-time work to interrogate the JVM about capabilities and cache the answers. */74void75checkCapabilities(JPLISAgent * agent);7677/* Takes the elements of the command string (agent class name and options string) and78* create java strings for them.79* Returns true if a classname was found. Makes no promises beyond the textual; says nothing about whether80* the class exists or can be loaded.81* If return value is true, sets outputClassname to a non-NULL local JNI reference.82* If return value is true, sets outputOptionsString either to NULL or to a non-NULL local JNI reference.83* If return value is false, neither output parameter is set.84*/85jboolean86commandStringIntoJavaStrings( JNIEnv * jnienv,87const char * classname,88const char * optionsString,89jstring * outputClassname,90jstring * outputOptionsString);9192/* Start one Java agent from the supplied parameters.93* Most of the logic lives in a helper function that lives over in Java code--94* we pass parameters out to Java and use our own Java helper to actually95* load the agent and call the premain.96* Returns true if the Java agent class is loaded and the premain/agentmain method completes97* with no exceptions, false otherwise.98*/99jboolean100invokeJavaAgentMainMethod( JNIEnv * jnienv,101jobject instrumentationImpl,102jmethodID agentMainMethod,103jstring className,104jstring optionsString);105106/* Once we have loaded the Java agent and called the premain,107* we can release the copies we have been keeping of the command line108* data (agent class name and option strings).109*/110void111deallocateCommandLineData(JPLISAgent * agent);112113/*114* Common support for various class list fetchers.115*/116typedef jvmtiError (*ClassListFetcher)117( jvmtiEnv * jvmtiEnv,118jobject classLoader,119jint * classCount,120jclass ** classes);121122/* Fetcher that ignores the class loader parameter, and uses the JVMTI to get a list of all classes.123* Returns a jvmtiError according to the underlying JVMTI service.124*/125jvmtiError126getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,127jobject classLoader,128jint * classCount,129jclass ** classes);130131/* Fetcher that uses the class loader parameter, and uses the JVMTI to get a list of all classes132* for which the supplied loader is the initiating loader.133* Returns a jvmtiError according to the underlying JVMTI service.134*/135jvmtiError136getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,137jobject classLoader,138jint * classCount,139jclass ** classes);140141/*142* Common guts for two native methods, which are the same except for the policy for fetching143* the list of classes.144* Either returns a local JNI reference to an array of references to java.lang.Class.145* Can throw, if it does will alter the JNIEnv with an outstanding exception.146*/147jobjectArray148commonGetClassList( JNIEnv * jnienv,149JPLISAgent * agent,150jobject classLoader,151ClassListFetcher fetcher);152153154/*155* Misc. utilities.156*/157158/* Checked exception mapper used by the redefine classes implementation.159* Allows ClassNotFoundException or UnmodifiableClassException; maps others160* to InternalError. Can return NULL in an error case.161*/162jthrowable163redefineClassMapper( JNIEnv * jnienv,164jthrowable throwableToMap);165166/* Turns a buffer of jclass * into a Java array whose elements are java.lang.Class.167* Can throw, if it does will alter the JNIEnv with an outstanding exception.168*/169jobjectArray170getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount);171172173JPLISEnvironment *174getJPLISEnvironment(jvmtiEnv * jvmtienv) {175JPLISEnvironment * environment = NULL;176jvmtiError jvmtierror = JVMTI_ERROR_NONE;177178jvmtierror = (*jvmtienv)->GetEnvironmentLocalStorage(179jvmtienv,180(void**)&environment);181/* can be called from any phase */182jplis_assert(jvmtierror == JVMTI_ERROR_NONE);183184if (jvmtierror == JVMTI_ERROR_NONE) {185jplis_assert(environment != NULL);186jplis_assert(environment->mJVMTIEnv == jvmtienv);187} else {188environment = NULL;189}190return environment;191}192193/*194* OnLoad processing code.195*/196197/*198* Creates a new JPLISAgent.199* Returns error if the agent cannot be created and initialized.200* The JPLISAgent* pointed to by agent_ptr is set to the new broker,201* or NULL if an error has occurred.202*/203JPLISInitializationError204createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {205JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;206jvmtiEnv * jvmtienv = NULL;207jint jnierror = JNI_OK;208209*agent_ptr = NULL;210jnierror = (*vm)->GetEnv( vm,211(void **) &jvmtienv,212JVMTI_VERSION_1_1);213if ( jnierror != JNI_OK ) {214initerror = JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT;215} else {216JPLISAgent * agent = allocateJPLISAgent(jvmtienv);217if ( agent == NULL ) {218initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;219} else {220initerror = initializeJPLISAgent( agent,221vm,222jvmtienv);223if ( initerror == JPLIS_INIT_ERROR_NONE ) {224*agent_ptr = agent;225} else {226deallocateJPLISAgent(jvmtienv, agent);227}228}229230/* don't leak envs */231if ( initerror != JPLIS_INIT_ERROR_NONE ) {232jvmtiError jvmtierror = (*jvmtienv)->DisposeEnvironment(jvmtienv);233/* can be called from any phase */234jplis_assert(jvmtierror == JVMTI_ERROR_NONE);235}236}237238return initerror;239}240241/*242* Allocates a JPLISAgent. Returns NULL if it cannot be allocated243*/244JPLISAgent *245allocateJPLISAgent(jvmtiEnv * jvmtienv) {246return (JPLISAgent *) allocate( jvmtienv,247sizeof(JPLISAgent));248}249250JPLISInitializationError251initializeJPLISAgent( JPLISAgent * agent,252JavaVM * vm,253jvmtiEnv * jvmtienv) {254jvmtiError jvmtierror = JVMTI_ERROR_NONE;255jvmtiPhase phase;256257agent->mJVM = vm;258agent->mNormalEnvironment.mJVMTIEnv = jvmtienv;259agent->mNormalEnvironment.mAgent = agent;260agent->mNormalEnvironment.mIsRetransformer = JNI_FALSE;261agent->mRetransformEnvironment.mJVMTIEnv = NULL; /* NULL until needed */262agent->mRetransformEnvironment.mAgent = agent;263agent->mRetransformEnvironment.mIsRetransformer = JNI_FALSE; /* JNI_FALSE until mJVMTIEnv is set */264agent->mAgentmainCaller = NULL;265agent->mInstrumentationImpl = NULL;266agent->mPremainCaller = NULL;267agent->mTransform = NULL;268agent->mRedefineAvailable = JNI_FALSE; /* assume no for now */269agent->mRedefineAdded = JNI_FALSE;270agent->mNativeMethodPrefixAvailable = JNI_FALSE; /* assume no for now */271agent->mNativeMethodPrefixAdded = JNI_FALSE;272agent->mAgentClassName = NULL;273agent->mOptionsString = NULL;274agent->mJarfile = NULL;275276/* make sure we can recover either handle in either direction.277* the agent has a ref to the jvmti; make it mutual278*/279jvmtierror = (*jvmtienv)->SetEnvironmentLocalStorage(280jvmtienv,281&(agent->mNormalEnvironment));282/* can be called from any phase */283jplis_assert(jvmtierror == JVMTI_ERROR_NONE);284285/* check what capabilities are available */286checkCapabilities(agent);287288/* check phase - if live phase then we don't need the VMInit event */289jvmtierror = (*jvmtienv)->GetPhase(jvmtienv, &phase);290/* can be called from any phase */291jplis_assert(jvmtierror == JVMTI_ERROR_NONE);292if (phase == JVMTI_PHASE_LIVE) {293return JPLIS_INIT_ERROR_NONE;294}295296if (phase != JVMTI_PHASE_ONLOAD) {297/* called too early or called too late; either way bail out */298return JPLIS_INIT_ERROR_FAILURE;299}300301/* now turn on the VMInit event */302if ( jvmtierror == JVMTI_ERROR_NONE ) {303jvmtiEventCallbacks callbacks;304memset(&callbacks, 0, sizeof(callbacks));305callbacks.VMInit = &eventHandlerVMInit;306307jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,308&callbacks,309sizeof(callbacks));310check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);311jplis_assert(jvmtierror == JVMTI_ERROR_NONE);312}313314if ( jvmtierror == JVMTI_ERROR_NONE ) {315jvmtierror = (*jvmtienv)->SetEventNotificationMode(316jvmtienv,317JVMTI_ENABLE,318JVMTI_EVENT_VM_INIT,319NULL /* all threads */);320check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);321jplis_assert(jvmtierror == JVMTI_ERROR_NONE);322}323324return (jvmtierror == JVMTI_ERROR_NONE)? JPLIS_INIT_ERROR_NONE : JPLIS_INIT_ERROR_FAILURE;325}326327void328deallocateJPLISAgent(jvmtiEnv * jvmtienv, JPLISAgent * agent) {329deallocate(jvmtienv, agent);330}331332333JPLISInitializationError334recordCommandLineData( JPLISAgent * agent,335const char * agentClassName,336const char * optionsString ) {337JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;338char * ourCopyOfAgentClassName = NULL;339char * ourCopyOfOptionsString = NULL;340341/* if no actual params, bail out now */342if ((agentClassName == NULL) || (*agentClassName == 0)) {343initerror = JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED;344} else {345ourCopyOfAgentClassName = allocate(jvmti(agent), strlen(agentClassName)+1);346if (ourCopyOfAgentClassName == NULL) {347initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;348} else {349if (optionsString != NULL) {350ourCopyOfOptionsString = allocate(jvmti(agent), strlen(optionsString)+1);351if (ourCopyOfOptionsString == NULL) {352deallocate(jvmti(agent), ourCopyOfAgentClassName);353initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;354}355}356}357}358359if (initerror == JPLIS_INIT_ERROR_NONE) {360strcpy(ourCopyOfAgentClassName, agentClassName);361if (optionsString != NULL) {362strcpy(ourCopyOfOptionsString, optionsString);363}364agent->mAgentClassName = ourCopyOfAgentClassName;365agent->mOptionsString = ourCopyOfOptionsString;366}367368return initerror;369}370371/*372* VMInit processing code.373*/374375376/*377* If this call fails, the JVM launch will ultimately be aborted,378* so we don't have to be super-careful to clean up in partial failure379* cases.380*/381jboolean382processJavaStart( JPLISAgent * agent,383JNIEnv * jnienv) {384jboolean result;385386/*387* OK, Java is up now. We can start everything that needs Java.388*/389390/*391* First make our fallback InternalError throwable.392*/393result = initializeFallbackError(jnienv);394jplis_assert_msg(result, "fallback init failed");395396/*397* Now make the InstrumentationImpl instance.398*/399if ( result ) {400result = createInstrumentationImpl(jnienv, agent);401jplis_assert_msg(result, "instrumentation instance creation failed");402}403404405/*406* Register a handler for ClassFileLoadHook (without enabling this event).407* Turn off the VMInit handler.408*/409if ( result ) {410result = setLivePhaseEventHandlers(agent);411jplis_assert_msg(result, "setting of live phase VM handlers failed");412}413414/*415* Load the Java agent, and call the premain.416*/417if ( result ) {418result = startJavaAgent(agent, jnienv,419agent->mAgentClassName, agent->mOptionsString,420agent->mPremainCaller);421jplis_assert_msg(result, "agent load/premain call failed");422}423424/*425* Finally surrender all of the tracking data that we don't need any more.426* If something is wrong, skip it, we will be aborting the JVM anyway.427*/428if ( result ) {429deallocateCommandLineData(agent);430}431432return result;433}434435jboolean436startJavaAgent( JPLISAgent * agent,437JNIEnv * jnienv,438const char * classname,439const char * optionsString,440jmethodID agentMainMethod) {441jboolean success = JNI_FALSE;442jstring classNameObject = NULL;443jstring optionsStringObject = NULL;444445success = commandStringIntoJavaStrings( jnienv,446classname,447optionsString,448&classNameObject,449&optionsStringObject);450451if (success) {452success = invokeJavaAgentMainMethod( jnienv,453agent->mInstrumentationImpl,454agentMainMethod,455classNameObject,456optionsStringObject);457}458459return success;460}461462void463deallocateCommandLineData( JPLISAgent * agent) {464deallocate(jvmti(agent), (void*)agent->mAgentClassName);465deallocate(jvmti(agent), (void*)agent->mOptionsString);466467/* zero things out so it is easier to see what is going on */468agent->mAgentClassName = NULL;469agent->mOptionsString = NULL;470}471472/*473* Create the java.lang.instrument.Instrumentation instance474* and access information for it (method IDs, etc)475*/476jboolean477createInstrumentationImpl( JNIEnv * jnienv,478JPLISAgent * agent) {479jclass implClass = NULL;480jboolean errorOutstanding = JNI_FALSE;481jobject resultImpl = NULL;482jmethodID premainCallerMethodID = NULL;483jmethodID agentmainCallerMethodID = NULL;484jmethodID transformMethodID = NULL;485jmethodID constructorID = NULL;486jobject localReference = NULL;487488/* First find the class of our implementation */489implClass = (*jnienv)->FindClass( jnienv,490JPLIS_INSTRUMENTIMPL_CLASSNAME);491errorOutstanding = checkForAndClearThrowable(jnienv);492errorOutstanding = errorOutstanding || (implClass == NULL);493jplis_assert_msg(!errorOutstanding, "find class on InstrumentationImpl failed");494495if ( !errorOutstanding ) {496constructorID = (*jnienv)->GetMethodID( jnienv,497implClass,498JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODNAME,499JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODSIGNATURE);500errorOutstanding = checkForAndClearThrowable(jnienv);501errorOutstanding = errorOutstanding || (constructorID == NULL);502jplis_assert_msg(!errorOutstanding, "find constructor on InstrumentationImpl failed");503}504505if ( !errorOutstanding ) {506jlong peerReferenceAsScalar = (jlong)(intptr_t) agent;507localReference = (*jnienv)->NewObject( jnienv,508implClass,509constructorID,510peerReferenceAsScalar,511agent->mRedefineAdded,512agent->mNativeMethodPrefixAdded);513errorOutstanding = checkForAndClearThrowable(jnienv);514errorOutstanding = errorOutstanding || (localReference == NULL);515jplis_assert_msg(!errorOutstanding, "call constructor on InstrumentationImpl failed");516}517518if ( !errorOutstanding ) {519resultImpl = (*jnienv)->NewGlobalRef(jnienv, localReference);520errorOutstanding = checkForAndClearThrowable(jnienv);521jplis_assert_msg(!errorOutstanding, "copy local ref to global ref");522}523524/* Now look up the method ID for the pre-main caller (we will need this more than once) */525if ( !errorOutstanding ) {526premainCallerMethodID = (*jnienv)->GetMethodID( jnienv,527implClass,528JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODNAME,529JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODSIGNATURE);530errorOutstanding = checkForAndClearThrowable(jnienv);531errorOutstanding = errorOutstanding || (premainCallerMethodID == NULL);532jplis_assert_msg(!errorOutstanding, "can't find premain invoker methodID");533}534535/* Now look up the method ID for the agent-main caller */536if ( !errorOutstanding ) {537agentmainCallerMethodID = (*jnienv)->GetMethodID( jnienv,538implClass,539JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODNAME,540JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODSIGNATURE);541errorOutstanding = checkForAndClearThrowable(jnienv);542errorOutstanding = errorOutstanding || (agentmainCallerMethodID == NULL);543jplis_assert_msg(!errorOutstanding, "can't find agentmain invoker methodID");544}545546/* Now look up the method ID for the transform method (we will need this constantly) */547if ( !errorOutstanding ) {548transformMethodID = (*jnienv)->GetMethodID( jnienv,549implClass,550JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODNAME,551JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODSIGNATURE);552errorOutstanding = checkForAndClearThrowable(jnienv);553errorOutstanding = errorOutstanding || (transformMethodID == NULL);554jplis_assert_msg(!errorOutstanding, "can't find transform methodID");555}556557if ( !errorOutstanding ) {558agent->mInstrumentationImpl = resultImpl;559agent->mPremainCaller = premainCallerMethodID;560agent->mAgentmainCaller = agentmainCallerMethodID;561agent->mTransform = transformMethodID;562}563564return !errorOutstanding;565}566567jboolean568commandStringIntoJavaStrings( JNIEnv * jnienv,569const char * classname,570const char * optionsString,571jstring * outputClassname,572jstring * outputOptionsString) {573jstring classnameJavaString = NULL;574jstring optionsJavaString = NULL;575jboolean errorOutstanding = JNI_TRUE;576577classnameJavaString = (*jnienv)->NewStringUTF(jnienv, classname);578errorOutstanding = checkForAndClearThrowable(jnienv);579jplis_assert_msg(!errorOutstanding, "can't create class name java string");580581if ( !errorOutstanding ) {582if ( optionsString != NULL) {583optionsJavaString = (*jnienv)->NewStringUTF(jnienv, optionsString);584errorOutstanding = checkForAndClearThrowable(jnienv);585jplis_assert_msg(!errorOutstanding, "can't create options java string");586}587588if ( !errorOutstanding ) {589*outputClassname = classnameJavaString;590*outputOptionsString = optionsJavaString;591}592}593594return !errorOutstanding;595}596597598jboolean599invokeJavaAgentMainMethod( JNIEnv * jnienv,600jobject instrumentationImpl,601jmethodID mainCallingMethod,602jstring className,603jstring optionsString) {604jboolean errorOutstanding = JNI_FALSE;605606jplis_assert(mainCallingMethod != NULL);607if ( mainCallingMethod != NULL ) {608(*jnienv)->CallVoidMethod( jnienv,609instrumentationImpl,610mainCallingMethod,611className,612optionsString);613errorOutstanding = checkForThrowable(jnienv);614if ( errorOutstanding ) {615logThrowable(jnienv);616}617checkForAndClearThrowable(jnienv);618}619return !errorOutstanding;620}621622jboolean623setLivePhaseEventHandlers( JPLISAgent * agent) {624jvmtiEventCallbacks callbacks;625jvmtiEnv * jvmtienv = jvmti(agent);626jvmtiError jvmtierror;627628/* first swap out the handlers (switch from the VMInit handler, which we do not need,629* to the ClassFileLoadHook handler, which is what the agents need from now on)630*/631memset(&callbacks, 0, sizeof(callbacks));632callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;633634jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,635&callbacks,636sizeof(callbacks));637check_phase_ret_false(jvmtierror);638jplis_assert(jvmtierror == JVMTI_ERROR_NONE);639640641if ( jvmtierror == JVMTI_ERROR_NONE ) {642/* turn off VMInit */643jvmtierror = (*jvmtienv)->SetEventNotificationMode(644jvmtienv,645JVMTI_DISABLE,646JVMTI_EVENT_VM_INIT,647NULL /* all threads */);648check_phase_ret_false(jvmtierror);649jplis_assert(jvmtierror == JVMTI_ERROR_NONE);650}651652return (jvmtierror == JVMTI_ERROR_NONE);653}654655/**656* Check if the can_redefine_classes capability is available.657*/658void659checkCapabilities(JPLISAgent * agent) {660jvmtiEnv * jvmtienv = jvmti(agent);661jvmtiCapabilities potentialCapabilities;662jvmtiError jvmtierror;663664memset(&potentialCapabilities, 0, sizeof(potentialCapabilities));665666jvmtierror = (*jvmtienv)->GetPotentialCapabilities(jvmtienv, &potentialCapabilities);667check_phase_ret(jvmtierror);668jplis_assert(jvmtierror == JVMTI_ERROR_NONE);669670if ( jvmtierror == JVMTI_ERROR_NONE ) {671if ( potentialCapabilities.can_redefine_classes == 1 ) {672agent->mRedefineAvailable = JNI_TRUE;673}674if ( potentialCapabilities.can_set_native_method_prefix == 1 ) {675agent->mNativeMethodPrefixAvailable = JNI_TRUE;676}677}678}679680/**681* Enable native method prefix in one JVM TI environment682*/683void684enableNativeMethodPrefixCapability(jvmtiEnv * jvmtienv) {685jvmtiCapabilities desiredCapabilities;686jvmtiError jvmtierror;687688jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);689/* can be called from any phase */690jplis_assert(jvmtierror == JVMTI_ERROR_NONE);691desiredCapabilities.can_set_native_method_prefix = 1;692jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);693check_phase_ret(jvmtierror);694jplis_assert(jvmtierror == JVMTI_ERROR_NONE);695}696697698/**699* Add the can_set_native_method_prefix capability700*/701void702addNativeMethodPrefixCapability(JPLISAgent * agent) {703if (agent->mNativeMethodPrefixAvailable && !agent->mNativeMethodPrefixAdded) {704jvmtiEnv * jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;705enableNativeMethodPrefixCapability(jvmtienv);706707jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;708if (jvmtienv != NULL) {709enableNativeMethodPrefixCapability(jvmtienv);710}711agent->mNativeMethodPrefixAdded = JNI_TRUE;712}713}714715/**716* Add the can_maintain_original_method_order capability (for testing)717*/718void719addOriginalMethodOrderCapability(JPLISAgent * agent) {720jvmtiEnv * jvmtienv = jvmti(agent);721jvmtiCapabilities desiredCapabilities;722jvmtiError jvmtierror;723724jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);725/* can be called from any phase */726jplis_assert(jvmtierror == JVMTI_ERROR_NONE);727desiredCapabilities.can_maintain_original_method_order = 1;728jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);729check_phase_ret(jvmtierror);730jplis_assert(jvmtierror == JVMTI_ERROR_NONE);731}732733/**734* Add the can_redefine_classes capability735*/736void737addRedefineClassesCapability(JPLISAgent * agent) {738jvmtiEnv * jvmtienv = jvmti(agent);739jvmtiCapabilities desiredCapabilities;740jvmtiError jvmtierror;741742if (agent->mRedefineAvailable && !agent->mRedefineAdded) {743jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);744/* can be called from any phase */745jplis_assert(jvmtierror == JVMTI_ERROR_NONE);746desiredCapabilities.can_redefine_classes = 1;747jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);748check_phase_ret(jvmtierror);749750/*751* With mixed premain/agentmain agents then it's possible that the752* capability was potentially available in the onload phase but753* subsequently unavailable in the live phase.754*/755jplis_assert(jvmtierror == JVMTI_ERROR_NONE ||756jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);757if (jvmtierror == JVMTI_ERROR_NONE) {758agent->mRedefineAdded = JNI_TRUE;759}760}761}762763static jobject764getModuleObject(jvmtiEnv* jvmti,765jobject loaderObject,766const char* cname) {767jvmtiError err = JVMTI_ERROR_NONE;768jobject moduleObject = NULL;769770/* find last slash in the class name */771char* last_slash = (cname == NULL) ? NULL : strrchr(cname, '/');772int len = (last_slash == NULL) ? 0 : (int)(last_slash - cname);773char* pkg_name_buf = (char*)malloc(len + 1);774775if (pkg_name_buf == NULL) {776fprintf(stderr, "OOM error in native tmp buffer allocation");777return NULL;778}779if (last_slash != NULL) {780strncpy(pkg_name_buf, cname, len);781}782pkg_name_buf[len] = '\0';783784err = (*jvmti)->GetNamedModule(jvmti, loaderObject, pkg_name_buf, &moduleObject);785free((void*)pkg_name_buf);786check_phase_ret_blob(err, NULL);787jplis_assert_msg(err == JVMTI_ERROR_NONE, "error in the JVMTI GetNamedModule");788789return moduleObject;790}791792/*793* Support for the JVMTI callbacks794*/795796void797transformClassFile( JPLISAgent * agent,798JNIEnv * jnienv,799jobject loaderObject,800const char* name,801jclass classBeingRedefined,802jobject protectionDomain,803jint class_data_len,804const unsigned char* class_data,805jint* new_class_data_len,806unsigned char** new_class_data,807jboolean is_retransformer) {808jboolean errorOutstanding = JNI_FALSE;809jstring classNameStringObject = NULL;810jarray classFileBufferObject = NULL;811jarray transformedBufferObject = NULL;812jsize transformedBufferSize = 0;813unsigned char * resultBuffer = NULL;814jboolean shouldRun = JNI_FALSE;815816/* only do this if we aren't already in the middle of processing a class on this thread */817shouldRun = tryToAcquireReentrancyToken(818jvmti(agent),819NULL); /* this thread */820821if ( shouldRun ) {822/* first marshall all the parameters */823classNameStringObject = (*jnienv)->NewStringUTF(jnienv,824name);825errorOutstanding = checkForAndClearThrowable(jnienv);826jplis_assert_msg(!errorOutstanding, "can't create name string");827828if ( !errorOutstanding ) {829classFileBufferObject = (*jnienv)->NewByteArray(jnienv,830class_data_len);831errorOutstanding = checkForAndClearThrowable(jnienv);832jplis_assert_msg(!errorOutstanding, "can't create byte array");833}834835if ( !errorOutstanding ) {836jbyte * typedBuffer = (jbyte *) class_data; /* nasty cast, dumb JNI interface, const missing */837/* The sign cast is safe. The const cast is dumb. */838(*jnienv)->SetByteArrayRegion( jnienv,839classFileBufferObject,8400,841class_data_len,842typedBuffer);843errorOutstanding = checkForAndClearThrowable(jnienv);844jplis_assert_msg(!errorOutstanding, "can't set byte array region");845}846847/* now call the JPL agents to do the transforming */848/* potential future optimization: may want to skip this if there are none */849if ( !errorOutstanding ) {850jobject moduleObject = NULL;851852if (classBeingRedefined == NULL) {853moduleObject = getModuleObject(jvmti(agent), loaderObject, name);854} else {855// Redefine or retransform, InstrumentationImpl.transform() will use856// classBeingRedefined.getModule() to get the module.857}858jplis_assert(agent->mInstrumentationImpl != NULL);859jplis_assert(agent->mTransform != NULL);860transformedBufferObject = (*jnienv)->CallObjectMethod(861jnienv,862agent->mInstrumentationImpl,863agent->mTransform,864moduleObject,865loaderObject,866classNameStringObject,867classBeingRedefined,868protectionDomain,869classFileBufferObject,870is_retransformer);871errorOutstanding = checkForAndClearThrowable(jnienv);872jplis_assert_msg(!errorOutstanding, "transform method call failed");873}874875/* Finally, unmarshall the parameters (if someone touched the buffer, tell the JVM) */876if ( !errorOutstanding ) {877if ( transformedBufferObject != NULL ) {878transformedBufferSize = (*jnienv)->GetArrayLength( jnienv,879transformedBufferObject);880errorOutstanding = checkForAndClearThrowable(jnienv);881jplis_assert_msg(!errorOutstanding, "can't get array length");882883if ( !errorOutstanding ) {884/* allocate the response buffer with the JVMTI allocate call.885* This is what the JVMTI spec says to do for Class File Load hook responses886*/887jvmtiError allocError = (*(jvmti(agent)))->Allocate(jvmti(agent),888transformedBufferSize,889&resultBuffer);890errorOutstanding = (allocError != JVMTI_ERROR_NONE);891jplis_assert_msg(!errorOutstanding, "can't allocate result buffer");892}893894if ( !errorOutstanding ) {895(*jnienv)->GetByteArrayRegion( jnienv,896transformedBufferObject,8970,898transformedBufferSize,899(jbyte *) resultBuffer);900errorOutstanding = checkForAndClearThrowable(jnienv);901jplis_assert_msg(!errorOutstanding, "can't get byte array region");902903/* in this case, we will not return the buffer to the JVMTI,904* so we need to deallocate it ourselves905*/906if ( errorOutstanding ) {907deallocate( jvmti(agent),908(void*)resultBuffer);909}910}911912if ( !errorOutstanding ) {913*new_class_data_len = (transformedBufferSize);914*new_class_data = resultBuffer;915}916}917}918919/* release the token */920releaseReentrancyToken( jvmti(agent),921NULL); /* this thread */922923}924925return;926}927928/*929* Misc. internal utilities.930*/931932/*933* The only checked exceptions we can throw are ClassNotFoundException and934* UnmodifiableClassException. All others map to InternalError.935*/936jthrowable937redefineClassMapper( JNIEnv * jnienv,938jthrowable throwableToMap) {939jthrowable mappedThrowable = NULL;940941jplis_assert(isSafeForJNICalls(jnienv));942jplis_assert(!isUnchecked(jnienv, throwableToMap));943944if ( isInstanceofClassName( jnienv,945throwableToMap,946"java/lang/ClassNotFoundException") ) {947mappedThrowable = throwableToMap;948} else {949if ( isInstanceofClassName( jnienv,950throwableToMap,951"java/lang/instrument/UnmodifiableClassException")) {952mappedThrowable = throwableToMap;953} else {954jstring message = NULL;955956message = getMessageFromThrowable(jnienv, throwableToMap);957mappedThrowable = createInternalError(jnienv, message);958}959}960961jplis_assert(isSafeForJNICalls(jnienv));962return mappedThrowable;963}964965jobjectArray966getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount) {967jclass classArrayClass = NULL;968jobjectArray localArray = NULL;969jint classIndex = 0;970jboolean errorOccurred = JNI_FALSE;971972/* get the class array class */973classArrayClass = (*jnienv)->FindClass(jnienv, "java/lang/Class");974errorOccurred = checkForThrowable(jnienv);975976if (!errorOccurred) {977jplis_assert_msg(classArrayClass != NULL, "FindClass returned null class");978979/* create the array for the classes */980localArray = (*jnienv)->NewObjectArray(jnienv, classCount, classArrayClass, NULL);981errorOccurred = checkForThrowable(jnienv);982983if (!errorOccurred) {984jplis_assert_msg(localArray != NULL, "NewObjectArray returned null array");985986/* now copy refs to all the classes and put them into the array */987for (classIndex = 0; classIndex < classCount; classIndex++) {988/* put class into array */989(*jnienv)->SetObjectArrayElement(jnienv, localArray, classIndex, classes[classIndex]);990errorOccurred = checkForThrowable(jnienv);991992if (errorOccurred) {993localArray = NULL;994break;995}996}997}998}9991000return localArray;1001}100210031004/* Return the environment with the retransformation capability.1005* Create it if it doesn't exist.1006* Return NULL if it can't be created.1007*/1008jvmtiEnv *1009retransformableEnvironment(JPLISAgent * agent) {1010jvmtiEnv * retransformerEnv = NULL;1011jint jnierror = JNI_OK;1012jvmtiCapabilities desiredCapabilities;1013jvmtiEventCallbacks callbacks;1014jvmtiError jvmtierror;10151016if (agent->mRetransformEnvironment.mJVMTIEnv != NULL) {1017return agent->mRetransformEnvironment.mJVMTIEnv;1018}1019jnierror = (*agent->mJVM)->GetEnv( agent->mJVM,1020(void **) &retransformerEnv,1021JVMTI_VERSION_1_1);1022if ( jnierror != JNI_OK ) {1023return NULL;1024}1025jvmtierror = (*retransformerEnv)->GetCapabilities(retransformerEnv, &desiredCapabilities);1026jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1027desiredCapabilities.can_retransform_classes = 1;1028if (agent->mNativeMethodPrefixAdded) {1029desiredCapabilities.can_set_native_method_prefix = 1;1030}10311032jvmtierror = (*retransformerEnv)->AddCapabilities(retransformerEnv, &desiredCapabilities);1033if (jvmtierror != JVMTI_ERROR_NONE) {1034/* cannot get the capability, dispose of the retransforming environment */1035jvmtierror = (*retransformerEnv)->DisposeEnvironment(retransformerEnv);1036jplis_assert(jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);1037return NULL;1038}1039memset(&callbacks, 0, sizeof(callbacks));1040callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;10411042jvmtierror = (*retransformerEnv)->SetEventCallbacks(retransformerEnv,1043&callbacks,1044sizeof(callbacks));1045jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1046if (jvmtierror == JVMTI_ERROR_NONE) {1047// install the retransforming environment1048agent->mRetransformEnvironment.mJVMTIEnv = retransformerEnv;1049agent->mRetransformEnvironment.mIsRetransformer = JNI_TRUE;10501051// Make it for ClassFileLoadHook handling1052jvmtierror = (*retransformerEnv)->SetEnvironmentLocalStorage(1053retransformerEnv,1054&(agent->mRetransformEnvironment));1055jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1056if (jvmtierror == JVMTI_ERROR_NONE) {1057return retransformerEnv;1058}1059}1060return NULL;1061}106210631064/*1065* Underpinnings for native methods1066*/10671068jboolean1069isModifiableClass(JNIEnv * jnienv, JPLISAgent * agent, jclass clazz) {1070jvmtiEnv * jvmtienv = jvmti(agent);1071jvmtiError jvmtierror;1072jboolean is_modifiable = JNI_FALSE;10731074jvmtierror = (*jvmtienv)->IsModifiableClass( jvmtienv,1075clazz,1076&is_modifiable);1077check_phase_ret_false(jvmtierror);1078jplis_assert(jvmtierror == JVMTI_ERROR_NONE);10791080return is_modifiable;1081}10821083jboolean1084isRetransformClassesSupported(JNIEnv * jnienv, JPLISAgent * agent) {1085return agent->mRetransformEnvironment.mIsRetransformer;1086}10871088void1089setHasTransformers(JNIEnv * jnienv, JPLISAgent * agent, jboolean has) {1090jvmtiEnv * jvmtienv = jvmti(agent);1091jvmtiError jvmtierror;10921093jplis_assert(jvmtienv != NULL);1094jvmtierror = (*jvmtienv)->SetEventNotificationMode(1095jvmtienv,1096has? JVMTI_ENABLE : JVMTI_DISABLE,1097JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,1098NULL /* all threads */);1099check_phase_ret(jvmtierror);1100jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1101}11021103void1104setHasRetransformableTransformers(JNIEnv * jnienv, JPLISAgent * agent, jboolean has) {1105jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);1106jvmtiError jvmtierror;11071108jplis_assert(retransformerEnv != NULL);1109jvmtierror = (*retransformerEnv)->SetEventNotificationMode(1110retransformerEnv,1111has? JVMTI_ENABLE : JVMTI_DISABLE,1112JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,1113NULL /* all threads */);1114check_phase_ret(jvmtierror);1115jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1116}11171118void1119retransformClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classes) {1120jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);1121jboolean errorOccurred = JNI_FALSE;1122jvmtiError errorCode = JVMTI_ERROR_NONE;1123jsize numClasses = 0;1124jclass * classArray = NULL;11251126/* This is supposed to be checked by caller, but just to be sure */1127if (retransformerEnv == NULL) {1128jplis_assert(retransformerEnv != NULL);1129errorOccurred = JNI_TRUE;1130errorCode = JVMTI_ERROR_MUST_POSSESS_CAPABILITY;1131}11321133/* This was supposed to be checked by caller too */1134if (!errorOccurred && classes == NULL) {1135jplis_assert(classes != NULL);1136errorOccurred = JNI_TRUE;1137errorCode = JVMTI_ERROR_NULL_POINTER;1138}11391140if (!errorOccurred) {1141numClasses = (*jnienv)->GetArrayLength(jnienv, classes);1142errorOccurred = checkForThrowable(jnienv);1143jplis_assert(!errorOccurred);11441145if (!errorOccurred && numClasses == 0) {1146jplis_assert(numClasses != 0);1147errorOccurred = JNI_TRUE;1148errorCode = JVMTI_ERROR_NULL_POINTER;1149}1150}11511152if (!errorOccurred) {1153classArray = (jclass *) allocate(retransformerEnv,1154numClasses * sizeof(jclass));1155errorOccurred = (classArray == NULL);1156jplis_assert(!errorOccurred);1157if (errorOccurred) {1158errorCode = JVMTI_ERROR_OUT_OF_MEMORY;1159}1160}11611162if (!errorOccurred) {1163jint index;1164for (index = 0; index < numClasses; index++) {1165classArray[index] = (*jnienv)->GetObjectArrayElement(jnienv, classes, index);1166errorOccurred = checkForThrowable(jnienv);1167jplis_assert(!errorOccurred);1168if (errorOccurred) {1169break;1170}11711172if (classArray[index] == NULL) {1173jplis_assert(classArray[index] != NULL);1174errorOccurred = JNI_TRUE;1175errorCode = JVMTI_ERROR_NULL_POINTER;1176break;1177}1178}1179}11801181if (!errorOccurred) {1182errorCode = (*retransformerEnv)->RetransformClasses(retransformerEnv,1183numClasses, classArray);1184errorOccurred = (errorCode != JVMTI_ERROR_NONE);1185}11861187/* Give back the buffer if we allocated it. Throw any exceptions after.1188*/1189if (classArray != NULL) {1190deallocate(retransformerEnv, (void*)classArray);1191}11921193/* Return back if we executed the JVMTI API in a wrong phase1194*/1195check_phase_ret(errorCode);11961197if (errorCode != JVMTI_ERROR_NONE) {1198createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);1199}12001201mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);1202}12031204/*1205* Java code must not call this with a null list or a zero-length list.1206*/1207void1208redefineClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classDefinitions) {1209jvmtiEnv* jvmtienv = jvmti(agent);1210jboolean errorOccurred = JNI_FALSE;1211jclass classDefClass = NULL;1212jmethodID getDefinitionClassMethodID = NULL;1213jmethodID getDefinitionClassFileMethodID = NULL;1214jvmtiClassDefinition* classDefs = NULL;1215jbyteArray* targetFiles = NULL;1216jsize numDefs = 0;12171218jplis_assert(classDefinitions != NULL);12191220numDefs = (*jnienv)->GetArrayLength(jnienv, classDefinitions);1221errorOccurred = checkForThrowable(jnienv);1222jplis_assert(!errorOccurred);12231224if (!errorOccurred) {1225jplis_assert(numDefs > 0);1226/* get method IDs for methods to call on class definitions */1227classDefClass = (*jnienv)->FindClass(jnienv, "java/lang/instrument/ClassDefinition");1228errorOccurred = checkForThrowable(jnienv);1229jplis_assert(!errorOccurred);1230}12311232if (!errorOccurred) {1233getDefinitionClassMethodID = (*jnienv)->GetMethodID( jnienv,1234classDefClass,1235"getDefinitionClass",1236"()Ljava/lang/Class;");1237errorOccurred = checkForThrowable(jnienv);1238jplis_assert(!errorOccurred);1239}12401241if (!errorOccurred) {1242getDefinitionClassFileMethodID = (*jnienv)->GetMethodID( jnienv,1243classDefClass,1244"getDefinitionClassFile",1245"()[B");1246errorOccurred = checkForThrowable(jnienv);1247jplis_assert(!errorOccurred);1248}12491250if (!errorOccurred) {1251classDefs = (jvmtiClassDefinition *) allocate(1252jvmtienv,1253numDefs * sizeof(jvmtiClassDefinition));1254errorOccurred = (classDefs == NULL);1255jplis_assert(!errorOccurred);1256if ( errorOccurred ) {1257createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);1258}12591260else {1261/*1262* We have to save the targetFile values that we compute so1263* that we can release the class_bytes arrays that are1264* returned by GetByteArrayElements(). In case of a JNI1265* error, we can't (easily) recompute the targetFile values1266* and we still want to free any memory we allocated.1267*/1268targetFiles = (jbyteArray *) allocate(jvmtienv,1269numDefs * sizeof(jbyteArray));1270errorOccurred = (targetFiles == NULL);1271jplis_assert(!errorOccurred);1272if ( errorOccurred ) {1273deallocate(jvmtienv, (void*)classDefs);1274createAndThrowThrowableFromJVMTIErrorCode(jnienv,1275JVMTI_ERROR_OUT_OF_MEMORY);1276}1277else {1278jint i, j;12791280// clear classDefs so we can correctly free memory during errors1281memset(classDefs, 0, numDefs * sizeof(jvmtiClassDefinition));12821283for (i = 0; i < numDefs; i++) {1284jclass classDef = NULL;12851286classDef = (*jnienv)->GetObjectArrayElement(jnienv, classDefinitions, i);1287errorOccurred = checkForThrowable(jnienv);1288jplis_assert(!errorOccurred);1289if (errorOccurred) {1290break;1291}12921293classDefs[i].klass = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassMethodID);1294errorOccurred = checkForThrowable(jnienv);1295jplis_assert(!errorOccurred);1296if (errorOccurred) {1297break;1298}12991300targetFiles[i] = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassFileMethodID);1301errorOccurred = checkForThrowable(jnienv);1302jplis_assert(!errorOccurred);1303if (errorOccurred) {1304break;1305}13061307classDefs[i].class_byte_count = (*jnienv)->GetArrayLength(jnienv, targetFiles[i]);1308errorOccurred = checkForThrowable(jnienv);1309jplis_assert(!errorOccurred);1310if (errorOccurred) {1311break;1312}13131314/*1315* Allocate class_bytes last so we don't have to free1316* memory on a partial row error.1317*/1318classDefs[i].class_bytes = (unsigned char*)(*jnienv)->GetByteArrayElements(jnienv, targetFiles[i], NULL);1319errorOccurred = checkForThrowable(jnienv);1320jplis_assert(!errorOccurred);1321if (errorOccurred) {1322break;1323}1324}13251326if (!errorOccurred) {1327jvmtiError errorCode = JVMTI_ERROR_NONE;1328errorCode = (*jvmtienv)->RedefineClasses(jvmtienv, numDefs, classDefs);1329if (errorCode == JVMTI_ERROR_WRONG_PHASE) {1330/* insulate caller from the wrong phase error */1331errorCode = JVMTI_ERROR_NONE;1332} else {1333errorOccurred = (errorCode != JVMTI_ERROR_NONE);1334if ( errorOccurred ) {1335createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);1336}1337}1338}13391340/*1341* Cleanup memory that we allocated above. If we had a1342* JNI error, a JVM/TI error or no errors, index 'i'1343* tracks how far we got in processing the classDefs1344* array. Note: ReleaseByteArrayElements() is safe to1345* call with a JNI exception pending.1346*/1347for (j = 0; j < i; j++) {1348if ((jbyte *)classDefs[j].class_bytes != NULL) {1349(*jnienv)->ReleaseByteArrayElements(jnienv,1350targetFiles[j], (jbyte *)classDefs[j].class_bytes,13510 /* copy back and free */);1352/*1353* Only check for error if we didn't already have one1354* so we don't overwrite errorOccurred.1355*/1356if (!errorOccurred) {1357errorOccurred = checkForThrowable(jnienv);1358jplis_assert(!errorOccurred);1359}1360}1361}1362deallocate(jvmtienv, (void*)targetFiles);1363deallocate(jvmtienv, (void*)classDefs);1364}1365}1366}13671368mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);1369}13701371/* Cheesy sharing. ClassLoader may be null. */1372jobjectArray1373commonGetClassList( JNIEnv * jnienv,1374JPLISAgent * agent,1375jobject classLoader,1376ClassListFetcher fetcher) {1377jvmtiEnv * jvmtienv = jvmti(agent);1378jboolean errorOccurred = JNI_FALSE;1379jvmtiError jvmtierror = JVMTI_ERROR_NONE;1380jint classCount = 0;1381jclass * classes = NULL;1382jobjectArray localArray = NULL;13831384/* retrieve the classes from the JVMTI agent */1385jvmtierror = (*fetcher)( jvmtienv,1386classLoader,1387&classCount,1388&classes);1389check_phase_ret_blob(jvmtierror, localArray);1390errorOccurred = (jvmtierror != JVMTI_ERROR_NONE);1391jplis_assert(!errorOccurred);13921393if ( errorOccurred ) {1394createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);1395} else {1396localArray = getObjectArrayFromClasses( jnienv,1397classes,1398classCount);1399errorOccurred = checkForThrowable(jnienv);1400jplis_assert(!errorOccurred);14011402/* do this whether or not we saw a problem */1403deallocate(jvmtienv, (void*)classes);1404}14051406mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);1407return localArray;14081409}14101411jvmtiError1412getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtienv,1413jobject classLoader,1414jint * classCount,1415jclass ** classes) {1416return (*jvmtienv)->GetLoadedClasses(jvmtienv, classCount, classes);1417}14181419jobjectArray1420getAllLoadedClasses(JNIEnv * jnienv, JPLISAgent * agent) {1421return commonGetClassList( jnienv,1422agent,1423NULL,1424getAllLoadedClassesClassListFetcher);1425}14261427jvmtiError1428getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtienv,1429jobject classLoader,1430jint * classCount,1431jclass ** classes) {1432return (*jvmtienv)->GetClassLoaderClasses(jvmtienv, classLoader, classCount, classes);1433}143414351436jobjectArray1437getInitiatedClasses(JNIEnv * jnienv, JPLISAgent * agent, jobject classLoader) {1438return commonGetClassList( jnienv,1439agent,1440classLoader,1441getInitiatedClassesClassListFetcher);1442}14431444jlong1445getObjectSize(JNIEnv * jnienv, JPLISAgent * agent, jobject objectToSize) {1446jvmtiEnv * jvmtienv = jvmti(agent);1447jlong objectSize = -1;1448jvmtiError jvmtierror = JVMTI_ERROR_NONE;14491450jvmtierror = (*jvmtienv)->GetObjectSize(jvmtienv, objectToSize, &objectSize);1451check_phase_ret_0(jvmtierror);1452jplis_assert(jvmtierror == JVMTI_ERROR_NONE);1453if ( jvmtierror != JVMTI_ERROR_NONE ) {1454createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);1455}14561457mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);1458return objectSize;1459}14601461void1462appendToClassLoaderSearch(JNIEnv * jnienv, JPLISAgent * agent, jstring jarFile, jboolean isBootLoader)1463{1464jvmtiEnv * jvmtienv = jvmti(agent);1465jboolean errorOutstanding;1466jvmtiError jvmtierror;1467const char* utf8Chars;1468jsize utf8Len;1469jboolean isCopy;1470char platformChars[MAXPATHLEN];1471int platformLen;14721473utf8Len = (*jnienv)->GetStringUTFLength(jnienv, jarFile);1474errorOutstanding = checkForAndClearThrowable(jnienv);14751476if (!errorOutstanding) {1477utf8Chars = (*jnienv)->GetStringUTFChars(jnienv, jarFile, &isCopy);1478errorOutstanding = checkForAndClearThrowable(jnienv);14791480if (!errorOutstanding && utf8Chars != NULL) {1481/*1482* JVMTI spec'ed to use modified UTF8. At this time this is not implemented1483* the platform encoding is used.1484*/1485platformLen = convertUft8ToPlatformString((char*)utf8Chars, utf8Len, platformChars, MAXPATHLEN);1486if (platformLen < 0) {1487createAndThrowInternalError(jnienv);1488(*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);1489return;1490}14911492(*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);1493errorOutstanding = checkForAndClearThrowable(jnienv);14941495if (!errorOutstanding) {14961497if (isBootLoader) {1498jvmtierror = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, platformChars);1499} else {1500jvmtierror = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, platformChars);1501}1502check_phase_ret(jvmtierror);15031504if ( jvmtierror != JVMTI_ERROR_NONE ) {1505createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);1506}1507}1508}1509}15101511mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);1512}15131514/*1515* Set the prefixes used to wrap native methods (so they can be instrumented).1516* Each transform can set a prefix, any that have been set come in as prefixArray.1517* Convert them in native strings in a native array then call JVM TI.1518* One a given call, this function handles either the prefixes for retransformable1519* transforms or for normal transforms.1520*/1521void1522setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefixArray,1523jboolean isRetransformable) {1524jvmtiEnv* jvmtienv;1525jvmtiError err = JVMTI_ERROR_NONE;1526jsize arraySize;1527jboolean errorOccurred = JNI_FALSE;15281529jplis_assert(prefixArray != NULL);15301531if (isRetransformable) {1532jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;1533} else {1534jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;1535}1536arraySize = (*jnienv)->GetArrayLength(jnienv, prefixArray);1537errorOccurred = checkForThrowable(jnienv);1538jplis_assert(!errorOccurred);15391540if (!errorOccurred) {1541/* allocate the native to hold the native prefixes */1542const char** prefixes = (const char**) allocate(jvmtienv,1543arraySize * sizeof(char*));1544/* since JNI ReleaseStringUTFChars needs the jstring from which the native1545* string was allocated, we store them in a parallel array */1546jstring* originForRelease = (jstring*) allocate(jvmtienv,1547arraySize * sizeof(jstring));1548errorOccurred = (prefixes == NULL || originForRelease == NULL);1549jplis_assert(!errorOccurred);1550if ( errorOccurred ) {1551createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);1552}1553else {1554jint inx = 0;1555jint i;1556for (i = 0; i < arraySize; i++) {1557jstring prefixStr = NULL;1558const char* prefix;1559jsize prefixLen;1560jboolean isCopy;15611562prefixStr = (jstring) ((*jnienv)->GetObjectArrayElement(jnienv,1563prefixArray, i));1564errorOccurred = checkForThrowable(jnienv);1565jplis_assert(!errorOccurred);1566if (errorOccurred) {1567break;1568}1569if (prefixStr == NULL) {1570continue;1571}15721573prefixLen = (*jnienv)->GetStringUTFLength(jnienv, prefixStr);1574errorOccurred = checkForThrowable(jnienv);1575jplis_assert(!errorOccurred);1576if (errorOccurred) {1577break;1578}15791580if (prefixLen > 0) {1581prefix = (*jnienv)->GetStringUTFChars(jnienv, prefixStr, &isCopy);1582errorOccurred = checkForThrowable(jnienv);1583jplis_assert(!errorOccurred);1584if (!errorOccurred && prefix != NULL) {1585prefixes[inx] = prefix;1586originForRelease[inx] = prefixStr;1587++inx;1588}1589}1590}15911592err = (*jvmtienv)->SetNativeMethodPrefixes(jvmtienv, inx, (char**)prefixes);1593/* can be called from any phase */1594jplis_assert(err == JVMTI_ERROR_NONE);15951596for (i = 0; i < inx; i++) {1597(*jnienv)->ReleaseStringUTFChars(jnienv, originForRelease[i], prefixes[i]);1598}1599}1600deallocate(jvmtienv, (void*)prefixes);1601deallocate(jvmtienv, (void*)originForRelease);1602}1603}160416051606