1 /*
  2  * Copyright (c) 2021, 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 <string.h>
 25 #include "jvmti.h"
 26 #include "jvmti_common.h"
 27 
 28 jrawMonitorID monitor;
 29 jrawMonitorID monitor_completed;
 30 
 31 jvmtiEnv *jvmti_env;
 32 
 33 
 34 static void
 35 set_breakpoint(JNIEnv *jni, jclass klass, const char *mname) {
 36   jlocation location = (jlocation)0L;
 37   jmethodID method = find_method(jvmti_env, jni, klass, mname);
 38   jvmtiError err;
 39 
 40   if (method == NULL) {
 41     jni->FatalError("Error in set_breakpoint: not found method");
 42   }
 43   err = jvmti_env->SetBreakpoint(method, location);
 44   check_jvmti_status(jni, err, "set_or_clear_breakpoint: error in JVMTI SetBreakpoint");
 45 
 46 
 47 }
 48 
 49 extern "C" {
 50 
 51 JNIEXPORT void JNICALL
 52 Java_WaitNotifySuspendedVThreadTask_setBreakpoint(JNIEnv *jni, jclass klass) {
 53   jvmtiError err;
 54 
 55   LOG("setBreakpoint: started\n");
 56   set_breakpoint(jni, klass, "methBreakpoint");
 57 
 58   // Enable Breakpoint events globally
 59   err = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_BREAKPOINT, NULL);
 60   check_jvmti_status(jni, err, "enableEvents: error in JVMTI SetEventNotificationMode: enable BREAKPOINT");
 61 
 62   LOG("setBreakpoint: finished\n");
 63 
 64 }
 65 
 66 JNIEXPORT void JNICALL
 67 Java_WaitNotifySuspendedVThreadTask_notifyRawMonitors(JNIEnv *jni, jclass klass, jthread thread) {
 68   LOG("Main thread: suspending virtual and carrier threads\n");
 69 
 70   check_jvmti_status(jni, jvmti_env->SuspendThread(thread), "SuspendThread thread");
 71   jthread cthread = get_carrier_thread(jvmti_env, jni, thread);
 72   check_jvmti_status(jni, jvmti_env->SuspendThread(cthread), "SuspendThread thread");
 73 
 74   {
 75     RawMonitorLocker rml(jvmti_env, jni, monitor);
 76 
 77     LOG("Main thread: calling monitor.notifyAll()\n");
 78     rml.notify_all();
 79   }
 80 
 81   RawMonitorLocker completed(jvmti_env, jni, monitor_completed);
 82 
 83   LOG("Main thread: resuming virtual thread\n");
 84   check_jvmti_status(jni, jvmti_env->ResumeThread(thread), "ResumeThread thread");
 85 
 86   LOG("Main thread: before monitor_completed.wait()\n");
 87   completed.wait();
 88   LOG("Main thread: after monitor_completed.wait()\n");
 89 
 90   LOG("Main thread: resuming carrier thread\n");
 91   check_jvmti_status(jni, jvmti_env->ResumeThread(cthread), "ResumeThread cthread");
 92 }
 93 
 94 } // extern "C"
 95 
 96 static void JNICALL
 97 Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
 98            jmethodID method, jlocation location) {
 99   char* mname = get_method_name(jvmti, jni, method);
100 
101   if (strcmp(mname, "methBreakpoint") != 0) {
102     LOG("FAILED: got  unexpected breakpoint in method %s()\n", mname);
103     deallocate(jvmti, jni, (void*)mname);
104     fatal(jni, "Error in breakpoint");
105     return;
106   }
107   char* tname = get_thread_name(jvmti, jni, thread);
108   const char* virt = jni->IsVirtualThread(thread) ? "virtual" : "carrier";
109 
110   {
111     RawMonitorLocker rml(jvmti, jni, monitor);
112 
113     LOG("Breakpoint: before monitor.wait(): %s in %s thread\n", mname, virt);
114     rml.wait();
115     LOG("Breakpoint: after monitor.wait(): %s in %s thread\n", mname, virt);
116   }
117 
118   RawMonitorLocker completed(jvmti, jni, monitor_completed);
119 
120   LOG("Breakpoint: calling monitor_completed.notifyAll()\n");
121   completed.notify_all();
122 
123   deallocate(jvmti, jni, (void*)tname);
124   deallocate(jvmti, jni, (void*)mname);
125 }
126 
127 /* ============================================================================= */
128 
129 jint
130 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
131   jvmtiEnv * jvmti = NULL;
132 
133   jvmtiEventCallbacks callbacks;
134   jvmtiCapabilities caps;
135   jvmtiError err;
136   jint res;
137 
138   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
139   if (res != JNI_OK || jvmti == NULL) {
140     LOG("Wrong result of a valid call to GetEnv!\n");
141     return JNI_ERR;
142   }
143 
144   jvmti_env = jvmti;
145   monitor = create_raw_monitor(jvmti, "Monitor");
146   monitor_completed = create_raw_monitor(jvmti, "Monitor Completed");
147 
148   /* add capability to generate compiled method events */
149   memset(&caps, 0, sizeof(jvmtiCapabilities));
150   caps.can_support_virtual_threads = 1;
151   caps.can_generate_breakpoint_events = 1;
152   caps.can_suspend = 1;
153 
154   err = jvmti->AddCapabilities(&caps);
155   if (err != JVMTI_ERROR_NONE) {
156     LOG("(AddCapabilities) unexpected error: %s (%d)\n",
157            TranslateError(err), err);
158     return JNI_ERR;
159   }
160 
161   err = jvmti->GetCapabilities(&caps);
162   if (err != JVMTI_ERROR_NONE) {
163     LOG("(GetCapabilities) unexpected error: %s (%d)\n",
164            TranslateError(err), err);
165     return JNI_ERR;
166   }
167 
168   /* set event callback */
169   LOG("setting event callbacks ...\n");
170   (void) memset(&callbacks, 0, sizeof(callbacks));
171   callbacks.Breakpoint = &Breakpoint;
172 
173   err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
174   if (err != JVMTI_ERROR_NONE) {
175     LOG("(SetEventCallbacks) unexpected error: %s (%d)\n",
176            TranslateError(err), err);
177     return JNI_ERR;
178   }
179 
180   return JNI_OK;
181 }