1 /*
  2  * Copyright (c) 2003, 2024, 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 "jvmti_common.hpp"
 28 #include "jvmti_thread.hpp"
 29 
 30 extern "C" {
 31 
 32 /* ========================================================================== */
 33 
 34 /* scaffold objects */
 35 static jvmtiEnv *jvmti = nullptr;
 36 static jlong timeout = 0;
 37 
 38 /* test objects */
 39 static jthread expected_thread = nullptr;
 40 static jobject expected_object = nullptr;
 41 static volatile int eventsCount = 0;
 42 
 43 
 44 /* Check GetPotentialCapabilities function
 45  */
 46 void JNICALL
 47 MonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr, jobject obj) {
 48 
 49   LOG("MonitorContendedEntered event:\n\tthread: %p, object: %p, expected object: %p\n",thr, obj, expected_object);
 50 
 51   print_thread_info(jvmti, jni, thr);
 52 
 53   if (expected_thread == nullptr) {
 54     jni->FatalError("expected_thread is null.");
 55   }
 56 
 57   if (expected_object == nullptr) {
 58     jni->FatalError("expected_object is null.");
 59   }
 60 
 61   /* check if event is for tested thread and for tested object */
 62   if (jni->IsSameObject(expected_thread, thr) &&
 63       jni->IsSameObject(expected_object, obj)) {
 64     eventsCount++;
 65     LOG("Increasing eventCount to %d\n", eventsCount);
 66   }
 67 }
 68 
 69 void JNICALL
 70 MonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr, jobject obj) {
 71 
 72   LOG("MonitorContendedEnter event:\n\tthread: %p, object: %p, expected object: %p\n",thr, obj, expected_object);
 73   print_thread_info(jvmti, jni, thr);
 74 
 75   if (expected_thread == nullptr) {
 76     jni->FatalError("expected_thread is null.");
 77   }
 78 
 79   if (expected_object == nullptr) {
 80     jni->FatalError("expected_object is null.");
 81   }
 82 
 83   /* check if event is for tested thread and for tested object */
 84   if (jni->IsSameObject(expected_thread, thr) &&
 85       jni->IsSameObject(expected_object, obj)) {
 86     eventsCount++;
 87     LOG("Increasing eventCount to %d\n", eventsCount);
 88   }
 89 }
 90 
 91 /* ========================================================================== */
 92 
 93 static int prepare() {
 94   jvmtiError err;
 95 
 96   LOG("Prepare: find tested thread\n");
 97 
 98   /* enable MonitorContendedEntered event */
 99   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, nullptr);
100   if (err != JVMTI_ERROR_NONE) {
101     LOG("Prepare: 11\n");
102     return JNI_FALSE;
103   }
104 
105   /* enable MonitorContendedEnter event */
106   err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, nullptr);
107   if (err != JVMTI_ERROR_NONE) {
108     LOG("Prepare: 11\n");
109     return JNI_FALSE;
110   }
111 
112   return JNI_TRUE;
113 }
114 
115 static int clean() {
116   jvmtiError err;
117   /* disable MonitorContendedEntered event */
118   err = jvmti->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,nullptr);
119   if (err != JVMTI_ERROR_NONE) {
120     set_agent_fail_status();
121   }
122   return JNI_TRUE;
123 }
124 
125 /* agent algorithm
126  */
127 static void JNICALL
128 agentProc(jvmtiEnv *jvmti, JNIEnv *agentJNI, void *arg) {
129 
130 /* wait for initial sync */
131   if (!agent_wait_for_sync(timeout))
132     return;
133 
134   if (!prepare()) {
135     set_agent_fail_status();
136     return;
137   }
138 
139   /* clear events count */
140   eventsCount = 0;
141 
142   /* resume debugee to catch MonitorContendedEntered event */
143   if (!((agent_resume_sync() == JNI_TRUE) && (agent_wait_for_sync(timeout) == JNI_TRUE))) {
144     return;
145   }
146 
147   LOG("Number of MonitorContendedEntered events: %d\n", eventsCount);
148 
149   if (eventsCount == 0) {
150     COMPLAIN("No any MonitorContendedEntered event\n");
151     set_agent_fail_status();
152   }
153 
154   if (!clean()) {
155     set_agent_fail_status();
156     return;
157   }
158 
159 /* resume debugee after last sync */
160   if (!agent_resume_sync())
161     return;
162 }
163 
164 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
165   jvmtiCapabilities caps;
166   jvmtiEventCallbacks callbacks;
167   jvmtiError err;
168   jint res;
169 
170   timeout = 60000;
171   LOG("Timeout: %d msc\n", (int) timeout);
172 
173   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
174   if (res != JNI_OK || jvmti == nullptr) {
175     LOG("Wrong result of a valid call to GetEnv!\n");
176     return JNI_ERR;
177   }
178 
179   err = init_agent_data(jvmti, &agent_data);
180   if (err != JVMTI_ERROR_NONE) {
181     return JNI_ERR;
182   }
183 
184   memset(&caps, 0, sizeof(jvmtiCapabilities));
185   caps.can_generate_monitor_events = 1;
186   caps.can_support_virtual_threads = 1;
187 
188   err = jvmti->AddCapabilities(&caps);
189   if (err != JVMTI_ERROR_NONE) {
190     LOG("(AddCapabilities) unexpected error: %s (%d)\n",
191            TranslateError(err), err);
192     return JNI_ERR;
193   }
194 
195   err = jvmti->GetCapabilities(&caps);
196   if (err != JVMTI_ERROR_NONE) {
197     LOG("(GetCapabilities) unexpected error: %s (%d)\n", TranslateError(err), err);
198     return JNI_ERR;
199   }
200 
201   if (!caps.can_generate_monitor_events) {
202     return JNI_ERR;
203   }
204 
205   memset(&callbacks, 0, sizeof(callbacks));
206   callbacks.MonitorContendedEntered = &MonitorContendedEntered;
207   callbacks.MonitorContendedEnter = &MonitorContendedEnter;
208 
209   err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
210   if (err != JVMTI_ERROR_NONE) {
211     return JNI_ERR;
212   }
213 
214   /* register agent proc and arg */
215   set_agent_proc(agentProc, nullptr);
216 
217   return JNI_OK;
218 }
219 
220 JNIEXPORT jint JNICALL Java_mcontentered01_getEventCount(JNIEnv *jni, jobject obj) {
221   return eventsCount;
222 }
223 
224 JNIEXPORT void JNICALL Java_mcontentered01_setExpected(JNIEnv *jni, jobject clz, jobject obj, jobject thread) {
225   LOG("Remembering global reference for monitor object is %p\n", obj);
226   /* make object accessible for a long time */
227   expected_object = jni->NewGlobalRef(obj);
228   if (expected_object == nullptr) {
229     jni->FatalError("Error saving global reference to monitor.\n");
230   }
231 
232   /* make thread accessable for a long time */
233   expected_thread = jni->NewGlobalRef(thread);
234   if (thread == nullptr) {
235     jni->FatalError("Error saving global reference to thread.\n");
236   }
237 
238   return;
239 }
240 
241 
242 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
243   return Agent_Initialize(jvm, options, reserved);
244 }
245 
246 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
247   return Agent_Initialize(jvm, options, reserved);
248 }
249 }