1 /*
  2  * Copyright (c) 2003, 2020, 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 "jvmti_common.h"
 28 
 29 extern "C" {
 30 
 31 
 32 #define PASSED 0
 33 #define STATUS_FAILED 2
 34 
 35 typedef struct {
 36     char *name;
 37     char *c_cls;
 38     char *c_name;
 39     char *c_sig;
 40     jlocation c_loc;
 41 } writable_exceptionInfo;
 42 
 43 typedef struct {
 44     const char *name;
 45     const char *c_cls;
 46     const char *c_name;
 47     const char *c_sig;
 48     jlocation c_loc;
 49 } exceptionInfo;
 50 
 51 static jvmtiEnv *jvmti = NULL;
 52 static jvmtiCapabilities caps;
 53 static jvmtiEventCallbacks callbacks;
 54 static jint result = PASSED;
 55 
 56 static exceptionInfo exs[] = {
 57   { "Lexcatch01c;",
 58     "Lexcatch01a;", "run", "()V", 14 },
 59   { "Ljava/lang/ArithmeticException;",
 60     "Lexcatch01a;", "run", "()V", 24 },
 61   { "Ljava/lang/ArrayIndexOutOfBoundsException;",
 62     "Lexcatch01a;", "run", "()V", 34 }
 63 };
 64 
 65 static jboolean isVirtualExpected = JNI_FALSE;
 66 static int eventsCount = 0;
 67 static int eventsExpected = 0;
 68 
 69 void JNICALL
 70 ExceptionCatch(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr,
 71         jmethodID method, jlocation location, jobject exception) {
 72     jvmtiError err;
 73     jclass cls;
 74     writable_exceptionInfo ex;
 75     char *generic;
 76     size_t i;
 77 
 78 
 79     LOG(">>> retrieving ExceptionCatch info ...\n");
 80 
 81     cls = jni->GetObjectClass(exception);
 82     err = jvmti->GetClassSignature(cls, &ex.name, &generic);
 83     if (err != JVMTI_ERROR_NONE) {
 84         LOG("(GetClassSignature#e) unexpected error: %s (%d)\n",
 85                TranslateError(err), err);
 86         result = STATUS_FAILED;
 87         return;
 88     }
 89     err = jvmti->GetMethodDeclaringClass(method, &cls);
 90     if (err != JVMTI_ERROR_NONE) {
 91         LOG("(GetMethodDeclaringClass) unexpected error: %s (%d)\n",
 92                TranslateError(err), err);
 93         result = STATUS_FAILED;
 94         return;
 95     }
 96     err = jvmti->GetClassSignature(cls, &ex.c_cls, &generic);
 97     if (err != JVMTI_ERROR_NONE) {
 98         LOG("(GetClassSignature#c) unexpected error: %s (%d)\n",
 99                TranslateError(err), err);
100         result = STATUS_FAILED;
101         return;
102     }
103     err = jvmti->GetMethodName(method,
104         &ex.c_name, &ex.c_sig, &generic);
105     if (err != JVMTI_ERROR_NONE) {
106         LOG("(GetMethodName) unexpected error: %s (%d)\n",
107                TranslateError(err), err);
108         result = STATUS_FAILED;
109         return;
110     }
111     ex.c_loc = location;
112     LOG(">>> %s\n", ex.name);
113     LOG(">>>    catch at %s.%s%s:0x%x%08x\n",
114            ex.c_cls, ex.c_name, ex.c_sig,
115            (jint)(ex.c_loc >> 32), (jint)ex.c_loc);
116     LOG(">>> ... done\n");
117 
118     for (i = 0; i < sizeof(exs)/sizeof(exceptionInfo); i++) {
119         if (ex.name != NULL && strcmp(ex.name, exs[i].name) == 0
120             && ex.c_cls != NULL && strcmp(ex.c_cls, exs[i].c_cls) == 0
121             && ex.c_name != NULL && strcmp(ex.c_name, exs[i].c_name) == 0
122             && ex.c_sig != NULL && strcmp(ex.c_sig, exs[i].c_sig) == 0
123             && ex.c_loc == exs[i].c_loc) {
124           jboolean isVirtual = jni->IsVirtualThread(thr);
125           if (isVirtualExpected != isVirtual) {
126             LOG("The thread IsVirtualThread %d differs from expected %d.\n", isVirtual, isVirtualExpected);
127             result = STATUS_FAILED;
128           } else {
129             eventsCount++;
130           }
131             break;
132         }
133     }
134     if (i == sizeof(exs)/sizeof(exceptionInfo)) {
135         LOG("Unexpected exception catch event:\n");
136         LOG("  %s\n", ex.name);
137         LOG("     catch at %s.%s%s:0x%x%08x\n",
138                ex.c_cls, ex.c_name, ex.c_sig,
139                (jint)(ex.c_loc >> 32), (jint)ex.c_loc);
140         result = STATUS_FAILED;
141     }
142 }
143 
144 #ifdef STATIC_BUILD
145 JNIEXPORT jint JNICALL Agent_OnLoad_excatch01(JavaVM *jvm, char *options, void *reserved) {
146     return Agent_Initialize(jvm, options, reserved);
147 }
148 JNIEXPORT jint JNICALL Agent_OnAttach_excatch01(JavaVM *jvm, char *options, void *reserved) {
149     return Agent_Initialize(jvm, options, reserved);
150 }
151 JNIEXPORT jint JNI_OnLoad_excatch01(JavaVM *jvm, char *options, void *reserved) {
152     return JNI_VERSION_1_8;
153 }
154 #endif
155 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
156     jvmtiCapabilities caps;
157     jvmtiError err;
158     jint res;
159 
160 
161     res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
162     if (res != JNI_OK || jvmti == NULL) {
163         LOG("Wrong result of a valid call to GetEnv!\n");
164         return JNI_ERR;
165     }
166 
167     memset(&caps, 0, sizeof(jvmtiCapabilities));
168     caps.can_generate_exception_events = 1;
169     caps.can_support_virtual_threads = 1;
170 
171     err = jvmti->AddCapabilities(&caps);
172     if (err != JVMTI_ERROR_NONE) {
173         LOG("(AddCapabilities) unexpected error: %s (%d)\n",
174                TranslateError(err), err);
175         return JNI_ERR;
176     }
177 
178     err = jvmti->GetCapabilities(&caps);
179     if (err != JVMTI_ERROR_NONE) {
180         LOG("(GetCapabilities) unexpected error: %s (%d)\n",
181                TranslateError(err), err);
182         return JNI_ERR;
183     }
184 
185     if (caps.can_generate_exception_events) {
186         callbacks.ExceptionCatch = &ExceptionCatch;
187         err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
188         if (err != JVMTI_ERROR_NONE) {
189             LOG("(SetEventCallbacks) unexpected error: %s (%d)\n",
190                    TranslateError(err), err);
191             return JNI_ERR;
192         }
193     } else {
194         LOG("Warning: Exception event is not implemented\n");
195     }
196 
197     return JNI_OK;
198 }
199 
200 JNIEXPORT jint JNICALL
201 Java_excatch01_check(JNIEnv *jni, jclass cls) {
202     jvmtiError err;
203     jclass clz;
204     jmethodID mid;
205     jthread thread;
206 
207 
208   if (jvmti == NULL) {
209         LOG("JVMTI client was not properly loaded!\n");
210         return STATUS_FAILED;
211     }
212 
213     clz = jni->FindClass("excatch01c");
214     if (clz == NULL) {
215         LOG("Cannot find excatch01c class!\n");
216         return STATUS_FAILED;
217     }
218     clz = jni->FindClass("excatch01b");
219     if (clz == NULL) {
220         LOG("Cannot find excatch01b class!\n");
221         return STATUS_FAILED;
222     }
223     clz = jni->FindClass("excatch01a");
224     if (clz == NULL) {
225         LOG("Cannot find excatch01a class!\n");
226         return STATUS_FAILED;
227     }
228     mid = jni->GetStaticMethodID(clz, "run", "()V");
229     if (mid == NULL) {
230         LOG("Cannot find method run!\n");
231         return STATUS_FAILED;
232     }
233 
234     err = jvmti->GetCurrentThread(&thread);
235     if (err != JVMTI_ERROR_NONE) {
236       LOG("Failed to get current thread: %s (%d)\n", TranslateError(err), err);
237       result = STATUS_FAILED;
238       return STATUS_FAILED;
239     }
240 
241     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
242             JVMTI_EVENT_EXCEPTION_CATCH, NULL);
243     if (err == JVMTI_ERROR_NONE) {
244         eventsExpected = sizeof(exs)/sizeof(exceptionInfo);
245     } else {
246         LOG("Failed to enable JVMTI_EVENT_EXCEPTION_CATCH: %s (%d)\n",
247                TranslateError(err), err);
248         result = STATUS_FAILED;
249     }
250 
251   eventsCount = 0;
252   isVirtualExpected = jni->IsVirtualThread(thread);
253 
254     jni->CallStaticVoidMethod(clz, mid);
255 
256     err = jvmti->SetEventNotificationMode(JVMTI_DISABLE,
257             JVMTI_EVENT_EXCEPTION_CATCH, NULL);
258     if (err != JVMTI_ERROR_NONE) {
259         LOG("Failed to disable JVMTI_EVENT_EXCEPTION_CATCH: %s (%d)\n",
260                TranslateError(err), err);
261         result = STATUS_FAILED;
262     }
263 
264     if (eventsCount != eventsExpected) {
265         LOG("Wrong number of exception catch events: %d, expected: %d\n",
266             eventsCount, eventsExpected);
267         result = STATUS_FAILED;
268     }
269     return result;
270 }
271 
272 
273 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
274   return Agent_Initialize(jvm, options, reserved);
275 }
276 
277 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
278   return Agent_Initialize(jvm, options, reserved);
279 }
280 
281 
282 }