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 "jni.h"
 27 #include "jni_md.h"
 28 #include "jvmti.h"
 29 
 30 #include "jvmti_common.h"
 31 
 32 extern "C" {
 33 
 34 #define STATUS_FAILED 2
 35 #define PASSED 0
 36 
 37 
 38 #define METH_NUM 4
 39 static const char *METHODS[][2] = {
 40     {"bpMethod", "()V"},
 41     {"bpMethod2", "()I"},
 42     {"bpMethodV", "()V"},
 43     {"bpMethod2V", "()I"}
 44 };
 45 
 46 static const jboolean METHODS_ATTRS[METH_NUM] = {JNI_FALSE, JNI_FALSE, JNI_TRUE, JNI_TRUE};
 47 
 48 
 49 static const char *CLASS_SIG = "Lbreakpoint01;";
 50 
 51 static const char *THREAD_NAME = "breakpoint01Thr";
 52 
 53 static volatile int bpEvents[METH_NUM];
 54 static volatile jint result = PASSED;
 55 static jvmtiEnv *jvmti = NULL;
 56 static jvmtiEventCallbacks callbacks;
 57 
 58 static volatile int callbacksEnabled = NSK_TRUE;
 59 static jrawMonitorID agent_lock;
 60 
 61 static void initCounters() {
 62   int i;
 63 
 64   for (i = 0; i < METH_NUM; i++)
 65     bpEvents[i] = 0;
 66 }
 67 
 68 static void setBP(jvmtiEnv *jvmti, JNIEnv *jni, jclass klass) {
 69   jmethodID mid;
 70   jvmtiError err;
 71   int i;
 72 
 73   for (i = 0; i < METH_NUM; i++) {
 74     mid = jni->GetMethodID(klass, METHODS[i][0], METHODS[i][1]);
 75     if (mid == nullptr) {
 76       jni->FatalError("failed to get ID for the java method\n");
 77     }
 78 
 79     err = jvmti->SetBreakpoint(mid, 0);
 80     if (err != JVMTI_ERROR_NONE) {
 81       jni->FatalError("failed to set breakpoint\n");
 82     }
 83   }
 84 }
 85 
 86 /** callback functions **/
 87 void JNICALL
 88 ClassLoad(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jclass klass) {
 89   char *sig, *generic;
 90   jvmtiError err;
 91 
 92   RawMonitorLocker rml(jvmti, jni, agent_lock);
 93   if (callbacksEnabled) {
 94     // GetClassSignature may be called only during the start or the live phase
 95     err = jvmti->GetClassSignature(klass, &sig, &generic);
 96     if (err != JVMTI_ERROR_NONE) {
 97       jni->FatalError("failed to obtain a class signature\n");
 98     }
 99 
100     if (sig != NULL && (strcmp(sig, CLASS_SIG) == 0)) {
101       LOG("ClassLoad event received for the class %s setting breakpoints ...\n", sig);
102       setBP(jvmti, jni, klass);
103     }
104   }
105 }
106 
107 void JNICALL
108 Breakpoint(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jmethodID method, jlocation location) {
109   jclass klass;
110   char *clsSig, *generic, *methNam, *methSig;
111   jvmtiThreadInfo thr_info;
112   jvmtiError err;
113   int checkStatus = PASSED;
114   int i;
115 
116   LOG(">>>> Breakpoint event received\n");
117 
118 /* checking thread info */
119   err = jvmti->GetThreadInfo(thread, &thr_info);
120   if (err != JVMTI_ERROR_NONE) {
121     result = STATUS_FAILED;
122     LOG("TEST FAILED: unable to get thread info during Breakpoint callback\n\n");
123     return;
124   }
125 
126   if (thr_info.name == NULL ||
127       strcmp(thr_info.name, THREAD_NAME) != 0) {
128     result = checkStatus = STATUS_FAILED;
129     LOG(
130         "TEST FAILED: Breakpoint event with unexpected thread info:\n"
131         "\tname: \"%s\"\ttype: %s %s thread\n\n",
132         (thr_info.name == NULL) ? "NULL" : thr_info.name,
133         (jni->IsVirtualThread(thread) == JNI_TRUE) ? "virtual" : "kernel",
134         (thr_info.is_daemon == JNI_TRUE) ? "deamon" : "user");
135   } else {
136     LOG("CHECK PASSED: thread name: \"%s\"\ttype: %s %s thread\n",
137            thr_info.name,        (jni->IsVirtualThread(thread) == JNI_TRUE) ? "virtual" : "kernel",
138         (thr_info.is_daemon == JNI_TRUE) ? "deamon" : "user");
139   }
140 
141 
142 /* checking location */
143   if (location != 0) {
144     result = checkStatus = STATUS_FAILED;
145     LOG("TEST FAILED: Breakpoint event with unexpected location %ld:\n\n", (long) location);
146   } else {
147     LOG("CHECK PASSED: location: %ld as expected\n", (long) location);
148   }
149 
150 /* checking method info */
151   err = jvmti->GetMethodDeclaringClass(method, &klass);
152   if (err != JVMTI_ERROR_NONE) {
153     result = checkStatus = STATUS_FAILED;
154     LOG("TEST FAILED: unable to get method declaring class during Breakpoint callback\n\n");
155     return;
156   }
157   err = jvmti->GetClassSignature(klass, &clsSig, &generic);
158   if (err != JVMTI_ERROR_NONE) {
159     result = checkStatus = STATUS_FAILED;
160     LOG("TEST FAILED: unable to obtain a class signature during Breakpoint callback\n\n");
161     return;
162   }
163   if (clsSig == NULL ||
164       strcmp(clsSig, CLASS_SIG) != 0) {
165     result = checkStatus = STATUS_FAILED;
166     LOG(
167         "TEST FAILED: Breakpoint event with unexpected class signature:\n"
168         "\t\"%s\"\n\n",
169         (clsSig == NULL) ? "NULL" : clsSig);
170   } else {
171     LOG("CHECK PASSED: class signature: \"%s\"\n", clsSig);
172   }
173 
174   err = jvmti->GetMethodName(method, &methNam, &methSig, NULL);
175   if (err != JVMTI_ERROR_NONE) {
176     result = checkStatus = STATUS_FAILED;
177     LOG("TEST FAILED: unable to get method name during Breakpoint callback\n\n");
178     return;
179   }
180 
181   for (i = 0; i < METH_NUM; i++) {
182     if (strcmp(methNam, METHODS[i][0]) == 0 &&
183         strcmp(methSig, METHODS[i][1]) == 0) {
184       LOG("CHECK PASSED: method name: \"%s\"\tsignature: \"%s\" %d\n", methNam, methSig, i);
185       jboolean isVirtual = jni->IsVirtualThread(thread);
186       if (isVirtual != METHODS_ATTRS[i]) {
187         LOG("TEST FAILED: IsVirtualThread check failed with unexpected result %d  when expected is %d\n", isVirtual, METHODS_ATTRS[i]);
188         result = checkStatus = STATUS_FAILED;
189       }
190       if (checkStatus == PASSED) {
191         bpEvents[i]++;
192       }
193       break;
194     }
195   }
196   err = jvmti->Deallocate((unsigned char *) methNam);
197   if (err != JVMTI_ERROR_NONE) {
198     result = STATUS_FAILED;
199     LOG("TEST FAILED: unable to deallocate memory pointed to method name\n\n");
200   }
201 
202   err = jvmti->Deallocate((unsigned char *) methSig);
203   if (err != JVMTI_ERROR_NONE) {
204     result = STATUS_FAILED;
205     LOG("TEST FAILED: unable to deallocate memory pointed to method signature\n\n");
206   }
207 
208   LOG("<<<<\n\n");
209 }
210 
211 void JNICALL
212 VMStart(jvmtiEnv *jvmti, JNIEnv *jni) {
213   RawMonitorLocker rml(jvmti, jni, agent_lock);
214   callbacksEnabled = NSK_TRUE;
215 }
216 
217 void JNICALL
218 VMDeath(jvmtiEnv *jvmti, JNIEnv *jni) {
219   RawMonitorLocker rml(jvmti, jni, agent_lock);
220   callbacksEnabled = NSK_FALSE;
221 }
222 /************************/
223 
224 JNIEXPORT jint JNICALL Java_breakpoint01_check(JNIEnv *jni, jobject obj) {
225   int i;
226 
227   for (i = 0; i < METH_NUM; i++) {
228     if (bpEvents[i] != 1) {
229       result = STATUS_FAILED;
230       LOG(
231           "TEST FAILED: wrong number of Breakpoint events\n"
232           "\tfor the method \"%s %s\":\n"
233           "\t\tgot: %d\texpected: 1\n",
234           METHODS[i][0], METHODS[i][1], bpEvents[i]);
235     } else {
236       LOG("CHECK PASSED: %d Breakpoint event(s) for the method \"%s %s\" as expected\n",
237              bpEvents[i], METHODS[i][0], METHODS[i][1]);
238     }
239   }
240 
241   return result;
242 }
243 
244 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
245   jvmtiCapabilities caps;
246   jvmtiError err;
247   jint res;
248 
249   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_9);
250   if (res != JNI_OK || jvmti == NULL) {
251     LOG("Wrong result of a valid call to GetEnv!\n");
252     return JNI_ERR;
253   }
254 
255   initCounters();
256 
257   memset(&caps, 0, sizeof(jvmtiCapabilities));
258   caps.can_generate_breakpoint_events = 1;
259   caps.can_support_virtual_threads = 1;
260 
261   err = jvmti->AddCapabilities(&caps);
262   if (err != JVMTI_ERROR_NONE) {
263     return JNI_ERR;
264   }
265 
266   err = jvmti->GetCapabilities(&caps);
267   if (err != JVMTI_ERROR_NONE) {
268     return JNI_ERR;
269   }
270 
271   if (!caps.can_generate_single_step_events)
272     LOG("Warning: generation of single step events is not implemented\n");
273 
274   /* set event callback */
275   LOG("setting event callbacks ...\n");
276   (void) memset(&callbacks, 0, sizeof(callbacks));
277   callbacks.ClassLoad = &ClassLoad;
278   callbacks.Breakpoint = &Breakpoint;
279   callbacks.VMStart = &VMStart;
280   callbacks.VMDeath = &VMDeath;
281 
282   err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
283   if (err != JVMTI_ERROR_NONE)
284     return JNI_ERR;
285 
286   LOG("setting event callbacks done\nenabling JVMTI events ...\n");
287 
288   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_START, NULL);
289   if (err != JVMTI_ERROR_NONE) {
290     return JNI_ERR;
291   }
292   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
293   if (err != JVMTI_ERROR_NONE)
294     return JNI_ERR;
295   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
296   if (err != JVMTI_ERROR_NONE)
297     return JNI_ERR;
298   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
299   if (err != JVMTI_ERROR_NONE)
300     return JNI_ERR;
301   LOG("enabling the events done\n\n");
302 
303   agent_lock = create_raw_monitor(jvmti, "agent_lock");
304 
305   if (agent_lock == NULL) {
306     return JNI_ERR;
307   }
308 
309   return JNI_OK;
310 }
311 
312 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
313   return Agent_Initialize(jvm, options, reserved);
314 }
315 
316 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
317   return Agent_Initialize(jvm, options, reserved);
318 }
319 
320 
321 }