1 /*
2  * Copyright (c) 2003, 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 "agent_common.h"
28 #include "JVMTITools.h"
29 
30 extern "C" {
31 
32 
33 #define PASSED 0
34 #define STATUS_FAILED 2
35 
36 static jvmtiEnv *jvmti = NULL;
37 static jvmtiCapabilities caps;
38 static jvmtiEventCallbacks callbacks;
39 static jint result = PASSED;
40 static jmethodID mid1, mid2;
41 
42 static jthread currThread = NULL, popThread = NULL;
43 static jclass currClass = NULL, popClass = NULL;
44 static jmethodID currMethod = NULL, popMethod = NULL;
45 static jboolean currFlag = JNI_FALSE, popFlag = JNI_FALSE;
46 static jint currLoc = 0, popLoc = 0;
47 
48 void JNICALL
FramePop(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jboolean wasPopedByException)49 FramePop(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
50         jmethodID method, jboolean wasPopedByException) {
51     jvmtiError err;
52 
53     popThread = env->NewGlobalRef(thread);
54 
55     err = jvmti_env->GetMethodDeclaringClass(method, &popClass);
56     if (err != JVMTI_ERROR_NONE) {
57         printf("(GetMethodDeclaringClass) unexpected error: %s (%d)\n",
58                TranslateError(err), err);
59         result = STATUS_FAILED;
60     }
61     popClass = (jclass) env->NewGlobalRef(popClass);
62 
63     popMethod = method;
64     popFlag = wasPopedByException;
65 
66     err = jvmti_env->GetLocalInt(thread, 0, 1, &popLoc);
67     if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
68             !caps.can_access_local_variables) {
69         /* It is OK */
70     } else if (err != JVMTI_ERROR_NONE) {
71         printf("(GetLocalInt#pop) unexpected error: %s (%d)\n",
72                TranslateError(err), err);
73         result = STATUS_FAILED;
74     }
75 }
76 
77 void JNICALL
ExceptionCatch(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jlocation location,jobject exception)78 ExceptionCatch(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
79         jmethodID method, jlocation location, jobject exception) {
80     jvmtiError err;
81 
82     if (method == mid1 || method == mid2) {
83         currThread = env->NewGlobalRef(thread);
84 
85         err = jvmti_env->GetMethodDeclaringClass(
86             method, &currClass);
87         if (err != JVMTI_ERROR_NONE) {
88             printf("(GetMethodDeclaringClass) unexpected error: %s (%d)\n",
89                    TranslateError(err), err);
90             result = STATUS_FAILED;
91         }
92         currClass = (jclass) env->NewGlobalRef(currClass);
93 
94         currMethod = method;
95 
96         err = jvmti_env->GetLocalInt(thread, 0, 1, &currLoc);
97         if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
98                 !caps.can_access_local_variables) {
99             /* It is OK */
100         } else if (err != JVMTI_ERROR_NONE) {
101             printf("(GetLocalInt#catch) unexpected error: %s (%d)\n",
102                    TranslateError(err), err);
103             result = STATUS_FAILED;
104         }
105         if (method == mid2) {
106             currFlag = JNI_TRUE;
107         }
108 
109         err = jvmti_env->NotifyFramePop(thread, 0);
110         if (err != JVMTI_ERROR_NONE) {
111             printf("(NotifyFramePop#catch) unexpected error: %s (%d)\n",
112                    TranslateError(err), err);
113             result = STATUS_FAILED;
114         }
115     }
116 }
117 
118 #ifdef STATIC_BUILD
Agent_OnLoad_nframepop001(JavaVM * jvm,char * options,void * reserved)119 JNIEXPORT jint JNICALL Agent_OnLoad_nframepop001(JavaVM *jvm, char *options, void *reserved) {
120     return Agent_Initialize(jvm, options, reserved);
121 }
Agent_OnAttach_nframepop001(JavaVM * jvm,char * options,void * reserved)122 JNIEXPORT jint JNICALL Agent_OnAttach_nframepop001(JavaVM *jvm, char *options, void *reserved) {
123     return Agent_Initialize(jvm, options, reserved);
124 }
JNI_OnLoad_nframepop001(JavaVM * jvm,char * options,void * reserved)125 JNIEXPORT jint JNI_OnLoad_nframepop001(JavaVM *jvm, char *options, void *reserved) {
126     return JNI_VERSION_1_8;
127 }
128 #endif
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)129 jint  Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
130     jint res;
131     jvmtiError err;
132 
133     res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
134     if (res != JNI_OK || jvmti == NULL) {
135         printf("Wrong result of a valid call to GetEnv!\n");
136         return JNI_ERR;
137     }
138 
139     err = jvmti->GetPotentialCapabilities(&caps);
140     if (err != JVMTI_ERROR_NONE) {
141         printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
142                TranslateError(err), err);
143         return JNI_ERR;
144     }
145 
146     err = jvmti->AddCapabilities(&caps);
147     if (err != JVMTI_ERROR_NONE) {
148         printf("(AddCapabilities) unexpected error: %s (%d)\n",
149                TranslateError(err), err);
150         return JNI_ERR;
151     }
152 
153     err = jvmti->GetCapabilities(&caps);
154     if (err != JVMTI_ERROR_NONE) {
155         printf("(GetCapabilities) unexpected error: %s (%d)\n",
156                TranslateError(err), err);
157         return JNI_ERR;
158     }
159 
160     if (caps.can_generate_frame_pop_events &&
161             caps.can_generate_exception_events) {
162         callbacks.ExceptionCatch = &ExceptionCatch;
163         callbacks.FramePop = &FramePop;
164         err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
165         if (err != JVMTI_ERROR_NONE) {
166             printf("(SetEventCallbacks) unexpected error: %s (%d)\n",
167                    TranslateError(err), err);
168             return JNI_ERR;
169         }
170     } else {
171         printf("Warning: FramePop or ExceptionCatch event is not implemented\n");
172     }
173 
174     if (!caps.can_access_local_variables) {
175         printf("Warning: GetLocalInt is not implemented\n");
176     }
177 
178     if (!caps.can_suspend) {
179         printf("Warning: suspend/resume is not implemented\n");
180     }
181 
182     return JNI_OK;
183 }
184 
185 JNIEXPORT void JNICALL
Java_nsk_jvmti_NotifyFramePop_nframepop001_getMethIds(JNIEnv * env,jclass cl)186 Java_nsk_jvmti_NotifyFramePop_nframepop001_getMethIds(JNIEnv *env, jclass cl) {
187     jvmtiError err;
188 
189     if (caps.can_generate_frame_pop_events) {
190         err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
191             JVMTI_EVENT_FRAME_POP, NULL);
192         if (err != JVMTI_ERROR_NONE) {
193             printf("Failed to enable FRAME_POP event: %s (%d)\n",
194                    TranslateError(err), err);
195             result = STATUS_FAILED;
196             return;
197         }
198     }
199 
200     mid1 = env->GetMethodID(cl, "meth01", "(I)V");
201     if (mid1 == NULL) {
202         printf("Cannot find method \"meth01\"\n");
203         result = STATUS_FAILED;
204         return;
205     }
206 
207     mid2 = env->GetMethodID(cl, "meth02", "(I)V");
208     if (mid2 == NULL) {
209         printf("Cannot find method \"meth02\"\n");
210         result = STATUS_FAILED;
211         return;
212     }
213 
214     if (caps.can_generate_exception_events) {
215         err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
216             JVMTI_EVENT_EXCEPTION_CATCH, NULL);
217         if (err != JVMTI_ERROR_NONE) {
218             printf("Failed to enable EXCEPTION_CATCH event: %s (%d)\n",
219                    TranslateError(err), err);
220             result = STATUS_FAILED;
221             return;
222         }
223     }
224 }
225 
226 JNIEXPORT void JNICALL
Java_nsk_jvmti_NotifyFramePop_nframepop001_setFramePopNotif(JNIEnv * env,jclass cl,jthread thr)227 Java_nsk_jvmti_NotifyFramePop_nframepop001_setFramePopNotif(JNIEnv *env,
228         jclass cl, jthread thr) {
229     jvmtiError err;
230 
231     if (!caps.can_generate_frame_pop_events || !caps.can_suspend) {
232         return;
233     }
234 
235     err = jvmti->SuspendThread(thr);
236     if (err != JVMTI_ERROR_NONE) {
237         printf("(SuspendThread) unexpected error: %s (%d)\n",
238                TranslateError(err), err);
239         result = STATUS_FAILED;
240     }
241 
242     currThread = env->NewGlobalRef(thr);
243 
244     currClass = env->FindClass("nsk/jvmti/NotifyFramePop/nframepop001a");
245     if (currClass == NULL) {
246         printf("Cannot find nsk.jvmti.NotifyFramePop.nframepop001a class!\n");
247         result = STATUS_FAILED;
248         return;
249     }
250     currClass = (jclass) env->NewGlobalRef(currClass);
251 
252     currMethod = env->GetMethodID(currClass, "run", "()V");
253     if (currMethod == NULL) {
254         printf("Cannot find method \"run\"\n");
255         result = STATUS_FAILED;
256     }
257 
258     err = jvmti->GetLocalInt(thr, 0, 1, &currLoc);
259     if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
260             !caps.can_access_local_variables) {
261         /* It is OK */
262     } else if (err != JVMTI_ERROR_NONE) {
263         printf("(GetLocalInt) unexpected error: %s (%d)\n",
264                TranslateError(err), err);
265         result = STATUS_FAILED;
266     }
267 
268     err = jvmti->NotifyFramePop(thr, 0);
269     if (err != JVMTI_ERROR_NONE) {
270         printf("(NotifyFramePop) unexpected error: %s (%d)\n",
271                TranslateError(err), err);
272         result = STATUS_FAILED;
273     }
274 
275     err = jvmti->ResumeThread(thr);
276     if (err != JVMTI_ERROR_NONE) {
277         printf("(ResumeThread) unexpected error: %s (%d)\n",
278                TranslateError(err), err);
279         result = STATUS_FAILED;
280     }
281 }
282 
283 JNIEXPORT void JNICALL
Java_nsk_jvmti_NotifyFramePop_nframepop001_checkFrame(JNIEnv * env,jclass cls,jint point)284 Java_nsk_jvmti_NotifyFramePop_nframepop001_checkFrame(JNIEnv *env,
285         jclass cls, jint point) {
286 
287     if (!caps.can_generate_frame_pop_events) {
288         return;
289     }
290 
291     if (!env->IsSameObject(currThread, popThread)) {
292         printf("Point %d: thread is not the same as expected\n", point);
293         result = STATUS_FAILED;
294     }
295 
296     if (!env->IsSameObject(currClass, popClass)) {
297         printf("Point %d: class is not the same as expected\n", point);
298         result = STATUS_FAILED;
299     }
300 
301     if (currMethod != popMethod) {
302         printf("Point %d: method ID expected: 0x%p, actual: 0x%p\n",
303             point, currMethod, popMethod);
304         result = STATUS_FAILED;
305     }
306 
307     if (currFlag != popFlag) {
308         printf("Point %d: was_poped_by_exception expected: %d, actual: %d\n",
309             point, currFlag, popFlag);
310         result = STATUS_FAILED;
311     }
312 
313     if (currLoc != popLoc) {
314         printf("Point %d: local expected: %d, actual: %d\n",
315             point, currLoc, popLoc);
316         result = STATUS_FAILED;
317     }
318 
319     currThread = NULL; popThread = NULL;
320     currClass = NULL; popClass = NULL;
321     currMethod = NULL; popMethod = NULL;
322     currFlag = JNI_FALSE; popFlag = JNI_FALSE;
323     currLoc = 0; popLoc = 0;
324 }
325 
326 JNIEXPORT jint JNICALL
Java_nsk_jvmti_NotifyFramePop_nframepop001_getRes(JNIEnv * env,jclass cls)327 Java_nsk_jvmti_NotifyFramePop_nframepop001_getRes(JNIEnv *env, jclass cls) {
328     return result;
329 }
330 
331 }
332