annotate src/java.instrument/share/native/libinstrument/JPLISAgent.c @ 15585:08d703b88378

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