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