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