1 /*
  2  * Copyright (c) 2020, 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 <atomic>
 26 
 27 #include "jvmti.h"
 28 #include "jvmti_common.h"
 29 
 30 extern "C" {
 31 
 32 static jvmtiEnv *jvmti = NULL;
 33 
 34 static std::atomic<bool> is_completed_test_in_event;
 35 
 36 static void
 37 check_jvmti_error_invalid_thread(JNIEnv* jni, const char* msg, jvmtiError err) {
 38   if (err != JVMTI_ERROR_INVALID_THREAD) {
 39     LOG("%s failed: expected JVMTI_ERROR_INVALID_THREAD instead of: %d\n", msg, err);
 40     fatal(jni, msg);
 41   }
 42 }
 43 
 44 static void JNICALL
 45 agent_proc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) {
 46   fatal(jni, "agent function was not expected to be called");
 47 }
 48 
 49 JNIEXPORT jboolean JNICALL
 50 Java_VThreadUnsupportedTest_isCompletedTestInEvent(JNIEnv *env, jobject obj) {
 51   return is_completed_test_in_event.load();
 52 }
 53 
 54 /*
 55  * Execute JVMTI functions which currently don't support vthreads and check that
 56  * they return error code JVMTI_ERROR_INVALID_THREAD correctly.
 57  */
 58 static void
 59 test_unsupported_jvmti_functions(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread) {
 60   jvmtiError err;
 61   jboolean is_vthread;
 62   jvmtiCapabilities caps;
 63   void* local_storage_data = NULL;
 64   jlong nanos;
 65 
 66   LOG("test_unsupported_jvmti_functions: started\n");
 67 
 68   is_vthread = jni->IsVirtualThread(vthread);
 69   if (is_vthread != JNI_TRUE) {
 70     fatal(jni, "IsVirtualThread failed to return JNI_TRUE");
 71   }
 72 
 73   err = jvmti->GetCapabilities(&caps);
 74   check_jvmti_status(jni, err, "GetCapabilities");
 75 
 76   if (caps.can_support_virtual_threads != JNI_TRUE) {
 77     fatal(jni, "Virtual threads are not supported");
 78   }
 79 
 80   LOG("Testing JVMTI functions which should not accept a virtual thread argument\n");
 81 
 82   LOG("Testing StopThread\n");
 83   err = jvmti->StopThread(vthread, vthread);
 84   check_jvmti_error_invalid_thread(jni, "StopThread", err);
 85 
 86   LOG("Testing PopFrame\n");
 87   err = jvmti->PopFrame(vthread);
 88   check_jvmti_error_invalid_thread(jni, "PopFrame", err);
 89 
 90   LOG("Testing ForceEarlyReturnVoid\n");
 91   err = jvmti->ForceEarlyReturnVoid(vthread);
 92   check_jvmti_error_invalid_thread(jni, "ForceEarlyReturnVoid", err);
 93 
 94   LOG("Testing GetThreadCpuTime\n");
 95   err = jvmti->GetThreadCpuTime(vthread, &nanos);
 96   check_jvmti_error_invalid_thread(jni, "GetThreadCpuTime", err);
 97 
 98   jthread cur_thread = get_current_thread(jvmti, jni);
 99   if (jni->IsVirtualThread(cur_thread)) {
100     LOG("Testing GetCurrentThreadCpuTime\n");
101     err = jvmti->GetCurrentThreadCpuTime(&nanos);
102     check_jvmti_error_invalid_thread(jni, "GetCurrentThreadCpuTime", err);
103   }
104 
105   err = jvmti->RunAgentThread(vthread, agent_proc, (const void*)NULL, JVMTI_THREAD_NORM_PRIORITY);
106   check_jvmti_error_invalid_thread(jni, "RunAgentThread", err);
107 
108   LOG("test_unsupported_jvmti_functions: finished\n");
109 }
110 
111 JNIEXPORT jboolean JNICALL
112 Java_VThreadUnsupportedTest_testJvmtiFunctionsInJNICall(JNIEnv *jni, jobject obj, jthread vthread) {
113   jvmtiError err = JVMTI_ERROR_NONE;
114 
115   LOG("testJvmtiFunctionsInJNICall: started\n");
116 
117   test_unsupported_jvmti_functions(jvmti, jni, vthread);
118 
119   LOG("testJvmtiFunctionsInJNICall: finished\n");
120 
121   return JNI_TRUE;
122 }
123 
124 // Parameters: (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread)
125 static void JNICALL
126 VirtualThreadMount(jvmtiEnv *jvmti, ...) {
127   va_list ap;
128   JNIEnv* jni = NULL;
129   jthread thread = NULL;
130 
131   va_start(ap, jvmti);
132   jni = va_arg(ap, JNIEnv*);
133   thread = va_arg(ap, jthread);
134   va_end(ap);
135 
136   LOG("Got VirtualThreadMount event\n");
137   fflush(stdout);
138   test_unsupported_jvmti_functions(jvmti, jni, thread);
139 
140   jlong nanos;
141   jvmtiError err = jvmti->GetCurrentThreadCpuTime(&nanos);
142   check_jvmti_error_invalid_thread(jni, "GetCurrentThreadCpuTime", err);
143 
144   is_completed_test_in_event.store(true);
145 }
146 
147 extern JNIEXPORT jint JNICALL
148 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
149   jvmtiCapabilities caps;
150   jvmtiError err;
151 
152   LOG("Agent_OnLoad started\n");
153   if (jvm->GetEnv((void **)(&jvmti), JVMTI_VERSION) != JNI_OK) {
154     return JNI_ERR;
155   }
156 
157   is_completed_test_in_event.store(false);
158 
159   err = set_ext_event_callback(jvmti, "VirtualThreadMount", VirtualThreadMount);
160   if (err != JVMTI_ERROR_NONE) {
161     LOG("Agent_OnLoad: Error in JVMTI SetExtEventCallback for VirtualThreadMount: %s(%d)\n",
162            TranslateError(err), err);
163     return JNI_ERR;
164   }
165  
166   memset(&caps, 0, sizeof (caps));
167   caps.can_suspend = 1;
168   caps.can_pop_frame = 1;
169   caps.can_force_early_return = 1;
170   caps.can_signal_thread = 1;
171   caps.can_support_virtual_threads = 1;
172   caps.can_access_local_variables = 1;
173   caps.can_get_thread_cpu_time = 1;
174   caps.can_get_current_thread_cpu_time = 1;
175 
176   err = jvmti->AddCapabilities(&caps);
177   if (err != JVMTI_ERROR_NONE) {
178     LOG("error in JVMTI AddCapabilities: %d\n", err);
179     return JNI_ERR;
180   }
181 
182   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, EXT_EVENT_VIRTUAL_THREAD_MOUNT, NULL);
183   if (err != JVMTI_ERROR_NONE) {
184     LOG("error in JVMTI SetEventNotificationMode: %d\n", err);
185     return JNI_ERR;
186   }
187 
188   LOG("Agent_OnLoad finished\n");
189   return JNI_OK;
190 }
191 
192 } // extern "C"