annotate src/share/instrument/JPLISAgent.c @ 4882:3c1ab134db71

7121600: Instrumentation.redefineClasses() leaks class bytes Summary: Call JNI ReleaseByteArrayElements() on memory returned by JNI GetByteArrayElements(). Also push test for 7122253. Reviewed-by: acorn, poonam
author dcubed
date Thu, 22 Dec 2011 18:35:48 -0800
parents 272483f6650b
children
rev   line source
duke@0 1 /*
ohair@3909 2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
duke@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@0 4 *
duke@0 5 * This code is free software; you can redistribute it and/or modify it
duke@0 6 * under the terms of the GNU General Public License version 2 only, as
ohair@2362 7 * published by the Free Software Foundation. Oracle designates this
duke@0 8 * particular file as subject to the "Classpath" exception as provided
ohair@2362 9 * by Oracle in the LICENSE file that accompanied this code.
duke@0 10 *
duke@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@0 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@0 15 * accompanied this code).
duke@0 16 *
duke@0 17 * You should have received a copy of the GNU General Public License version
duke@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@0 20 *
ohair@2362 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@2362 22 * or visit www.oracle.com if you need additional information or have any
ohair@2362 23 * questions.
duke@0 24 */
duke@0 25
duke@0 26 /*
duke@0 27 * Copyright 2003 Wily Technology, Inc.
duke@0 28 */
duke@0 29
duke@0 30 #include <jni.h>
duke@0 31 #include <jvmti.h>
duke@0 32 #include <stdlib.h>
duke@0 33 #include <string.h>
duke@0 34 #include "JPLISAgent.h"
duke@0 35 #include "JPLISAssert.h"
duke@0 36 #include "Utilities.h"
duke@0 37 #include "Reentrancy.h"
duke@0 38 #include "JavaExceptions.h"
duke@0 39
duke@0 40 #include "EncodingSupport.h"
ohair@502 41 #include "FileSystemSupport.h" /* For MAXPATHLEN & uintptr_t */
duke@0 42
duke@0 43 #include "sun_instrument_InstrumentationImpl.h"
duke@0 44
duke@0 45 /*
duke@0 46 * The JPLISAgent manages the initialization all of the Java programming language Agents.
duke@0 47 * It also supports the native method bridge between the JPLIS and the JVMTI.
duke@0 48 * It maintains a single JVMTI Env that all JPL agents share.
duke@0 49 * It parses command line requests and creates individual Java agents.
duke@0 50 */
duke@0 51
duke@0 52
duke@0 53 /*
duke@0 54 * private prototypes
duke@0 55 */
duke@0 56
duke@0 57 /* Allocates an unformatted JPLIS agent data structure. Returns NULL if allocation fails. */
duke@0 58 JPLISAgent *
duke@0 59 allocateJPLISAgent(jvmtiEnv * jvmtiEnv);
duke@0 60
duke@0 61 /* Initializes an already-allocated JPLIS agent data structure. */
duke@0 62 JPLISInitializationError
duke@0 63 initializeJPLISAgent( JPLISAgent * agent,
duke@0 64 JavaVM * vm,
duke@0 65 jvmtiEnv * jvmtienv);
duke@0 66 /* De-allocates a JPLIS agent data structure. Only used in partial-failure cases at startup;
duke@0 67 * in normal usage the JPLIS agent lives forever
duke@0 68 */
duke@0 69 void
duke@0 70 deallocateJPLISAgent( jvmtiEnv * jvmtienv,
duke@0 71 JPLISAgent * agent);
duke@0 72
duke@0 73 /* Does one-time work to interrogate the JVM about capabilities and cache the answers. */
duke@0 74 void
duke@0 75 checkCapabilities(JPLISAgent * agent);
duke@0 76
duke@0 77 /* Takes the elements of the command string (agent class name and options string) and
duke@0 78 * create java strings for them.
duke@0 79 * Returns true if a classname was found. Makes no promises beyond the textual; says nothing about whether
duke@0 80 * the class exists or can be loaded.
duke@0 81 * If return value is true, sets outputClassname to a non-NULL local JNI reference.
duke@0 82 * If return value is true, sets outputOptionsString either to NULL or to a non-NULL local JNI reference.
duke@0 83 * If return value is false, neither output parameter is set.
duke@0 84 */
duke@0 85 jboolean
duke@0 86 commandStringIntoJavaStrings( JNIEnv * jnienv,
duke@0 87 const char * classname,
duke@0 88 const char * optionsString,
duke@0 89 jstring * outputClassname,
duke@0 90 jstring * outputOptionsString);
duke@0 91
duke@0 92 /* Start one Java agent from the supplied parameters.
duke@0 93 * Most of the logic lives in a helper function that lives over in Java code--
duke@0 94 * we pass parameters out to Java and use our own Java helper to actually
duke@0 95 * load the agent and call the premain.
duke@0 96 * Returns true if the Java agent class is loaded and the premain/agentmain method completes
duke@0 97 * with no exceptions, false otherwise.
duke@0 98 */
duke@0 99 jboolean
duke@0 100 invokeJavaAgentMainMethod( JNIEnv * jnienv,
duke@0 101 jobject instrumentationImpl,
duke@0 102 jmethodID agentMainMethod,
duke@0 103 jstring className,
duke@0 104 jstring optionsString);
duke@0 105
duke@0 106 /* Once we have loaded the Java agent and called the premain,
duke@0 107 * we can release the copies we have been keeping of the command line
duke@0 108 * data (agent class name and option strings).
duke@0 109 */
duke@0 110 void
duke@0 111 deallocateCommandLineData(JPLISAgent * agent);
duke@0 112
duke@0 113 /*
duke@0 114 * Common support for various class list fetchers.
duke@0 115 */
duke@0 116 typedef jvmtiError (*ClassListFetcher)
duke@0 117 ( jvmtiEnv * jvmtiEnv,
duke@0 118 jobject classLoader,
duke@0 119 jint * classCount,
duke@0 120 jclass ** classes);
duke@0 121
duke@0 122 /* Fetcher that ignores the class loader parameter, and uses the JVMTI to get a list of all classes.
duke@0 123 * Returns a jvmtiError according to the underlying JVMTI service.
duke@0 124 */
duke@0 125 jvmtiError
duke@0 126 getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,
duke@0 127 jobject classLoader,
duke@0 128 jint * classCount,
duke@0 129 jclass ** classes);
duke@0 130
duke@0 131 /* Fetcher that uses the class loader parameter, and uses the JVMTI to get a list of all classes
duke@0 132 * for which the supplied loader is the initiating loader.
duke@0 133 * Returns a jvmtiError according to the underlying JVMTI service.
duke@0 134 */
duke@0 135 jvmtiError
duke@0 136 getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtiEnv,
duke@0 137 jobject classLoader,
duke@0 138 jint * classCount,
duke@0 139 jclass ** classes);
duke@0 140
duke@0 141 /*
duke@0 142 * Common guts for two native methods, which are the same except for the policy for fetching
duke@0 143 * the list of classes.
duke@0 144 * Either returns a local JNI reference to an array of references to java.lang.Class.
duke@0 145 * Can throw, if it does will alter the JNIEnv with an outstanding exception.
duke@0 146 */
duke@0 147 jobjectArray
duke@0 148 commonGetClassList( JNIEnv * jnienv,
duke@0 149 JPLISAgent * agent,
duke@0 150 jobject classLoader,
duke@0 151 ClassListFetcher fetcher);
duke@0 152
duke@0 153
duke@0 154 /*
duke@0 155 * Misc. utilities.
duke@0 156 */
duke@0 157
duke@0 158 /* Checked exception mapper used by the redefine classes implementation.
duke@0 159 * Allows ClassNotFoundException or UnmodifiableClassException; maps others
duke@0 160 * to InternalError. Can return NULL in an error case.
duke@0 161 */
duke@0 162 jthrowable
duke@0 163 redefineClassMapper( JNIEnv * jnienv,
duke@0 164 jthrowable throwableToMap);
duke@0 165
duke@0 166 /* Turns a buffer of jclass * into a Java array whose elements are java.lang.Class.
duke@0 167 * Can throw, if it does will alter the JNIEnv with an outstanding exception.
duke@0 168 */
duke@0 169 jobjectArray
duke@0 170 getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount);
duke@0 171
duke@0 172
duke@0 173 JPLISEnvironment *
duke@0 174 getJPLISEnvironment(jvmtiEnv * jvmtienv) {
duke@0 175 JPLISEnvironment * environment = NULL;
duke@0 176 jvmtiError jvmtierror = JVMTI_ERROR_NONE;
duke@0 177
duke@0 178 jvmtierror = (*jvmtienv)->GetEnvironmentLocalStorage(
duke@0 179 jvmtienv,
duke@0 180 (void**)&environment);
dcubed@142 181 /* can be called from any phase */
duke@0 182 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 183
duke@0 184 if (jvmtierror == JVMTI_ERROR_NONE) {
duke@0 185 jplis_assert(environment != NULL);
duke@0 186 jplis_assert(environment->mJVMTIEnv == jvmtienv);
duke@0 187 } else {
duke@0 188 environment = NULL;
duke@0 189 }
duke@0 190 return environment;
duke@0 191 }
duke@0 192
duke@0 193 /*
duke@0 194 * OnLoad processing code.
duke@0 195 */
duke@0 196
duke@0 197 /*
duke@0 198 * Creates a new JPLISAgent.
duke@0 199 * Returns error if the agent cannot be created and initialized.
duke@0 200 * The JPLISAgent* pointed to by agent_ptr is set to the new broker,
duke@0 201 * or NULL if an error has occurred.
duke@0 202 */
duke@0 203 JPLISInitializationError
duke@0 204 createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {
duke@0 205 JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
duke@0 206 jvmtiEnv * jvmtienv = NULL;
duke@0 207 jint jnierror = JNI_OK;
duke@0 208
duke@0 209 *agent_ptr = NULL;
duke@0 210 jnierror = (*vm)->GetEnv( vm,
duke@0 211 (void **) &jvmtienv,
kamg@3389 212 JVMTI_VERSION_1_1);
duke@0 213 if ( jnierror != JNI_OK ) {
duke@0 214 initerror = JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT;
duke@0 215 } else {
duke@0 216 JPLISAgent * agent = allocateJPLISAgent(jvmtienv);
duke@0 217 if ( agent == NULL ) {
duke@0 218 initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
duke@0 219 } else {
duke@0 220 initerror = initializeJPLISAgent( agent,
duke@0 221 vm,
duke@0 222 jvmtienv);
duke@0 223 if ( initerror == JPLIS_INIT_ERROR_NONE ) {
duke@0 224 *agent_ptr = agent;
duke@0 225 } else {
duke@0 226 deallocateJPLISAgent(jvmtienv, agent);
duke@0 227 }
duke@0 228 }
duke@0 229
duke@0 230 /* don't leak envs */
duke@0 231 if ( initerror != JPLIS_INIT_ERROR_NONE ) {
duke@0 232 jvmtiError jvmtierror = (*jvmtienv)->DisposeEnvironment(jvmtienv);
dcubed@142 233 /* can be called from any phase */
duke@0 234 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 235 }
duke@0 236 }
duke@0 237
duke@0 238 return initerror;
duke@0 239 }
duke@0 240
duke@0 241 /*
duke@0 242 * Allocates a JPLISAgent. Returns NULL if it cannot be allocated
duke@0 243 */
duke@0 244 JPLISAgent *
duke@0 245 allocateJPLISAgent(jvmtiEnv * jvmtienv) {
duke@0 246 return (JPLISAgent *) allocate( jvmtienv,
duke@0 247 sizeof(JPLISAgent));
duke@0 248 }
duke@0 249
duke@0 250 JPLISInitializationError
duke@0 251 initializeJPLISAgent( JPLISAgent * agent,
duke@0 252 JavaVM * vm,
duke@0 253 jvmtiEnv * jvmtienv) {
duke@0 254 jvmtiError jvmtierror = JVMTI_ERROR_NONE;
duke@0 255 jvmtiPhase phase;
duke@0 256
duke@0 257 agent->mJVM = vm;
duke@0 258 agent->mNormalEnvironment.mJVMTIEnv = jvmtienv;
duke@0 259 agent->mNormalEnvironment.mAgent = agent;
duke@0 260 agent->mNormalEnvironment.mIsRetransformer = JNI_FALSE;
duke@0 261 agent->mRetransformEnvironment.mJVMTIEnv = NULL; /* NULL until needed */
duke@0 262 agent->mRetransformEnvironment.mAgent = agent;
dcubed@139 263 agent->mRetransformEnvironment.mIsRetransformer = JNI_FALSE; /* JNI_FALSE until mJVMTIEnv is set */
duke@0 264 agent->mAgentmainCaller = NULL;
duke@0 265 agent->mInstrumentationImpl = NULL;
duke@0 266 agent->mPremainCaller = NULL;
duke@0 267 agent->mTransform = NULL;
duke@0 268 agent->mRedefineAvailable = JNI_FALSE; /* assume no for now */
duke@0 269 agent->mRedefineAdded = JNI_FALSE;
duke@0 270 agent->mNativeMethodPrefixAvailable = JNI_FALSE; /* assume no for now */
duke@0 271 agent->mNativeMethodPrefixAdded = JNI_FALSE;
duke@0 272 agent->mAgentClassName = NULL;
duke@0 273 agent->mOptionsString = NULL;
duke@0 274
duke@0 275 /* make sure we can recover either handle in either direction.
duke@0 276 * the agent has a ref to the jvmti; make it mutual
duke@0 277 */
duke@0 278 jvmtierror = (*jvmtienv)->SetEnvironmentLocalStorage(
duke@0 279 jvmtienv,
duke@0 280 &(agent->mNormalEnvironment));
dcubed@142 281 /* can be called from any phase */
duke@0 282 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 283
duke@0 284 /* check what capabilities are available */
duke@0 285 checkCapabilities(agent);
duke@0 286
duke@0 287 /* check phase - if live phase then we don't need the VMInit event */
dcubed@141 288 jvmtierror = (*jvmtienv)->GetPhase(jvmtienv, &phase);
dcubed@142 289 /* can be called from any phase */
duke@0 290 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 291 if (phase == JVMTI_PHASE_LIVE) {
duke@0 292 return JPLIS_INIT_ERROR_NONE;
duke@0 293 }
duke@0 294
dcubed@142 295 if (phase != JVMTI_PHASE_ONLOAD) {
dcubed@142 296 /* called too early or called too late; either way bail out */
dcubed@142 297 return JPLIS_INIT_ERROR_FAILURE;
dcubed@142 298 }
dcubed@142 299
duke@0 300 /* now turn on the VMInit event */
duke@0 301 if ( jvmtierror == JVMTI_ERROR_NONE ) {
duke@0 302 jvmtiEventCallbacks callbacks;
duke@0 303 memset(&callbacks, 0, sizeof(callbacks));
duke@0 304 callbacks.VMInit = &eventHandlerVMInit;
duke@0 305
duke@0 306 jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,
duke@0 307 &callbacks,
duke@0 308 sizeof(callbacks));
dcubed@142 309 check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);
duke@0 310 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 311 }
duke@0 312
duke@0 313 if ( jvmtierror == JVMTI_ERROR_NONE ) {
duke@0 314 jvmtierror = (*jvmtienv)->SetEventNotificationMode(
duke@0 315 jvmtienv,
duke@0 316 JVMTI_ENABLE,
duke@0 317 JVMTI_EVENT_VM_INIT,
duke@0 318 NULL /* all threads */);
dcubed@142 319 check_phase_ret_blob(jvmtierror, JPLIS_INIT_ERROR_FAILURE);
duke@0 320 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 321 }
duke@0 322
duke@0 323 return (jvmtierror == JVMTI_ERROR_NONE)? JPLIS_INIT_ERROR_NONE : JPLIS_INIT_ERROR_FAILURE;
duke@0 324 }
duke@0 325
duke@0 326 void
duke@0 327 deallocateJPLISAgent(jvmtiEnv * jvmtienv, JPLISAgent * agent) {
duke@0 328 deallocate(jvmtienv, agent);
duke@0 329 }
duke@0 330
duke@0 331
duke@0 332 JPLISInitializationError
duke@0 333 recordCommandLineData( JPLISAgent * agent,
duke@0 334 const char * agentClassName,
duke@0 335 const char * optionsString ) {
duke@0 336 JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE;
duke@0 337 char * ourCopyOfAgentClassName = NULL;
duke@0 338 char * ourCopyOfOptionsString = NULL;
duke@0 339
duke@0 340 /* if no actual params, bail out now */
duke@0 341 if ((agentClassName == NULL) || (*agentClassName == 0)) {
duke@0 342 initerror = JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED;
duke@0 343 } else {
duke@0 344 ourCopyOfAgentClassName = allocate(jvmti(agent), strlen(agentClassName)+1);
duke@0 345 if (ourCopyOfAgentClassName == NULL) {
duke@0 346 initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
duke@0 347 } else {
duke@0 348 if (optionsString != NULL) {
duke@0 349 ourCopyOfOptionsString = allocate(jvmti(agent), strlen(optionsString)+1);
duke@0 350 if (ourCopyOfOptionsString == NULL) {
duke@0 351 deallocate(jvmti(agent), ourCopyOfAgentClassName);
duke@0 352 initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;
duke@0 353 }
duke@0 354 }
duke@0 355 }
duke@0 356 }
duke@0 357
duke@0 358 if (initerror == JPLIS_INIT_ERROR_NONE) {
duke@0 359 strcpy(ourCopyOfAgentClassName, agentClassName);
duke@0 360 if (optionsString != NULL) {
duke@0 361 strcpy(ourCopyOfOptionsString, optionsString);
duke@0 362 }
duke@0 363 agent->mAgentClassName = ourCopyOfAgentClassName;
duke@0 364 agent->mOptionsString = ourCopyOfOptionsString;
duke@0 365 }
duke@0 366
duke@0 367 return initerror;
duke@0 368 }
duke@0 369
duke@0 370 /*
duke@0 371 * VMInit processing code.
duke@0 372 */
duke@0 373
duke@0 374
duke@0 375 /*
duke@0 376 * If this call fails, the JVM launch will ultimately be aborted,
duke@0 377 * so we don't have to be super-careful to clean up in partial failure
duke@0 378 * cases.
duke@0 379 */
duke@0 380 jboolean
duke@0 381 processJavaStart( JPLISAgent * agent,
duke@0 382 JNIEnv * jnienv) {
duke@0 383 jboolean result;
duke@0 384
duke@0 385 /*
duke@0 386 * OK, Java is up now. We can start everything that needs Java.
duke@0 387 */
duke@0 388
duke@0 389 /*
duke@0 390 * First make our emergency fallback InternalError throwable.
duke@0 391 */
duke@0 392 result = initializeFallbackError(jnienv);
duke@0 393 jplis_assert(result);
duke@0 394
duke@0 395 /*
duke@0 396 * Now make the InstrumentationImpl instance.
duke@0 397 */
duke@0 398 if ( result ) {
duke@0 399 result = createInstrumentationImpl(jnienv, agent);
duke@0 400 jplis_assert(result);
duke@0 401 }
duke@0 402
duke@0 403
duke@0 404 /*
duke@0 405 * Then turn off the VMInit handler and turn on the ClassFileLoadHook.
duke@0 406 * This way it is on before anyone registers a transformer.
duke@0 407 */
duke@0 408 if ( result ) {
duke@0 409 result = setLivePhaseEventHandlers(agent);
duke@0 410 jplis_assert(result);
duke@0 411 }
duke@0 412
duke@0 413 /*
duke@0 414 * Load the Java agent, and call the premain.
duke@0 415 */
duke@0 416 if ( result ) {
duke@0 417 result = startJavaAgent(agent, jnienv,
duke@0 418 agent->mAgentClassName, agent->mOptionsString,
duke@0 419 agent->mPremainCaller);
duke@0 420 }
duke@0 421
duke@0 422 /*
duke@0 423 * Finally surrender all of the tracking data that we don't need any more.
duke@0 424 * If something is wrong, skip it, we will be aborting the JVM anyway.
duke@0 425 */
duke@0 426 if ( result ) {
duke@0 427 deallocateCommandLineData(agent);
duke@0 428 }
duke@0 429
duke@0 430 return result;
duke@0 431 }
duke@0 432
duke@0 433 jboolean
duke@0 434 startJavaAgent( JPLISAgent * agent,
duke@0 435 JNIEnv * jnienv,
duke@0 436 const char * classname,
duke@0 437 const char * optionsString,
duke@0 438 jmethodID agentMainMethod) {
duke@0 439 jboolean success = JNI_FALSE;
duke@0 440 jstring classNameObject = NULL;
duke@0 441 jstring optionsStringObject = NULL;
duke@0 442
duke@0 443 success = commandStringIntoJavaStrings( jnienv,
duke@0 444 classname,
duke@0 445 optionsString,
duke@0 446 &classNameObject,
duke@0 447 &optionsStringObject);
duke@0 448
duke@0 449 if (success) {
duke@0 450 success = invokeJavaAgentMainMethod( jnienv,
duke@0 451 agent->mInstrumentationImpl,
duke@0 452 agentMainMethod,
duke@0 453 classNameObject,
duke@0 454 optionsStringObject);
duke@0 455 }
duke@0 456
duke@0 457 return success;
duke@0 458 }
duke@0 459
duke@0 460 void
duke@0 461 deallocateCommandLineData( JPLISAgent * agent) {
duke@0 462 deallocate(jvmti(agent), (void*)agent->mAgentClassName);
duke@0 463 deallocate(jvmti(agent), (void*)agent->mOptionsString);
duke@0 464
duke@0 465 /* zero things out so it is easier to see what is going on */
duke@0 466 agent->mAgentClassName = NULL;
duke@0 467 agent->mOptionsString = NULL;
duke@0 468 }
duke@0 469
duke@0 470 /*
duke@0 471 * Create the java.lang.instrument.Instrumentation instance
duke@0 472 * and access information for it (method IDs, etc)
duke@0 473 */
duke@0 474 jboolean
duke@0 475 createInstrumentationImpl( JNIEnv * jnienv,
duke@0 476 JPLISAgent * agent) {
duke@0 477 jclass implClass = NULL;
duke@0 478 jboolean errorOutstanding = JNI_FALSE;
duke@0 479 jobject resultImpl = NULL;
duke@0 480 jmethodID premainCallerMethodID = NULL;
duke@0 481 jmethodID agentmainCallerMethodID = NULL;
duke@0 482 jmethodID transformMethodID = NULL;
duke@0 483 jmethodID constructorID = NULL;
duke@0 484 jobject localReference = NULL;
duke@0 485
duke@0 486 /* First find the class of our implementation */
duke@0 487 implClass = (*jnienv)->FindClass( jnienv,
duke@0 488 JPLIS_INSTRUMENTIMPL_CLASSNAME);
duke@0 489 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 490 errorOutstanding = errorOutstanding || (implClass == NULL);
duke@0 491 jplis_assert_msg(!errorOutstanding, "find class on InstrumentationImpl failed");
duke@0 492
duke@0 493 if ( !errorOutstanding ) {
duke@0 494 constructorID = (*jnienv)->GetMethodID( jnienv,
duke@0 495 implClass,
duke@0 496 JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODNAME,
duke@0 497 JPLIS_INSTRUMENTIMPL_CONSTRUCTOR_METHODSIGNATURE);
duke@0 498 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 499 errorOutstanding = errorOutstanding || (constructorID == NULL);
duke@0 500 jplis_assert_msg(!errorOutstanding, "find constructor on InstrumentationImpl failed");
duke@0 501 }
duke@0 502
duke@0 503 if ( !errorOutstanding ) {
duke@0 504 jlong peerReferenceAsScalar = (jlong)(intptr_t) agent;
duke@0 505 localReference = (*jnienv)->NewObject( jnienv,
duke@0 506 implClass,
duke@0 507 constructorID,
duke@0 508 peerReferenceAsScalar,
duke@0 509 agent->mRedefineAdded,
duke@0 510 agent->mNativeMethodPrefixAdded);
duke@0 511 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 512 errorOutstanding = errorOutstanding || (localReference == NULL);
duke@0 513 jplis_assert_msg(!errorOutstanding, "call constructor on InstrumentationImpl failed");
duke@0 514 }
duke@0 515
duke@0 516 if ( !errorOutstanding ) {
duke@0 517 resultImpl = (*jnienv)->NewGlobalRef(jnienv, localReference);
duke@0 518 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 519 jplis_assert_msg(!errorOutstanding, "copy local ref to global ref");
duke@0 520 }
duke@0 521
duke@0 522 /* Now look up the method ID for the pre-main caller (we will need this more than once) */
duke@0 523 if ( !errorOutstanding ) {
duke@0 524 premainCallerMethodID = (*jnienv)->GetMethodID( jnienv,
duke@0 525 implClass,
duke@0 526 JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODNAME,
duke@0 527 JPLIS_INSTRUMENTIMPL_PREMAININVOKER_METHODSIGNATURE);
duke@0 528 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 529 errorOutstanding = errorOutstanding || (premainCallerMethodID == NULL);
duke@0 530 jplis_assert_msg(!errorOutstanding, "can't find premain invoker methodID");
duke@0 531 }
duke@0 532
duke@0 533 /* Now look up the method ID for the agent-main caller */
duke@0 534 if ( !errorOutstanding ) {
duke@0 535 agentmainCallerMethodID = (*jnienv)->GetMethodID( jnienv,
duke@0 536 implClass,
duke@0 537 JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODNAME,
duke@0 538 JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODSIGNATURE);
duke@0 539 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 540 errorOutstanding = errorOutstanding || (agentmainCallerMethodID == NULL);
duke@0 541 jplis_assert_msg(!errorOutstanding, "can't find agentmain invoker methodID");
duke@0 542 }
duke@0 543
duke@0 544 /* Now look up the method ID for the transform method (we will need this constantly) */
duke@0 545 if ( !errorOutstanding ) {
duke@0 546 transformMethodID = (*jnienv)->GetMethodID( jnienv,
duke@0 547 implClass,
duke@0 548 JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODNAME,
duke@0 549 JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODSIGNATURE);
duke@0 550 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 551 errorOutstanding = errorOutstanding || (transformMethodID == NULL);
duke@0 552 jplis_assert_msg(!errorOutstanding, "can't find transform methodID");
duke@0 553 }
duke@0 554
duke@0 555 if ( !errorOutstanding ) {
duke@0 556 agent->mInstrumentationImpl = resultImpl;
duke@0 557 agent->mPremainCaller = premainCallerMethodID;
duke@0 558 agent->mAgentmainCaller = agentmainCallerMethodID;
duke@0 559 agent->mTransform = transformMethodID;
duke@0 560 }
duke@0 561
duke@0 562 return !errorOutstanding;
duke@0 563 }
duke@0 564
duke@0 565 jboolean
duke@0 566 commandStringIntoJavaStrings( JNIEnv * jnienv,
duke@0 567 const char * classname,
duke@0 568 const char * optionsString,
duke@0 569 jstring * outputClassname,
duke@0 570 jstring * outputOptionsString) {
duke@0 571 jstring classnameJavaString = NULL;
duke@0 572 jstring optionsJavaString = NULL;
duke@0 573 jboolean errorOutstanding = JNI_TRUE;
duke@0 574
duke@0 575 classnameJavaString = (*jnienv)->NewStringUTF(jnienv, classname);
duke@0 576 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 577 jplis_assert_msg(!errorOutstanding, "can't create class name java string");
duke@0 578
duke@0 579 if ( !errorOutstanding ) {
duke@0 580 if ( optionsString != NULL) {
duke@0 581 optionsJavaString = (*jnienv)->NewStringUTF(jnienv, optionsString);
duke@0 582 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 583 jplis_assert_msg(!errorOutstanding, "can't create options java string");
duke@0 584 }
duke@0 585
duke@0 586 if ( !errorOutstanding ) {
duke@0 587 *outputClassname = classnameJavaString;
duke@0 588 *outputOptionsString = optionsJavaString;
duke@0 589 }
duke@0 590 }
duke@0 591
duke@0 592 return !errorOutstanding;
duke@0 593 }
duke@0 594
duke@0 595
duke@0 596 jboolean
duke@0 597 invokeJavaAgentMainMethod( JNIEnv * jnienv,
duke@0 598 jobject instrumentationImpl,
duke@0 599 jmethodID mainCallingMethod,
duke@0 600 jstring className,
duke@0 601 jstring optionsString) {
duke@0 602 jboolean errorOutstanding = JNI_FALSE;
duke@0 603
duke@0 604 jplis_assert(mainCallingMethod != NULL);
duke@0 605 if ( mainCallingMethod != NULL ) {
duke@0 606 (*jnienv)->CallVoidMethod( jnienv,
duke@0 607 instrumentationImpl,
duke@0 608 mainCallingMethod,
duke@0 609 className,
duke@0 610 optionsString);
duke@0 611 errorOutstanding = checkForThrowable(jnienv);
duke@0 612 if ( errorOutstanding ) {
duke@0 613 logThrowable(jnienv);
duke@0 614 }
duke@0 615 checkForAndClearThrowable(jnienv);
duke@0 616 }
duke@0 617 return !errorOutstanding;
duke@0 618 }
duke@0 619
duke@0 620 jboolean
duke@0 621 setLivePhaseEventHandlers( JPLISAgent * agent) {
duke@0 622 jvmtiEventCallbacks callbacks;
duke@0 623 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 624 jvmtiError jvmtierror;
duke@0 625
duke@0 626 /* first swap out the handlers (switch from the VMInit handler, which we do not need,
duke@0 627 * to the ClassFileLoadHook handler, which is what the agents need from now on)
duke@0 628 */
duke@0 629 memset(&callbacks, 0, sizeof(callbacks));
duke@0 630 callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
duke@0 631
duke@0 632 jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv,
duke@0 633 &callbacks,
duke@0 634 sizeof(callbacks));
dcubed@142 635 check_phase_ret_false(jvmtierror);
duke@0 636 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 637
duke@0 638
duke@0 639 if ( jvmtierror == JVMTI_ERROR_NONE ) {
duke@0 640 /* turn off VMInit */
duke@0 641 jvmtierror = (*jvmtienv)->SetEventNotificationMode(
duke@0 642 jvmtienv,
duke@0 643 JVMTI_DISABLE,
duke@0 644 JVMTI_EVENT_VM_INIT,
duke@0 645 NULL /* all threads */);
dcubed@142 646 check_phase_ret_false(jvmtierror);
duke@0 647 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 648 }
duke@0 649
duke@0 650 if ( jvmtierror == JVMTI_ERROR_NONE ) {
duke@0 651 /* turn on ClassFileLoadHook */
duke@0 652 jvmtierror = (*jvmtienv)->SetEventNotificationMode(
duke@0 653 jvmtienv,
duke@0 654 JVMTI_ENABLE,
duke@0 655 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
duke@0 656 NULL /* all threads */);
dcubed@142 657 check_phase_ret_false(jvmtierror);
duke@0 658 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 659 }
duke@0 660
duke@0 661 return (jvmtierror == JVMTI_ERROR_NONE);
duke@0 662 }
duke@0 663
duke@0 664 /**
duke@0 665 * Check if the can_redefine_classes capability is available.
duke@0 666 */
duke@0 667 void
duke@0 668 checkCapabilities(JPLISAgent * agent) {
duke@0 669 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 670 jvmtiCapabilities potentialCapabilities;
duke@0 671 jvmtiError jvmtierror;
duke@0 672
duke@0 673 memset(&potentialCapabilities, 0, sizeof(potentialCapabilities));
duke@0 674
duke@0 675 jvmtierror = (*jvmtienv)->GetPotentialCapabilities(jvmtienv, &potentialCapabilities);
dcubed@142 676 check_phase_ret(jvmtierror);
duke@0 677 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 678
duke@0 679 if ( jvmtierror == JVMTI_ERROR_NONE ) {
duke@0 680 if ( potentialCapabilities.can_redefine_classes == 1 ) {
duke@0 681 agent->mRedefineAvailable = JNI_TRUE;
duke@0 682 }
duke@0 683 if ( potentialCapabilities.can_set_native_method_prefix == 1 ) {
duke@0 684 agent->mNativeMethodPrefixAvailable = JNI_TRUE;
duke@0 685 }
duke@0 686 }
duke@0 687 }
duke@0 688
duke@0 689 /**
duke@0 690 * Enable native method prefix in one JVM TI environment
duke@0 691 */
duke@0 692 void
duke@0 693 enableNativeMethodPrefixCapability(jvmtiEnv * jvmtienv) {
duke@0 694 jvmtiCapabilities desiredCapabilities;
duke@0 695 jvmtiError jvmtierror;
duke@0 696
duke@0 697 jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 698 /* can be called from any phase */
duke@0 699 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 700 desiredCapabilities.can_set_native_method_prefix = 1;
duke@0 701 jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 702 check_phase_ret(jvmtierror);
duke@0 703 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 704 }
duke@0 705
duke@0 706
duke@0 707 /**
duke@0 708 * Add the can_set_native_method_prefix capability
duke@0 709 */
duke@0 710 void
duke@0 711 addNativeMethodPrefixCapability(JPLISAgent * agent) {
duke@0 712 if (agent->mNativeMethodPrefixAvailable && !agent->mNativeMethodPrefixAdded) {
duke@0 713 jvmtiEnv * jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;
duke@0 714 enableNativeMethodPrefixCapability(jvmtienv);
duke@0 715
duke@0 716 jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;
duke@0 717 if (jvmtienv != NULL) {
duke@0 718 enableNativeMethodPrefixCapability(jvmtienv);
duke@0 719 }
duke@0 720 agent->mNativeMethodPrefixAdded = JNI_TRUE;
duke@0 721 }
duke@0 722 }
duke@0 723
duke@0 724 /**
duke@0 725 * Add the can_maintain_original_method_order capability (for testing)
duke@0 726 */
duke@0 727 void
duke@0 728 addOriginalMethodOrderCapability(JPLISAgent * agent) {
duke@0 729 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 730 jvmtiCapabilities desiredCapabilities;
duke@0 731 jvmtiError jvmtierror;
duke@0 732
duke@0 733 jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 734 /* can be called from any phase */
duke@0 735 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 736 desiredCapabilities.can_maintain_original_method_order = 1;
duke@0 737 jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 738 check_phase_ret(jvmtierror);
duke@0 739 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 740 }
duke@0 741
duke@0 742 /**
duke@0 743 * Add the can_redefine_classes capability
duke@0 744 */
duke@0 745 void
duke@0 746 addRedefineClassesCapability(JPLISAgent * agent) {
duke@0 747 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 748 jvmtiCapabilities desiredCapabilities;
duke@0 749 jvmtiError jvmtierror;
duke@0 750
duke@0 751 if (agent->mRedefineAvailable && !agent->mRedefineAdded) {
duke@0 752 jvmtierror = (*jvmtienv)->GetCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 753 /* can be called from any phase */
duke@0 754 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 755 desiredCapabilities.can_redefine_classes = 1;
duke@0 756 jvmtierror = (*jvmtienv)->AddCapabilities(jvmtienv, &desiredCapabilities);
dcubed@142 757 check_phase_ret(jvmtierror);
duke@0 758
duke@0 759 /*
duke@0 760 * With mixed premain/agentmain agents then it's possible that the
duke@0 761 * capability was potentially available in the onload phase but
duke@0 762 * subsequently unavailable in the live phase.
duke@0 763 */
duke@0 764 jplis_assert(jvmtierror == JVMTI_ERROR_NONE ||
duke@0 765 jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);
duke@0 766 if (jvmtierror == JVMTI_ERROR_NONE) {
duke@0 767 agent->mRedefineAdded = JNI_TRUE;
duke@0 768 }
duke@0 769 }
duke@0 770 }
duke@0 771
duke@0 772
duke@0 773 /*
duke@0 774 * Support for the JVMTI callbacks
duke@0 775 */
duke@0 776
duke@0 777 void
duke@0 778 transformClassFile( JPLISAgent * agent,
duke@0 779 JNIEnv * jnienv,
duke@0 780 jobject loaderObject,
duke@0 781 const char* name,
duke@0 782 jclass classBeingRedefined,
duke@0 783 jobject protectionDomain,
duke@0 784 jint class_data_len,
duke@0 785 const unsigned char* class_data,
duke@0 786 jint* new_class_data_len,
duke@0 787 unsigned char** new_class_data,
duke@0 788 jboolean is_retransformer) {
duke@0 789 jboolean errorOutstanding = JNI_FALSE;
duke@0 790 jstring classNameStringObject = NULL;
duke@0 791 jarray classFileBufferObject = NULL;
duke@0 792 jarray transformedBufferObject = NULL;
duke@0 793 jsize transformedBufferSize = 0;
duke@0 794 unsigned char * resultBuffer = NULL;
duke@0 795 jboolean shouldRun = JNI_FALSE;
duke@0 796
duke@0 797 /* only do this if we aren't already in the middle of processing a class on this thread */
duke@0 798 shouldRun = tryToAcquireReentrancyToken(
duke@0 799 jvmti(agent),
duke@0 800 NULL); /* this thread */
duke@0 801
duke@0 802 if ( shouldRun ) {
duke@0 803 /* first marshall all the parameters */
duke@0 804 classNameStringObject = (*jnienv)->NewStringUTF(jnienv,
duke@0 805 name);
duke@0 806 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 807 jplis_assert_msg(!errorOutstanding, "can't create name string");
duke@0 808
duke@0 809 if ( !errorOutstanding ) {
duke@0 810 classFileBufferObject = (*jnienv)->NewByteArray(jnienv,
duke@0 811 class_data_len);
duke@0 812 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 813 jplis_assert_msg(!errorOutstanding, "can't create byte arrau");
duke@0 814 }
duke@0 815
duke@0 816 if ( !errorOutstanding ) {
duke@0 817 jbyte * typedBuffer = (jbyte *) class_data; /* nasty cast, dumb JNI interface, const missing */
duke@0 818 /* The sign cast is safe. The const cast is dumb. */
duke@0 819 (*jnienv)->SetByteArrayRegion( jnienv,
duke@0 820 classFileBufferObject,
duke@0 821 0,
duke@0 822 class_data_len,
duke@0 823 typedBuffer);
duke@0 824 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 825 jplis_assert_msg(!errorOutstanding, "can't set byte array region");
duke@0 826 }
duke@0 827
duke@0 828 /* now call the JPL agents to do the transforming */
duke@0 829 /* potential future optimization: may want to skip this if there are none */
duke@0 830 if ( !errorOutstanding ) {
duke@0 831 jplis_assert(agent->mInstrumentationImpl != NULL);
duke@0 832 jplis_assert(agent->mTransform != NULL);
duke@0 833 transformedBufferObject = (*jnienv)->CallObjectMethod(
duke@0 834 jnienv,
duke@0 835 agent->mInstrumentationImpl,
duke@0 836 agent->mTransform,
duke@0 837 loaderObject,
duke@0 838 classNameStringObject,
duke@0 839 classBeingRedefined,
duke@0 840 protectionDomain,
duke@0 841 classFileBufferObject,
duke@0 842 is_retransformer);
duke@0 843 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 844 jplis_assert_msg(!errorOutstanding, "transform method call failed");
duke@0 845 }
duke@0 846
duke@0 847 /* Finally, unmarshall the parameters (if someone touched the buffer, tell the JVM) */
duke@0 848 if ( !errorOutstanding ) {
duke@0 849 if ( transformedBufferObject != NULL ) {
duke@0 850 transformedBufferSize = (*jnienv)->GetArrayLength( jnienv,
duke@0 851 transformedBufferObject);
duke@0 852 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 853 jplis_assert_msg(!errorOutstanding, "can't get array length");
duke@0 854
duke@0 855 if ( !errorOutstanding ) {
duke@0 856 /* allocate the response buffer with the JVMTI allocate call.
duke@0 857 * This is what the JVMTI spec says to do for Class File Load hook responses
duke@0 858 */
duke@0 859 jvmtiError allocError = (*(jvmti(agent)))->Allocate(jvmti(agent),
duke@0 860 transformedBufferSize,
duke@0 861 &resultBuffer);
duke@0 862 errorOutstanding = (allocError != JVMTI_ERROR_NONE);
duke@0 863 jplis_assert_msg(!errorOutstanding, "can't allocate result buffer");
duke@0 864 }
duke@0 865
duke@0 866 if ( !errorOutstanding ) {
duke@0 867 (*jnienv)->GetByteArrayRegion( jnienv,
duke@0 868 transformedBufferObject,
duke@0 869 0,
duke@0 870 transformedBufferSize,
duke@0 871 (jbyte *) resultBuffer);
duke@0 872 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 873 jplis_assert_msg(!errorOutstanding, "can't get byte array region");
duke@0 874
duke@0 875 /* in this case, we will not return the buffer to the JVMTI,
duke@0 876 * so we need to deallocate it ourselves
duke@0 877 */
duke@0 878 if ( errorOutstanding ) {
duke@0 879 deallocate( jvmti(agent),
duke@0 880 (void*)resultBuffer);
duke@0 881 }
duke@0 882 }
duke@0 883
duke@0 884 if ( !errorOutstanding ) {
duke@0 885 *new_class_data_len = (transformedBufferSize);
duke@0 886 *new_class_data = resultBuffer;
duke@0 887 }
duke@0 888 }
duke@0 889 }
duke@0 890
duke@0 891 /* release the token */
duke@0 892 releaseReentrancyToken( jvmti(agent),
duke@0 893 NULL); /* this thread */
duke@0 894
duke@0 895 }
duke@0 896
duke@0 897 return;
duke@0 898 }
duke@0 899
duke@0 900 /*
duke@0 901 * Misc. internal utilities.
duke@0 902 */
duke@0 903
duke@0 904 /*
duke@0 905 * The only checked exceptions we can throw are ClassNotFoundException and
duke@0 906 * UnmodifiableClassException. All others map to InternalError.
duke@0 907 */
duke@0 908 jthrowable
duke@0 909 redefineClassMapper( JNIEnv * jnienv,
duke@0 910 jthrowable throwableToMap) {
duke@0 911 jthrowable mappedThrowable = NULL;
duke@0 912
duke@0 913 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 914 jplis_assert(!isUnchecked(jnienv, throwableToMap));
duke@0 915
duke@0 916 if ( isInstanceofClassName( jnienv,
duke@0 917 throwableToMap,
duke@0 918 "java/lang/ClassNotFoundException") ) {
duke@0 919 mappedThrowable = throwableToMap;
duke@0 920 } else {
duke@0 921 if ( isInstanceofClassName( jnienv,
duke@0 922 throwableToMap,
duke@0 923 "java/lang/instrument/UnmodifiableClassException")) {
duke@0 924 mappedThrowable = throwableToMap;
duke@0 925 } else {
duke@0 926 jstring message = NULL;
duke@0 927
duke@0 928 message = getMessageFromThrowable(jnienv, throwableToMap);
duke@0 929 mappedThrowable = createInternalError(jnienv, message);
duke@0 930 }
duke@0 931 }
duke@0 932
duke@0 933 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 934 return mappedThrowable;
duke@0 935 }
duke@0 936
duke@0 937 jobjectArray
duke@0 938 getObjectArrayFromClasses(JNIEnv* jnienv, jclass* classes, jint classCount) {
duke@0 939 jclass classArrayClass = NULL;
duke@0 940 jobjectArray localArray = NULL;
duke@0 941 jint classIndex = 0;
duke@0 942 jboolean errorOccurred = JNI_FALSE;
duke@0 943
duke@0 944 /* get the class array class */
duke@0 945 classArrayClass = (*jnienv)->FindClass(jnienv, "java/lang/Class");
duke@0 946 errorOccurred = checkForThrowable(jnienv);
duke@0 947
duke@0 948 if (!errorOccurred) {
duke@0 949 jplis_assert_msg(classArrayClass != NULL, "FindClass returned null class");
duke@0 950
duke@0 951 /* create the array for the classes */
duke@0 952 localArray = (*jnienv)->NewObjectArray(jnienv, classCount, classArrayClass, NULL);
duke@0 953 errorOccurred = checkForThrowable(jnienv);
duke@0 954
duke@0 955 if (!errorOccurred) {
duke@0 956 jplis_assert_msg(localArray != NULL, "NewObjectArray returned null array");
duke@0 957
duke@0 958 /* now copy refs to all the classes and put them into the array */
duke@0 959 for (classIndex = 0; classIndex < classCount; classIndex++) {
duke@0 960 /* put class into array */
duke@0 961 (*jnienv)->SetObjectArrayElement(jnienv, localArray, classIndex, classes[classIndex]);
duke@0 962 errorOccurred = checkForThrowable(jnienv);
duke@0 963
duke@0 964 if (errorOccurred) {
duke@0 965 localArray = NULL;
duke@0 966 break;
duke@0 967 }
duke@0 968 }
duke@0 969 }
duke@0 970 }
duke@0 971
duke@0 972 return localArray;
duke@0 973 }
duke@0 974
duke@0 975
duke@0 976 /* Return the environment with the retransformation capability.
duke@0 977 * Create it if it doesn't exist.
duke@0 978 * Return NULL if it can't be created.
duke@0 979 */
duke@0 980 jvmtiEnv *
duke@0 981 retransformableEnvironment(JPLISAgent * agent) {
duke@0 982 jvmtiEnv * retransformerEnv = NULL;
duke@0 983 jint jnierror = JNI_OK;
duke@0 984 jvmtiCapabilities desiredCapabilities;
duke@0 985 jvmtiEventCallbacks callbacks;
duke@0 986 jvmtiError jvmtierror;
duke@0 987
duke@0 988 if (agent->mRetransformEnvironment.mJVMTIEnv != NULL) {
duke@0 989 return agent->mRetransformEnvironment.mJVMTIEnv;
duke@0 990 }
duke@0 991 jnierror = (*agent->mJVM)->GetEnv( agent->mJVM,
duke@0 992 (void **) &retransformerEnv,
kamg@3389 993 JVMTI_VERSION_1_1);
duke@0 994 if ( jnierror != JNI_OK ) {
duke@0 995 return NULL;
duke@0 996 }
duke@0 997 jvmtierror = (*retransformerEnv)->GetCapabilities(retransformerEnv, &desiredCapabilities);
duke@0 998 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 999 desiredCapabilities.can_retransform_classes = 1;
duke@0 1000 if (agent->mNativeMethodPrefixAdded) {
duke@0 1001 desiredCapabilities.can_set_native_method_prefix = 1;
duke@0 1002 }
duke@0 1003
duke@0 1004 jvmtierror = (*retransformerEnv)->AddCapabilities(retransformerEnv, &desiredCapabilities);
duke@0 1005 if (jvmtierror != JVMTI_ERROR_NONE) {
duke@0 1006 /* cannot get the capability, dispose of the retransforming environment */
duke@0 1007 jvmtierror = (*retransformerEnv)->DisposeEnvironment(retransformerEnv);
duke@0 1008 jplis_assert(jvmtierror == JVMTI_ERROR_NOT_AVAILABLE);
duke@0 1009 return NULL;
duke@0 1010 }
duke@0 1011 memset(&callbacks, 0, sizeof(callbacks));
duke@0 1012 callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
duke@0 1013
duke@0 1014 jvmtierror = (*retransformerEnv)->SetEventCallbacks(retransformerEnv,
duke@0 1015 &callbacks,
duke@0 1016 sizeof(callbacks));
duke@0 1017 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 1018 if (jvmtierror == JVMTI_ERROR_NONE) {
duke@0 1019 // install the retransforming environment
duke@0 1020 agent->mRetransformEnvironment.mJVMTIEnv = retransformerEnv;
dcubed@139 1021 agent->mRetransformEnvironment.mIsRetransformer = JNI_TRUE;
duke@0 1022
duke@0 1023 // Make it for ClassFileLoadHook handling
duke@0 1024 jvmtierror = (*retransformerEnv)->SetEnvironmentLocalStorage(
duke@0 1025 retransformerEnv,
duke@0 1026 &(agent->mRetransformEnvironment));
duke@0 1027 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 1028 if (jvmtierror == JVMTI_ERROR_NONE) {
duke@0 1029 return retransformerEnv;
duke@0 1030 }
duke@0 1031 }
duke@0 1032 return NULL;
duke@0 1033 }
duke@0 1034
duke@0 1035
duke@0 1036 /*
duke@0 1037 * Underpinnings for native methods
duke@0 1038 */
duke@0 1039
duke@0 1040 jboolean
duke@0 1041 isModifiableClass(JNIEnv * jnienv, JPLISAgent * agent, jclass clazz) {
duke@0 1042 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 1043 jvmtiError jvmtierror;
duke@0 1044 jboolean is_modifiable = JNI_FALSE;
duke@0 1045
duke@0 1046 jvmtierror = (*jvmtienv)->IsModifiableClass( jvmtienv,
duke@0 1047 clazz,
duke@0 1048 &is_modifiable);
dcubed@142 1049 check_phase_ret_false(jvmtierror);
duke@0 1050 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 1051
duke@0 1052 return is_modifiable;
duke@0 1053 }
duke@0 1054
duke@0 1055 jboolean
duke@0 1056 isRetransformClassesSupported(JNIEnv * jnienv, JPLISAgent * agent) {
dcubed@139 1057 return agent->mRetransformEnvironment.mIsRetransformer;
duke@0 1058 }
duke@0 1059
duke@0 1060 void
duke@0 1061 setHasRetransformableTransformers(JNIEnv * jnienv, JPLISAgent * agent, jboolean has) {
duke@0 1062 jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);
duke@0 1063 jvmtiError jvmtierror;
duke@0 1064
duke@0 1065 jplis_assert(retransformerEnv != NULL);
duke@0 1066 jvmtierror = (*retransformerEnv)->SetEventNotificationMode(
duke@0 1067 retransformerEnv,
duke@0 1068 has? JVMTI_ENABLE : JVMTI_DISABLE,
duke@0 1069 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
duke@0 1070 NULL /* all threads */);
duke@0 1071 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 1072 }
duke@0 1073
duke@0 1074 void
duke@0 1075 retransformClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classes) {
duke@0 1076 jvmtiEnv * retransformerEnv = retransformableEnvironment(agent);
duke@0 1077 jboolean errorOccurred = JNI_FALSE;
duke@0 1078 jvmtiError errorCode = JVMTI_ERROR_NONE;
duke@0 1079 jsize numClasses = 0;
duke@0 1080 jclass * classArray = NULL;
duke@0 1081
duke@0 1082 /* This is supposed to be checked by caller, but just to be sure */
duke@0 1083 if (retransformerEnv == NULL) {
duke@0 1084 jplis_assert(retransformerEnv != NULL);
duke@0 1085 errorOccurred = JNI_TRUE;
duke@0 1086 errorCode = JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
duke@0 1087 }
duke@0 1088
duke@0 1089 /* This was supposed to be checked by caller too */
duke@0 1090 if (!errorOccurred && classes == NULL) {
duke@0 1091 jplis_assert(classes != NULL);
duke@0 1092 errorOccurred = JNI_TRUE;
duke@0 1093 errorCode = JVMTI_ERROR_NULL_POINTER;
duke@0 1094 }
duke@0 1095
duke@0 1096 if (!errorOccurred) {
duke@0 1097 numClasses = (*jnienv)->GetArrayLength(jnienv, classes);
duke@0 1098 errorOccurred = checkForThrowable(jnienv);
duke@0 1099 jplis_assert(!errorOccurred);
dcubed@140 1100
dcubed@140 1101 if (!errorOccurred && numClasses == 0) {
dcubed@140 1102 jplis_assert(numClasses != 0);
dcubed@140 1103 errorOccurred = JNI_TRUE;
dcubed@140 1104 errorCode = JVMTI_ERROR_NULL_POINTER;
dcubed@140 1105 }
duke@0 1106 }
duke@0 1107
duke@0 1108 if (!errorOccurred) {
duke@0 1109 classArray = (jclass *) allocate(retransformerEnv,
duke@0 1110 numClasses * sizeof(jclass));
duke@0 1111 errorOccurred = (classArray == NULL);
duke@0 1112 jplis_assert(!errorOccurred);
duke@0 1113 if (errorOccurred) {
duke@0 1114 errorCode = JVMTI_ERROR_OUT_OF_MEMORY;
duke@0 1115 }
duke@0 1116 }
duke@0 1117
duke@0 1118 if (!errorOccurred) {
duke@0 1119 jint index;
duke@0 1120 for (index = 0; index < numClasses; index++) {
duke@0 1121 classArray[index] = (*jnienv)->GetObjectArrayElement(jnienv, classes, index);
duke@0 1122 errorOccurred = checkForThrowable(jnienv);
duke@0 1123 jplis_assert(!errorOccurred);
duke@0 1124 if (errorOccurred) {
duke@0 1125 break;
duke@0 1126 }
dcubed@140 1127
dcubed@140 1128 if (classArray[index] == NULL) {
dcubed@140 1129 jplis_assert(classArray[index] != NULL);
dcubed@140 1130 errorOccurred = JNI_TRUE;
dcubed@140 1131 errorCode = JVMTI_ERROR_NULL_POINTER;
dcubed@140 1132 break;
dcubed@140 1133 }
duke@0 1134 }
duke@0 1135 }
duke@0 1136
duke@0 1137 if (!errorOccurred) {
duke@0 1138 errorCode = (*retransformerEnv)->RetransformClasses(retransformerEnv,
duke@0 1139 numClasses, classArray);
duke@0 1140 errorOccurred = (errorCode != JVMTI_ERROR_NONE);
duke@0 1141 }
duke@0 1142
duke@0 1143 /* Give back the buffer if we allocated it. Throw any exceptions after.
duke@0 1144 */
duke@0 1145 if (classArray != NULL) {
duke@0 1146 deallocate(retransformerEnv, (void*)classArray);
duke@0 1147 }
duke@0 1148
duke@0 1149 if (errorCode != JVMTI_ERROR_NONE) {
duke@0 1150 createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);
duke@0 1151 }
duke@0 1152
duke@0 1153 mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);
duke@0 1154 }
duke@0 1155
duke@0 1156 /*
duke@0 1157 * Java code must not call this with a null list or a zero-length list.
duke@0 1158 */
duke@0 1159 void
duke@0 1160 redefineClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classDefinitions) {
duke@0 1161 jvmtiEnv* jvmtienv = jvmti(agent);
duke@0 1162 jboolean errorOccurred = JNI_FALSE;
duke@0 1163 jclass classDefClass = NULL;
duke@0 1164 jmethodID getDefinitionClassMethodID = NULL;
duke@0 1165 jmethodID getDefinitionClassFileMethodID = NULL;
duke@0 1166 jvmtiClassDefinition* classDefs = NULL;
dcubed@4882 1167 jbyteArray* targetFiles = NULL;
duke@0 1168 jsize numDefs = 0;
duke@0 1169
duke@0 1170 jplis_assert(classDefinitions != NULL);
duke@0 1171
duke@0 1172 numDefs = (*jnienv)->GetArrayLength(jnienv, classDefinitions);
duke@0 1173 errorOccurred = checkForThrowable(jnienv);
duke@0 1174 jplis_assert(!errorOccurred);
duke@0 1175
duke@0 1176 if (!errorOccurred) {
duke@0 1177 jplis_assert(numDefs > 0);
duke@0 1178 /* get method IDs for methods to call on class definitions */
duke@0 1179 classDefClass = (*jnienv)->FindClass(jnienv, "java/lang/instrument/ClassDefinition");
duke@0 1180 errorOccurred = checkForThrowable(jnienv);
duke@0 1181 jplis_assert(!errorOccurred);
duke@0 1182 }
duke@0 1183
duke@0 1184 if (!errorOccurred) {
duke@0 1185 getDefinitionClassMethodID = (*jnienv)->GetMethodID( jnienv,
duke@0 1186 classDefClass,
duke@0 1187 "getDefinitionClass",
duke@0 1188 "()Ljava/lang/Class;");
duke@0 1189 errorOccurred = checkForThrowable(jnienv);
duke@0 1190 jplis_assert(!errorOccurred);
duke@0 1191 }
duke@0 1192
duke@0 1193 if (!errorOccurred) {
duke@0 1194 getDefinitionClassFileMethodID = (*jnienv)->GetMethodID( jnienv,
duke@0 1195 classDefClass,
duke@0 1196 "getDefinitionClassFile",
duke@0 1197 "()[B");
duke@0 1198 errorOccurred = checkForThrowable(jnienv);
duke@0 1199 jplis_assert(!errorOccurred);
duke@0 1200 }
duke@0 1201
duke@0 1202 if (!errorOccurred) {
duke@0 1203 classDefs = (jvmtiClassDefinition *) allocate(
duke@0 1204 jvmtienv,
duke@0 1205 numDefs * sizeof(jvmtiClassDefinition));
duke@0 1206 errorOccurred = (classDefs == NULL);
duke@0 1207 jplis_assert(!errorOccurred);
duke@0 1208 if ( errorOccurred ) {
duke@0 1209 createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);
duke@0 1210 }
dcubed@4882 1211
duke@0 1212 else {
dcubed@4882 1213 /*
dcubed@4882 1214 * We have to save the targetFile values that we compute so
dcubed@4882 1215 * that we can release the class_bytes arrays that are
dcubed@4882 1216 * returned by GetByteArrayElements(). In case of a JNI
dcubed@4882 1217 * error, we can't (easily) recompute the targetFile values
dcubed@4882 1218 * and we still want to free any memory we allocated.
dcubed@4882 1219 */
dcubed@4882 1220 targetFiles = (jbyteArray *) allocate(jvmtienv,
dcubed@4882 1221 numDefs * sizeof(jbyteArray));
dcubed@4882 1222 errorOccurred = (targetFiles == NULL);
dcubed@4882 1223 jplis_assert(!errorOccurred);
dcubed@4882 1224 if ( errorOccurred ) {
dcubed@4882 1225 deallocate(jvmtienv, (void*)classDefs);
dcubed@4882 1226 createAndThrowThrowableFromJVMTIErrorCode(jnienv,
dcubed@4882 1227 JVMTI_ERROR_OUT_OF_MEMORY);
dcubed@4882 1228 }
dcubed@4882 1229 else {
dcubed@4882 1230 jint i, j;
duke@0 1231
dcubed@4882 1232 // clear classDefs so we can correctly free memory during errors
dcubed@4882 1233 memset(classDefs, 0, numDefs * sizeof(jvmtiClassDefinition));
dcubed@4882 1234
dcubed@4882 1235 for (i = 0; i < numDefs; i++) {
dcubed@4882 1236 jclass classDef = NULL;
dcubed@4882 1237
dcubed@4882 1238 classDef = (*jnienv)->GetObjectArrayElement(jnienv, classDefinitions, i);
dcubed@4882 1239 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1240 jplis_assert(!errorOccurred);
dcubed@4882 1241 if (errorOccurred) {
dcubed@4882 1242 break;
dcubed@4882 1243 }
dcubed@4882 1244
dcubed@4882 1245 classDefs[i].klass = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassMethodID);
dcubed@4882 1246 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1247 jplis_assert(!errorOccurred);
dcubed@4882 1248 if (errorOccurred) {
dcubed@4882 1249 break;
dcubed@4882 1250 }
dcubed@4882 1251
dcubed@4882 1252 targetFiles[i] = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassFileMethodID);
dcubed@4882 1253 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1254 jplis_assert(!errorOccurred);
dcubed@4882 1255 if (errorOccurred) {
dcubed@4882 1256 break;
dcubed@4882 1257 }
dcubed@4882 1258
dcubed@4882 1259 classDefs[i].class_byte_count = (*jnienv)->GetArrayLength(jnienv, targetFiles[i]);
dcubed@4882 1260 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1261 jplis_assert(!errorOccurred);
dcubed@4882 1262 if (errorOccurred) {
dcubed@4882 1263 break;
dcubed@4882 1264 }
dcubed@4882 1265
dcubed@4882 1266 /*
dcubed@4882 1267 * Allocate class_bytes last so we don't have to free
dcubed@4882 1268 * memory on a partial row error.
dcubed@4882 1269 */
dcubed@4882 1270 classDefs[i].class_bytes = (unsigned char*)(*jnienv)->GetByteArrayElements(jnienv, targetFiles[i], NULL);
dcubed@4882 1271 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1272 jplis_assert(!errorOccurred);
dcubed@4882 1273 if (errorOccurred) {
dcubed@4882 1274 break;
dcubed@4882 1275 }
duke@0 1276 }
duke@0 1277
dcubed@4882 1278 if (!errorOccurred) {
dcubed@4882 1279 jvmtiError errorCode = JVMTI_ERROR_NONE;
dcubed@4882 1280 errorCode = (*jvmtienv)->RedefineClasses(jvmtienv, numDefs, classDefs);
dcubed@4882 1281 if (errorCode == JVMTI_ERROR_WRONG_PHASE) {
dcubed@4882 1282 /* insulate caller from the wrong phase error */
dcubed@4882 1283 errorCode = JVMTI_ERROR_NONE;
dcubed@4882 1284 } else {
dcubed@4882 1285 errorOccurred = (errorCode != JVMTI_ERROR_NONE);
dcubed@4882 1286 if ( errorOccurred ) {
dcubed@4882 1287 createAndThrowThrowableFromJVMTIErrorCode(jnienv, errorCode);
dcubed@4882 1288 }
dcubed@4882 1289 }
duke@0 1290 }
duke@0 1291
dcubed@4882 1292 /*
dcubed@4882 1293 * Cleanup memory that we allocated above. If we had a
dcubed@4882 1294 * JNI error, a JVM/TI error or no errors, index 'i'
dcubed@4882 1295 * tracks how far we got in processing the classDefs
dcubed@4882 1296 * array. Note: ReleaseByteArrayElements() is safe to
dcubed@4882 1297 * call with a JNI exception pending.
dcubed@4882 1298 */
dcubed@4882 1299 for (j = 0; j < i; j++) {
dcubed@4882 1300 if ((jbyte *)classDefs[j].class_bytes != NULL) {
dcubed@4882 1301 (*jnienv)->ReleaseByteArrayElements(jnienv,
dcubed@4882 1302 targetFiles[j], (jbyte *)classDefs[j].class_bytes,
dcubed@4882 1303 0 /* copy back and free */);
dcubed@4882 1304 /*
dcubed@4882 1305 * Only check for error if we didn't already have one
dcubed@4882 1306 * so we don't overwrite errorOccurred.
dcubed@4882 1307 */
dcubed@4882 1308 if (!errorOccurred) {
dcubed@4882 1309 errorOccurred = checkForThrowable(jnienv);
dcubed@4882 1310 jplis_assert(!errorOccurred);
dcubed@4882 1311 }
dcubed@4882 1312 }
duke@0 1313 }
dcubed@4882 1314 deallocate(jvmtienv, (void*)targetFiles);
dcubed@4882 1315 deallocate(jvmtienv, (void*)classDefs);
duke@0 1316 }
duke@0 1317 }
duke@0 1318 }
duke@0 1319
duke@0 1320 mapThrownThrowableIfNecessary(jnienv, redefineClassMapper);
duke@0 1321 }
duke@0 1322
duke@0 1323 /* Cheesy sharing. ClassLoader may be null. */
duke@0 1324 jobjectArray
duke@0 1325 commonGetClassList( JNIEnv * jnienv,
duke@0 1326 JPLISAgent * agent,
duke@0 1327 jobject classLoader,
duke@0 1328 ClassListFetcher fetcher) {
duke@0 1329 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 1330 jboolean errorOccurred = JNI_FALSE;
duke@0 1331 jvmtiError jvmtierror = JVMTI_ERROR_NONE;
duke@0 1332 jint classCount = 0;
duke@0 1333 jclass * classes = NULL;
duke@0 1334 jobjectArray localArray = NULL;
duke@0 1335
duke@0 1336 /* retrieve the classes from the JVMTI agent */
duke@0 1337 jvmtierror = (*fetcher)( jvmtienv,
duke@0 1338 classLoader,
duke@0 1339 &classCount,
duke@0 1340 &classes);
dcubed@142 1341 check_phase_ret_blob(jvmtierror, localArray);
duke@0 1342 errorOccurred = (jvmtierror != JVMTI_ERROR_NONE);
duke@0 1343 jplis_assert(!errorOccurred);
duke@0 1344
duke@0 1345 if ( errorOccurred ) {
duke@0 1346 createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
duke@0 1347 } else {
duke@0 1348 localArray = getObjectArrayFromClasses( jnienv,
duke@0 1349 classes,
duke@0 1350 classCount);
duke@0 1351 errorOccurred = checkForThrowable(jnienv);
duke@0 1352 jplis_assert(!errorOccurred);
duke@0 1353
duke@0 1354 /* do this whether or not we saw a problem */
duke@0 1355 deallocate(jvmtienv, (void*)classes);
duke@0 1356 }
duke@0 1357
duke@0 1358 mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
duke@0 1359 return localArray;
duke@0 1360
duke@0 1361 }
duke@0 1362
duke@0 1363 jvmtiError
duke@0 1364 getAllLoadedClassesClassListFetcher( jvmtiEnv * jvmtienv,
duke@0 1365 jobject classLoader,
duke@0 1366 jint * classCount,
duke@0 1367 jclass ** classes) {
duke@0 1368 return (*jvmtienv)->GetLoadedClasses(jvmtienv, classCount, classes);
duke@0 1369 }
duke@0 1370
duke@0 1371 jobjectArray
duke@0 1372 getAllLoadedClasses(JNIEnv * jnienv, JPLISAgent * agent) {
duke@0 1373 return commonGetClassList( jnienv,
duke@0 1374 agent,
duke@0 1375 NULL,
duke@0 1376 getAllLoadedClassesClassListFetcher);
duke@0 1377 }
duke@0 1378
duke@0 1379 jvmtiError
duke@0 1380 getInitiatedClassesClassListFetcher( jvmtiEnv * jvmtienv,
duke@0 1381 jobject classLoader,
duke@0 1382 jint * classCount,
duke@0 1383 jclass ** classes) {
duke@0 1384 return (*jvmtienv)->GetClassLoaderClasses(jvmtienv, classLoader, classCount, classes);
duke@0 1385 }
duke@0 1386
duke@0 1387
duke@0 1388 jobjectArray
duke@0 1389 getInitiatedClasses(JNIEnv * jnienv, JPLISAgent * agent, jobject classLoader) {
duke@0 1390 return commonGetClassList( jnienv,
duke@0 1391 agent,
duke@0 1392 classLoader,
duke@0 1393 getInitiatedClassesClassListFetcher);
duke@0 1394 }
duke@0 1395
duke@0 1396 jlong
duke@0 1397 getObjectSize(JNIEnv * jnienv, JPLISAgent * agent, jobject objectToSize) {
duke@0 1398 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 1399 jlong objectSize = -1;
duke@0 1400 jvmtiError jvmtierror = JVMTI_ERROR_NONE;
duke@0 1401
duke@0 1402 jvmtierror = (*jvmtienv)->GetObjectSize(jvmtienv, objectToSize, &objectSize);
dcubed@142 1403 check_phase_ret_0(jvmtierror);
duke@0 1404 jplis_assert(jvmtierror == JVMTI_ERROR_NONE);
duke@0 1405 if ( jvmtierror != JVMTI_ERROR_NONE ) {
duke@0 1406 createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
duke@0 1407 }
duke@0 1408
duke@0 1409 mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
duke@0 1410 return objectSize;
duke@0 1411 }
duke@0 1412
duke@0 1413 void
duke@0 1414 appendToClassLoaderSearch(JNIEnv * jnienv, JPLISAgent * agent, jstring jarFile, jboolean isBootLoader)
duke@0 1415 {
duke@0 1416 jvmtiEnv * jvmtienv = jvmti(agent);
duke@0 1417 jboolean errorOutstanding;
duke@0 1418 jvmtiError jvmtierror;
duke@0 1419 const char* utf8Chars;
duke@0 1420 jsize utf8Len;
duke@0 1421 jboolean isCopy;
duke@0 1422 char platformChars[MAXPATHLEN];
duke@0 1423 int platformLen;
duke@0 1424
duke@0 1425 utf8Len = (*jnienv)->GetStringUTFLength(jnienv, jarFile);
duke@0 1426 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 1427
duke@0 1428 if (!errorOutstanding) {
duke@0 1429 utf8Chars = (*jnienv)->GetStringUTFChars(jnienv, jarFile, &isCopy);
duke@0 1430 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 1431
duke@0 1432 if (!errorOutstanding && utf8Chars != NULL) {
duke@0 1433 /*
duke@0 1434 * JVMTI spec'ed to use modified UTF8. At this time this is not implemented
duke@0 1435 * the platform encoding is used.
duke@0 1436 */
duke@0 1437 platformLen = convertUft8ToPlatformString((char*)utf8Chars, utf8Len, platformChars, MAXPATHLEN);
duke@0 1438 if (platformLen < 0) {
duke@0 1439 createAndThrowInternalError(jnienv);
duke@0 1440 return;
duke@0 1441 }
duke@0 1442
duke@0 1443 (*jnienv)->ReleaseStringUTFChars(jnienv, jarFile, utf8Chars);
duke@0 1444 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 1445
duke@0 1446 if (!errorOutstanding) {
duke@0 1447
duke@0 1448 if (isBootLoader) {
duke@0 1449 jvmtierror = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, platformChars);
duke@0 1450 } else {
duke@0 1451 jvmtierror = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, platformChars);
duke@0 1452 }
dcubed@142 1453 check_phase_ret(jvmtierror);
duke@0 1454
duke@0 1455 if ( jvmtierror != JVMTI_ERROR_NONE ) {
duke@0 1456 createAndThrowThrowableFromJVMTIErrorCode(jnienv, jvmtierror);
duke@0 1457 }
duke@0 1458 }
duke@0 1459 }
duke@0 1460 }
duke@0 1461
duke@0 1462 mapThrownThrowableIfNecessary(jnienv, mapAllCheckedToInternalErrorMapper);
duke@0 1463 }
duke@0 1464
duke@0 1465 /*
duke@0 1466 * Set the prefixes used to wrap native methods (so they can be instrumented).
duke@0 1467 * Each transform can set a prefix, any that have been set come in as prefixArray.
duke@0 1468 * Convert them in native strings in a native array then call JVM TI.
duke@0 1469 * One a given call, this function handles either the prefixes for retransformable
duke@0 1470 * transforms or for normal transforms.
duke@0 1471 */
duke@0 1472 void
duke@0 1473 setNativeMethodPrefixes(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray prefixArray,
duke@0 1474 jboolean isRetransformable) {
duke@0 1475 jvmtiEnv* jvmtienv;
duke@0 1476 jvmtiError err = JVMTI_ERROR_NONE;
duke@0 1477 jsize arraySize;
duke@0 1478 jboolean errorOccurred = JNI_FALSE;
duke@0 1479
duke@0 1480 jplis_assert(prefixArray != NULL);
duke@0 1481
duke@0 1482 if (isRetransformable) {
duke@0 1483 jvmtienv = agent->mRetransformEnvironment.mJVMTIEnv;
duke@0 1484 } else {
duke@0 1485 jvmtienv = agent->mNormalEnvironment.mJVMTIEnv;
duke@0 1486 }
duke@0 1487 arraySize = (*jnienv)->GetArrayLength(jnienv, prefixArray);
duke@0 1488 errorOccurred = checkForThrowable(jnienv);
duke@0 1489 jplis_assert(!errorOccurred);
duke@0 1490
duke@0 1491 if (!errorOccurred) {
duke@0 1492 /* allocate the native to hold the native prefixes */
duke@0 1493 const char** prefixes = (const char**) allocate(jvmtienv,
duke@0 1494 arraySize * sizeof(char*));
duke@0 1495 /* since JNI ReleaseStringUTFChars needs the jstring from which the native
duke@0 1496 * string was allocated, we store them in a parallel array */
duke@0 1497 jstring* originForRelease = (jstring*) allocate(jvmtienv,
duke@0 1498 arraySize * sizeof(jstring));
duke@0 1499 errorOccurred = (prefixes == NULL || originForRelease == NULL);
duke@0 1500 jplis_assert(!errorOccurred);
duke@0 1501 if ( errorOccurred ) {
duke@0 1502 createAndThrowThrowableFromJVMTIErrorCode(jnienv, JVMTI_ERROR_OUT_OF_MEMORY);
duke@0 1503 }
duke@0 1504 else {
duke@0 1505 jint inx = 0;
duke@0 1506 jint i;
duke@0 1507 for (i = 0; i < arraySize; i++) {
duke@0 1508 jstring prefixStr = NULL;
duke@0 1509 const char* prefix;
duke@0 1510 jsize prefixLen;
duke@0 1511 jboolean isCopy;
duke@0 1512
duke@0 1513 prefixStr = (jstring) ((*jnienv)->GetObjectArrayElement(jnienv,
duke@0 1514 prefixArray, i));
duke@0 1515 errorOccurred = checkForThrowable(jnienv);
duke@0 1516 jplis_assert(!errorOccurred);
duke@0 1517 if (errorOccurred) {
duke@0 1518 break;
duke@0 1519 }
duke@0 1520 if (prefixStr == NULL) {
duke@0 1521 continue;
duke@0 1522 }
duke@0 1523
duke@0 1524 prefixLen = (*jnienv)->GetStringUTFLength(jnienv, prefixStr);
duke@0 1525 errorOccurred = checkForThrowable(jnienv);
duke@0 1526 jplis_assert(!errorOccurred);
duke@0 1527 if (errorOccurred) {
duke@0 1528 break;
duke@0 1529 }
duke@0 1530
duke@0 1531 if (prefixLen > 0) {
duke@0 1532 prefix = (*jnienv)->GetStringUTFChars(jnienv, prefixStr, &isCopy);
duke@0 1533 errorOccurred = checkForThrowable(jnienv);
duke@0 1534 jplis_assert(!errorOccurred);
duke@0 1535 if (!errorOccurred && prefix != NULL) {
duke@0 1536 prefixes[inx] = prefix;
duke@0 1537 originForRelease[inx] = prefixStr;
duke@0 1538 ++inx;
duke@0 1539 }
duke@0 1540 }
duke@0 1541 }
duke@0 1542
duke@0 1543 err = (*jvmtienv)->SetNativeMethodPrefixes(jvmtienv, inx, (char**)prefixes);
dcubed@142 1544 /* can be called from any phase */
duke@0 1545 jplis_assert(err == JVMTI_ERROR_NONE);
duke@0 1546
duke@0 1547 for (i = 0; i < inx; i++) {
duke@0 1548 (*jnienv)->ReleaseStringUTFChars(jnienv, originForRelease[i], prefixes[i]);
duke@0 1549 }
duke@0 1550 }
duke@0 1551 deallocate(jvmtienv, (void*)prefixes);
duke@0 1552 deallocate(jvmtienv, (void*)originForRelease);
duke@0 1553 }
duke@0 1554 }