# HG changeset patch # User rrich # Date 1594383879 -7200 # Fri Jul 10 14:24:39 2020 +0200 # Node ID 8c80c99d7c54c3fc3fa382659f1cba582765fb73 # Parent e2622818f0bd30e736252eba101fe7d2c27f400b 8227745: GetOwnedMonitorStackDepthInfoALot micro benchmark diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/GetOwnedMonitorStackDepthInfoALot.java b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/GetOwnedMonitorStackDepthInfoALot.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/GetOwnedMonitorStackDepthInfoALot.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2020 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class GetOwnedMonitorStackDepthInfoALot { + + private static final int s = 1000; // ms per s + public static final long K = 1024L; + public static final long M = 1024L * K; + public static final long G = 1024L * M; + + // Calls of dontinline_testMethod() during warm-up + public static final long TEST_METHOD_WARMUP_CALLS = 20*K; + // Loop iterations in dontinline_testMethod during warm-up + public static final long TEST_METHOD_WARMUP_LOOP_COUNT = 100*K; + + // Timebox for measurement + public static final long TIMEBOX_MS = 10*s; + // Loop iterations in dontinline_testMethod during timebox calibration + public static final long TIMEBOX_CALIBRATION_LOOP_COUNT = 1*G; + + // Calls of dontinline_testMethod() during measurement + public static final long MEASUREMENT_CALLS = 5; + + // JVMTI agent polls this field. During warm-up it will call GetOwnedMonitorStackDepthInfo every 10ms + public static boolean inWarmUp = true; + + public static void main(String[] args) { + new TestCase00_GlobalEscape().run(); + new TestCase01_ArgEscape().run(); + new TestCase01_ArgEscape_less_OSR_execution().run(); + new TestCase02_NoEscape().run(); + } + + public static class Adder { + private long v1; + private long v2; + + public Adder(long v1, long v2) { + this.v1 = v1; + this.v2 = v2; + } + + public long sum() { + return v1 + v2; + } + } + + public static abstract class TestCaseBase { + public static Object lock; + public static long checksum; + + public long dontinline_sharedTestMethod2(Adder addr) { + return 42L; + } + + public final void run() { + String testCaseName = getClass().getName(); + lock = this; + msg_cr(); + msg("################################ " + testCaseName); + + msg("Warm-Up"); + inWarmUp = true; + long duration = dontinline_call_testmethod_alot(testCaseName, TEST_METHOD_WARMUP_CALLS, TEST_METHOD_WARMUP_LOOP_COUNT, false /*print duration*/); + inWarmUp = false; + msg("Warm-Up End. Duration: " + duration/1000 + "s"); + + msg("Begin Calibration"); + duration = dontinline_call_testmethod_alot(testCaseName, 1, TIMEBOX_CALIBRATION_LOOP_COUNT, false /*print duration*/); + long timeboxLoopCount = (long)((double)TIMEBOX_CALIBRATION_LOOP_COUNT / (double) duration * TIMEBOX_MS); + msg("End Calibration. Duration: " + duration + " ms. " + TIMEBOX_MS/s + " s timebox loop count: " + timeboxLoopCount/M + " M."); + + msg("Begin Measurement"); + duration = dontinline_call_testmethod_alot(testCaseName, MEASUREMENT_CALLS, timeboxLoopCount, true /*print duration*/); + msg("End Measurement. Duration: " + duration/1000 + "s"); + } + + public final long dontinline_call_testmethod_alot(String testCaseName, long calls, long loopCount, boolean doPrint) { + long total_duration = 0; + msg("Calling testmethod " + calls + " times with " + loopCount + " loop iterations per call."); + for (long i=0; i 0) { + Adder adder = new Adder(result, iterations); + currentAdder = adder; // GlobalEscape + result = dontinline_sharedTestMethod2(adder) + + adder.sum(); + } + } + return result; + } + } + + // Worst case for JDK-8227745 as GetOwnedMonitorStackDepthInfo triggers unnecessary + // deoptimizations, because not-escapeing objects are in scope. The jit cannot even optimize, + // because the allocations are ArgEscape. + public static class TestCase01_ArgEscape extends TestCaseBase { + + public long dontinline_testMethod(long iterations) { + long result = 0; + synchronized(lock) { + while(iterations-- > 0) { + Adder adder = new Adder(result, iterations); + result = dontinline_sharedTestMethod2(adder) // ArgEscape + + adder.sum(); + } + } + return result; + } + } + + // Like TestCase01_ArgEscape except that the one extremely long running loop is split into outer + // and inner loop in 2 methods. This has the effect that the time spent executing a method + // compiled for OSR is limited. + public static class TestCase01_ArgEscape_less_OSR_execution extends TestCase01_ArgEscape { + public static final long INNER_ITERATIONS = 100*K; + + public long dontinline_testMethod(long iterations) { + long outerIterations = iterations / INNER_ITERATIONS; + long result = 0; + while(outerIterations-- > 0) { + result = dontinline_testMethod_inner(INNER_ITERATIONS); + } + return result; + } + + public long dontinline_testMethod_inner(long iterations) { + long result = 0; + synchronized(lock) { + while(iterations-- > 0) { + Adder adder = new Adder(result, iterations); + result = dontinline_sharedTestMethod2(adder) // ArgEscape + + adder.sum(); + } + } + return result; + } + } + + // Slightly better than TestCase01_ArgEscape, because the NoEscape allocation can be scalar + // replaced. + public static class TestCase02_NoEscape extends TestCaseBase { + + public long dontinline_testMethod(long iterations) { + long result = 0; + synchronized(lock) { + while(iterations-- > 0) { + Adder adder = new Adder(result, iterations); + result = dontinline_testMethod2(null) // NoEscape + + adder.sum(); + } + } + return result; + } + + public long dontinline_testMethod2(Adder addr) { + return 42L; + } + } + + public static void msg_cr() { + System.out.println(); + } + + public static void msg(String m) { + System.out.println("### BENCHMARK: " + m); + } + +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/libGetOwnedMonitorStackDepthInfoALot.c b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/libGetOwnedMonitorStackDepthInfoALot.c new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo/libGetOwnedMonitorStackDepthInfoALot.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include +#include +#include "jvmti.h" +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JNI_ENV_ARG + +#ifdef __cplusplus +#define JNI_ENV_ARG(x, y) y +#define JNI_ENV_PTR(x) x +#else +#define JNI_ENV_ARG(x,y) x, y +#define JNI_ENV_PTR(x) (*x) +#endif + +#endif + +#define FAILED -1 + +static jvmtiEnv *jvmti; +static jthread main_thread; +static unsigned long interval_ms; +static unsigned int verbose; + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); + +static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) { + char *errMsg; + jvmtiError result; + + result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg); + if (result == JVMTI_ERROR_NONE) { + fprintf(stderr, "****** AGENT: %s: %s (%d)\n", message, errMsg, errCode); + (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg); + } else { + fprintf(stderr, "****** AGENT: %s (%d)\n", message, errCode); + } +} + +JNIEXPORT void JNICALL +GetOwnedMonitorStackDepthInfoALot(jvmtiEnv * jvmti, JNIEnv* env, void * arg) { + jvmtiError err; + jvmtiThreadInfo thread_info; + jint monitor_count; + jvmtiMonitorStackDepthInfo* stack_depth_info; + jint* depthsPtr; + jint idx = 0; + uint64_t iterations = 0; + int interval_s = interval_ms / 1000; + int interval_ms_subsecond = interval_ms - interval_s * 1000; + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = interval_ms_subsecond * 1000 * 1000; + int time_last_verbose_msg = 0; + + if (interval_ms == 0) { + printf("*** AGENT: GetOwnedMonitorStackDepthInfoALot thread: exiting (interval_ms == 0)\n"); + return; + } + + err = (*jvmti)->GetThreadInfo(jvmti, main_thread, &thread_info); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, "GetOwnedMonitorStackDepthInfoALot: error in JVMTI GetThreadInfo"); + (*env)->FatalError(env, "GetOwnedMonitorStackDepthInfoALot: error in JVMTI GetThreadInfo\n"); + } + + printf("*** AGENT: GetOwnedMonitorStackDepthInfoALot thread: polling thread '%s' every %d ms (nanosleep %dns)\n", + thread_info.name, interval_ms, req.tv_nsec); + do { + err = (*jvmti)->GetOwnedMonitorStackDepthInfo(jvmti, main_thread, &monitor_count, &stack_depth_info); + if (err == JVMTI_ERROR_NONE) { + (*jvmti)->Deallocate(jvmti, (unsigned char *) stack_depth_info); + } + + if (verbose && (time_last_verbose_msg >= 1000)) { + time_last_verbose_msg = 0; + printf("*** AGENT: GetOwnedMonitorStackDepthInfoALot thread: %ld iterations. Last monitor count: %d\n", iterations, monitor_count); + fflush(stdout); + } + iterations++; + + time_last_verbose_msg += interval_ms; + if ((interval_s > 0) && (sleep(interval_s) != 0)) { + perror("err: "); + (*env)->FatalError(env, "sleep failed.\n"); + } + if ((interval_ms_subsecond > 0) && nanosleep(&req, NULL) != 0) { + perror("err: "); + (*env)->FatalError(env, "nanosleep failed.\n"); + } + } while (err == JVMTI_ERROR_NONE); + + if (err != JVMTI_ERROR_THREAD_NOT_ALIVE && err != JVMTI_ERROR_WRONG_PHASE) { + ShowErrorMessage(jvmti, err, "GetOwnedMonitorStackDepthInfoALot: error in JVMTI GetOwnedMonitorStackDepthInfo"); + (*env)->FatalError(env, "GetOwnedMonitorStackDepthInfoALot: error in JVMTI GetOwnedMonitorStackDepthInfo\n"); + } + + (*jvmti)->Deallocate(jvmti, (unsigned char *) thread_info.name); + + printf("*** AGENT: GetOwnedMonitorStackDepthInfoALot thread: exiting\n"); +} + +void JNICALL VMInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thr) { + jvmtiError err; + jrawMonitorID wait_lock; + jobject gomi_name; + jclass thread_clas; + jmethodID thread_ctro; + jthread gomi_thread; + + printf("*** AGENT: VM init event\n"); + + main_thread = (*env)->NewGlobalRef(env, thr); + + gomi_name = (*env)->NewStringUTF(env, "GetOwnedMonitorStackDepthInfoALot Thread"); + if (gomi_name == NULL) { + (*env)->FatalError(env, "VMInit: NewStringUTF failed\n"); + } + + thread_clas = (*env)->FindClass(env, "java/lang/Thread"); + if (gomi_name == NULL) { + (*env)->FatalError(env, "VMInit: java.lang.Thread class not found\n"); + } + + thread_ctro = (*env)->GetMethodID(env, thread_clas, "", "(Ljava/lang/String;)V"); + if (thread_ctro == NULL) { + (*env)->FatalError(env, "VMInit: failed to get ID for the Thread ctor\n"); + } + + gomi_thread = (jthread) (*env)->NewObject(env, thread_clas, thread_ctro, gomi_name); + if (gomi_thread == NULL) { + (*env)->FatalError(env, "VMInit: Failed to allocate thread object\n"); + } + + err = (*jvmti)->RunAgentThread(jvmti, gomi_thread, &GetOwnedMonitorStackDepthInfoALot, NULL, + JVMTI_THREAD_NORM_PRIORITY); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "VMInit: failed to start GetOwnedMonitorStackDepthInfoALot thread"); + return; + } +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + printf("*** AGENT: Agent_OnLoad started.\n"); + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + printf("*** AGENT: Agent_OnAttach started."); + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *jvm, void *reserved) { + jint res; + JNIEnv *env; + + printf("*** AGENT: JNI_OnLoad started."); + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env), + JNI_VERSION_9); + if (res != JNI_OK || env == NULL) { + fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res); + return JNI_ERR; + } + + return JNI_VERSION_9; +} + +static +jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + jvmtiError err; + jvmtiCapabilities caps; + jvmtiEventCallbacks callbacks; + JNIEnv* env; + char* opt = options; + char* opt_end; + + printf("*** AGENT: Agent_Initialize started. Options=%s\n", options == NULL ? "n/a" : options); + + interval_ms = 1000; + verbose = 0; + if (opt != NULL) { + if (opt[0] == 'h') { + printf("Accepted options ::= help | [frequency[,startup-delay[,verbose]]]"); + } else { + int val; + val = strtol(opt, &opt_end, 10); + if (opt != opt_end) { + interval_ms = val; + opt = opt_end + 1; + verbose = opt[0] == 'v' ? 1 : 0; + val = strtol(opt, &opt_end, 10); + } + } + } + + printf("*** AGENT: interval : %d ms\n", interval_ms); + printf("*** AGENT: verbose : %d\n", verbose); + + memset(&caps, 0, sizeof(caps)); + memset(&callbacks, 0, sizeof(callbacks)); + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == NULL) { + fprintf(stderr, "Error: GetEnv(JVMTI_VERSION_9) call failed(%d)!\n", res); + return JNI_ERR; + } + + caps.can_get_owned_monitor_stack_depth_info = 1; + + err = (*jvmti)->AddCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI AddCapabilities"); + return JNI_ERR; + } + + err = (*jvmti)->GetCapabilities(jvmti, &caps); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI GetCapabilities"); + return JNI_ERR; + } + + if (!caps.can_get_owned_monitor_stack_depth_info) { + fprintf(stderr, "Warning: GetOwnedMonitorStackDepthInfo is not implemented\n"); + return JNI_ERR; + } + + callbacks.VMInit = &VMInit; + err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI SetEventCallbacks"); + return JNI_ERR; + } + + err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); + if (err != JVMTI_ERROR_NONE) { + ShowErrorMessage(jvmti, err, + "Agent_OnLoad: error in JVMTI SetEventNotificationMode"); + return JNI_ERR; + } + + printf("*** AGENT: Agent_Initialize finished\n"); + return JNI_OK; +} + +#ifdef __cplusplus +} +#endif