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