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 #include <stdio.h>
 25 #include <stdlib.h>
 26 #include <string.h>
 27 #include <jvmti.h>
 28 #include "jvmti_common.h"
 29 
 30 
 31 extern "C" {
 32 
 33 
 34 #define PASSED 0
 35 #define STATUS_FAILED 2
 36 #define WAIT_TIME 1000
 37 
 38 static jvmtiEnv *jvmti = NULL;
 39 static jvmtiEventCallbacks callbacks;
 40 static jint result = PASSED;
 41 static jrawMonitorID wait_lock;
 42 static const char *threadName = NULL;
 43 static int startsCount = 0;
 44 static int startsExpected = 0;
 45 static int endsCount = 0;
 46 static int endsExpected = 0;
 47 
 48 void JNICALL ThreadStart(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) {
 49   jvmtiError err;
 50   jvmtiThreadInfo inf;
 51 
 52   err = jvmti->GetThreadInfo(thread, &inf);
 53   if (err != JVMTI_ERROR_NONE) {
 54     LOG("(GetThreadInfo, start) unexpected error: %s (%d)\n",
 55            TranslateError(err), err);
 56     result = STATUS_FAILED;
 57   }
 58 
 59   LOG(">>> start: %s\n", inf.name);
 60 
 61   if (inf.name != NULL && strcmp(inf.name, threadName) == 0) {
 62     startsCount++;
 63   }
 64 }
 65 
 66 void JNICALL ThreadEnd(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) {
 67   jvmtiError err;
 68   jvmtiThreadInfo inf;
 69 
 70   err = jvmti->GetThreadInfo(thread, &inf);
 71   if (err != JVMTI_ERROR_NONE) {
 72     LOG("(GetThreadInfo, end) unexpected error: %s (%d)\n",
 73            TranslateError(err), err);
 74     result = STATUS_FAILED;
 75   }
 76 
 77   LOG(">>> end: %s\n", inf.name);
 78 
 79   if (inf.name != NULL && strcmp(inf.name, threadName) == 0) {
 80     endsCount++;
 81   }
 82 }
 83 
 84 #ifdef STATIC_BUILD
 85 JNIEXPORT jint JNICALL Agent_OnLoad_threadstart03(JavaVM *jvm, char *options, void *reserved) {
 86     return Agent_Initialize(jvm, options, reserved);
 87 }
 88 JNIEXPORT jint JNICALL Agent_OnAttach_threadstart03(JavaVM *jvm, char *options, void *reserved) {
 89     return Agent_Initialize(jvm, options, reserved);
 90 }
 91 JNIEXPORT jint JNI_OnLoad_threadstart03(JavaVM *jvm, char *options, void *reserved) {
 92     return JNI_VERSION_1_8;
 93 }
 94 #endif
 95 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 96   jvmtiError err;
 97   jint res;
 98 
 99   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
100   if (res != JNI_OK || jvmti == NULL) {
101     LOG("Wrong result of a valid call to GetEnv!\n");
102     return JNI_ERR;
103   }
104 
105   callbacks.ThreadStart = &ThreadStart;
106   callbacks.ThreadEnd = &ThreadEnd;
107   err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
108   if (err != JVMTI_ERROR_NONE) {
109     LOG("(SetEventCallbacks) unexpected error: %s (%d)\n",
110            TranslateError(err), err);
111     return JNI_ERR;
112   }
113 
114   return JNI_OK;
115 }
116 
117 static void JNICALL
118 threadProc(jvmtiEnv* jvmti, JNIEnv* jni, void *unused) {
119   RawMonitorLocker wait_locker(jvmti, jni, wait_lock);
120   wait_locker.notify();
121 }
122 
123 JNIEXPORT jint JNICALL
124 Java_threadstart03_check(JNIEnv *jni, jclass cls, jthread thr, jstring name) {
125   jvmtiError err;
126 
127   if (jvmti == NULL) {
128     LOG("JVMTI client was not properly loaded!\n");
129     return STATUS_FAILED;
130   }
131 
132   threadName = jni->GetStringUTFChars(name, NULL);
133   if (threadName == NULL) {
134     LOG("Failed to copy UTF-8 string!\n");
135     return STATUS_FAILED;
136   }
137 
138   wait_lock = create_raw_monitor(jvmti, "_wait_lock");
139 
140   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
141                                         JVMTI_EVENT_THREAD_START, NULL);
142   if (err == JVMTI_ERROR_NONE) {
143     startsExpected = 1;
144   } else {
145     LOG("Failed to enable JVMTI_EVENT_THREAD_START: %s (%d)\n",
146            TranslateError(err), err);
147     result = STATUS_FAILED;
148   }
149 
150   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
151                                         JVMTI_EVENT_THREAD_END, NULL);
152   if (err == JVMTI_ERROR_NONE) {
153     endsExpected = 1;
154   } else {
155     LOG("Failed to enable JVMTI_EVENT_THREAD_END: %s (%d)\n",
156            TranslateError(err), err);
157     result = STATUS_FAILED;
158   }
159 
160   LOG(">>> starting agent thread ...\n");
161 
162   {
163     RawMonitorLocker wait_locker(jvmti, jni, wait_lock);
164     err = jvmti->RunAgentThread(thr, threadProc,
165                                 NULL, JVMTI_THREAD_MAX_PRIORITY);
166     if (err != JVMTI_ERROR_NONE) {
167       LOG("(RunAgentThread) unexpected error: %s (%d)\n",
168              TranslateError(err), err);
169       result = STATUS_FAILED;
170     }
171     wait_locker.wait();
172   }
173 
174   {
175     RawMonitorLocker wait_locker(jvmti, jni, wait_lock);
176     // Wait for up to 3 seconds for the thread end event
177 
178     for (int i = 0; i < 3; i++) {
179       wait_locker.wait( (jlong) WAIT_TIME);
180       if (endsCount == endsExpected || err != JVMTI_ERROR_NONE) {
181         break;
182       }
183     }
184 
185   }
186 
187   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE,
188                                         JVMTI_EVENT_THREAD_START, NULL);
189   if (err != JVMTI_ERROR_NONE) {
190     LOG("Failed to disable JVMTI_EVENT_THREAD_START: %s (%d)\n",
191            TranslateError(err), err);
192     result = STATUS_FAILED;
193   }
194 
195   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE,
196                                         JVMTI_EVENT_THREAD_END, NULL);
197   if (err != JVMTI_ERROR_NONE) {
198     LOG("Failed to disable JVMTI_EVENT_THREAD_END: %s (%d)\n",
199            TranslateError(err), err);
200     result = STATUS_FAILED;
201   }
202 
203   if (startsCount != startsExpected) {
204     LOG("Wrong number of thread start events: %d, expected: %d\n",
205            startsCount, startsExpected);
206     result = STATUS_FAILED;
207   }
208 
209   if (endsCount != endsExpected) {
210     LOG("Wrong number of thread end events: %d, expected: %d\n",
211            endsCount, endsExpected);
212     result = STATUS_FAILED;
213   }
214 
215   return result;
216 }
217 
218 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
219   return Agent_Initialize(jvm, options, reserved);
220 }
221 
222 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
223   return Agent_Initialize(jvm, options, reserved);
224 }
225 
226 }