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 #ifndef JVMTI_THREAD_H
 25 #define JVMTI_THREAD_H
 26 
 27 #include <stdio.h>
 28 #include <stdlib.h>
 29 #include <stdarg.h>
 30 #include <string.h>
 31 #include <ctype.h>
 32 
 33 
 34 
 35 #ifdef _WIN32
 36 
 37 #define LL "I64"
 38 #include <STDDEF.H>
 39 
 40 #else // !_WIN32
 41 
 42 #include <stdint.h>
 43 
 44 #ifdef _LP64
 45 #define LL "l"
 46 #else
 47 #define LL "ll"
 48 #endif
 49 
 50 #endif // !_WIN32
 51 
 52 
 53 extern "C" {
 54 
 55 
 56 #define NSK_STATUS_PASSED       0
 57 #define NSK_STATUS_FAILED       2
 58 
 59 static jvmtiEnv* agent_jvmti_env = NULL;
 60 static JNIEnv* agent_jni_env = NULL;
 61 
 62 static volatile int currentAgentStatus = NSK_STATUS_PASSED;
 63 
 64 static jthread agentThread = NULL;
 65 static jvmtiStartFunction agentThreadProc = NULL;
 66 static void* agentThreadArg = NULL;
 67 
 68 void nsk_jvmti_setFailStatus() {
 69   currentAgentStatus = NSK_STATUS_FAILED;
 70 }
 71 
 72 jint nsk_jvmti_getStatus() {
 73   return currentAgentStatus;
 74 }
 75 
 76 typedef enum { NEW, RUNNABLE, WAITING, SUSPENDED, TERMINATED } thread_state_t;
 77 
 78 typedef struct agent_data_t {
 79   volatile thread_state_t thread_state;
 80   int last_debuggee_status;
 81   jrawMonitorID monitor;
 82 } agent_data_t;
 83 
 84 int nsk_jvmti_setAgentProc(jvmtiStartFunction proc, void* arg) {
 85   agentThreadProc = proc;
 86   agentThreadArg = arg;
 87   return NSK_TRUE;
 88 }
 89 
 90 static agent_data_t agent_data;
 91 
 92 
 93 static jvmtiError init_agent_data(jvmtiEnv *jvmti_env, agent_data_t *data) {
 94   data->thread_state = NEW;
 95   data->last_debuggee_status = NSK_STATUS_PASSED;
 96   agent_jvmti_env = jvmti_env;
 97   return jvmti_env->CreateRawMonitor("agent_data_monitor", &data->monitor);
 98 }
 99 
100 void exitOnError(jvmtiError error) {
101   if (error != JVMTI_ERROR_NONE) {
102     exit(error);
103   }
104 }
105 
106 /** Wait for sync point with Java code. */
107 int nsk_jvmti_waitForSync(jlong timeout) {
108   static const int inc_timeout = 1000;
109 
110   jlong t = 0;
111   int result = NSK_TRUE;
112 
113   RawMonitorLocker monitor_locker(agent_jvmti_env, agent_jni_env, agent_data.monitor);
114 
115   agent_data.thread_state = WAITING;
116 
117   /* SP2.2-n - notify agent is waiting and wait */
118   /* SP4.1-n - notify agent is waiting and wait */
119   monitor_locker.notify();
120 
121   while (agent_data.thread_state == WAITING) {
122     /* SP3.2-w - wait to start test */
123     /* SP6.2-w - wait to end test */
124     monitor_locker.wait(inc_timeout);
125 
126     if (timeout == 0) continue;
127 
128     t += inc_timeout;
129 
130     if (t >= timeout) break;
131   }
132 
133   if (agent_data.thread_state == WAITING) {
134       NSK_COMPLAIN1("No status sync occured for timeout: %" LL "d ms\n", timeout);
135     nsk_jvmti_setFailStatus();
136     result = NSK_FALSE;
137   }
138 
139   return result;
140 }
141 
142 /** Resume java code suspended on sync point. */
143 int nsk_jvmti_resumeSync() {
144   int result;
145   RawMonitorLocker monitor_locker(agent_jvmti_env, agent_jni_env, agent_data.monitor);
146 
147   if (agent_data.thread_state == SUSPENDED) {
148     result = NSK_TRUE;
149     agent_data.thread_state = RUNNABLE;
150     /* SP5.2-n - notify suspend done */
151     /* SP7.2-n - notify agent end */
152     monitor_locker.notify();
153   }
154   else {
155     NSK_COMPLAIN0("Debuggee was not suspended on status sync\n");
156     nsk_jvmti_setFailStatus();
157     result = NSK_FALSE;
158   }
159 
160   return NSK_TRUE;
161 }
162 
163 /* ============================================================================= */
164 static void set_agent_thread_state(thread_state_t value) {
165   RawMonitorLocker monitor_locker(agent_jvmti_env, agent_jni_env, agent_data.monitor);
166   agent_data.thread_state = value;
167   monitor_locker.notify();
168 }
169 
170 /** Wrapper for user agent thread. */
171 static void JNICALL
172 agentThreadWrapper(jvmtiEnv* jvmti_env, JNIEnv* agentJNI, void* arg) {
173   agent_jni_env = agentJNI;
174 
175   /* run user agent proc */
176   {
177     set_agent_thread_state(RUNNABLE);
178 
179     // TODO was NSK_TRACE
180     (*agentThreadProc)(jvmti_env, agentJNI, agentThreadArg);
181 
182     set_agent_thread_state(TERMINATED);
183   }
184 
185   /* finalize agent thread */
186   {
187     /* gelete global ref for agent thread */
188     agentJNI->DeleteGlobalRef(agentThread);
189     agentThread = NULL;
190   }
191 }
192 
193 
194 /** Start wrapper for user agent thread. */
195 static jthread startAgentThreadWrapper(JNIEnv *jni_env, jvmtiEnv* jvmti_env) {
196   const jint  THREAD_PRIORITY = JVMTI_THREAD_MAX_PRIORITY;
197   const char* THREAD_NAME = "JVMTI agent thread";
198   const char* THREAD_CLASS_NAME = "java/lang/Thread";
199   const char* THREAD_CTOR_NAME = "<init>";
200   const char* THREAD_CTOR_SIGNATURE = "(Ljava/lang/String;)V";
201 
202   jobject threadName = NULL;
203   jclass threadClass = NULL;
204   jmethodID threadCtor = NULL;
205   jobject threadObject = NULL;
206   jobject threadGlobalRef = NULL;
207   jvmtiError err;
208 
209   threadClass = jni_env->FindClass(THREAD_CLASS_NAME);
210   if (threadClass == NULL) {
211     return NULL;
212   }
213 
214   threadCtor = jni_env->GetMethodID(threadClass, THREAD_CTOR_NAME, THREAD_CTOR_SIGNATURE);
215   if (threadCtor == NULL) {
216     return NULL;
217   }
218 
219   threadName = jni_env->NewStringUTF(THREAD_NAME);
220   if (threadName == NULL) {
221     return NULL;
222   }
223 
224   threadObject = jni_env->NewObject(threadClass, threadCtor, threadName);
225   if (threadObject == NULL) {
226     return NULL;
227   }
228 
229   threadGlobalRef = jni_env->NewGlobalRef(threadObject);
230   if (threadGlobalRef == NULL) {
231     jni_env->DeleteLocalRef(threadObject);
232     return NULL;
233   }
234   agentThread = (jthread)threadGlobalRef;
235 
236   err = jvmti_env->RunAgentThread(agentThread, &agentThreadWrapper, agentThreadArg, THREAD_PRIORITY);
237   if (err != JVMTI_ERROR_NONE) {
238     jni_env->DeleteGlobalRef(threadGlobalRef);
239     jni_env->DeleteLocalRef(threadObject);
240     return NULL;
241   }
242   return agentThread;
243 }
244 
245 /** Run registered user agent thread via wrapper. */
246 static jthread nsk_jvmti_runAgentThread(JNIEnv *jni_env, jvmtiEnv* jvmti_env) {
247   /* start agent thread wrapper */
248   jthread thread = startAgentThreadWrapper(jni_env, jvmti_env);
249   if (thread == NULL) {
250     nsk_jvmti_setFailStatus();
251     return NULL;
252   }
253 
254   return thread;
255 }
256 
257 /** Sync point called from Java code. */
258 static jint syncDebuggeeStatus(JNIEnv* jni_env, jvmtiEnv* jvmti_env, jint debuggeeStatus) {
259   jint result = NSK_STATUS_FAILED;
260 
261   printf("Data %p %p\n", jvmti_env, agent_data.monitor);
262   RawMonitorLocker monitor_locker(agent_jvmti_env, agent_jni_env, agent_data.monitor);
263 
264   /* save last debugee status */
265   agent_data.last_debuggee_status = debuggeeStatus;
266 
267   /* we don't enter if-stmt in second call */
268   if (agent_data.thread_state == NEW) {
269     if (nsk_jvmti_runAgentThread(jni_env, jvmti_env) == NULL) {
270       return result;
271     }
272 
273     /* SP2.2-w - wait for agent thread */
274     while (agent_data.thread_state == NEW) {
275       monitor_locker.wait();
276     }
277   }
278 
279   /* wait for sync permit */
280   /* we don't enter loop in first call */
281   while (agent_data.thread_state != WAITING && agent_data.thread_state != TERMINATED) {
282     /* SP4.2-w - second wait for agent thread */
283    monitor_locker.wait();
284   }
285 
286   if (agent_data.thread_state != TERMINATED) {
287     agent_data.thread_state = SUSPENDED;
288     /* SP3.2-n - notify to start test */
289     /* SP6.2-n - notify to end test */
290     monitor_locker.notify();
291   } else {
292     NSK_COMPLAIN0("Debuggee status sync aborted because agent thread has finished\n");
293     return result;
294   }
295 
296   /* update status from debuggee */
297   if (debuggeeStatus != NSK_STATUS_PASSED) {
298     printf("FAIL: Status is %d\n", debuggeeStatus);
299     nsk_jvmti_setFailStatus();
300   }
301 
302   while (agent_data.thread_state == SUSPENDED) {
303     /* SP5.2-w - wait while testing */
304     /* SP7.2 - wait for agent end */
305     monitor_locker.wait();
306   }
307 
308   agent_data.last_debuggee_status = nsk_jvmti_getStatus();
309   result = agent_data.last_debuggee_status;
310   return result;
311 }
312 
313 /** Native function for Java code to provide sync point. */
314 JNIEXPORT jint JNICALL
315 Java_jdk_test_lib_jvmti_DebugeeClass_checkStatus(JNIEnv* jni_env, jclass cls, jint debuggeeStatus) {
316   jint status;
317   printf("Synchronization point checkStatus(%d) called.\n", debuggeeStatus);
318   status = syncDebuggeeStatus(jni_env, agent_jvmti_env, debuggeeStatus);
319   return status;
320 }
321 
322 
323 /** Native function for Java code to reset agent data. */
324 JNIEXPORT void JNICALL
325 Java_jdk_test_lib_jvmti_DebugeeClass_resetAgentData(JNIEnv* jni, jclass cls) {
326   RawMonitorLocker monitor_locker(agent_jvmti_env, jni, agent_data.monitor);
327   /* wait for agentThreadWrapper() to finish */
328   while (agent_data.thread_state != TERMINATED) {
329     monitor_locker.wait(10);
330   }
331   agent_data.thread_state = NEW;
332   agent_data.last_debuggee_status = NSK_STATUS_PASSED;
333 }
334 
335 
336 
337 }
338 
339 #endif