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