1 /*
  2  * Copyright (c) 2017, 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 "jvmti.h"
 27 #include "jni.h"
 28 
 29 #ifdef __cplusplus
 30 extern "C" {
 31 #endif
 32 
 33 #ifndef JNI_ENV_ARG
 34 
 35 #ifdef __cplusplus
 36 #define JNI_ENV_ARG(x, y) y
 37 #define JNI_ENV_PTR(x) x
 38 #else
 39 #define JNI_ENV_ARG(x,y) x, y
 40 #define JNI_ENV_PTR(x) (*x)
 41 #endif
 42 
 43 #endif
 44 
 45 #define PASSED 0
 46 #define FAILED 2
 47 
 48 #define TEST_CLASS "GetOwnedMonitorInfoTest"
 49 
 50 static volatile jboolean event_has_posted = JNI_FALSE;
 51 static volatile jint status = PASSED;
 52 static volatile jclass testClass = NULL;
 53 
 54 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
 55 
 56 static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {
 57     char *errMsg;
 58     jvmtiError result;
 59 
 60     result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg);
 61     if (result == JVMTI_ERROR_NONE) {
 62         fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode);
 63         (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg);
 64     } else {
 65         fprintf(stderr, "%s (%d)\n", message, errCode);
 66     }
 67 }
 68 
 69 static jboolean CheckLockObject(JNIEnv *env, jobject monitor) {
 70     if (testClass == NULL) {
 71         // JNI_OnLoad has not been called yet, so can't possibly be an instance of TEST_CLASS.
 72         return JNI_FALSE;
 73     }
 74     return (*env)->IsInstanceOf(env, monitor, testClass);
 75 }
 76 
 77 JNIEXPORT void JNICALL
 78 MonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jobject monitor) {
 79     jvmtiError err;
 80     jvmtiThreadInfo threadInfo;
 81     jint monitorCount;
 82     jobject *ownedMonitors;
 83 
 84     if (CheckLockObject(env, monitor) == JNI_FALSE) {
 85         return;
 86     }
 87 
 88     err = (*jvmti)->GetThreadInfo(jvmti, thread, &threadInfo);
 89     if (err != JVMTI_ERROR_NONE) {
 90         ShowErrorMessage(jvmti, err,
 91                          "MonitorContendedEnter: error in JVMTI GetThreadInfo");
 92         status = FAILED;
 93         event_has_posted = JNI_TRUE;
 94         return;
 95     }
 96     err = (*jvmti)->GetOwnedMonitorInfo(jvmti, thread, &monitorCount, &ownedMonitors);
 97     if (err != JVMTI_ERROR_NONE) {
 98         ShowErrorMessage(jvmti, err,
 99                          "MonitorContendedEnter: error in JVMTI GetOwnedMonitorInfo");
100         status = FAILED;
101         event_has_posted = JNI_TRUE;
102         return;
103     }
104 
105     printf("MonitorContendedEnter: %s owns %d monitor(s)\n",
106            threadInfo.name, monitorCount);
107 
108     (*jvmti)->Deallocate(jvmti, (unsigned char *)ownedMonitors);
109     (*jvmti)->Deallocate(jvmti, (unsigned char *)threadInfo.name);
110 
111     if (monitorCount != 0) {
112         fprintf(stderr, "MonitorContendedEnter: FAIL: monitorCount should be zero.\n");
113         status = FAILED;
114     }
115 
116     event_has_posted = JNI_TRUE;
117 }
118 
119 JNIEXPORT void JNICALL
120 MonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jobject monitor) {
121     jvmtiError err;
122     jvmtiThreadInfo threadInfo;
123     jint monitorCount;
124     jobject *ownedMonitors;
125 
126     if (CheckLockObject(env, monitor) == JNI_FALSE) {
127         return;
128     }
129 
130     err = (*jvmti)->GetThreadInfo(jvmti, thread, &threadInfo);
131     if (err != JVMTI_ERROR_NONE) {
132         ShowErrorMessage(jvmti, err,
133                          "MonitorContendedEntered: error in JVMTI GetThreadInfo");
134         status = FAILED;
135         return;
136     }
137     err = (*jvmti)->GetOwnedMonitorInfo(jvmti, thread, &monitorCount, &ownedMonitors);
138     if (err != JVMTI_ERROR_NONE) {
139         ShowErrorMessage(jvmti, err,
140                          "MonitorContendedEntered: error in JVMTI GetOwnedMonitorInfo");
141         status = FAILED;
142         return;
143     }
144 
145     printf("MonitorContendedEntered: %s owns %d monitor(s)\n",
146            threadInfo.name, monitorCount);
147 
148     (*jvmti)->Deallocate(jvmti, (unsigned char *)ownedMonitors);
149     (*jvmti)->Deallocate(jvmti, (unsigned char *)threadInfo.name);
150 
151     if (monitorCount != 1) {
152         fprintf(stderr, "MonitorContendedEnter: FAIL: monitorCount should be one.\n");
153         status = FAILED;
154     }
155 }
156 
157 JNIEXPORT jint JNICALL
158 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
159     return Agent_Initialize(jvm, options, reserved);
160 }
161 
162 JNIEXPORT jint JNICALL
163 Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
164     return Agent_Initialize(jvm, options, reserved);
165 }
166 
167 JNIEXPORT jint JNICALL
168 JNI_OnLoad(JavaVM *jvm, void *reserved) {
169     jint res;
170     JNIEnv *env;
171 
172     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env),
173                                    JNI_VERSION_9);
174     if (res != JNI_OK || env == NULL) {
175         fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);
176         return JNI_ERR;
177     }
178 
179     testClass = (*env)->FindClass(env, TEST_CLASS);
180     if (testClass != NULL) {
181       testClass = (*env)->NewGlobalRef(env, testClass);
182     }
183     if (testClass == NULL) {
184         fprintf(stderr, "Error: Could not load class %s!\n", TEST_CLASS);
185         return JNI_ERR;
186     }
187 
188     return JNI_VERSION_9;
189 }
190 
191 static
192 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
193     jint res;
194     jvmtiError err;
195     jvmtiEnv *jvmti;
196     jvmtiCapabilities caps;
197     jvmtiEventCallbacks callbacks;
198 
199     printf("Agent_OnLoad started\n");
200 
201     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
202                                    JVMTI_VERSION_9);
203     if (res != JNI_OK || jvmti == NULL) {
204         fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
205         return JNI_ERR;
206     }
207 
208     err = (*jvmti)->GetPotentialCapabilities(jvmti, &caps);
209     if (err != JVMTI_ERROR_NONE) {
210         ShowErrorMessage(jvmti, err,
211                          "Agent_OnLoad: error in JVMTI GetPotentialCapabilities");
212         return JNI_ERR;
213     }
214 
215     err = (*jvmti)->AddCapabilities(jvmti, &caps);
216     if (err != JVMTI_ERROR_NONE) {
217         ShowErrorMessage(jvmti, err,
218                          "Agent_OnLoad: error in JVMTI AddCapabilities");
219         return JNI_ERR;
220     }
221 
222     err = (*jvmti)->GetCapabilities(jvmti, &caps);
223     if (err != JVMTI_ERROR_NONE) {
224         ShowErrorMessage(jvmti, err,
225                          "Agent_OnLoad: error in JVMTI GetCapabilities");
226         return JNI_ERR;
227     }
228 
229     if (!caps.can_generate_monitor_events) {
230         fprintf(stderr, "Warning: Monitor events are not implemented\n");
231         return JNI_ERR;
232     }
233     if (!caps.can_get_owned_monitor_info) {
234         fprintf(stderr, "Warning: GetOwnedMonitorInfo is not implemented\n");
235         return JNI_ERR;
236     }
237 
238     memset(&callbacks, 0, sizeof(callbacks));
239     callbacks.MonitorContendedEnter   = &MonitorContendedEnter;
240     callbacks.MonitorContendedEntered = &MonitorContendedEntered;
241 
242     err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(jvmtiEventCallbacks));
243     if (err != JVMTI_ERROR_NONE) {
244         ShowErrorMessage(jvmti, err,
245                          "Agent_OnLoad: error in JVMTI SetEventCallbacks");
246         return JNI_ERR;
247     }
248 
249     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
250                                              JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
251     if (err != JVMTI_ERROR_NONE) {
252         ShowErrorMessage(jvmti, err,
253                          "Agent_OnLoad: error in JVMTI SetEventNotificationMode #1");
254         return JNI_ERR;
255     }
256     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
257                                              JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
258     if (err != JVMTI_ERROR_NONE) {
259         ShowErrorMessage(jvmti, err,
260                          "Agent_OnLoad: error in JVMTI SetEventNotificationMode #2");
261         return JNI_ERR;
262     }
263     printf("Agent_OnLoad finished\n");
264     return JNI_OK;
265 }
266 
267 JNIEXPORT jint JNICALL
268 Java_GetOwnedMonitorInfoTest_check(JNIEnv *env, jclass cls) {
269     return status;
270 }
271 
272 JNIEXPORT jboolean JNICALL
273 Java_GetOwnedMonitorInfoTest_hasEventPosted(JNIEnv *env, jclass cls) {
274     return event_has_posted;
275 }
276 
277 #ifdef __cplusplus
278 }
279 #endif