1 /*
2  * Copyright (c) 2007, 2018, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include "jvmti.h"
27 #include "jni_tools.h"
28 #include "agent_common.h"
29 #include "JVMTITools.h"
30 
31 extern "C" {
32 
33 
34 #define PASSED 0
35 #define STATUS_FAILED 2
36 
37 #define RETURN_FAILED errCode = STATUS_FAILED; fflush(0); return
38 
39 static jvmtiEnv *jvmti = NULL;
40 static jvmtiCapabilities caps;
41 static jvmtiEventCallbacks callbacks;
42 static jint errCode = PASSED;
43 static jboolean printdump = JNI_TRUE;
44 
45 static jmethodID midRun            = NULL;
46 static jmethodID midCountDownLong  = NULL;
47 static jmethodID midCheckPoint     = NULL;
48 
49 static jint framesExpected = 0;
50 static jint framesCount = 0;
51 static jint methodExitEventCount = 0;
52 
53 static const char *cls_exp = "Lnsk/jvmti/unit/ForceEarlyReturn/earlyretlong$earlyretThread;";
54 
55 static jlong val_exp = 0L;
56 static const char *sig_exp = "(I)J";
57 static const char *name_exp = "countDownLong";
58 
59 static const char *argName = "nestingCount";
60 
check(jvmtiEnv * jvmti_env,jthread thr,jmethodID mid,jlocation loc,jint frame_no)61 void check(jvmtiEnv *jvmti_env, jthread thr, jmethodID mid,
62            jlocation loc, jint frame_no) {
63     jvmtiError err;
64     jclass cls;
65     jlocation loc_exp = (frame_no == 0) ? 0x15 : 0xd;
66     char *sigClass, *name, *sig, *generic;
67     jvmtiLocalVariableEntry *table = NULL;
68     jint entryCount = 0;
69     jint argValue;
70     jint j;
71 
72     err = jvmti_env->GetMethodDeclaringClass(mid, &cls);
73     if (err != JVMTI_ERROR_NONE) {
74         printf("(GetMethodDeclaringClass#%d) unexpected error: %s (%d)\n",
75                frame_no, TranslateError(err), err);
76         RETURN_FAILED;
77     }
78 
79     err = jvmti_env->GetClassSignature(cls, &sigClass, &generic);
80     if (err != JVMTI_ERROR_NONE) {
81         printf("(GetClassSignature#%d) unexpected error: %s (%d)\n",
82                frame_no, TranslateError(err), err);
83         RETURN_FAILED;
84     }
85 
86     err = jvmti_env->GetMethodName(mid, &name, &sig, &generic);
87     if (err != JVMTI_ERROR_NONE) {
88         printf("(GetMethodName#%d) unexpected error: %s (%d)\n",
89                frame_no, TranslateError(err), err);
90         RETURN_FAILED;
91     }
92 
93     /* Get Local Variable Table to be able to get the argument value
94      * from current method frame and compare it with the expected value
95      */
96     err = jvmti_env->GetLocalVariableTable(mid, &entryCount, &table);
97     if (err != JVMTI_ERROR_NONE) {
98         printf("(GetLocalVariableTable#%d) unexpected error: %s (%d)\n",
99                frame_no, TranslateError(err), err);
100         RETURN_FAILED;
101     }
102     if (table != NULL) {
103         for (j = 0; j < entryCount; j++) {
104             if (strcmp(table[j].name, argName) == 0) {
105                 err = jvmti_env->GetLocalInt(thr, 0,
106                     table[j].slot, &argValue);
107                 if (err != JVMTI_ERROR_NONE) {
108                     printf("(GetLocalInt#%d) unexpected error: %s (%d)\n",
109                            frame_no, TranslateError(err), err);
110                     RETURN_FAILED;
111                 }
112             }
113         }
114     }
115 
116     if (printdump == JNI_TRUE) {
117         printf("\n>>> step %d: \"%s.%s%s\"\n", frame_no, sigClass, name, sig);
118         printf(">>>   location: %#x%08x", (jint)(loc >> 32), (jint)loc);
119         printf(", arg value: %d\n", argValue);
120     }
121 
122     if (sigClass == NULL || strcmp(sigClass, cls_exp) != 0) {
123         printf("(step %d) Wrong class sig: \"%s\",\n", frame_no, sigClass);
124         printf(" expected: \"%s\"\n", cls_exp);
125         RETURN_FAILED;
126     }
127     if (name == NULL || strcmp(name, name_exp) != 0) {
128         printf("(step %d) wrong method name: \"%s\",", frame_no, name);
129         printf(" expected: \"%s\"\n", name_exp);
130         RETURN_FAILED;
131     }
132     if (sig == NULL || strcmp(sig, sig_exp) != 0) {
133         printf("(step %d) wrong method sig: \"%s\",", frame_no, sig);
134         printf(" expected: \"%s\"\n", sig_exp);
135         RETURN_FAILED;
136     }
137     if (loc != loc_exp) {
138         printf("(step %d) wrong location: %#x%08x,",
139                frame_no, (jint)(loc >> 32), (jint)loc);
140         printf(" expected: %#x\n", (jint)loc_exp);
141         RETURN_FAILED;
142     }
143     if (argValue != frame_no) {
144         printf("(step %d) wrong argument value: %d,", frame_no, argValue);
145         printf(" expected: %d\n", frame_no);
146         RETURN_FAILED;
147     }
148 
149     if (sigClass != NULL) {
150         jvmti_env->Deallocate((unsigned char*)sigClass);
151     }
152     if (name != NULL) {
153         jvmti_env->Deallocate((unsigned char*)name);
154     }
155     if (sig != NULL) {
156         jvmti_env->Deallocate((unsigned char*)sig);
157     }
158     if (table != NULL) {
159         for (j = 0; j < entryCount; j++) {
160             jvmti_env->Deallocate((unsigned char*)(table[j].name));
161             jvmti_env->Deallocate((unsigned char*)(table[j].signature));
162         }
163         jvmti_env->Deallocate((unsigned char*)table);
164     }
165     if (methodExitEventCount != (framesCount + 1)) {
166         printf("(step %d) wrong methodExitEventCount: %d,",
167                frame_no, methodExitEventCount);
168         printf(" expected: %d\n", framesCount + 1);
169         RETURN_FAILED;
170     }
171     fflush(0);
172 }
173 
Breakpoint(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jlocation location)174 void JNICALL Breakpoint(jvmtiEnv *jvmti_env, JNIEnv *env,
175         jthread thread, jmethodID method, jlocation location) {
176     jvmtiError err;
177 
178     if (midCheckPoint != method) {
179         printf("bp: don't know where we get called from");
180         RETURN_FAILED;
181     }
182 
183     if (printdump == JNI_TRUE) {
184         printf(">>> breakpoint in checkPoint\n");
185     }
186 
187     err = jvmti_env->ClearBreakpoint(midCheckPoint, 0);
188     if (err != JVMTI_ERROR_NONE) {
189         printf("(ClearBreakpoint) unexpected error: %s (%d)\n",
190                TranslateError(err), err);
191         RETURN_FAILED;
192     }
193 
194     err = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
195         JVMTI_EVENT_SINGLE_STEP, thread);
196     if (err != JVMTI_ERROR_NONE) {
197         printf("Cannot enable single step events: %s (%d)\n",
198                TranslateError(err), err);
199         RETURN_FAILED;
200     }
201 
202     err = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
203         JVMTI_EVENT_METHOD_EXIT, thread);
204     if (err != JVMTI_ERROR_NONE) {
205         printf("Cannot enable method exit events: %s (%d)\n",
206                TranslateError(err), err);
207         RETURN_FAILED;
208     }
209 
210     err = jvmti_env->ForceEarlyReturnVoid(thread);
211     if (err != JVMTI_ERROR_NONE) {
212         printf("(ForceEarlyReturn) unexpected error: %s (%d)\n",
213                TranslateError(err), err);
214         RETURN_FAILED;
215     }
216     fflush(0);
217 }
218 
SingleStep(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jlocation location)219 void JNICALL SingleStep(jvmtiEnv *jvmti_env, JNIEnv *env,
220         jthread thread, jmethodID method, jlocation location) {
221     jvmtiError err;
222 
223     if (method == midRun) {
224         if (printdump == JNI_TRUE) {
225             printf(">>> returned early %d frames till method \"run()\"\n",
226                    framesCount);
227         }
228 
229         err = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
230             JVMTI_EVENT_SINGLE_STEP, thread);
231         if (err != JVMTI_ERROR_NONE) {
232             printf("Cannot disable single step events: %s (%d)\n",
233                    TranslateError(err), err);
234             RETURN_FAILED;
235         }
236         err = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
237             JVMTI_EVENT_METHOD_EXIT, thread);
238         if (err != JVMTI_ERROR_NONE) {
239             printf("Cannot disable method exit events: %s (%d)\n",
240                    TranslateError(err), err);
241             RETURN_FAILED;
242         }
243     } else if (method == midCountDownLong) {
244         check(jvmti_env, thread, method, location, framesCount);
245         framesCount++;
246 
247         err = jvmti_env->ForceEarlyReturnLong(thread, ++val_exp);
248         if (err != JVMTI_ERROR_NONE) {
249             printf("(ForceEarlyReturn) unexpected error: %s (%d)\n",
250                    TranslateError(err), err);
251             RETURN_FAILED;
252         }
253     }
254     fflush(0);
255 }
256 
MethodExit(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jboolean was_popped_by_exception,jvalue value)257 void JNICALL MethodExit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
258         jmethodID method, jboolean was_popped_by_exception, jvalue value) {
259     jlong ret_val = value.j;
260     int * val_ptr = (int *) &ret_val;
261     int * exp_ptr = (int *) &val_exp;
262 
263     methodExitEventCount++;
264     if (method == midCountDownLong) {
265         printf(">>> ForceEarlyReturnLong value: dec: %" LL "d, hex: %#x %#x\n",
266                 ret_val, val_ptr[0], val_ptr[1]);
267         printf(">>>      expected return value: dec: %" LL "d, hex: %#x %#x\n",
268                 val_exp, exp_ptr[0], exp_ptr[1]);
269 
270         if (ret_val != val_exp) {
271             printf("Wrong ForceEarlyReturnLong return value: %" LL "d\n", ret_val);
272             errCode = STATUS_FAILED;
273         }
274         if (was_popped_by_exception) {
275             printf("Method was_popped_by_exception unexpectedly\n");
276             errCode = STATUS_FAILED;
277         }
278     }
279     fflush(0);
280 }
281 
282 #ifdef STATIC_BUILD
Agent_OnLoad_earlyretlong(JavaVM * jvm,char * options,void * reserved)283 JNIEXPORT jint JNICALL Agent_OnLoad_earlyretlong(JavaVM *jvm, char *options, void *reserved) {
284     return Agent_Initialize(jvm, options, reserved);
285 }
Agent_OnAttach_earlyretlong(JavaVM * jvm,char * options,void * reserved)286 JNIEXPORT jint JNICALL Agent_OnAttach_earlyretlong(JavaVM *jvm, char *options, void *reserved) {
287     return Agent_Initialize(jvm, options, reserved);
288 }
JNI_OnLoad_earlyretlong(JavaVM * jvm,char * options,void * reserved)289 JNIEXPORT jint JNI_OnLoad_earlyretlong(JavaVM *jvm, char *options, void *reserved) {
290     return JNI_VERSION_1_8;
291 }
292 #endif
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)293 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
294     jvmtiError err;
295     jint res;
296 
297     if (options != NULL && strcmp(options, "printdump") == 0) {
298         printf("Printdump is turned on!\n");
299 
300         printdump = JNI_TRUE;
301     }
302 
303     res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
304     if (res != JNI_OK || jvmti == NULL) {
305         printf("Wrong error code from a valid call to GetEnv!\n");
306         return JNI_ERR;
307     }
308 
309     err = jvmti->GetPotentialCapabilities(&caps);
310     if (err != JVMTI_ERROR_NONE) {
311         printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
312                TranslateError(err), err);
313         return JNI_ERR;
314     }
315 
316     err = jvmti->AddCapabilities(&caps);
317     if (err != JVMTI_ERROR_NONE) {
318         printf("(AddCapabilities) unexpected error: %s (%d)\n",
319                TranslateError(err), err);
320         return JNI_ERR;
321     }
322 
323     err = jvmti->GetCapabilities(&caps);
324     if (err != JVMTI_ERROR_NONE) {
325         printf("(GetCapabilities) unexpected error: %s (%d)\n",
326                TranslateError(err), err);
327         return JNI_ERR;
328     }
329 
330     if (!caps.can_force_early_return) {
331         printf("Warning: ForceEarlyReturn is not implemented\n");
332     }
333 
334     if (caps.can_generate_breakpoint_events &&
335         caps.can_generate_method_exit_events &&
336         caps.can_generate_single_step_events)
337     {
338         callbacks.Breakpoint = &Breakpoint;
339         callbacks.SingleStep = &SingleStep;
340         callbacks.MethodExit = &MethodExit;
341         err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
342         if (err != JVMTI_ERROR_NONE) {
343             printf("(SetEventCallbacks) unexpected error: %s (%d)\n",
344                    TranslateError(err), err);
345             return JNI_ERR;
346         }
347     } else {
348         printf("Warning: Breakpoint or SingleStep event are not implemented\n");
349     }
350 
351     return JNI_OK;
352 }
353 
354 JNIEXPORT void JNICALL
Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_getReady(JNIEnv * env,jclass c,jclass cls,jint depth,jlong retval_base)355 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_getReady(
356     JNIEnv *env, jclass c, jclass cls, jint depth, jlong retval_base) {
357     jvmtiError err;
358 
359     val_exp = retval_base;
360     if (jvmti == NULL) {
361         printf("JVMTI client was not properly loaded!\n");
362         RETURN_FAILED;
363     }
364 
365     if (!caps.can_force_early_return ||
366         !caps.can_generate_breakpoint_events ||
367         !caps.can_generate_method_exit_events ||
368         !caps.can_generate_single_step_events) {
369         return;
370     }
371 
372     midRun = env->GetMethodID(cls, "run", "()V");
373     if (midRun == NULL) {
374         printf("Cannot find Method ID for method run\n");
375         RETURN_FAILED;
376     }
377 
378     midCheckPoint = env->GetMethodID(cls, "checkPoint", "()V");
379     if (midCheckPoint == NULL) {
380         printf("Cannot find Method ID for method checkPoint\n");
381         RETURN_FAILED;
382     }
383 
384     midCountDownLong = env->GetMethodID(cls, "countDownLong", "(I)J");
385     if (midCountDownLong == NULL) {
386         printf("Cannot find Method ID for method countDownLong\n");
387         RETURN_FAILED;
388     }
389 
390     err = jvmti->SetBreakpoint(midCheckPoint, 0);
391     if (err != JVMTI_ERROR_NONE) {
392         printf("(SetBreakpoint) unexpected error: %s (%d)\n",
393                TranslateError(err), err);
394         RETURN_FAILED;
395     }
396 
397     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
398         JVMTI_EVENT_BREAKPOINT, NULL);
399     if (err != JVMTI_ERROR_NONE) {
400         printf("Failed to enable BREAKPOINT event: %s (%d)\n",
401                TranslateError(err), err);
402         RETURN_FAILED;
403     } else {
404         framesExpected = depth;
405     }
406 }
407 
408 JNIEXPORT void JNICALL
Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_printLong(JNIEnv * env,jclass cls,jlong val)409 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_printLong(
410       JNIEnv *env, jclass cls, jlong val) {
411     jint *iptr = (jint *) &val;
412 
413     printf("\n>>> Returned value: dec: %" LL "d, hex: %#x %#x\n",
414             val, iptr[0], iptr[1]);
415     fflush(0);
416     return;
417 }
418 
419 JNIEXPORT jint JNICALL
Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_check(JNIEnv * env,jclass cls)420 Java_nsk_jvmti_unit_ForceEarlyReturn_earlyretlong_check(JNIEnv *env, jclass cls) {
421     if (framesCount != framesExpected) {
422         printf("Wrong number of returned early frames: %d, expected: %d\n",
423             framesCount, framesExpected);
424         errCode = STATUS_FAILED;
425     }
426     fflush(0);
427     return errCode;
428 }
429 
430 }
431