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