openjdk-17/Add-JBooster-RPC-module-and-basic-framework.patch
2025-02-25 16:45:21 +08:00

15920 lines
602 KiB
Diff

---
make/autoconf/jvm-features.m4 | 41 +-
make/autoconf/spec.gmk.in | 1 +
make/common/Modules.gmk | 5 +
make/conf/module-loader-map.conf | 1 +
make/data/hotspot-symbols/symbols-unix | 6 +
make/hotspot/lib/JvmFeatures.gmk | 5 +
.../visualstudio/hotspot/CreateVSProject.gmk | 1 +
make/modules/jdk.jbooster/Launcher.gmk | 34 +
make/modules/jdk.jbooster/Lib.gmk | 43 ++
.../linux/jbooster/net/clientStream_linux.cpp | 144 +++++
.../net/communicationStream_linux.cpp | 37 ++
.../net/serverListeningThread_linux.cpp | 157 +++++
.../jbooster/utilities/fileUtils_linux.cpp | 57 ++
.../share/classfile/systemDictionary.cpp | 16 +
src/hotspot/share/classfile/vmSymbols.hpp | 6 +
src/hotspot/share/include/jvm.h | 30 +
.../jbooster/client/clientDaemonThread.cpp | 102 +++
.../jbooster/client/clientDaemonThread.hpp | 54 ++
.../jbooster/client/clientDataManager.cpp | 274 ++++++++
.../jbooster/client/clientDataManager.hpp | 127 ++++
.../jbooster/client/clientMessageHandler.cpp | 348 ++++++++++
.../jbooster/client/clientMessageHandler.hpp | 99 +++
.../jbooster/client/clientStartupSignal.cpp | 214 ++++++
.../jbooster/client/clientStartupSignal.hpp | 64 ++
.../share/jbooster/dataTransmissionUtils.cpp | 432 +++++++++++++
.../share/jbooster/dataTransmissionUtils.hpp | 247 +++++++
.../share/jbooster/jBoosterManager.cpp | 154 +++++
.../share/jbooster/jBoosterManager.hpp | 69 ++
.../share/jbooster/jBoosterSymbols.hpp | 33 +
.../share/jbooster/jClientArguments.cpp | 405 ++++++++++++
.../share/jbooster/jClientArguments.hpp | 115 ++++
src/hotspot/share/jbooster/jClientVMFlags.cpp | 177 +++++
src/hotspot/share/jbooster/jClientVMFlags.hpp | 96 +++
.../share/jbooster/jbooster_globals.hpp | 125 ++++
src/hotspot/share/jbooster/lazyAot.cpp | 537 +++++++++++++++
src/hotspot/share/jbooster/lazyAot.hpp | 111 ++++
.../share/jbooster/net/clientStream.cpp | 267 ++++++++
.../share/jbooster/net/clientStream.hpp | 69 ++
.../jbooster/net/communicationStream.cpp | 183 ++++++
.../jbooster/net/communicationStream.hpp | 152 +++++
.../net/communicationStream.inline.hpp | 125 ++++
src/hotspot/share/jbooster/net/errorCode.cpp | 50 ++
src/hotspot/share/jbooster/net/errorCode.hpp | 79 +++
src/hotspot/share/jbooster/net/message.hpp | 89 +++
.../share/jbooster/net/message.inline.hpp | 92 +++
.../share/jbooster/net/messageBuffer.cpp | 61 ++
.../share/jbooster/net/messageBuffer.hpp | 114 ++++
.../jbooster/net/messageBuffer.inline.hpp | 137 ++++
.../share/jbooster/net/messageType.cpp | 40 ++
.../share/jbooster/net/messageType.hpp | 81 +++
src/hotspot/share/jbooster/net/netCommon.hpp | 154 +++++
.../share/jbooster/net/rpcCompatibility.cpp | 73 +++
.../share/jbooster/net/rpcCompatibility.hpp | 50 ++
.../share/jbooster/net/serialization.cpp | 333 ++++++++++
.../share/jbooster/net/serialization.hpp | 187 ++++++
.../jbooster/net/serializationWrappers.cpp | 440 +++++++++++++
.../jbooster/net/serializationWrappers.hpp | 276 ++++++++
.../net/serializationWrappers.inline.hpp | 302 +++++++++
.../jbooster/net/serverListeningThread.cpp | 155 +++++
.../jbooster/net/serverListeningThread.hpp | 67 ++
.../share/jbooster/net/serverStream.cpp | 234 +++++++
.../share/jbooster/net/serverStream.hpp | 71 ++
.../jbooster/server/serverControlThread.cpp | 293 +++++++++
.../jbooster/server/serverControlThread.hpp | 102 +++
.../jbooster/server/serverDataManager.cpp | 610 ++++++++++++++++++
.../jbooster/server/serverDataManager.hpp | 428 ++++++++++++
.../jbooster/server/serverDataManagerLog.cpp | 145 +++++
.../jbooster/server/serverMessageHandler.cpp | 406 ++++++++++++
.../jbooster/server/serverMessageHandler.hpp | 75 +++
.../jbooster/utilities/concurrentHashMap.hpp | 164 +++++
.../utilities/concurrentHashMap.inline.hpp | 244 +++++++
.../share/jbooster/utilities/debugUtils.cpp | 41 ++
.../share/jbooster/utilities/debugUtils.hpp | 61 ++
.../jbooster/utilities/debugUtils.inline.hpp | 67 ++
.../share/jbooster/utilities/fileUtils.cpp | 178 +++++
.../share/jbooster/utilities/fileUtils.hpp | 76 +++
.../jbooster/utilities/scalarHashMap.hpp | 96 +++
.../utilities/scalarHashMap.inline.hpp | 72 +++
src/hotspot/share/logging/logTag.hpp | 4 +
src/hotspot/share/memory/allocation.hpp | 1 +
src/hotspot/share/oops/instanceKlass.cpp | 12 +
src/hotspot/share/oops/instanceKlass.hpp | 10 +
src/hotspot/share/oops/method.hpp | 15 +
src/hotspot/share/oops/methodData.cpp | 155 ++++-
src/hotspot/share/oops/methodData.hpp | 75 +++
src/hotspot/share/prims/jvm.cpp | 42 ++
src/hotspot/share/runtime/arguments.cpp | 53 ++
src/hotspot/share/runtime/arguments.hpp | 6 +
src/hotspot/share/runtime/flags/allFlags.hpp | 15 +-
src/hotspot/share/runtime/globals.hpp | 5 +
src/hotspot/share/runtime/java.cpp | 13 +-
src/hotspot/share/runtime/thread.cpp | 9 +
.../share/utilities/concurrentHashTable.hpp | 4 +
src/hotspot/share/utilities/hashtable.cpp | 3 +
src/hotspot/share/utilities/hashtable.hpp | 2 +-
src/hotspot/share/utilities/macros.hpp | 10 +
src/hotspot/share/utilities/stringUtils.cpp | 63 ++
src/hotspot/share/utilities/stringUtils.hpp | 28 +
src/java.base/share/classes/module-info.java | 1 +
.../share/lib/security/default.policy | 9 +
.../jbooster/JBoosterCompilationContext.java | 186 ++++++
.../jdk/vm/ci/jbooster/package-info.java | 27 +
.../share/classes/module-info.java | 1 +
.../share/classes/jdk/jbooster/Commands.java | 166 +++++
.../classes/jdk/jbooster/ConnectionPool.java | 73 +++
.../share/classes/jdk/jbooster/JBooster.java | 231 +++++++
.../jdk/jbooster/JBoosterClassLoader.java | 68 ++
.../JBoosterCompilationContextImpl.java | 168 +++++
.../share/classes/jdk/jbooster/Options.java | 282 ++++++++
.../jbooster/api/JBoosterStartupSignal.java | 163 +++++
.../share/classes/module-info.java | 36 ++
.../share/native/libjbooster/JBooster.c | 45 ++
.../libjbooster/JBoosterStartupSignal.c | 33 +
test/hotspot/gtest/jbooster/test_net.cpp | 517 +++++++++++++++
test/hotspot/gtest/jbooster/test_util.cpp | 182 ++++++
test/jdk/tools/jbooster/JBoosterCmdTest.java | 133 ++++
test/jdk/tools/jbooster/JBoosterNetTest.java | 152 +++++
test/jdk/tools/jbooster/JBoosterTestBase.java | 201 ++++++
test/jdk/tools/jbooster/SimpleClient.java | 35 +
test/jdk/tools/launcher/HelpFlagsTest.java | 1 +
test/jdk/tools/launcher/VersionCheck.java | 2 +
121 files changed, 14530 insertions(+), 13 deletions(-)
create mode 100644 make/modules/jdk.jbooster/Launcher.gmk
create mode 100644 make/modules/jdk.jbooster/Lib.gmk
create mode 100644 src/hotspot/os/linux/jbooster/net/clientStream_linux.cpp
create mode 100644 src/hotspot/os/linux/jbooster/net/communicationStream_linux.cpp
create mode 100644 src/hotspot/os/linux/jbooster/net/serverListeningThread_linux.cpp
create mode 100644 src/hotspot/os/linux/jbooster/utilities/fileUtils_linux.cpp
create mode 100644 src/hotspot/share/jbooster/client/clientDaemonThread.cpp
create mode 100644 src/hotspot/share/jbooster/client/clientDaemonThread.hpp
create mode 100644 src/hotspot/share/jbooster/client/clientDataManager.cpp
create mode 100644 src/hotspot/share/jbooster/client/clientDataManager.hpp
create mode 100644 src/hotspot/share/jbooster/client/clientMessageHandler.cpp
create mode 100644 src/hotspot/share/jbooster/client/clientMessageHandler.hpp
create mode 100644 src/hotspot/share/jbooster/client/clientStartupSignal.cpp
create mode 100644 src/hotspot/share/jbooster/client/clientStartupSignal.hpp
create mode 100644 src/hotspot/share/jbooster/dataTransmissionUtils.cpp
create mode 100644 src/hotspot/share/jbooster/dataTransmissionUtils.hpp
create mode 100644 src/hotspot/share/jbooster/jBoosterManager.cpp
create mode 100644 src/hotspot/share/jbooster/jBoosterManager.hpp
create mode 100644 src/hotspot/share/jbooster/jBoosterSymbols.hpp
create mode 100644 src/hotspot/share/jbooster/jClientArguments.cpp
create mode 100644 src/hotspot/share/jbooster/jClientArguments.hpp
create mode 100644 src/hotspot/share/jbooster/jClientVMFlags.cpp
create mode 100644 src/hotspot/share/jbooster/jClientVMFlags.hpp
create mode 100644 src/hotspot/share/jbooster/jbooster_globals.hpp
create mode 100644 src/hotspot/share/jbooster/lazyAot.cpp
create mode 100644 src/hotspot/share/jbooster/lazyAot.hpp
create mode 100644 src/hotspot/share/jbooster/net/clientStream.cpp
create mode 100644 src/hotspot/share/jbooster/net/clientStream.hpp
create mode 100644 src/hotspot/share/jbooster/net/communicationStream.cpp
create mode 100644 src/hotspot/share/jbooster/net/communicationStream.hpp
create mode 100644 src/hotspot/share/jbooster/net/communicationStream.inline.hpp
create mode 100644 src/hotspot/share/jbooster/net/errorCode.cpp
create mode 100644 src/hotspot/share/jbooster/net/errorCode.hpp
create mode 100644 src/hotspot/share/jbooster/net/message.hpp
create mode 100644 src/hotspot/share/jbooster/net/message.inline.hpp
create mode 100644 src/hotspot/share/jbooster/net/messageBuffer.cpp
create mode 100644 src/hotspot/share/jbooster/net/messageBuffer.hpp
create mode 100644 src/hotspot/share/jbooster/net/messageBuffer.inline.hpp
create mode 100644 src/hotspot/share/jbooster/net/messageType.cpp
create mode 100644 src/hotspot/share/jbooster/net/messageType.hpp
create mode 100644 src/hotspot/share/jbooster/net/netCommon.hpp
create mode 100644 src/hotspot/share/jbooster/net/rpcCompatibility.cpp
create mode 100644 src/hotspot/share/jbooster/net/rpcCompatibility.hpp
create mode 100644 src/hotspot/share/jbooster/net/serialization.cpp
create mode 100644 src/hotspot/share/jbooster/net/serialization.hpp
create mode 100644 src/hotspot/share/jbooster/net/serializationWrappers.cpp
create mode 100644 src/hotspot/share/jbooster/net/serializationWrappers.hpp
create mode 100644 src/hotspot/share/jbooster/net/serializationWrappers.inline.hpp
create mode 100644 src/hotspot/share/jbooster/net/serverListeningThread.cpp
create mode 100644 src/hotspot/share/jbooster/net/serverListeningThread.hpp
create mode 100644 src/hotspot/share/jbooster/net/serverStream.cpp
create mode 100644 src/hotspot/share/jbooster/net/serverStream.hpp
create mode 100644 src/hotspot/share/jbooster/server/serverControlThread.cpp
create mode 100644 src/hotspot/share/jbooster/server/serverControlThread.hpp
create mode 100644 src/hotspot/share/jbooster/server/serverDataManager.cpp
create mode 100644 src/hotspot/share/jbooster/server/serverDataManager.hpp
create mode 100644 src/hotspot/share/jbooster/server/serverDataManagerLog.cpp
create mode 100644 src/hotspot/share/jbooster/server/serverMessageHandler.cpp
create mode 100644 src/hotspot/share/jbooster/server/serverMessageHandler.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/concurrentHashMap.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/concurrentHashMap.inline.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/debugUtils.cpp
create mode 100644 src/hotspot/share/jbooster/utilities/debugUtils.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/debugUtils.inline.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/fileUtils.cpp
create mode 100644 src/hotspot/share/jbooster/utilities/fileUtils.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/scalarHashMap.hpp
create mode 100644 src/hotspot/share/jbooster/utilities/scalarHashMap.inline.hpp
create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/JBoosterCompilationContext.java
create mode 100644 src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/package-info.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/Commands.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/ConnectionPool.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/JBooster.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterClassLoader.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterCompilationContextImpl.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/Options.java
create mode 100644 src/jdk.jbooster/share/classes/jdk/jbooster/api/JBoosterStartupSignal.java
create mode 100644 src/jdk.jbooster/share/classes/module-info.java
create mode 100644 src/jdk.jbooster/share/native/libjbooster/JBooster.c
create mode 100644 src/jdk.jbooster/share/native/libjbooster/JBoosterStartupSignal.c
create mode 100644 test/hotspot/gtest/jbooster/test_net.cpp
create mode 100644 test/hotspot/gtest/jbooster/test_util.cpp
create mode 100644 test/jdk/tools/jbooster/JBoosterCmdTest.java
create mode 100644 test/jdk/tools/jbooster/JBoosterNetTest.java
create mode 100644 test/jdk/tools/jbooster/JBoosterTestBase.java
create mode 100644 test/jdk/tools/jbooster/SimpleClient.java
diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4
index aa99b037b..c6aab63f8 100644
--- a/make/autoconf/jvm-features.m4
+++ b/make/autoconf/jvm-features.m4
@@ -44,7 +44,7 @@
m4_define(jvm_features_valid, m4_normalize( \
ifdef([custom_jvm_features_valid], custom_jvm_features_valid) \
\
- cds compiler1 compiler2 dtrace epsilongc g1gc jfr jni-check \
+ cds compiler1 compiler2 dtrace epsilongc g1gc jbooster jfr jni-check \
jvmci jvmti link-time-opt management minimal nmt opt-size parallelgc \
serialgc services shenandoahgc static-build vm-structs zero zgc \
))
@@ -61,6 +61,7 @@ m4_define(jvm_feature_desc_compiler2, [enable hotspot compiler C2])
m4_define(jvm_feature_desc_dtrace, [enable dtrace support])
m4_define(jvm_feature_desc_epsilongc, [include the epsilon (no-op) garbage collector])
m4_define(jvm_feature_desc_g1gc, [include the G1 garbage collector])
+m4_define(jvm_feature_desc_jbooster, [enable JBooster])
m4_define(jvm_feature_desc_jfr, [enable JDK Flight Recorder (JFR)])
m4_define(jvm_feature_desc_jni_check, [enable -Xcheck:jni support])
m4_define(jvm_feature_desc_jvmci, [enable JVM Compiler Interface (JVMCI)])
@@ -268,6 +269,22 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_DTRACE],
])
])
+###############################################################################
+# Check if the feature 'jbooster' is available on this platform.
+#
+AC_DEFUN_ONCE([JVM_FEATURES_CHECK_JBOOSTER],
+[
+ JVM_FEATURES_CHECK_AVAILABILITY(jbooster, [
+ AC_MSG_CHECKING([if platform is supported by JBOOSTER])
+ if test "x$OPENJDK_TARGET_CPU" = "xx86_64"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no, $OPENJDK_TARGET_CPU])
+ AVAILABLE=false
+ fi
+ ])
+])
+
###############################################################################
# Check if the feature 'jfr' is available on this platform.
#
@@ -405,6 +422,7 @@ AC_DEFUN_ONCE([JVM_FEATURES_PREPARE_PLATFORM],
JVM_FEATURES_CHECK_CDS
JVM_FEATURES_CHECK_DTRACE
+ JVM_FEATURES_CHECK_JBOOSTER
JVM_FEATURES_CHECK_JFR
JVM_FEATURES_CHECK_JVMCI
JVM_FEATURES_CHECK_SHENANDOAHGC
@@ -433,21 +451,21 @@ AC_DEFUN([JVM_FEATURES_PREPARE_VARIANT],
# Check which features are unavailable for this JVM variant.
# This means that is not possible to build these features for this variant.
if test "x$variant" = "xminimal"; then
- JVM_FEATURES_VARIANT_UNAVAILABLE="cds zero"
+ JVM_FEATURES_VARIANT_UNAVAILABLE="cds jbooster zero"
elif test "x$variant" = "xcore"; then
- JVM_FEATURES_VARIANT_UNAVAILABLE="cds minimal zero"
+ JVM_FEATURES_VARIANT_UNAVAILABLE="cds jbooster minimal zero"
elif test "x$variant" = "xzero"; then
JVM_FEATURES_VARIANT_UNAVAILABLE="cds compiler1 compiler2 \
- jvmci minimal zgc"
+ jbooster jvmci minimal zgc"
else
JVM_FEATURES_VARIANT_UNAVAILABLE="minimal zero"
fi
# Check which features should be off by default for this JVM variant.
if test "x$variant" = "xclient"; then
- JVM_FEATURES_VARIANT_FILTER="compiler2 jvmci link-time-opt opt-size"
+ JVM_FEATURES_VARIANT_FILTER="compiler2 jbooster jvmci link-time-opt opt-size"
elif test "x$variant" = "xminimal"; then
- JVM_FEATURES_VARIANT_FILTER="cds compiler2 dtrace epsilongc g1gc \
+ JVM_FEATURES_VARIANT_FILTER="cds compiler2 dtrace epsilongc g1gc jbooster \
jfr jni-check jvmci jvmti management nmt parallelgc services \
shenandoahgc vm-structs zgc"
if test "x$OPENJDK_TARGET_CPU" = xarm ; then
@@ -458,12 +476,12 @@ AC_DEFUN([JVM_FEATURES_PREPARE_VARIANT],
link-time-opt"
fi
elif test "x$variant" = "xcore"; then
- JVM_FEATURES_VARIANT_FILTER="compiler1 compiler2 jvmci \
+ JVM_FEATURES_VARIANT_FILTER="compiler1 compiler2 jbooster jvmci \
link-time-opt opt-size"
elif test "x$variant" = "xzero"; then
- JVM_FEATURES_VARIANT_FILTER="jfr link-time-opt opt-size"
+ JVM_FEATURES_VARIANT_FILTER="jbooster jfr link-time-opt opt-size"
else
- JVM_FEATURES_VARIANT_FILTER="link-time-opt opt-size"
+ JVM_FEATURES_VARIANT_FILTER="jbooster link-time-opt opt-size"
fi
])
@@ -558,6 +576,9 @@ AC_DEFUN([JVM_FEATURES_VERIFY],
if JVM_FEATURES_IS_ACTIVE(compiler2); then
INCLUDE_COMPILER2="true"
fi
+ if JVM_FEATURES_IS_ACTIVE(jbooster); then
+ INCLUDE_JBOOSTER="true"
+ fi
# Verify that we have at least one gc selected (i.e., feature named "*gc").
if ! JVM_FEATURES_IS_ACTIVE(.*gc); then
@@ -582,6 +603,7 @@ AC_DEFUN_ONCE([JVM_FEATURES_SETUP],
ENABLE_CDS="true"
INCLUDE_JVMCI="true"
INCLUDE_COMPILER2="false"
+ INCLUDE_JBOOSTER="false"
for variant in $JVM_VARIANTS; do
# Figure out if any features are unavailable, or should be filtered out
@@ -619,5 +641,6 @@ AC_DEFUN_ONCE([JVM_FEATURES_SETUP],
AC_SUBST(INCLUDE_JVMCI)
AC_SUBST(INCLUDE_COMPILER2)
+ AC_SUBST(INCLUDE_JBOOSTER)
])
diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in
index 2fdda9655..b73bbfb2d 100644
--- a/make/autoconf/spec.gmk.in
+++ b/make/autoconf/spec.gmk.in
@@ -865,6 +865,7 @@ PNG_CFLAGS:=@PNG_CFLAGS@
INCLUDE_SA=@INCLUDE_SA@
INCLUDE_JVMCI=@INCLUDE_JVMCI@
INCLUDE_COMPILER2=@INCLUDE_COMPILER2@
+INCLUDE_JBOOSTER=@INCLUDE_JBOOSTER@
OS_VERSION_MAJOR:=@OS_VERSION_MAJOR@
OS_VERSION_MINOR:=@OS_VERSION_MINOR@
diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk
index c3c6a1bf4..514f6a2a0 100644
--- a/make/common/Modules.gmk
+++ b/make/common/Modules.gmk
@@ -66,6 +66,11 @@ ifeq ($(INCLUDE_JVMCI), false)
MODULES_FILTER += jdk.internal.vm.compiler.management
endif
+# Filter out jbooster specific modules if jbooster is disabled
+ifeq ($(INCLUDE_JBOOSTER), false)
+ MODULES_FILTER += jdk.jbooster
+endif
+
# jpackage is only on windows, macosx, and linux
ifeq ($(call isTargetOs, windows macosx linux), false)
MODULES_FILTER += jdk.jpackage
diff --git a/make/conf/module-loader-map.conf b/make/conf/module-loader-map.conf
index f72d96123..051ca4dc8 100644
--- a/make/conf/module-loader-map.conf
+++ b/make/conf/module-loader-map.conf
@@ -82,6 +82,7 @@ PLATFORM_MODULES= \
jdk.crypto.ec \
jdk.dynalink \
jdk.httpserver \
+ jdk.jbooster \
jdk.jsobject \
jdk.localedata \
jdk.naming.dns \
diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix
index b14c5338d..d731017ab 100644
--- a/make/data/hotspot-symbols/symbols-unix
+++ b/make/data/hotspot-symbols/symbols-unix
@@ -206,3 +206,9 @@ JVM_AddReadsModule
JVM_DefineArchivedModules
JVM_DefineModule
JVM_SetBootLoaderUnnamedModule
+
+# JBooster related
+JVM_JBoosterInitVM
+JVM_JBoosterHandleConnection
+JVM_JBoosterPrintStoredClientData
+JVM_JBoosterStartupNativeCallback
diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk
index 1f16d0a91..0c83ec254 100644
--- a/make/hotspot/lib/JvmFeatures.gmk
+++ b/make/hotspot/lib/JvmFeatures.gmk
@@ -165,6 +165,11 @@ ifneq ($(call check-jvm-feature, jfr), true)
JVM_EXCLUDE_FILES += compilerEvent.cpp
endif
+ifneq ($(call check-jvm-feature, jbooster), true)
+ JVM_CFLAGS_FEATURES += -DINCLUDE_JBOOSTER=0
+ JVM_EXCLUDE_PATTERNS += jbooster
+endif
+
################################################################################
ifeq ($(call check-jvm-feature, link-time-opt), true)
diff --git a/make/ide/visualstudio/hotspot/CreateVSProject.gmk b/make/ide/visualstudio/hotspot/CreateVSProject.gmk
index 3db28db63..0ce9f412b 100644
--- a/make/ide/visualstudio/hotspot/CreateVSProject.gmk
+++ b/make/ide/visualstudio/hotspot/CreateVSProject.gmk
@@ -113,6 +113,7 @@ ifeq ($(call isTargetOs, windows), true)
-hidePath .jcheck \
-hidePath jdk.hotspot.agent \
-hidePath jdk.internal.vm.ci \
+ -hidePath jdk.jbooster \
-hidePath jdk.jfr \
-compiler VC10 \
-jdkTargetRoot $(call FixPath, $(JDK_OUTPUTDIR)) \
diff --git a/make/modules/jdk.jbooster/Launcher.gmk b/make/modules/jdk.jbooster/Launcher.gmk
new file mode 100644
index 000000000..1b03ec55e
--- /dev/null
+++ b/make/modules/jdk.jbooster/Launcher.gmk
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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 LauncherCommon.gmk
+
+$(eval $(call SetupBuildLauncher, jbooster, \
+ MAIN_CLASS := jdk.jbooster.Main, \
+ JAVA_ARGS := \
+ -XX:+UnlockExperimentalVMOptions \
+ -XX:+AsJBooster \
+ , \
+))
diff --git a/make/modules/jdk.jbooster/Lib.gmk b/make/modules/jdk.jbooster/Lib.gmk
new file mode 100644
index 000000000..a57bff6d7
--- /dev/null
+++ b/make/modules/jdk.jbooster/Lib.gmk
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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 LibCommon.gmk
+
+################################################################################
+
+$(eval $(call SetupJdkLibrary, BUILD_LIBJBOOSTER, \
+ NAME := jbooster, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CFLAGS_JDKLIB), \
+ LDFLAGS := $(LDFLAGS_JDKLIB) \
+ $(call SET_SHARED_LIBRARY_ORIGIN), \
+ LIBS_unix := -ljvm, \
+))
+
+$(BUILD_LIBJBOOSTER): $(call FindLib, java.base, java)
+
+TARGETS += $(BUILD_LIBJBOOSTER)
+
+################################################################################
diff --git a/src/hotspot/os/linux/jbooster/net/clientStream_linux.cpp b/src/hotspot/os/linux/jbooster/net/clientStream_linux.cpp
new file mode 100644
index 000000000..6425ef0f2
--- /dev/null
+++ b/src/hotspot/os/linux/jbooster/net/clientStream_linux.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <netdb.h> // for addrinfo
+#include <netinet/tcp.h> // for TCP_NODELAY
+#include <string.h> // for memset()
+#include <sys/socket.h>
+
+#include "jbooster/net/clientStream.hpp"
+#include "jbooster/net/errorCode.hpp"
+#include "logging/log.hpp"
+#include "runtime/os.hpp"
+
+#define LOG_INNER(socket_fd, address, port, err_code, format, args...) \
+ log_error(jbooster, rpc)(format ": socket=%d, address=\"%s:%s\", error=%s(\"%s\").", \
+ ##args, socket_fd, address, port, \
+ JBErr::err_name(err_code), JBErr::err_message(err_code)); \
+
+static int resolve_address(addrinfo** res_addr, const char* address, const char* port) {
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET; // IPv4 only
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0; // any protocol
+
+ int gai_err = getaddrinfo(address, port, &hints, res_addr);
+ if (gai_err != 0) {
+ if (gai_err == EAI_SYSTEM) {
+ LOG_INNER(-1, address, port, errno, "Failed to get addrinfo");
+ } else {
+ log_error(jbooster, rpc)("Failed to get addrinfo: address=\"%s:%s\", gai_err=\"%s\".",
+ address, port, gai_strerror(gai_err));
+ }
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Initializes the options of the client socket.
+ */
+static int init_socket_opts(int socket_fd, const char* address, const char* port, uint32_t timeout_ms) {
+#define TRY_SETSOCKOPT(socket_fd, level, optname, optval) \
+ if (setsockopt(socket_fd, level, optname, (void*)&optval, sizeof(optval)) < 0) { \
+ LOG_INNER(socket_fd, address, port, errno, "Failed to set socket opt " #optname); \
+ return -1; \
+ }
+
+ // enable keep alive
+ int val_keep_alive = 1;
+ TRY_SETSOCKOPT(socket_fd, SOL_SOCKET, SO_KEEPALIVE, val_keep_alive);
+
+ // set linger after close() or shutdown() to 2 seconds
+ linger val_linger = { 1, 2 };
+ TRY_SETSOCKOPT(socket_fd, SOL_SOCKET, SO_LINGER, val_linger);
+
+ // set receiving and sending timeout to `timeout_ms`
+ timeval val_timeout = { timeout_ms / 1000 , (timeout_ms % 1000) * 1000 };
+ TRY_SETSOCKOPT(socket_fd, SOL_SOCKET, SO_RCVTIMEO, val_timeout);
+ TRY_SETSOCKOPT(socket_fd, SOL_SOCKET, SO_SNDTIMEO, val_timeout);
+
+ // disable Nagle to reduce latency
+ int val_no_delay = 1;
+ TRY_SETSOCKOPT(socket_fd, IPPROTO_TCP, TCP_NODELAY, val_no_delay);
+
+ return 0;
+#undef TRY_SETSOCKOPT
+}
+
+int ClientStream::try_to_connect_once(int* res_fd, const char* address, const char* port, uint32_t timeout_ms) {
+ errno = 0;
+
+ int res_err = 0;
+ int conn_fd = -1;
+ // addrinfo res of getaddrinfo()
+ addrinfo* res_addr = nullptr;
+
+ do {
+ if (resolve_address(&res_addr, address, port) < 0) {
+ res_err = errno;
+ break;
+ }
+
+ // open socket
+ addrinfo* addr_p;
+ for (addr_p = res_addr; addr_p; addr_p = addr_p->ai_next) {
+ conn_fd = socket(addr_p->ai_family, addr_p->ai_socktype, addr_p->ai_protocol);
+ if (conn_fd >= 0) break;
+ }
+ if (conn_fd < 0) {
+ res_err = errno;
+ LOG_INNER(conn_fd, address, port, res_err, "Failed to create the socket");
+ break;
+ }
+
+ // configure socket
+ if (init_socket_opts(conn_fd, address, port, timeout_ms) < 0) {
+ res_err = errno;
+ break;
+ }
+
+ // connect socket
+ if (connect(conn_fd, addr_p->ai_addr, addr_p->ai_addrlen) < 0) {
+ res_err = errno;
+ LOG_INNER(conn_fd, address, port, res_err, "Failed to build the connection");
+ break;
+ }
+
+ // success
+ assert(errno == 0, "why errno=%s", JBErr::err_name(errno));
+ freeaddrinfo(res_addr);
+ *res_fd = conn_fd;
+ return 0;
+ } while (false);
+
+ // fail
+ errno = 0;
+ if (res_addr != nullptr) freeaddrinfo(res_addr);
+ if (conn_fd >= 0) close(conn_fd);
+ if (res_err == 0) res_err = JBErr::UNKNOWN;
+ *res_fd = -1;
+ return res_err;
+}
diff --git a/src/hotspot/os/linux/jbooster/net/communicationStream_linux.cpp b/src/hotspot/os/linux/jbooster/net/communicationStream_linux.cpp
new file mode 100644
index 000000000..5ebe6c994
--- /dev/null
+++ b/src/hotspot/os/linux/jbooster/net/communicationStream_linux.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <sys/socket.h>
+
+#include "jbooster/net/communicationStream.inline.hpp"
+
+bool CommunicationStream::set_read_write_timeout(uint32_t timeout_ms) {
+ timeval val_timeout = { timeout_ms / 1000 , (timeout_ms % 1000) * 1000 };
+ if (setsockopt(_conn_fd, SOL_SOCKET, SO_RCVTIMEO, (void*) &val_timeout, sizeof(val_timeout)) < 0) {
+ return false;
+ }
+ if (setsockopt(_conn_fd, SOL_SOCKET, SO_SNDTIMEO, (void*) &val_timeout, sizeof(val_timeout)) < 0) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/hotspot/os/linux/jbooster/net/serverListeningThread_linux.cpp b/src/hotspot/os/linux/jbooster/net/serverListeningThread_linux.cpp
new file mode 100644
index 000000000..bc3eae5b0
--- /dev/null
+++ b/src/hotspot/os/linux/jbooster/net/serverListeningThread_linux.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <netdb.h> // for addrinfo
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include "jbooster/net/errorCode.hpp"
+#include "jbooster/net/serverListeningThread.hpp"
+#include "logging/log.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+
+static int init_server_socket_opts(int socket_fd) {
+ // enable reuse of address
+ int val_reuse_address = 1;
+ if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&val_reuse_address, sizeof(val_reuse_address)) < 0) {
+ log_error(jbooster, rpc)("Failed to set socket opt SO_REUSEADDR: %s.", strerror(errno));
+ return -1;
+ }
+
+ // enable keep alive
+ int val_keep_alive = 1;
+ if (setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&val_keep_alive, sizeof(val_keep_alive)) < 0) {
+ log_error(jbooster, rpc)("Failed to set socket opt SO_KEEPALIVE: %s.", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_accepted_socket_opts(int conn_fd, uint32_t timeout_ms) {
+ // set receiving and sending timeout to `timeout_ms`
+ timeval val_timeout = { timeout_ms / 1000 , (timeout_ms % 1000) * 1000 };
+
+ if (setsockopt(conn_fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&val_timeout, sizeof(val_timeout)) < 0) {
+ log_error(jbooster, rpc)("Failed to set socket opt SO_RCVTIMEO: %s.", strerror(errno));
+ return -1;
+ }
+
+ if (setsockopt(conn_fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&val_timeout, sizeof(val_timeout)) < 0) {
+ log_error(jbooster, rpc)("Failed to set socket opt SO_SNDTIMEO: %s.", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Returns the socket file descriptor (or -1 if failed to open socket).
+ */
+static int bind_address(const char* address, uint16_t port) {
+ // open socket
+ int socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // AF_INET for IPv4 only
+ if (socket_fd < 0) {
+ log_error(jbooster, rpc)("Failed to open a socket.");
+ return socket_fd;
+ }
+
+ // configure socket
+ if (init_server_socket_opts(socket_fd) < 0) {
+ close(socket_fd);
+ return -1;
+ }
+
+ // bind address and port
+ sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+ if (bind(socket_fd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ log_error(jbooster, rpc)("Failed to bind the socket.");
+ close(socket_fd);
+ return -1;
+ }
+
+ // listen
+ if (listen(socket_fd, SOMAXCONN) < 0) {
+ log_error(jbooster, rpc)("Failed to listen to the socket.");
+ close(socket_fd);
+ return -1;
+ }
+
+ return socket_fd;
+}
+
+/**
+ * Keep listening for client requests.
+ */
+int ServerListeningThread::run_listener(TRAPS) {
+ ThreadToNativeFromVM ttn(THREAD);
+ int server_fd = bind_address(_address, _port);
+ if (server_fd < 0) {
+ log_error(jbooster, rpc)("Failed to bind address '%s:%d'.", _address, (int) _port);
+ return EADDRINUSE;
+ }
+
+ pollfd pfd = {0};
+ pfd.fd = server_fd;
+ pfd.events = POLLIN;
+
+ sockaddr_in acc_addr;
+ socklen_t acc_addrlen = sizeof(acc_addr);
+
+ log_info(jbooster, rpc)("The JBooster server is serving on %s:%d.", _address, (int) _port);
+ while (!get_exit_flag()) {
+ int pcode = poll(&pfd, 1, 100);
+ if (pcode < 0) {
+ if (errno == EINTR) continue;
+ log_error(jbooster, rpc)("Failed to poll: %s.", strerror(errno));
+ return errno;
+ }
+ else if (pcode == 0) continue; // timed out
+ else if (pfd.revents != POLLIN) {
+ log_error(jbooster, rpc)("Unexpected poll revent: %d.", pfd.revents);
+ return JBErr::UNKNOWN;
+ }
+
+ while (!get_exit_flag()) {
+ int conn_fd = accept(server_fd, (sockaddr*)&acc_addr, &acc_addrlen);
+ if (conn_fd < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ log_error(jbooster, rpc)("Failed to accept: %s.", strerror(errno));
+ }
+ break;
+ }
+ if (init_accepted_socket_opts(conn_fd, _timeout_ms) < 0) break;
+
+ handle_new_connection(conn_fd, THREAD);
+ }
+ }
+
+ close(server_fd);
+ log_debug(jbooster, rpc)("The JBooster server listener thread stopped.");
+ return 0;
+}
diff --git a/src/hotspot/os/linux/jbooster/utilities/fileUtils_linux.cpp b/src/hotspot/os/linux/jbooster/utilities/fileUtils_linux.cpp
new file mode 100644
index 000000000..0dd4ed523
--- /dev/null
+++ b/src/hotspot/os/linux/jbooster/utilities/fileUtils_linux.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <dirent.h>
+
+#include "jbooster/utilities/fileUtils.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/os.inline.hpp"
+
+FileUtils::ListDir::ListDir(const char* path): _path(path) {
+ _cur = nullptr;
+ _ds = os::opendir(path);
+}
+
+FileUtils::ListDir::~ListDir() {
+ if (_ds != nullptr) os::closedir(_ds);
+}
+
+bool FileUtils::ListDir::next() {
+ if (_ds == nullptr) return false;
+ return (_cur = os::readdir(_ds)) != nullptr;
+}
+
+const char* FileUtils::ListDir::name() {
+ assert(_cur != nullptr, "sanity");
+ return _cur->d_name;
+}
+
+bool FileUtils::ListDir::is_file() {
+ assert(_cur != nullptr, "sanity");
+ return _cur->d_type == DT_REG;
+}
+
+bool FileUtils::ListDir::is_dir() {
+ assert(_cur != nullptr, "sanity");
+ return _cur->d_type == DT_DIR;
+}
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index 96fbf375b..7cdc25406 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -86,6 +86,9 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
+#if INCLUDE_JBOOSTER
+#include "jbooster/client/clientStartupSignal.hpp"
+#endif // INCLUDE_JBOOSTER
ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL;
SymbolPropertyTable* SystemDictionary::_invoke_method_table = NULL;
@@ -900,6 +903,19 @@ InstanceKlass* SystemDictionary::resolve_class_from_stream(
// throw potential ClassFormatErrors.
InstanceKlass* k = NULL;
+#if INCLUDE_JBOOSTER
+ if (UseJBooster && JBoosterStartupSignal != nullptr) {
+ if (ClientStartupSignal::is_target_klass(class_name)) {
+ unsigned char* ptr = const_cast<unsigned char*>(st->buffer());
+ int len = st->length();
+ bool success = ClientStartupSignal::try_inject_startup_callback(&ptr, &len, THREAD);
+ if (success) {
+ st = new ClassFileStream(ptr, len, st->source(), st->need_verify());
+ }
+ }
+ }
+#endif // INCLUDE_JBOOSTER
+
#if INCLUDE_CDS
if (!DumpSharedSpaces) {
k = SystemDictionaryShared::lookup_from_stream(class_name,
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 79a6991b1..6a1e5cf6c 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -31,6 +31,9 @@
#include "oops/symbol.hpp"
#include "utilities/macros.hpp"
#include "utilities/enumIterator.hpp"
+#if INCLUDE_JBOOSTER
+#include "jbooster/jBoosterSymbols.hpp"
+#endif // INCLUDE_JBOOSTER
// The class vmSymbols is a name space for fast lookup of
// symbols commonly used in the VM.
@@ -708,6 +711,9 @@
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
template(url_void_signature, "(Ljava/net/URL;)V") \
\
+ /* JBooster */ \
+ JBOOSTER_ONLY(JBOOSTER_TEMPLATES(template)) \
+ \
/*end*/
// enum for figuring positions and size of Symbol::_vm_symbols[]
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index c38684cee..49c932aec 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -1096,6 +1096,36 @@ JVM_GetTemporaryDirectory(JNIEnv *env);
JNIEXPORT jobjectArray JNICALL
JVM_GetEnclosingMethodInfo(JNIEnv* env, jclass ofClass);
+
+/*************************************************************************
+ JBooster Support
+ ************************************************************************/
+
+/**
+ * Init the JBooster server in Hotspot.
+ */
+JNIEXPORT void JNICALL
+JVM_JBoosterInitVM(JNIEnv *env, jint server_port, jint connection_timeout, jint cleanup_timeout, jstring cache_path);
+
+/**
+ * Handle a TCP connection.
+ */
+JNIEXPORT void JNICALL
+JVM_JBoosterHandleConnection(JNIEnv *env, jint connection_fd);
+
+/**
+ * Print data in ServerDataManager.
+ */
+JNIEXPORT void JNICALL
+JVM_JBoosterPrintStoredClientData(JNIEnv *env, jboolean print_all);
+
+/**
+ * Callback of startup signal.
+ */
+JNIEXPORT void JNICALL
+JVM_JBoosterStartupNativeCallback(JNIEnv *env);
+
+
/*
* This structure is used by the launcher to get the default thread
* stack size from the VM using JNI_GetDefaultJavaVMInitArgs() with a
diff --git a/src/hotspot/share/jbooster/client/clientDaemonThread.cpp b/src/hotspot/share/jbooster/client/clientDaemonThread.cpp
new file mode 100644
index 000000000..e92205c1e
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientDaemonThread.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/javaClasses.inline.hpp"
+#include "classfile/vmClasses.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/client/clientDaemonThread.hpp"
+#include "jbooster/client/clientMessageHandler.hpp"
+#include "jbooster/net/clientStream.hpp"
+#include "logging/log.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/thread.inline.hpp"
+
+JavaThread* ClientDaemonThread::_the_java_thread = nullptr;
+
+void ClientDaemonThread::start_thread(TRAPS) {
+ JavaThread* new_thread = new JavaThread(&client_daemon_thread_entry);
+ JavaThread::vm_exit_on_osthread_failure(new_thread);
+ guarantee(_the_java_thread == nullptr, "sanity");
+ _the_java_thread = new_thread;
+
+ Handle string = java_lang_String::create_from_str("JBooster Client Daemon", CATCH);
+ Handle thread_group(THREAD, Universe::system_thread_group());
+ Handle thread_oop = JavaCalls::construct_new_instance(
+ vmClasses::Thread_klass(),
+ vmSymbols::threadgroup_string_void_signature(),
+ thread_group,
+ string,
+ CATCH);
+
+ Klass* group = vmClasses::ThreadGroup_klass();
+ JavaValue result(T_VOID);
+ JavaCalls::call_special(&result,
+ thread_group,
+ group,
+ vmSymbols::add_method_name(),
+ vmSymbols::thread_void_signature(),
+ thread_oop,
+ THREAD);
+
+ JavaThread::start_internal_daemon(THREAD, new_thread, thread_oop, MinPriority);
+}
+
+void ClientDaemonThread::daemon_run(TRAPS) {
+ ThreadToNativeFromVM ttn(THREAD);
+
+ ClientStream cs(JBoosterAddress, JBoosterPort, JBoosterManager::heartbeat_timeout(), THREAD);
+ JB_TRY {
+ JB_THROW(cs.connect_and_init_stream());
+ JB_THROW(cs.send_request(MessageType::ClientDaemonTask));
+
+ while (true) {
+ JB_THROW(daemon_loop(&cs, THREAD));
+ }
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ log_warning(jbooster)("Heartbeat timeout. The server seems to be dead.");
+ } JB_CATCH_END;
+}
+
+int ClientDaemonThread::daemon_loop(ClientStream* client_stream, TRAPS) {
+ MessageType type;
+ JB_RETURN(client_stream->recv_request(type));
+ switch (type) {
+ case MessageType::Heartbeat:
+ JB_RETURN(handle_heartbeat(client_stream));
+ break;
+ default:
+ guarantee(false, "handle it");
+ }
+ return 0;
+}
+
+int ClientDaemonThread::handle_heartbeat(ClientStream* client_stream) {
+ int magic;
+ JB_RETURN(client_stream->parse_request(&magic));
+ JB_RETURN(client_stream->send_response(&magic));
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/client/clientDaemonThread.hpp b/src/hotspot/share/jbooster/client/clientDaemonThread.hpp
new file mode 100644
index 000000000..f35f5017e
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientDaemonThread.hpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_CLIENT_CLIENTDAEMONTHREAD_HPP
+#define SHARE_JBOOSTER_CLIENT_CLIENTDAEMONTHREAD_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+#include "runtime/thread.hpp"
+
+class ClientStream;
+
+/**
+ * Goals of this thread:
+ * - Send keep-alive packages to the server;
+ * - Send bytecode of the newly loaded klasses to the server;
+ *
+ * This thread is simple, so all static methods are used.
+ */
+class ClientDaemonThread: public AllStatic {
+private:
+ static JavaThread* _the_java_thread;
+
+ static void client_daemon_thread_entry(JavaThread* thread, TRAPS) { daemon_run(thread); }
+
+ static void daemon_run(TRAPS);
+ static int daemon_loop(ClientStream* client_stream, TRAPS);
+
+ static int handle_heartbeat(ClientStream* client_stream);
+
+public:
+ static void start_thread(TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_CLIENT_CLIENTDAEMONTHREAD_HPP
diff --git a/src/hotspot/share/jbooster/client/clientDataManager.cpp b/src/hotspot/share/jbooster/client/clientDataManager.cpp
new file mode 100644
index 000000000..6326b6e8d
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientDataManager.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/client/clientDaemonThread.hpp"
+#include "jbooster/client/clientDataManager.hpp"
+#include "jbooster/client/clientStartupSignal.hpp"
+#include "jbooster/net/clientStream.hpp"
+#include "logging/log.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/java.hpp"
+#include "runtime/timerTrace.hpp"
+#include "utilities/formatBuffer.hpp"
+
+ClientDataManager* ClientDataManager::_singleton = nullptr;
+
+ClientDataManager::ClientDataManager() {
+ _random_id = JBoosterManager::calc_random_id();
+ _startup_end = false;
+ _program_args = nullptr;
+ _program_str_id = nullptr;
+ _cache_dir_path = nullptr;
+
+ _allow_aot = false;
+ _allow_cds = false;
+ _allow_clr = false;
+
+ _using_aot = false;
+ _using_cds = false;
+ _using_clr = false;
+
+ _cache_clr_path = nullptr;
+ _cache_cds_path = nullptr;
+ _cache_aot_path = nullptr;
+
+ _session_id = 0;
+ _program_id = 0;
+ _server_random_id = 0;
+ _server_available = false;
+}
+
+static bool is_option_on(const char* flag_name, const char* options, const char* option) {
+ const char* start = strstr(options, option);
+ if (start == nullptr) return false;
+ if (start == options) return true;
+ if (*(start - 1) == '+') return true;
+ if (*(start - 1) == '-') return false;
+ vm_exit_during_initialization(err_msg("Failed to parse \"%s\" in \"%s\" of %s.", option, options, flag_name));
+ return false;
+}
+
+void ClientDataManager::init_client_vm_options() {
+ // manage boost packages
+ if (!FLAG_IS_DEFAULT(BoostStopAtLevel) && !FLAG_IS_DEFAULT(UseBoostPackages)) {
+ vm_exit_during_initialization("Either BoostStopAtLevel or UseBoostPackages can be set.");
+ }
+
+ if (!FLAG_IS_DEFAULT(UseBoostPackages)) {
+ if (strcmp(UseBoostPackages, "all") == 0) {
+ _allow_clr = _allow_cds = _allow_aot = _allow_pgo = true;
+ } else {
+ _allow_clr = is_option_on("UseBoostPackages", UseBoostPackages, "clr");
+ _allow_cds = is_option_on("UseBoostPackages", UseBoostPackages, "cds");
+ _allow_aot = is_option_on("UseBoostPackages", UseBoostPackages, "aot");
+ _allow_pgo = is_option_on("UseBoostPackages", UseBoostPackages, "pgo");
+ if (_allow_pgo) _allow_aot = true;
+ }
+ } else {
+ switch (BoostStopAtLevel) {
+ case 4: _allow_pgo = true;
+ case 3: _allow_aot = true;
+ case 2: _allow_cds = true;
+ case 1: _allow_clr = true;
+ case 0: break;
+ default: break;
+ }
+ }
+
+ if (JBoosterStartupSignal != nullptr) {
+ ClientStartupSignal::init_phase1();
+ }
+}
+
+void ClientDataManager::init_const() {
+ _cache_dir_path = JBoosterCachePath;
+ _program_args = new JClientArguments(true);
+ _program_str_id = JBoosterManager::calc_program_string_id(_program_args->program_name(),
+ _program_args->program_entry(),
+ _program_args->is_jar(),
+ _program_args->hash());
+ _cache_clr_path = JBoosterManager::calc_cache_path(_cache_dir_path, _program_str_id, "clr.log");
+ _cache_cds_path = JBoosterManager::calc_cache_path(_cache_dir_path, _program_str_id, "cds.jsa");
+ _cache_aot_path = JBoosterManager::calc_cache_path(_cache_dir_path, _program_str_id, "aot.so");
+}
+
+void ClientDataManager::init_client_duty() {
+ // Connect to jbooster before initializing CDS, before loading java_lang_classes
+ // and before starting the compiler threads.
+ ClientStream client_stream(JBoosterAddress, JBoosterPort, JBoosterTimeout, nullptr);
+ int rpc_err = client_stream.connect_and_init_session(&_using_clr, &_using_cds, &_using_aot);
+ if (_using_aot && _allow_pgo) _using_pgo = true;
+ set_server_available(rpc_err == 0);
+}
+
+void ClientDataManager::init_client_duty_under_local_mode() {
+ set_server_available(false);
+ _using_clr = FileUtils::is_file(_cache_clr_path);
+ _using_cds = FileUtils::is_file(_cache_cds_path);
+ _using_aot = FileUtils::is_file(_cache_aot_path);
+ if (_using_aot && _allow_pgo) _using_pgo = true;
+}
+
+jint ClientDataManager::init_clr_options() {
+ if (!is_clr_allowed()) return JNI_OK;
+ return JNI_OK;
+}
+
+jint ClientDataManager::init_cds_options() {
+ if (!is_cds_allowed()) return JNI_OK;
+ return JNI_OK;
+}
+
+jint ClientDataManager::init_aot_options() {
+ if (!is_aot_allowed()) return JNI_OK;
+ return JNI_OK;
+}
+
+jint ClientDataManager::init_pgo_options() {
+ if (!is_pgo_allowed()) return JNI_OK;
+ return JNI_OK;
+}
+
+void ClientDataManager::print_init_state() {
+ log_info(jbooster)("Using boost packages:\n"
+ " - CLR: allowed_to_use=%s,\tis_being_used=%s\n"
+ " - CDS: allowed_to_use=%s,\tis_being_used=%s\n"
+ " - AOT: allowed_to_use=%s,\tis_being_used=%s\n"
+ " - PGO: allowed_to_use=%s,\tis_being_used=%s",
+ BOOL_TO_STR(is_clr_allowed()), BOOL_TO_STR(is_clr_being_used()),
+ BOOL_TO_STR(is_cds_allowed()), BOOL_TO_STR(is_cds_being_used()),
+ BOOL_TO_STR(is_aot_allowed()), BOOL_TO_STR(is_aot_being_used()),
+ BOOL_TO_STR(is_pgo_allowed()), BOOL_TO_STR(is_pgo_being_used()));
+}
+
+static void check_jbooster_port() {
+ if (JBoosterPort == nullptr) {
+ vm_exit_during_initialization("JBoosterPort is not set!");
+ }
+
+ int len = strlen(JBoosterPort);
+ if (len > 1 && JBoosterPort[0] == '0') {
+ vm_exit_during_initialization(
+ err_msg("JBoosterPort \"%s\" should have no leading zero.", JBoosterPort)
+ );
+ }
+ for (int i = 0; i < len; ++i) {
+ if (!isdigit(JBoosterPort[i])) {
+ vm_exit_during_initialization(
+ err_msg("JBoosterPort \"%s\" should contain only digits.", JBoosterPort)
+ );
+ }
+ }
+
+ julong v;
+ if (!Arguments::atojulong(JBoosterPort, &v)) {
+ vm_exit_during_initialization(
+ err_msg("JBoosterPort \"%s\" is not a unsigned integer.", JBoosterPort)
+ );
+ }
+ uint uint_v = (uint) v;
+ if (uint_v < 1024u || uint_v > 65535u) {
+ vm_exit_during_initialization(
+ err_msg("JBoosterPort \"%s\" is outside the allowed range [1024, 65535].", JBoosterPort)
+ );
+ }
+}
+
+static void check_cache_path() {
+ if (JBoosterCachePath == nullptr) {
+ FLAG_SET_DEFAULT(JBoosterCachePath, JBoosterManager::calc_cache_dir_path(true));
+ }
+ FileUtils::mkdirs(JBoosterCachePath);
+}
+
+jint ClientDataManager::init_phase1() {
+ JBoosterManager::client_only();
+
+ if (Arguments::java_command() == nullptr) {
+ if (FLAG_SET_CMDLINE(UseJBooster, false) != JVMFlag::SUCCESS) {
+ return JNI_EINVAL;
+ }
+ return JNI_OK;
+ }
+
+ if (JBoosterLocalMode) {
+ if (JBoosterPort != nullptr) {
+ vm_exit_during_initialization("Either JBoosterLocalMode or JBoosterPort can be set.");
+ }
+
+ if (JBoosterStartupSignal != nullptr) {
+ log_warning(jbooster)("JBoosterStartupSignal will not be used under local mode.");
+ if (FLAG_SET_CMDLINE(JBoosterStartupSignal, nullptr) != JVMFlag::SUCCESS) {
+ return JNI_EINVAL;
+ }
+ }
+ } else {
+ check_jbooster_port();
+ }
+ check_cache_path();
+
+ _singleton = new ClientDataManager();
+ jint jni_err = JNI_OK;
+
+ TraceTime tt("Init cache", TRACETIME_LOG(Info, jbooster));
+ _singleton->init_client_vm_options();
+ _singleton->init_const();
+ if (!JBoosterLocalMode) {
+ _singleton->init_client_duty();
+ } else {
+ _singleton->init_client_duty_under_local_mode();
+ }
+
+ jni_err = _singleton->init_clr_options();
+ if (jni_err != JNI_OK) return jni_err;
+ jni_err = _singleton->init_cds_options();
+ if (jni_err != JNI_OK) return jni_err;
+ jni_err = _singleton->init_aot_options();
+ if (jni_err != JNI_OK) return jni_err;
+ jni_err = _singleton->init_pgo_options();
+ if (jni_err != JNI_OK) return jni_err;
+
+ _singleton->print_init_state();
+ if (!_singleton->is_server_available()) return escape();
+ return JNI_OK;
+}
+
+void ClientDataManager::init_phase2(TRAPS) {
+ JBoosterManager::client_only();
+ if (JBoosterStartupSignal != nullptr) {
+ ClientStartupSignal::init_phase2();
+ }
+ ClientDaemonThread::start_thread(CHECK);
+}
+
+jint ClientDataManager::escape() {
+ if (FLAG_SET_CMDLINE(UseJBooster, false) != JVMFlag::SUCCESS) {
+ return JNI_EINVAL;
+ }
+
+ if(!JBoosterLocalMode){
+ log_error(jbooster)("Rolled back to the original path (UseJBooster=false), since the server is unavailable.");
+ }
+ return JNI_OK;
+}
diff --git a/src/hotspot/share/jbooster/client/clientDataManager.hpp b/src/hotspot/share/jbooster/client/clientDataManager.hpp
new file mode 100644
index 000000000..0a778a07e
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientDataManager.hpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_CLIENT_CLIENTDATAMANAGER_HPP
+#define SHARE_JBOOSTER_CLIENT_CLIENTDATAMANAGER_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/jClientArguments.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+
+class ClientDataManager: public CHeapObj<mtJBooster> {
+ friend class ClientStream;
+
+ static ClientDataManager* _singleton;
+
+ uint64_t _random_id;
+ bool _startup_end;
+ JClientArguments* _program_args;
+ const char* _program_str_id;
+ const char* _cache_dir_path;
+
+ bool _allow_clr;
+ bool _allow_cds;
+ bool _allow_aot;
+
+ bool _using_clr;
+ bool _using_cds;
+ bool _using_aot;
+
+ const char* _cache_clr_path;
+ const char* _cache_cds_path;
+ const char* _cache_aot_path;
+
+ uint32_t _session_id;
+ uint32_t _program_id;
+ uint64_t _server_random_id;
+ bool _server_available;
+
+private:
+ NONCOPYABLE(ClientDataManager);
+
+ ClientDataManager();
+
+ void init_client_vm_options();
+ void init_const();
+ void init_client_duty();
+ void init_client_duty_under_local_mode();
+ jint init_clr_options();
+ jint init_cds_options();
+ jint init_aot_options();
+ jint init_pgo_options();
+ void print_init_state();
+
+protected:
+ void set_session_id(uint32_t sid) { _session_id = sid; }
+ void set_program_id(uint32_t pid) { _program_id = pid; }
+ void set_server_random_id(uint64_t id) { _server_random_id = id; }
+
+public:
+ static ClientDataManager& get() {
+ JBoosterManager::client_only();
+ assert(_singleton != nullptr, "sanity");
+ return *_singleton;
+ }
+
+ static jint init_phase1();
+ static void init_phase2(TRAPS);
+
+ // Escape to the original path without jbooster if the server cannot be connected.
+ static jint escape();
+
+ uint64_t random_id() { return _random_id; }
+
+ bool is_startup_end() { return _startup_end; }
+ void set_startup_end() { _startup_end = true; }
+
+ JClientArguments* program_args() { return _program_args; }
+ const char* program_str_id() { return _program_str_id; }
+
+ // $HOME/.jbooster/client
+ const char* cache_dir_path() { return _cache_dir_path; }
+
+ bool is_clr_allowed() { return _allow_clr; }
+ bool is_cds_allowed() { return _allow_cds; }
+ bool is_aot_allowed() { return _allow_aot; }
+
+ bool is_clr_being_used() { return _using_clr; }
+ bool is_cds_being_used() { return _using_cds; }
+ bool is_aot_being_used() { return _using_aot; }
+
+ // <cache_dir>/client/cache-<parogram_str_id>-clr.log
+ const char* cache_clr_path() { return _cache_clr_path; }
+ // <cache_dir>/client/cache-<parogram_str_id>-cds.jsa
+ const char* cache_cds_path() { return _cache_cds_path; }
+ // <cache_dir>/client/cache-<parogram_str_id>-aot.so
+ const char* cache_aot_path() { return _cache_aot_path; }
+
+ uint32_t session_id() { return _session_id; }
+ uint32_t program_id() { return _program_id; }
+
+ uint64_t server_random_id() { return _server_random_id; }
+
+ bool is_server_available() { return _server_available; }
+ void set_server_available(bool avl) { _server_available = avl; }
+};
+
+#endif // SHARE_JBOOSTER_CLIENT_CLIENTDATAMANAGER_HPP
diff --git a/src/hotspot/share/jbooster/client/clientMessageHandler.cpp b/src/hotspot/share/jbooster/client/clientMessageHandler.cpp
new file mode 100644
index 000000000..0ebe350a0
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientMessageHandler.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "cds/dynamicArchive.hpp"
+#include "classfile/classLoaderData.inline.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "jbooster/client/clientDataManager.hpp"
+#include "jbooster/client/clientMessageHandler.hpp"
+#include "jbooster/dataTransmissionUtils.hpp"
+#include "jbooster/lazyAot.hpp"
+#include "jbooster/net/clientStream.hpp"
+#include "jbooster/net/serializationWrappers.inline.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/method.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/timerTrace.hpp"
+#include "utilities/growableArray.hpp"
+
+ClientMessageHandler::ClientMessageHandler(ClientStream* client_stream):
+ _client_stream(client_stream),
+ _no_more_server_message(false) {
+ // Make sure all the members are trivial.
+ memset(&_clent_message_ctx, 0, sizeof(_clent_message_ctx));
+}
+
+int ClientMessageHandler::handle_a_message_from_server(MessageType msg_type) {
+ DebugUtils::assert_thread_in_native();
+
+ _no_more_server_message = false;
+ switch (msg_type) {
+#define MESSAGE_TYPE_CASE(MT) \
+ case MessageType::MT: \
+ JB_RETURN(handle_##MT()); \
+ break; \
+
+ MESSAGES_FOR_CLIENT_TO_HANDLE(MESSAGE_TYPE_CASE)
+#undef MESSAGE_TYPE_CASE
+ default:
+ JB_RETURN(JBErr::BAD_MSG_TYPE);
+ break;
+ }
+ return 0;
+}
+
+int ClientMessageHandler::handle_messages_from_server() {
+ DebugUtils::assert_thread_in_native();
+
+ MessageType msg_type;
+ do {
+ JB_RETURN(cs().recv_request(msg_type));
+ JB_RETURN(handle_a_message_from_server(msg_type));
+ } while (!_no_more_server_message);
+ return 0;
+}
+
+void ClientMessageHandler::trigger_cache_generation_tasks(TriggerTaskPhase phase, TRAPS) {
+ DebugUtils::assert_thread_in_native();
+
+ ClientStream cs(JBoosterAddress, JBoosterPort, JBoosterTimeout, THREAD);
+ ClientStream* client_stream = &cs;
+
+ JB_TRY {
+ JB_THROW(client_stream->connect_and_init_stream());
+ ClientMessageHandler msg_handler(client_stream);
+
+ bool cache_file_task = false;
+ if (ClientDataManager::get().is_clr_allowed() || ClientDataManager::get().is_cds_allowed()) {
+ cache_file_task = (phase == ON_SHUTDOWN);
+ }
+ if (cache_file_task) {
+ JB_THROW(msg_handler.send_cache_file_sync_task());
+ }
+
+ bool lazy_aot_task = false;
+ if (ClientDataManager::get().is_aot_allowed()) {
+ lazy_aot_task = ((phase == ON_STARTUP) || ((phase == ON_SHUTDOWN) && !ClientDataManager::get().is_startup_end()));
+ }
+ if (lazy_aot_task) {
+ JB_THROW(msg_handler.send_lazy_aot_compilation_task());
+ }
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ client_stream->LOG_OR_CRASH();
+ } JB_CATCH_END;
+
+ if (HAS_PENDING_EXCEPTION) {
+ LogTarget(Warning, jbooster) lt;
+ if (lt.is_enabled()) {
+ lt.print("Unhandled exception at ClientMessageHandler::trigger_cache_generation_tasks()!");
+ }
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ }
+}
+
+// ------------------------------- Message Handlers --------------------------------
+
+int ClientMessageHandler::handle_EndOfCurrentPhase() {
+ _no_more_server_message = true;
+ return 0;
+}
+
+static void dump_cds() {
+ JavaThread* THREAD = JavaThread::current();
+ TraceTime tt("Duration CDS", TRACETIME_LOG(Info, jbooster));
+ ThreadInVMfromNative tivm(THREAD);
+ ExceptionMark em(THREAD);
+
+ const char* cds_path = ClientDataManager::get().cache_cds_path();
+ // SharedDynamicArchivePath is "<cds-path>.tmp" here.
+ const char* tmp_cds_path = Arguments::GetSharedDynamicArchivePath();
+
+ // Try to create the tmp file (get the file lock) before DynamicArchive::dump().
+ // Do not try to create the tmp file in FileMapInfo::open_for_write() because if
+ // the file fails to be created, the whole process will exit (see
+ // FileMapInfo::fail_stop()).
+ int fd = JBoosterManager::create_and_open_tmp_cache_file(tmp_cds_path);
+ if (fd < 0) {
+ log_error(jbooster, cds)("Failed to open the tmp cds dump file \"%s\". Skip dump. "
+ "Why is some other process dumping?",
+ tmp_cds_path);
+ return;
+ }
+ os::close(fd);
+ fd = -1;
+
+ bool rename_successful = false;
+ // Skip dump if some other process already dumped.
+ if (!FileUtils::is_file(cds_path)) {
+ DynamicArchive::dump();
+ chmod(tmp_cds_path, S_IREAD);
+ rename_successful = FileUtils::rename(tmp_cds_path, cds_path);
+ } else {
+ log_warning(jbooster, cds)("The cds jsa file \"%s\" already exists. Skip dump.", cds_path);
+ }
+
+ if (!rename_successful) {
+ FileUtils::remove(tmp_cds_path);
+ }
+
+ if (HAS_PENDING_EXCEPTION) {
+ LogTarget(Error, jbooster, cds) lt;
+ if (lt.is_enabled()) {
+ lt.print("ArchiveClassesAtExit has failed:");
+ }
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ }
+}
+
+int ClientMessageHandler::handle_CacheAggressiveCDS() {
+ if (DynamicDumpSharedSpaces && ClientDataManager::get().is_cds_allowed()) {
+ dump_cds();
+ }
+ FileWrapper file(ClientDataManager::get().cache_cds_path(),
+ SerializationMode::SERIALIZE);
+ JB_RETURN(file.send_file(&cs()));
+ return 0;
+}
+
+int ClientMessageHandler::handle_CacheClassLoaderResource() {
+ FileWrapper file(ClientDataManager::get().cache_clr_path(),
+ SerializationMode::SERIALIZE);
+ JB_RETURN(file.send_file(&cs()));
+ return 0;
+}
+
+int ClientMessageHandler::handle_ClassLoaderLocators() {
+ GrowableArray<ClassLoaderLocator> clls;
+ GrowableArray<ClassLoaderData*>* all_loaders = ctx().all_class_loaders;
+ for (GrowableArrayIterator<ClassLoaderData*> iter = all_loaders->begin();
+ iter != all_loaders->end();
+ ++iter) {
+ clls.append(ClassLoaderLocator(*iter));
+ }
+ ArrayWrapper<ClassLoaderLocator> cll_aw(&clls);
+ JB_RETURN(cs().send_response(&cll_aw));
+ return 0;
+}
+
+int ClientMessageHandler::handle_DataOfClassLoaders() {
+ ArrayWrapper<ClassLoaderLocator> cll_aw(false);
+ JB_RETURN(cs().parse_request(&cll_aw));
+ ClassLoaderLocator* loader_locators = cll_aw.get_array<ClassLoaderLocator>();
+ GrowableArray<ClassLoaderChain> chains;
+ for (int i = 0; i < cll_aw.size(); ++i) {
+ ClassLoaderData* data = loader_locators[i].class_loader_data();
+ chains.append(ClassLoaderChain(data));
+ }
+ ArrayWrapper<ClassLoaderChain> aw(&chains);
+ JB_RETURN(cs().send_response(&aw));
+ return 0;
+}
+
+int ClientMessageHandler::handle_KlassLocators() {
+ GrowableArray<KlassLocator> kls;
+ GrowableArray<InstanceKlass*>* klasses = ctx().all_sorted_klasses;
+ for (GrowableArrayIterator<InstanceKlass*> iter = klasses->begin();
+ iter != klasses->end();
+ ++iter) {
+ kls.append(KlassLocator(*iter));
+ }
+ ArrayWrapper<KlassLocator> kl_aw(&kls);
+ JB_RETURN(cs().send_response(&kl_aw));
+ return 0;
+}
+
+int ClientMessageHandler::handle_DataOfKlasses() {
+ Thread* thread = Thread::current();
+ ArrayWrapper<KlassLocator> klasses_aw(false);
+ JB_RETURN(cs().parse_request(&klasses_aw));
+ KlassLocator* klass_locators = klasses_aw.get_array<KlassLocator>();
+ ResourceMark rm;
+ InstanceKlass** klasses = NEW_RESOURCE_ARRAY(InstanceKlass*, klasses_aw.size());
+ for (int i = 0; i < klasses_aw.size(); ++i) {
+ KlassLocator& w = klass_locators[i];
+ Handle loader(thread, w.class_loader_locator().class_loader_data()->class_loader());
+ Klass* k = SystemDictionary::find_instance_klass(w.class_name(), loader, Handle());
+ if (k == nullptr) {
+ klasses[i] = nullptr;
+ ResourceMark rm;
+ log_warning(jbooster, compilation)("Unresolved class \"%s\".",
+ w.class_name()->as_C_string());
+ } else {
+ klasses[i] = InstanceKlass::cast(k);
+ }
+ }
+ ArrayWrapper<InstanceKlass> klass_array(klasses, klasses_aw.size());
+ JB_RETURN(cs().send_response(&klass_array));
+ return 0;
+}
+
+int ClientMessageHandler::handle_MethodLocators() {
+ GrowableArray<MethodLocator> mls;
+ GrowableArray<Method*>* methods;
+ bool to_compile;
+ JB_RETURN(cs().parse_request(&to_compile));
+ if (to_compile) {
+ methods = ctx().methods_to_compile;
+ } else {
+ methods = ctx().methods_not_compile;
+ }
+ for (GrowableArrayIterator<Method*> iter = methods->begin();
+ iter != methods->end();
+ ++iter) {
+ mls.append(MethodLocator(*iter));
+ }
+ ArrayWrapper<MethodLocator> ml_aw(&mls);
+ JB_RETURN(cs().send_response(&ml_aw));
+ return 0;
+}
+
+int ClientMessageHandler::handle_Profilinginfo() {
+ ResourceMark rm;
+ ArrayWrapper<uintptr_t> aw(false);
+ JB_RETURN(cs().parse_request(&aw));
+ uintptr_t* klass_array = aw.get_array<uintptr_t>();
+ ProfileDataCollector data_collector(aw.size(), (InstanceKlass**) klass_array);
+ JB_RETURN(cs().send_response(&data_collector));
+ return 0;
+}
+
+int ClientMessageHandler::handle_ArrayKlasses() {
+ ResourceMark rm;
+ GrowableArray<ArrayKlass*>* array_klasses = ctx().array_klasses;
+ ArrayWrapper<ArrayKlass> klass_array(array_klasses);
+ JB_RETURN(cs().send_response(&klass_array));
+ return 0;
+}
+
+// ---------------------------------- Some Tasks -----------------------------------
+
+int ClientMessageHandler::send_cache_file_sync_task() {
+ DebugUtils::assert_thread_nonjava_or_in_native();
+ JB_RETURN(cs().send_request(MessageType::CacheFilesSyncTask));
+ JB_RETURN(handle_messages_from_server());
+ return 0;
+}
+
+int ClientMessageHandler::send_lazy_aot_compilation_task() {
+ DebugUtils::assert_thread_nonjava_or_in_native();
+ JavaThread* THREAD = JavaThread::current();
+ HandleMark hm(THREAD);
+ ResourceMark rm(THREAD);
+
+ JB_RETURN(cs().send_request(MessageType::LazyAOTCompilationTask));
+ bool should_send_klasses;
+ JB_RETURN(cs().recv_response(&should_send_klasses));
+ if (!should_send_klasses) {
+ log_info(jbooster, compilation)("The server doesn't need klass data now.");
+ return 0;
+ }
+
+ // Keep the class loaders alive until the end of this function.
+ ClassLoaderKeepAliveMark clka;
+
+ GrowableArray<ClassLoaderData*> all_loaders;
+ GrowableArray<InstanceKlass*> klasses_to_compile;
+ GrowableArray<Method*> methods_to_compile;
+ GrowableArray<Method*> methods_not_compile;
+ GrowableArray<InstanceKlass*> all_sorted_klasses;
+ GrowableArray<ArrayKlass*> array_klasses;
+
+ {
+ TraceTime tt("Duration AOT", TRACETIME_LOG(Info, jbooster));
+ LazyAOT::collect_all_klasses_to_compile(clka,
+ &all_loaders,
+ &klasses_to_compile,
+ &methods_to_compile,
+ &methods_not_compile,
+ &all_sorted_klasses,
+ &array_klasses,
+ CHECK_(JBErr::THREAD_EXCEPTION));
+ }
+
+ ctx().all_class_loaders = &all_loaders;
+ ctx().klasses_to_compile = &klasses_to_compile;
+ ctx().methods_to_compile = &methods_to_compile;
+ ctx().methods_not_compile = &methods_not_compile;
+ ctx().all_sorted_klasses = &all_sorted_klasses;
+ ctx().array_klasses = &array_klasses;
+ JB_RETURN(handle_messages_from_server());
+
+ log_info(jbooster, compilation)("All %d klasses have been sent to the server.",
+ all_sorted_klasses.length());
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/client/clientMessageHandler.hpp b/src/hotspot/share/jbooster/client/clientMessageHandler.hpp
new file mode 100644
index 000000000..66844247c
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientMessageHandler.hpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_CLIENT_CLIENTMESSAGEHANDLER_HPP
+#define SHARE_JBOOSTER_CLIENT_CLIENTMESSAGEHANDLER_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/messageType.hpp"
+
+#define MESSAGES_FOR_CLIENT_TO_HANDLE(F) \
+ F(EndOfCurrentPhase ) \
+ F(CacheAggressiveCDS ) \
+ F(CacheClassLoaderResource) \
+ F(ClassLoaderLocators ) \
+ F(DataOfClassLoaders ) \
+ F(KlassLocators ) \
+ F(DataOfKlasses ) \
+ F(MethodLocators ) \
+ F(Profilinginfo ) \
+ F(ArrayKlasses ) \
+
+class ArrayKlass;
+class ClassLoaderData;
+class ClientStream;
+template <typename T> class GrowableArray;
+class InstanceKlass;
+class JavaThread;
+class Method;
+
+/**
+ * Try not to use TRAPS here because these functions may be called on any thread.
+ */
+class ClientMessageHandler: public StackObj {
+public:
+ enum TriggerTaskPhase {
+ ON_STARTUP,
+ ON_SHUTDOWN
+ };
+
+ /**
+ * All the members should be trivial.
+ */
+ struct ClientMessageContext {
+ GrowableArray<ClassLoaderData*>* all_class_loaders;
+ GrowableArray<InstanceKlass*>* klasses_to_compile;
+ GrowableArray<Method*>* methods_to_compile;
+ GrowableArray<Method*>* methods_not_compile;
+ GrowableArray<InstanceKlass*>* all_sorted_klasses;
+ GrowableArray<ArrayKlass*>* array_klasses;
+ };
+
+private:
+ ClientStream* _client_stream;
+ ClientMessageContext _clent_message_ctx;
+ bool _no_more_server_message;
+
+private:
+ NONCOPYABLE(ClientMessageHandler);
+
+#define DECLARE_HANDLER(MT) int handle_##MT();
+ MESSAGES_FOR_CLIENT_TO_HANDLE(DECLARE_HANDLER)
+#undef DECLARE_HANDLER
+
+public:
+ ClientMessageHandler(ClientStream* client_stream);
+
+ ClientStream& cs() { return *_client_stream; }
+ ClientMessageContext& ctx() { return _clent_message_ctx; }
+
+ static void trigger_cache_generation_tasks(TriggerTaskPhase phase, TRAPS);
+
+ int handle_a_message_from_server(MessageType msg_type);
+ int handle_messages_from_server();
+
+ int send_cache_file_sync_task();
+ int send_lazy_aot_compilation_task();
+};
+
+#endif // SHARE_JBOOSTER_CLIENT_CLIENTMESSAGEHANDLER_HPP
diff --git a/src/hotspot/share/jbooster/client/clientStartupSignal.cpp b/src/hotspot/share/jbooster/client/clientStartupSignal.cpp
new file mode 100644
index 000000000..65c3233f7
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientStartupSignal.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "jbooster/client/clientStartupSignal.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "memory/oopFactory.hpp"
+#include "oops/typeArrayOop.inline.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "utilities/stringUtils.hpp"
+
+volatile bool ClientStartupSignal::_startup_class_found = false;
+
+Symbol* ClientStartupSignal::_klass_name_sym = nullptr;
+
+const char* ClientStartupSignal::_klass_name = nullptr;
+const char* ClientStartupSignal::_method_name = nullptr;
+const char* ClientStartupSignal::_method_signature = nullptr;
+
+static void guarantee_good_format(bool good) {
+ if (good) return;
+ vm_exit_during_initialization(err_msg(
+ "Invalid format: JBoosterStartupSignal=\"%s\".", JBoosterStartupSignal));
+}
+
+/**
+ * JBoosterStartupSignal follow the format of Method::name_and_sig_as_C_string():
+ * "java.lang.String.matches(Ljava/lang/String;)Z"
+ * This is also ok (but make sure that the method is not overloaded):
+ * "java.lang.String.matches"
+ */
+void ClientStartupSignal::init_x_flag() {
+ char* s = StringUtils::copy_to_heap(JBoosterStartupSignal, mtJBooster);
+ int len = strlen(s);
+ int ke = 0; // klass end
+ int lb = 0, rb = 0;
+ guarantee_good_format(len >= 5 && s[0] != '.' && s[0] != '/' && s[0] != '(');
+ for (int i = 0; i < len; ++i) {
+ switch (s[i]) {
+ case '.': s[i] = '/'; // no break here
+ case '/': if (lb == 0) ke = i; break;
+ case '(':
+ guarantee_good_format(lb == 0);
+ lb = i;
+ break;
+ case ')':
+ guarantee_good_format(lb != 0 && rb == 0);
+ rb = i;
+ break;
+ default: break;
+ }
+ }
+
+ if (lb == 0) {
+ _method_signature = nullptr;
+ } else {
+ _method_signature = StringUtils::copy_to_heap(s + lb, mtJBooster);
+ s[lb] = '\0';
+ }
+ _method_name = StringUtils::copy_to_heap(s + ke + 1, mtJBooster);
+ s[ke] = '\0';
+ _klass_name = StringUtils::copy_to_heap(s, mtJBooster);
+
+ StringUtils::free_from_heap(s);
+
+ if (log_is_enabled(Trace, jbooster)) {
+ log_trace(jbooster)("startup_method_klass=%s", _klass_name);
+ log_trace(jbooster)("startup_method_method=%s", _method_name);
+ log_trace(jbooster)("startup_method_signature=%s", _method_signature);
+ }
+}
+
+void ClientStartupSignal::init_d_flag() {
+ jint res = Arguments::init_jbooster_startup_signal_properties(_klass_name,
+ _method_name,
+ _method_signature);
+ if (res != JNI_OK) {
+ vm_exit_during_initialization("Failed to set -D flags for JBoosterStartupSignal");
+ }
+}
+
+void ClientStartupSignal::init_phase1() {
+ JBoosterManager::client_only();
+ assert(JBoosterStartupSignal != nullptr, "sanity");
+ init_x_flag();
+ init_d_flag();
+}
+
+void ClientStartupSignal::init_phase2() {
+ JBoosterManager::client_only();
+ assert(JBoosterStartupSignal != nullptr, "sanity");
+ _klass_name_sym = SymbolTable::new_symbol(_klass_name);
+}
+
+void ClientStartupSignal::free() {
+ StringUtils::free_from_heap(_klass_name);
+ StringUtils::free_from_heap(_method_name);
+ StringUtils::free_from_heap(_method_signature);
+
+ if (_klass_name_sym != nullptr) {
+ _klass_name_sym->decrement_refcount();
+ _klass_name_sym = nullptr;
+ }
+}
+
+bool ClientStartupSignal::inject_startup_callback(unsigned char** data_ptr, int* data_len, TRAPS) {
+ int len = *data_len;
+
+ // arg1
+ typeArrayOop bytes_oop = oopFactory::new_byteArray(len, CHECK_false);
+ typeArrayHandle bytes_h(THREAD, bytes_oop);
+ const jbyte* src = reinterpret_cast<const jbyte*>(*data_ptr);
+ ArrayAccess<>::arraycopy_from_native(src, bytes_h(), typeArrayOopDesc::element_offset<jbyte>(0), len);
+
+ // arg 2
+ Handle method_name_h = java_lang_String::create_from_str(_method_name, CHECK_false);
+
+ // arg 3
+ Handle method_signature_h = _method_signature == nullptr
+ ? Handle()
+ : java_lang_String::create_from_str(_method_signature, CHECK_false);
+
+ // wrap args
+ JavaValue result(T_ARRAY);
+ JavaCallArguments java_args;
+ java_args.push_oop(bytes_h);
+ java_args.push_oop(method_name_h);
+ java_args.push_oop(method_signature_h);
+
+ // callee klass
+ TempNewSymbol ss_name = SymbolTable::new_symbol("jdk/jbooster/api/JBoosterStartupSignal");
+ Klass* ss_k = SystemDictionary::resolve_or_null(
+ ss_name,
+ Handle(THREAD, SystemDictionary::java_platform_loader()),
+ Handle(), CHECK_false);
+ guarantee(ss_k != nullptr && ss_k->is_instance_klass(), "sanity");
+ InstanceKlass* ss_ik = InstanceKlass::cast(ss_k);
+
+ // call it
+ TempNewSymbol inject_method_name = SymbolTable::new_symbol("injectStartupCallback");
+ TempNewSymbol inject_method_signature = SymbolTable::new_symbol("([BLjava/lang/String;Ljava/lang/String;)[B");
+ JavaCalls::call_static(&result,
+ ss_ik,
+ inject_method_name,
+ inject_method_signature,
+ &java_args, CHECK_false);
+ typeArrayHandle res_bytes_h(THREAD, (typeArrayOop) result.get_oop());
+ if (res_bytes_h.is_null()) return false;
+
+ // copy java byte array to cpp
+ unsigned char* res_bytes_base = reinterpret_cast<unsigned char*>(res_bytes_h->byte_at_addr(0));
+ int res_len = res_bytes_h->length();
+
+ unsigned char* res_bytes = NEW_RESOURCE_ARRAY(unsigned char, res_len);
+ memcpy(res_bytes, res_bytes_base, res_len);
+
+ *data_ptr = res_bytes;
+ *data_len = res_len;
+ return true;
+}
+
+bool ClientStartupSignal::try_inject_startup_callback(unsigned char** data_ptr, int* data_len, TRAPS) {
+ bool success = false;
+ if (Atomic::cmpxchg(&_startup_class_found, false, true) == /* prior value */ false) {
+ success = inject_startup_callback(data_ptr, data_len, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ success = false;
+ }
+ }
+
+ if (success) {
+ log_info(jbooster, startup)("Successfully injected startup callback to: klass=%s, method=%s%s.",
+ ClientStartupSignal::klass_name(),
+ ClientStartupSignal::method_name(),
+ ClientStartupSignal::method_signature());
+ } else {
+ log_warning(jbooster, startup)("Failed to inject startup callback to: klass=%s, method=%s%s.",
+ ClientStartupSignal::klass_name(),
+ ClientStartupSignal::method_name(),
+ ClientStartupSignal::method_signature());
+ if (HAS_PENDING_EXCEPTION) {
+ LogTarget(Warning, jbooster, startup) lt;
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ }
+ }
+
+ return success;
+}
diff --git a/src/hotspot/share/jbooster/client/clientStartupSignal.hpp b/src/hotspot/share/jbooster/client/clientStartupSignal.hpp
new file mode 100644
index 000000000..7641edc41
--- /dev/null
+++ b/src/hotspot/share/jbooster/client/clientStartupSignal.hpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_CLIENT_CLIENTSTARTUPSIGNAL_HPP
+#define SHARE_JBOOSTER_CLIENT_CLIENTSTARTUPSIGNAL_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+
+class InstanceKlass;
+class Method;
+class Symbol;
+
+/**
+ * This class is used to determine whether the current program is
+ * at the end of the startup phase.
+ */
+class ClientStartupSignal: public AllStatic {
+ static volatile bool _startup_class_found;
+
+ static Symbol* _klass_name_sym;
+
+ static const char* _klass_name;
+ static const char* _method_name;
+ static const char* _method_signature;
+
+private:
+ static void init_x_flag();
+ static void init_d_flag();
+ static bool inject_startup_callback(unsigned char** data_ptr, int* data_len, TRAPS);
+
+public:
+ static void init_phase1();
+ static void init_phase2();
+ void free();
+
+ static const char* klass_name() { return _klass_name; }
+ static const char* method_name() { return _method_name; }
+ static const char* method_signature() { return _method_signature; }
+
+ static bool is_target_klass(Symbol* klass_name) { return _klass_name_sym == klass_name; }
+ static bool try_inject_startup_callback(unsigned char** data_ptr, int* data_len, TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_CLIENT_CLIENTSTARTUPSIGNAL_HPP
diff --git a/src/hotspot/share/jbooster/dataTransmissionUtils.cpp b/src/hotspot/share/jbooster/dataTransmissionUtils.cpp
new file mode 100644
index 000000000..5d2235dca
--- /dev/null
+++ b/src/hotspot/share/jbooster/dataTransmissionUtils.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/classLoaderData.inline.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/dataTransmissionUtils.hpp"
+#include "jbooster/net/serializationWrappers.inline.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "oops/methodData.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+
+// -------------------------------- ClassLoaderKey ---------------------------------
+
+ClassLoaderKey::ClassLoaderKey(const ClassLoaderKey& key):
+ _loader_class_name(key._loader_class_name),
+ _loader_name(key._loader_name),
+ _first_loaded_class_name(key._first_loaded_class_name) {
+ // change ref only for the server
+ if (AsJBooster) {
+ if (_loader_class_name != nullptr) {
+ _loader_class_name->increment_refcount();
+ }
+ if (_loader_name != nullptr) {
+ _loader_name->increment_refcount();
+ }
+ if (_first_loaded_class_name != nullptr) {
+ _first_loaded_class_name->increment_refcount();
+ }
+ }
+}
+
+ClassLoaderKey::~ClassLoaderKey() {
+ if (AsJBooster) {
+ if (_loader_class_name != nullptr) {
+ _loader_class_name->decrement_refcount();
+ }
+ if (_loader_name != nullptr) {
+ _loader_name->decrement_refcount();
+ }
+ if (_first_loaded_class_name != nullptr) {
+ _first_loaded_class_name->decrement_refcount();
+ }
+ }
+}
+
+ClassLoaderKey& ClassLoaderKey::operator = (const ClassLoaderKey& other) {
+ if (AsJBooster) {
+ if (_loader_class_name != nullptr) {
+ _loader_class_name->decrement_refcount();
+ }
+ if (_loader_name != nullptr) {
+ _loader_name->decrement_refcount();
+ }
+ if (_first_loaded_class_name != nullptr) {
+ _first_loaded_class_name->decrement_refcount();
+ }
+ }
+
+ _loader_class_name = other._loader_class_name;
+ _loader_name = other._loader_name;
+ _first_loaded_class_name = other._first_loaded_class_name;
+
+ if (AsJBooster) {
+ if (_loader_class_name != nullptr) {
+ _loader_class_name->increment_refcount();
+ }
+ if (_loader_name != nullptr) {
+ _loader_name->increment_refcount();
+ }
+ if (_first_loaded_class_name != nullptr) {
+ _first_loaded_class_name->increment_refcount();
+ }
+ }
+ return *this;
+}
+
+ClassLoaderKey ClassLoaderKey::boot_loader_key() {
+ return ClassLoaderKey(nullptr, nullptr, nullptr);
+}
+bool ClassLoaderKey::is_boot_loader() const {
+ return _loader_class_name == nullptr
+ && _loader_name == nullptr
+ && _first_loaded_class_name == nullptr;
+}
+ClassLoaderKey ClassLoaderKey::platform_loader_key() {
+ return ClassLoaderKey(vmSymbols::jdk_internal_loader_ClassLoaders_PlatformClassLoader(), nullptr, nullptr);
+}
+bool ClassLoaderKey::is_platform_loader() const {
+ return _loader_class_name == vmSymbols::jdk_internal_loader_ClassLoaders_PlatformClassLoader()
+ && _loader_name == nullptr
+ && _first_loaded_class_name == nullptr;
+}
+
+ClassLoaderKey ClassLoaderKey::key_of_class_loader_data(ClassLoaderData* cld) {
+ if (cld->is_boot_class_loader_data()) {
+ return boot_loader_key();
+ } else if (cld->is_platform_class_loader_data()) {
+ return platform_loader_key();
+ }
+
+ Klass* loader_class = cld->class_loader_klass();
+ Symbol* loader_class_name = loader_class == nullptr ? nullptr : loader_class->name();
+ Symbol* loader_name = cld->name();
+ Klass* first_klass = cld->klasses();
+ if (first_klass == nullptr) {
+ return ClassLoaderKey(loader_class_name, loader_name, nullptr);
+ }
+
+ while (first_klass->next_link() != nullptr) {
+ first_klass = first_klass->next_link();
+ }
+ Symbol* first_loaded_class_name = first_klass->name();
+ return ClassLoaderKey(loader_class_name, loader_name, first_loaded_class_name);
+}
+
+// ------------------------------- ClassLoaderChain --------------------------------
+
+void ClassLoaderChain::parse_cld() const {
+ if (!_chain.is_empty()) return; // already parsed
+
+ JavaThread* THREAD = JavaThread::current();
+ ClassLoaderData* cld = _cld;
+ do {
+ assert(cld != nullptr, "sanity");
+ ClassLoaderKey key = ClassLoaderKey::key_of_class_loader_data(cld);
+ _chain.append(ClassLoaderChain::Node(key, (uintptr_t) cld));
+ if (cld->is_boot_class_loader_data()) break;
+ Handle cur_h(THREAD, cld->class_loader());
+ Handle parent_h(THREAD, java_lang_ClassLoader::parent(cur_h()));
+ cld = ClassLoaderData::class_loader_data_or_null(parent_h());
+ if (cld == nullptr) {
+ // [JBOOSTER TODO] this is a temp solution
+ cld = ClassLoaderData::the_null_class_loader_data();
+ }
+ } while (true);
+}
+
+int ClassLoaderChain::serialize(MessageBuffer& buf) const {
+ parse_cld();
+ JB_RETURN(buf.serialize_no_meta(_chain.length()));
+ for (GrowableArrayIterator<Node> it = _chain.begin(); it != _chain.end(); ++it) {
+ uintptr_t addr = (*it).client_cld_addr;
+ ClassLoaderKey key = (*it).key;
+ JB_RETURN(buf.serialize_no_meta(addr));
+ JB_RETURN(buf.serialize_with_meta(key.loader_class_name()));
+ JB_RETURN(buf.serialize_with_meta(key.loader_name()));
+ JB_RETURN(buf.serialize_with_meta(key.first_loaded_class_name()));
+ }
+ return 0;
+}
+
+int ClassLoaderChain::deserialize(MessageBuffer& buf) {
+ assert(_chain.is_empty(), "sanity");
+ int size;
+ JB_RETURN(buf.deserialize_ref_no_meta(size));
+ for (int i = 0; i < size; ++i) {
+ uintptr_t addr;
+ Symbol* loader_class_name = nullptr;
+ Symbol* loader_name = nullptr;
+ Symbol* first_loaded_class_name = nullptr;
+ JB_RETURN(buf.deserialize_ref_no_meta(addr));
+ JB_RETURN(buf.deserialize_with_meta(loader_class_name));
+ JB_RETURN(buf.deserialize_with_meta(loader_name));
+ JB_RETURN(buf.deserialize_with_meta(first_loaded_class_name));
+ _chain.append(ClassLoaderChain::Node(ClassLoaderKey(loader_class_name, loader_name, first_loaded_class_name), addr));
+ }
+ return 0;
+}
+
+// ------------------------------ ClassLoaderLocator -------------------------------
+
+static ClassLoaderData* cld_of_cl_oop(oop class_loader_oop) {
+ return ClassLoaderData::class_loader_data_or_null(class_loader_oop);
+}
+
+ClassLoaderLocator::ClassLoaderLocator(Handle class_loader_h):
+ ClassLoaderLocator(cld_of_cl_oop(class_loader_h())) {}
+
+void ClassLoaderLocator::set_class_loader_data(Handle class_loader_h) {
+ _class_loader_data = cld_of_cl_oop(class_loader_h());
+}
+
+int ClassLoaderLocator::serialize(MessageBuffer& buf) const {
+ if (UseJBooster) {
+ JB_RETURN(buf.serialize_no_meta((uintptr_t) _class_loader_data));
+ } else if (AsJBooster) {
+ if (_class_loader_data != nullptr) {
+ Thread* THREAD = Thread::current();
+ address client_addr = ((ServerStream*) buf.stream())->session_data()
+ ->client_cld_address(_class_loader_data, THREAD);
+ _client_cld_address = (uintptr_t) client_addr;
+ guarantee(_client_cld_address != 0, "client CLD address not in server");
+ } else {
+ guarantee(_client_cld_address != 0, "client CLD address is not specified");
+ }
+ JB_RETURN(buf.serialize_no_meta(_client_cld_address));
+ } else fatal("Not jbooster environment?");
+ return 0;
+}
+
+int ClassLoaderLocator::deserialize(MessageBuffer& buf) {
+ uintptr_t client_addr;
+ JB_RETURN(buf.deserialize_ref_no_meta(client_addr));
+ if (UseJBooster) {
+ _class_loader_data = (ClassLoaderData*) client_addr;
+ } else if (AsJBooster) {
+ _client_cld_address = client_addr;
+ recheck_class_loader_data(buf);
+ } else fatal("Not jbooster environment?");
+ return 0;
+}
+
+ClassLoaderData* ClassLoaderLocator::recheck_class_loader_data(MessageBuffer& buf) {
+ Thread* THREAD = Thread::current();
+ // _class_loader_data can be null if the mapping is not found.
+ _class_loader_data = ((ServerStream*) buf.stream())->session_data()
+ ->server_cld_address((address) _client_cld_address, THREAD);
+ return _class_loader_data;
+}
+
+// --------------------------------- KlassLocator ----------------------------------
+
+KlassLocator::KlassLocator(InstanceKlass* klass):
+ _class_name(klass->name()),
+ _client_klass((uintptr_t) klass),
+ _fingerprint(klass->get_stored_fingerprint()),
+ _class_loader_locator(klass->class_loader_data()) {
+}
+
+int KlassLocator::serialize(MessageBuffer& buf) const {
+ JB_RETURN(buf.serialize_with_meta(_class_name));
+ JB_RETURN(buf.serialize_no_meta(_client_klass));
+ JB_RETURN(buf.serialize_no_meta(_fingerprint));
+ JB_RETURN(buf.serialize_with_meta(&_class_loader_locator));
+ return 0;
+}
+
+int KlassLocator::deserialize(MessageBuffer& buf) {
+ JB_RETURN(buf.deserialize_with_meta(_class_name));
+ JB_RETURN(buf.deserialize_ref_no_meta(_client_klass));
+ JB_RETURN(buf.deserialize_ref_no_meta(_fingerprint));
+ JB_RETURN(buf.deserialize_with_meta(&_class_loader_locator));
+ return 0;
+}
+
+InstanceKlass* KlassLocator::try_to_get_ik(TRAPS) {
+ ClassLoaderData* cld = _class_loader_locator.class_loader_data();
+ if (cld == nullptr) {
+ return nullptr;
+ }
+ Handle loader(THREAD, cld->class_loader());
+ Klass* k = SystemDictionary::resolve_or_null(_class_name, loader, Handle(), THREAD);
+ if (k == nullptr || HAS_PENDING_EXCEPTION) {
+ if (cld->is_boot_class_loader_data() || cld->is_platform_class_loader_data()) {
+ log_warning(jbooster, serialization)("The %s class \"%s\" not found.",
+ cld->is_boot_class_loader_data() ? "boot" : "platform",
+ _class_name->as_C_string());
+ if (HAS_PENDING_EXCEPTION && !PENDING_EXCEPTION->is_a(vmClasses::ClassNotFoundException_klass())) {
+ LogTarget(Warning, jbooster, serialization) lt;
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ }
+ }
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+ return nullptr;
+ }
+ return InstanceKlass::cast(k);
+}
+
+// --------------------------------- MethodLocator ---------------------------------
+
+int MethodLocator::serialize(MessageBuffer& buf) const {
+ JB_RETURN(buf.serialize_no_meta((uintptr_t) _method->method_holder()));
+ JB_RETURN(buf.serialize_with_meta(_method->name()));
+ JB_RETURN(buf.serialize_with_meta(_method->signature()));
+#ifdef ASSERT
+ JB_RETURN(buf.serialize_with_meta(_method->method_holder()->name()));
+#endif
+ return 0;
+}
+
+int MethodLocator::deserialize(MessageBuffer& buf) {
+ Thread* THREAD = Thread::current();
+
+ uintptr_t holder_ptr;
+ JB_RETURN(buf.deserialize_ref_no_meta(holder_ptr));
+ InstanceKlass* holder = (InstanceKlass*) ((ServerStream*) buf.stream())
+ ->session_data()
+ ->server_klass_address((address) holder_ptr, THREAD);
+
+ Symbol* client_name = nullptr;
+ Symbol* client_signature = nullptr;
+ JB_RETURN(buf.deserialize_with_meta(client_name));
+ JB_RETURN(buf.deserialize_with_meta(client_signature));
+ TempNewSymbol client_name_wrapper = client_name;
+ TempNewSymbol client_signature_wrapper = client_signature;
+
+#ifdef ASSERT
+ Symbol* klass_name = nullptr;
+ JB_RETURN(buf.deserialize_with_meta(klass_name));
+ TempNewSymbol klass_name_wrapper = klass_name;
+#endif // ASSERT
+
+ if (holder != nullptr) {
+ assert(klass_name == holder->name(), "sanity");
+ _method = holder->find_method(client_name, client_signature);
+ if (_method == nullptr) {
+ ResourceMark rm(THREAD);
+ log_debug(jbooster, serialization)("Method \"%s%s\" not found in \"%s\".",
+ client_name->as_C_string(),
+ client_signature->as_C_string(),
+ holder->internal_name());
+ }
+ } else {
+ ResourceMark rm(THREAD);
+#ifdef ASSERT
+ log_debug(jbooster, serialization)("The holder of \"%s%s\" is null (should be \"%s\").",
+ client_name->as_C_string(),
+ client_signature->as_C_string(),
+ klass_name->as_C_string());
+#else
+ log_debug(jbooster, serialization)("The holder of \"%s%s\" is null.",
+ client_name->as_C_string(), client_signature->as_C_string());
+#endif // ASSERT
+ _method = nullptr;
+ }
+ return 0;
+}
+
+// ----------------------------- ProfileDataCollector ------------------------------
+
+ProfileDataCollector::ProfileDataCollector(uint32_t size, InstanceKlass** klass_array_base):
+ _klass_size(size),
+ _klass_array_base(klass_array_base) {}
+
+int ProfileDataCollector::serialize(MessageBuffer& buf) const {
+ for (uint32_t i = 0; i < klass_size(); i++) {
+ Array<Method*>* methods = klass_at(i)->methods();
+ uint32_t cnt = 0;
+ for (int j= 0; j < methods->length(); j++) {
+ MethodData* method_data = methods->at(j)->method_data();
+ if (method_data != nullptr && method_data->is_mature()) {
+ ++cnt;
+ }
+ }
+ JB_RETURN(buf.serialize_no_meta(cnt));
+ for (int j= 0; j < methods->length(); j++) {
+ Method* method = methods->at(j);
+ MethodData* method_data = method->method_data();
+ if (method_data != nullptr && method_data->is_mature()) {
+ Symbol* name = method->name();
+ Symbol* signature = method->signature();
+ JB_RETURN(buf.serialize_with_meta(name));
+ JB_RETURN(buf.serialize_with_meta(signature));
+ MemoryWrapper md_mem(method_data, method_data->size_in_bytes());
+ JB_RETURN(buf.serialize_no_meta(md_mem));
+ }
+ }
+ }
+ return 0;
+}
+
+int ProfileDataCollector::deserialize(MessageBuffer& buf) {
+ JavaThread* THREAD = JavaThread::current();
+ ResourceMark rm(THREAD);
+ JClientSessionData* session_data = ((ServerStream*) buf.stream())->session_data();
+
+ for (uint32_t i = 0; i < klass_size(); i++) {
+ ThreadInVMfromNative tiv(THREAD);
+ InstanceKlass* klass = klass_at(i);
+
+ int cnt;
+ JB_RETURN(buf.deserialize_ref_no_meta(cnt));
+ for (int j = 0; j < cnt; ++j) {
+ Symbol* name = nullptr;
+ Symbol* signature = nullptr;
+ JB_RETURN(buf.deserialize_with_meta(name));
+ JB_RETURN(buf.deserialize_with_meta(signature));
+ TempNewSymbol name_wrapper = name;
+ TempNewSymbol signature_wrapper = signature;
+ MemoryWrapper md_mem;
+ JB_RETURN(buf.deserialize_ref_no_meta(md_mem));
+ guarantee(name != nullptr, "sanity");
+ guarantee(signature != nullptr, "sanity");
+ guarantee(!md_mem.is_null(), "sanity");
+
+ Method* method = klass->find_method(name, signature);
+ if (method == nullptr) {
+ log_warning(jbooster, serialization)("Method not found in klass: method=%s%s, klass=%s",
+ name->as_C_string(),
+ signature->as_C_string(),
+ klass->internal_name());
+ continue;
+ }
+ MethodData* method_data = MethodData::create_instance_for_jbooster(method,
+ (int) md_mem.size(), (char*) md_mem.get_memory(), CATCH);
+ session_data->add_method_data((address) method, (address) method_data, THREAD);
+ }
+ }
+ return 0;
+}
+
+InstanceKlass* ProfileDataCollector::klass_at(uint32_t index) const {
+ guarantee(index < _klass_size,"sanity");
+ return _klass_array_base[index];
+}
diff --git a/src/hotspot/share/jbooster/dataTransmissionUtils.hpp b/src/hotspot/share/jbooster/dataTransmissionUtils.hpp
new file mode 100644
index 000000000..b224af873
--- /dev/null
+++ b/src/hotspot/share/jbooster/dataTransmissionUtils.hpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_DATATRANSMISSIONUTILS_HPP
+#define SHARE_JBOOSTER_DATATRANSMISSIONUTILS_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "utilities/growableArray.hpp"
+
+class Symbol;
+class InstanceKlass;
+class MethodData;
+
+/**
+ * To uniquely identify a class loader.
+ */
+class ClassLoaderKey final: public StackObj {
+ // Fields that uniquely identify a class loader.
+ Symbol* _loader_class_name;
+ Symbol* _loader_name;
+ Symbol* _first_loaded_class_name;
+
+public:
+ ClassLoaderKey(Symbol* loader_class_name, Symbol* loader_name, Symbol* first_loaded_class_name):
+ _loader_class_name(loader_class_name),
+ _loader_name(loader_name),
+ _first_loaded_class_name(first_loaded_class_name) { /* no need to increment_refcount() */ }
+ ClassLoaderKey(const ClassLoaderKey& key);
+
+ ~ClassLoaderKey();
+
+ Symbol* loader_class_name() const { return _loader_class_name; }
+ Symbol* loader_name() const { return _loader_name; }
+ Symbol* first_loaded_class_name() const { return _first_loaded_class_name; }
+
+ uintx hash() const {
+ int v = 0;
+ v = v * 31 + primitive_hash(_loader_class_name);
+ v = v * 31 + primitive_hash(_loader_name);
+ v = v * 31 + primitive_hash(_first_loaded_class_name);
+ return v;
+ }
+ bool equals(const ClassLoaderKey& other) const {
+ return _loader_class_name == other._loader_class_name
+ && _loader_name == other._loader_name
+ && _first_loaded_class_name == other._first_loaded_class_name;
+ }
+ ClassLoaderKey& operator = (const ClassLoaderKey& other);
+
+ static ClassLoaderKey boot_loader_key();
+ bool is_boot_loader() const;
+ static ClassLoaderKey platform_loader_key();
+ bool is_platform_loader() const;
+
+ static ClassLoaderKey key_of_class_loader_data(ClassLoaderData* cld);
+};
+
+/**
+ * The parent chain of the target class loader.
+ * With the chain we can rebuild the dependencies for all class loaders on the
+ * server. Notice that we assume that all class loaders follow the parents
+ * delegation model (for now).
+ */
+class ClassLoaderChain: public StackObj {
+public:
+ struct Node: public StackObj {
+ ClassLoaderKey key;
+ uintptr_t client_cld_addr;
+
+ Node(): key(nullptr, nullptr, nullptr), client_cld_addr(0) {}
+ Node(const ClassLoaderKey& key, uintptr_t adr): key(key), client_cld_addr(adr) {}
+ Node(const Node& node): key(node.key), client_cld_addr(node.client_cld_addr) {}
+
+ Node& operator = (const Node& node) {
+ key = node.key;
+ client_cld_addr = node.client_cld_addr;
+ return *this;
+ }
+ };
+
+private:
+ ClassLoaderData* _cld;
+ mutable GrowableArray<Node> _chain;
+
+private:
+ void parse_cld() const;
+
+public:
+ ClassLoaderChain(): _cld(nullptr), _chain() {}
+ ClassLoaderChain(ClassLoaderData* cld): _cld(cld), _chain() {
+ assert(_cld != nullptr, "sanity");
+ }
+ ClassLoaderChain(const ClassLoaderChain& clc): _cld(clc._cld), _chain() {
+ assert(_cld != nullptr, "sanity");
+ }
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ ClassLoaderChain& operator = (const ClassLoaderChain& clc) {
+ _cld = clc._cld;
+ assert(_cld != nullptr, "sanity");
+ assert(_chain.is_empty(), "sanity");
+ return *this;
+ }
+
+ GrowableArray<Node>* chain() { return &_chain; }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(ClassLoaderChain);
+
+/**
+ * To locate the class loader oop or its ClassLoaderData.
+ *
+ * Class loaders are frequently transmitted between the client and server.
+ * Therefore, only the ID (for now we use the pointer) of the loader are
+ * transferred to reduce the data to be transmitted.
+ *
+ * * To transmit complete class loader data to the server, please use the
+ * ClassLoaderData class itself.
+ */
+class ClassLoaderLocator: public StackObj {
+ ClassLoaderData* _class_loader_data;
+ mutable uintptr_t _client_cld_address; // only used by the server
+
+public:
+ ClassLoaderLocator(Handle class_loader_h);
+ ClassLoaderLocator(ClassLoaderData* data): _class_loader_data(data),
+ _client_cld_address(0) {}
+ ClassLoaderLocator(): _class_loader_data(nullptr),
+ _client_cld_address(0) {}
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ void set_class_loader_data(Handle class_loader_h);
+ void set_class_loader_data(ClassLoaderData* cld) { _class_loader_data = cld; }
+ ClassLoaderData* class_loader_data() const { return _class_loader_data; }
+
+ // only used by the server
+ ClassLoaderData* recheck_class_loader_data(MessageBuffer& buf);
+ uintptr_t client_cld_address() { return _client_cld_address; }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(ClassLoaderLocator);
+
+/**
+ * To locate a klass.
+ */
+class KlassLocator: public StackObj {
+ Symbol* _class_name;
+ uintptr_t _client_klass;
+ uint64_t _fingerprint;
+ ClassLoaderLocator _class_loader_locator;
+
+public:
+ KlassLocator(InstanceKlass* klass);
+ KlassLocator(): _class_name(nullptr),
+ _client_klass(0u),
+ _fingerprint(0u),
+ _class_loader_locator((ClassLoaderData*)nullptr) {}
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ void set_class_name(Symbol* class_name) { _class_name = class_name; }
+ Symbol* class_name() const { return _class_name; }
+
+ uintptr_t client_klass() { return _client_klass; }
+
+ void set_fingerprint(uint64_t fingerprint) { _fingerprint = fingerprint; }
+ uint64_t fingerprint() { return _fingerprint; }
+
+ void set_class_loader(Handle class_loader_h) {
+ _class_loader_locator.set_class_loader_data(class_loader_h);
+ }
+ void set_class_loader(ClassLoaderData* class_loader_data) {
+ _class_loader_locator.set_class_loader_data(class_loader_data);
+ }
+
+ ClassLoaderLocator& class_loader_locator() { return _class_loader_locator; }
+
+ InstanceKlass* try_to_get_ik(TRAPS);
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(KlassLocator);
+
+/**
+ * To locate a method.
+ */
+class MethodLocator: public StackObj {
+ Method* _method;
+
+public:
+ MethodLocator(Method* method): _method(method) {}
+ MethodLocator(): _method(nullptr) {}
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ Method* get_method() { return _method; }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(MethodLocator);
+
+/**
+ * To collect method data.
+ */
+class ProfileDataCollector: public StackObj {
+private:
+ uint32_t _klass_size;
+ InstanceKlass** _klass_array_base;
+
+public:
+ ProfileDataCollector(uint32_t size, InstanceKlass** klass_array_base);
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ uint32_t klass_size() const { return _klass_size; }
+ InstanceKlass* klass_at(uint32_t index) const;
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(ProfileDataCollector);
+
+#endif // SHARE_JBOOSTER_DATATRANSMISSIONUTILS_HPP
diff --git a/src/hotspot/share/jbooster/jBoosterManager.cpp b/src/hotspot/share/jbooster/jBoosterManager.cpp
new file mode 100644
index 000000000..9a8959589
--- /dev/null
+++ b/src/hotspot/share/jbooster/jBoosterManager.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/client/clientDataManager.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "logging/log.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/java.hpp"
+#include "runtime/os.hpp"
+#include "utilities/formatBuffer.hpp"
+
+#ifdef ASSERT
+void JBoosterManager::client_only() {
+ assert(UseJBooster, "client only");
+}
+
+void JBoosterManager::server_only() {
+ assert(AsJBooster, "server only");
+}
+#endif // ASSERT
+
+const char* JBoosterManager::calc_cache_dir_path(bool is_client) {
+ const char* fs = FileUtils::separator();
+ const char* user_home = FileUtils::home_path();
+ int buf_len = strlen(user_home) + strlen(fs) * 2 + 9 /* ".jbooster" */ + 6 /* "client" or "server" */ + 1;
+ char* buf = NEW_C_HEAP_ARRAY(char, buf_len, mtJBooster);
+ int cnt = snprintf(buf, buf_len, "%s%s.jbooster%s%s", user_home, fs, fs, is_client ? "client" : "server");
+ guarantee(buf_len == cnt + 1, "sanity");
+ return buf;
+}
+
+const char* JBoosterManager::calc_program_string_id(const char* program_name,
+ const char* program_entry,
+ bool is_jar,
+ uint32_t program_hash) {
+ // use simple class name (if it is a class name)
+ const char* tmp = is_jar ? nullptr : strrchr(program_entry, '.');
+ const char* short_entry = (tmp == nullptr ? program_entry : tmp + 1);
+
+ int max_res_len = -1;
+ char* res = nullptr;
+ int snp_len = 0;
+ if (program_name == nullptr) {
+ // short_entry + '-' + program_hash + '\0'
+ max_res_len = strlen(short_entry) + 1 + 8 + 1;
+ res = NEW_C_HEAP_ARRAY(char, max_res_len, mtJBooster);
+ snp_len = snprintf(res, max_res_len, "%s-%x", short_entry, program_hash);
+ } else {
+ // program_name + '-' + short_entry + '-' + program_hash + '\0'
+ max_res_len = strlen(program_name) + 1 + strlen(short_entry) + 1 + 8 + 1;
+ res = NEW_C_HEAP_ARRAY(char, max_res_len, mtJBooster);
+ snp_len = snprintf(res, max_res_len, "%s-%s-%x", program_name, short_entry, program_hash);
+ }
+ guarantee(snp_len <= max_res_len, "sanity");
+ log_info(jbooster)("Human-readable program string id: \"%s\".", res);
+ return res;
+}
+
+const char* JBoosterManager::calc_cache_path(const char* cache_dir,
+ const char* program_str_id,
+ const char* cache_type) {
+ const char* fs = FileUtils::separator();
+ int buf_len = strlen(cache_dir) + strlen(fs) + 6 + strlen(program_str_id) + 1 + strlen(cache_type) + 1;
+ char* buf = NEW_C_HEAP_ARRAY(char, buf_len, mtJBooster);
+ int cnt = snprintf(buf, buf_len, "%s%scache-%s-%s", cache_dir, fs, program_str_id, cache_type);
+ guarantee(buf_len == cnt + 1, "sanity");
+ return buf;
+}
+
+/**
+ * Returns "<cache-path>.tmp".
+ * The returned tmp path string is allocated on the heap!
+ */
+const char* JBoosterManager::calc_tmp_cache_path(const char* cache_path) {
+ int cache_path_len = strlen(cache_path);
+ const int tmp_cache_path_len = cache_path_len + 5; // ".tmp"
+ char* tmp_cache_path = NEW_C_HEAP_ARRAY(char, tmp_cache_path_len, mtJBooster);
+ snprintf(tmp_cache_path, tmp_cache_path_len, "%s.tmp", cache_path);
+ return tmp_cache_path;
+}
+
+uint64_t JBoosterManager::calc_random_id() {
+ // nano_time + random_int, to make it as unique as possible.
+ return (((uint64_t) os::javaTimeNanos()) >> 4) ^ (((uint64_t) os::random()) << (64 - 8 - 4));
+}
+
+/**
+ * Try to create and open the tmp file atomically (we treat the tmp file
+ * as a lock). The file will not be opened if it already exists.
+ * Returns the file fd.
+ */
+int JBoosterManager::create_and_open_tmp_cache_file(const char* tmp_cache_path) {
+ // Use O_CREAT & O_EXCL to make sure the opened file is created by the
+ // current thread.
+ return os::open(tmp_cache_path, O_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);
+}
+
+jint JBoosterManager::init_common() {
+ if (FLAG_SET_CMDLINE(CalculateClassFingerprint, true) != JVMFlag::SUCCESS) {
+ return JNI_EINVAL;
+ }
+
+ return JNI_OK;
+}
+
+jint JBoosterManager::init_phase1() {
+ // NOTICE: In this phase Thread::current() is not initialized yet.
+ assert(UseJBooster || AsJBooster, "sanity");
+ if (UseJBooster && AsJBooster) {
+ vm_exit_during_initialization("Both UseJBooster and AsJBooster are set?!");
+ }
+
+ jint jni_err = JNI_OK;
+ jni_err = init_common();
+ if (jni_err != JNI_OK) return jni_err;
+
+ if (UseJBooster) {
+ return ClientDataManager::init_phase1();
+ } else if (AsJBooster) {
+ return ServerDataManager::init_phase1();
+ }
+ return JNI_OK;
+}
+
+void JBoosterManager::init_phase2(TRAPS) {
+ assert(UseJBooster == !AsJBooster, "sanity");
+
+ if (UseJBooster) {
+ ClientDataManager::init_phase2(CHECK);
+ } else if (AsJBooster) {
+ ServerDataManager::init_phase2(CHECK);
+ }
+}
diff --git a/src/hotspot/share/jbooster/jBoosterManager.hpp b/src/hotspot/share/jbooster/jBoosterManager.hpp
new file mode 100644
index 000000000..eb42f869a
--- /dev/null
+++ b/src/hotspot/share/jbooster/jBoosterManager.hpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_JBOOSTERMANAGER_HPP
+#define SHARE_JBOOSTER_JBOOSTERMANAGER_HPP
+
+#include "jbooster/jbooster_globals.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/globals.hpp"
+#include "utilities/exceptions.hpp"
+
+/**
+ * Manage some common parts about JBooster.
+ * @see ClientDataManager
+ * @see ServerDataManager
+ */
+class JBoosterManager: public AllStatic {
+public:
+ static const uint32_t _heartbeat_timeout = 4 * 60 * 1000; // 4 minutes
+
+private:
+ static jint init_common();
+
+public:
+ // Invoked in Threads::create_vm().
+ static jint init_phase1();
+ static void init_phase2(TRAPS);
+
+ static void client_only() NOT_DEBUG_RETURN;
+ static void server_only() NOT_DEBUG_RETURN;
+
+ static uint64_t calc_random_id();
+
+ static const char* calc_cache_dir_path(bool is_client);
+ static const char* calc_program_string_id(const char* program_name,
+ const char* program_entry,
+ bool is_jar,
+ uint32_t program_hash);
+ static const char* calc_cache_path(const char* cache_dir,
+ const char* program_str_id,
+ const char* cache_type);
+ static const char* calc_tmp_cache_path(const char* cache_path);
+
+ static int create_and_open_tmp_cache_file(const char* tmp_cache_path);
+
+ static uint32_t heartbeat_timeout() { return _heartbeat_timeout; }
+};
+
+#endif // SHARE_JBOOSTER_JBOOSTERMANAGER_HPP
diff --git a/src/hotspot/share/jbooster/jBoosterSymbols.hpp b/src/hotspot/share/jbooster/jBoosterSymbols.hpp
new file mode 100644
index 000000000..720e17d58
--- /dev/null
+++ b/src/hotspot/share/jbooster/jBoosterSymbols.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_JBOOSTERSYMBOLS_HPP
+#define SHARE_JBOOSTER_JBOOSTERSYMBOLS_HPP
+
+#define JBOOSTER_TEMPLATES(template) \
+ template(receiveConnection_name, "receiveConnection") \
+ template(codesource_signature, "Ljava/security/CodeSource;") \
+ template(getProtectionDomainByURLString_name, "getProtectionDomainByURLString") \
+ template(getProtectionDomainByURLString_signature, "(Ljava/lang/String;)Ljava/security/ProtectionDomain;") \
+
+#endif // SHARE_JBOOSTER_JBOOSTERSYMBOLS_HPP
diff --git a/src/hotspot/share/jbooster/jClientArguments.cpp b/src/hotspot/share/jbooster/jClientArguments.cpp
new file mode 100644
index 000000000..2c32b6482
--- /dev/null
+++ b/src/hotspot/share/jbooster/jClientArguments.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/client/clientDataManager.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/jClientArguments.hpp"
+#include "jbooster/net/communicationStream.hpp"
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "logging/log.hpp"
+#include "logging/logStream.hpp"
+#include "runtime/abstract_vm_version.hpp"
+#include "runtime/arguments.hpp"
+#include "utilities/stringUtils.hpp"
+
+static JClientArguments::CpuArch calc_cpu() {
+#ifdef X86
+ return JClientArguments::CpuArch::CPU_X86;
+#endif
+#ifdef AARCH64
+ return JClientArguments::CpuArch::CPU_AARCH64;
+#endif
+#ifdef ARM
+ return JClientArguments::CpuArch::CPU_ARM;
+#endif
+ return JClientArguments::CpuArch::CPU_UNKOWN;
+}
+
+static uint32_t calc_new_hash(uint32_t old_hash, uint32_t ele_hash) {
+ return 31 * old_hash + ele_hash;
+}
+
+/**
+ * Returns the jar file name as the program name.
+ * > This function is only for the client.
+ * > A new C-heap string will be returned.
+ */
+const char* calc_program_entry_by_jar(const char* app_cp, int app_cp_len) {
+ assert(strlen(FileUtils::separator()) == 1, "sanity");
+ const char* file_start = strrchr(app_cp, FileUtils::separator()[0]);
+ if (file_start == nullptr) file_start = app_cp;
+ else ++file_start;
+ int len = app_cp + app_cp_len - file_start;
+ if (strncmp(file_start + len - 4, ".jar", 4) == 0) len -= 4;
+ char* res = NEW_C_HEAP_ARRAY(char, len + 1, mtJBooster);
+ memcpy(res, file_start, len);
+ res[len] = '\0';
+ return res;
+}
+
+/**
+ * Return the main class full name.
+ * > This function is only for the client.
+ * > A new C-heap string will be returned.
+ */
+const char* calc_program_entry_by_class(const char* full_cmd, int full_cmd_len) {
+ int len = full_cmd_len;
+ const char* clazz_end = strchr(full_cmd, ' ');
+ if (clazz_end != nullptr) len = clazz_end - full_cmd;
+ char* res = NEW_C_HEAP_ARRAY(char, len + 1, mtJBooster);
+ memcpy(res, full_cmd, len);
+ res[len] = '\0';
+ return res;
+}
+
+/**
+ * Returns the hash of the classpath names.
+ * > This function is only for the client.
+ */
+static uint32_t calc_classpath_name_hash(const char *app_cp, int app_cp_strlen) {
+ return StringUtils::hash_code(app_cp, app_cp_strlen + 1);
+}
+
+/**
+ * Returns the hash of the classpath timestamps.
+ * > This function is only for the client.
+ */
+static uint32_t calc_classpath_timestamp_hash(const char *app_cp, int app_cp_strlen) {
+ const int path_len = strlen(os::path_separator());
+ uint32_t h = 0;
+ char* tmp = StringUtils::copy_to_heap(app_cp, app_cp_strlen + 1, mtJBooster);
+ char* p = tmp;
+ while (p != nullptr) {
+ char* nxt = strstr(p, os::path_separator());
+ if (nxt != nullptr) {
+ *nxt = '\0';
+ nxt += path_len;
+ }
+ if (nxt - p != path_len) { // not empty path
+ h = calc_new_hash(h, primitive_hash(FileUtils::modify_time(p)));
+ }
+ p = nxt;
+ }
+ StringUtils::free_from_heap(tmp);
+ return h;
+}
+
+static uint32_t calc_agent_name_hash() {
+ uint32_t h = 0;
+ if (Arguments::init_agents_at_startup()) {
+ for (AgentLibrary* agent = Arguments::agents(); agent != nullptr; agent = agent->next()) {
+ h = calc_new_hash(h, StringUtils::hash_code(agent->name()));
+ }
+ }
+ return h;
+}
+
+/**
+ * Returns the java commands in the cmd line. Different from
+ * Arguments::java_command(), the return value excludes the file path.
+ * > This function is only for the client.
+ * > A new C-heap string will be returned if it is not null.
+ */
+static const char* calc_java_commands_by_jar(const char* full_cmd, int app_cp_len) {
+ if (full_cmd[app_cp_len] == '\0') return nullptr;
+ return StringUtils::copy_to_heap(full_cmd + app_cp_len + 1, mtJBooster);
+}
+
+/**
+ * Return the java commands in the cmd line. Different from
+ * Arguments::java_command(), the return value excludes the class name.
+ * > This function is only for the client.
+ * > A new C-heap string will be returned if it is not null.
+ */
+const char* calc_java_commands_by_class(const char* full_cmd, int full_cmd_len) {
+ const char* start = strchr(full_cmd, ' ');
+ if (start == nullptr) start = full_cmd + full_cmd_len;
+ else ++start;
+ return StringUtils::copy_to_heap(start, mtJBooster);
+}
+
+JClientArguments::JClientArguments(bool is_client) {
+ if (is_client) {
+ init_for_client();
+ _state = INITIALIZED_FOR_CLIENT;
+ } else {
+ _state = NOT_INITIALIZED_FOR_SEREVR;
+ }
+}
+
+JClientArguments::~JClientArguments() {
+ if (_state == OBJECT_MOVED) return; // let the new object to free the members
+
+ if (_state == INITIALIZED_FOR_CLIENT) {
+ // [FOR EACH ARG]
+ // _internal_jvm_info is shared. Do not free it.
+ // _program_name is shared. Do not free it.
+ // _program_entry is shared. Do not free it.
+ // _java_commands is shared. Do not free it.
+ delete _related_flags;
+ } else if (_state == INITIALIZED_FOR_SEREVR) {
+ // [FOR EACH ARG]
+ StringUtils::free_from_heap(_internal_jvm_info);
+ StringUtils::free_from_heap(_program_name);
+ StringUtils::free_from_heap(_program_entry);
+ StringUtils::free_from_heap(_java_commands);
+ delete _related_flags;
+ } else {
+ guarantee(_state == NOT_INITIALIZED_FOR_SEREVR, "sanity");
+ // All the members are not initialized (may be random value).
+ }
+}
+
+void JClientArguments::init_for_client() {
+ const char* full_cmd = Arguments::java_command();
+ const char* app_cp = Arguments::get_appclasspath();
+ int full_cmd_len = strlen(full_cmd);
+ int app_cp_len = strlen(app_cp);
+
+ // The classpath is ignored when using "-jar" and in this case the
+ // app classpath is the jar file path.
+ bool is_jar = strncmp(full_cmd, app_cp, app_cp_len) == 0;
+
+ // [FOR EACH ARG]
+ _cpu_arch = calc_cpu();
+ _jvm_version = Abstract_VM_Version::jvm_version();
+ _internal_jvm_info = Abstract_VM_Version::internal_vm_info_string();
+ _program_name = StringUtils::copy_to_heap(JBoosterProgramName, mtJBooster);
+ _program_entry = is_jar ? calc_program_entry_by_jar(app_cp, app_cp_len)
+ : calc_program_entry_by_class(full_cmd, full_cmd_len);
+ _is_jar = is_jar;
+ _classpath_name_hash = calc_classpath_name_hash(app_cp, app_cp_len);
+ _classpath_timestamp_hash = calc_classpath_timestamp_hash(app_cp, app_cp_len);
+ _agent_name_hash = calc_agent_name_hash();
+ if (JBoosterClientStrictMatch) {
+ _java_commands = is_jar ? calc_java_commands_by_jar(full_cmd, app_cp_len)
+ : calc_java_commands_by_class(full_cmd, full_cmd_len);
+ } else {
+ _java_commands = StringUtils::copy_to_heap("<ignored>", mtJBooster);
+ }
+ _jbooster_allow_clr = ClientDataManager::get().is_clr_allowed();
+ _jbooster_allow_cds = ClientDataManager::get().is_cds_allowed();
+ _jbooster_allow_aot = ClientDataManager::get().is_aot_allowed();
+ _related_flags = new JClientVMFlags(true);
+
+ _hash = calc_hash();
+}
+
+uint32_t JClientArguments::calc_hash() {
+ uint32_t result = 1;
+
+ // [FOR EACH ARG]
+ result = calc_new_hash(result, primitive_hash(_cpu_arch));
+ result = calc_new_hash(result, primitive_hash(_jvm_version));
+ result = calc_new_hash(result, StringUtils::hash_code(_internal_jvm_info));
+ result = calc_new_hash(result, StringUtils::hash_code(_program_name));
+ result = calc_new_hash(result, StringUtils::hash_code(_program_entry));
+ result = calc_new_hash(result, _is_jar);
+ result = calc_new_hash(result, _classpath_name_hash);
+ result = calc_new_hash(result, _classpath_timestamp_hash);
+ result = calc_new_hash(result, _agent_name_hash);
+ result = calc_new_hash(result, StringUtils::hash_code(_java_commands));
+ result = calc_new_hash(result, primitive_hash(_jbooster_allow_clr));
+ result = calc_new_hash(result, primitive_hash(_jbooster_allow_cds));
+ result = calc_new_hash(result, primitive_hash(_jbooster_allow_aot));
+ result = calc_new_hash(result, _related_flags->hash(_jbooster_allow_clr, _jbooster_allow_cds, _jbooster_allow_aot));
+
+ return result;
+}
+
+bool JClientArguments::equals(const JClientArguments* that) const {
+ assert(that != nullptr, "sanity");
+
+ // [FOR EACH ARG]
+ if (this->_cpu_arch != that->_cpu_arch ) return false;
+ if (this->_jvm_version != that->_jvm_version ) return false;
+ if (StringUtils::compare(this->_internal_jvm_info, that->_internal_jvm_info) != 0) return false;
+ if (StringUtils::compare(this->_program_name, that->_program_name) != 0) return false;
+ if (StringUtils::compare(this->_program_entry, that->_program_entry) != 0) return false;
+ if (this->_is_jar != that->_is_jar) return false;
+ if (this->_classpath_name_hash != that->_classpath_name_hash) return false;
+ if (this->_classpath_timestamp_hash != that->_classpath_timestamp_hash) return false;
+ if (this->_agent_name_hash != that->_agent_name_hash) return false;
+ if (StringUtils::compare(this->_java_commands, that->_java_commands) != 0) return false;
+ if (this->_jbooster_allow_clr != that->_jbooster_allow_clr) return false;
+ if (this->_jbooster_allow_cds != that->_jbooster_allow_cds) return false;
+ if (this->_jbooster_allow_aot != that->_jbooster_allow_aot) return false;
+ if (this->_jbooster_allow_pgo != that->_jbooster_allow_pgo) return false;
+ if (!this->_related_flags->equals(that->_related_flags,
+ _jbooster_allow_clr,
+ _jbooster_allow_cds,
+ _jbooster_allow_aot)) return false;
+ return true;
+}
+
+void JClientArguments::print_args(outputStream* st) const {
+ // [FOR EACH ARG]
+ st->print_cr(" hash: %x", _hash);
+ st->print_cr(" cpu_arch: %s", cpu_arch_str(_cpu_arch));
+ st->print_cr(" jvm_version: %u", _jvm_version);
+ st->print_cr(" jvm_info: \"%s\"", _internal_jvm_info);
+ st->print_cr(" program_name: %s", _program_name);
+ st->print_cr(" program_entry: %s", _program_entry);
+ st->print_cr(" is_jar: %s", BOOL_TO_STR(_is_jar));
+ st->print_cr(" classpath_name_hash: %x", _classpath_name_hash);
+ st->print_cr(" classpath_timestamp_hash: %x", _classpath_timestamp_hash);
+ st->print_cr(" agent_name_hash: %x", _agent_name_hash);
+ st->print_cr(" java_commands: \"%s\"", _java_commands);
+ st->print_cr(" allow_clr: %s", BOOL_TO_STR(_jbooster_allow_clr));
+ st->print_cr(" allow_cds: %s", BOOL_TO_STR(_jbooster_allow_cds));
+ st->print_cr(" allow_aot: %s", BOOL_TO_STR(_jbooster_allow_aot));
+ st->print_cr(" allow_pgo: %s", BOOL_TO_STR(_jbooster_allow_pgo));
+ st->print_cr(" vm_flags:");
+ st->print_cr(" hash: %u", _related_flags->hash(_jbooster_allow_clr,
+ _jbooster_allow_cds,
+ _jbooster_allow_aot));
+ _related_flags->print_flags(st);
+}
+
+JClientArguments* JClientArguments::move() {
+ JClientArguments* new_obj = new JClientArguments(*this); // shallow copy!
+ _state = OBJECT_MOVED;
+ return new_obj;
+}
+
+int JClientArguments::serialize(MessageBuffer& buf) const {
+ // [FOR EACH ARG]
+ JB_RETURN(buf.serialize_no_meta(_cpu_arch));
+ JB_RETURN(buf.serialize_no_meta(_jvm_version));
+ JB_RETURN(buf.serialize_with_meta(&_internal_jvm_info));
+ JB_RETURN(buf.serialize_with_meta(&_program_name));
+ JB_RETURN(buf.serialize_with_meta(&_program_entry));
+ JB_RETURN(buf.serialize_no_meta(_is_jar));
+ JB_RETURN(buf.serialize_no_meta(_classpath_name_hash));
+ JB_RETURN(buf.serialize_no_meta(_classpath_timestamp_hash));
+ JB_RETURN(buf.serialize_no_meta(_agent_name_hash));
+ JB_RETURN(buf.serialize_with_meta(&_java_commands));
+ JB_RETURN(buf.serialize_no_meta(_jbooster_allow_clr));
+ JB_RETURN(buf.serialize_no_meta(_jbooster_allow_cds));
+ JB_RETURN(buf.serialize_no_meta(_jbooster_allow_aot));
+ JB_RETURN(buf.serialize_with_meta(_related_flags));
+
+ JB_RETURN(buf.serialize_no_meta(_hash));
+
+ return 0;
+}
+
+int JClientArguments::deserialize(MessageBuffer& buf) {
+ guarantee(_state == NOT_INITIALIZED_FOR_SEREVR, "init twice?");
+
+ // [FOR EACH ARG]
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_cpu_arch));
+ JB_RETURN(buf.deserialize_ref_no_meta(_jvm_version));
+
+ StringWrapper sw_internal_jvm_info;
+ JB_RETURN(buf.deserialize_with_meta(&sw_internal_jvm_info));
+ _internal_jvm_info = sw_internal_jvm_info.export_string();
+
+ StringWrapper sw_program_name;
+ JB_RETURN(buf.deserialize_with_meta(&sw_program_name));
+ _program_name = sw_program_name.export_string();
+
+ StringWrapper sw_program_entry;
+ JB_RETURN(buf.deserialize_with_meta(&sw_program_entry));
+ _program_entry = sw_program_entry.export_string();
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_is_jar));
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_classpath_name_hash));
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_classpath_timestamp_hash));
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_agent_name_hash));
+
+ StringWrapper sw_java_commands;
+ JB_RETURN(buf.deserialize_with_meta(&sw_java_commands));
+ _java_commands = sw_java_commands.export_string();
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_jbooster_allow_clr));
+ JB_RETURN(buf.deserialize_ref_no_meta(_jbooster_allow_cds));
+ JB_RETURN(buf.deserialize_ref_no_meta(_jbooster_allow_aot));
+
+ _related_flags = new JClientVMFlags(false);
+ JB_RETURN(buf.deserialize_with_meta(_related_flags));
+
+ // end of for each arg
+
+ JB_RETURN(buf.deserialize_ref_no_meta(_hash));
+ guarantee(_hash == calc_hash(), "sanity");
+ _state = INITIALIZED_FOR_SEREVR;
+ LogTarget(Info, jbooster) lt;
+ if (lt.is_enabled()) {
+ ResourceMark rm;
+ LogStream ls(lt);
+ ls.print_cr("Received JClientArguments, stream_id=%u:", buf.stream()->stream_id());
+ print_args(&ls);
+ }
+ return 0;
+}
+
+/**
+ * The arg "reason" will only be set when the return value of this function is false.
+ * The value of "reason" should be literal string so you don't have to free it.
+ */
+bool JClientArguments::check_compatibility_with_server(const char** reason) {
+ const char* different_item = nullptr;
+ if (_cpu_arch != calc_cpu()) {
+ different_item = "CPU arch";
+ } else if (_jvm_version != Abstract_VM_Version::jvm_version()) {
+ different_item = "JVM version";
+ } else if (StringUtils::compare(_internal_jvm_info, Abstract_VM_Version::internal_vm_info_string()) != 0) {
+ different_item = "JVM info";
+ } else if (_related_flags->get_UseG1GC() != true) {
+ different_item = "G1 GC";
+ } else {
+ return true;
+ }
+
+ assert(different_item != nullptr, "sanity");
+ *reason = different_item;
+ return false;
+}
+
+const char* JClientArguments::cpu_arch_str(CpuArch cpu_arch) {
+ switch (cpu_arch) {
+ case CpuArch::CPU_UNKOWN: return "unknown";
+ case CpuArch::CPU_X86: return "x86";
+ case CpuArch::CPU_ARM: return "arm";
+ case CpuArch::CPU_AARCH64: return "aarch64";
+ default: break;
+ }
+ return "error";
+}
diff --git a/src/hotspot/share/jbooster/jClientArguments.hpp b/src/hotspot/share/jbooster/jClientArguments.hpp
new file mode 100644
index 000000000..e3574db02
--- /dev/null
+++ b/src/hotspot/share/jbooster/jClientArguments.hpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_JCLIENTARGUMENTS_HPP
+#define SHARE_JBOOSTER_JCLIENTARGUMENTS_HPP
+
+#include "jbooster/jClientVMFlags.hpp"
+#include "jbooster/net/serialization.hpp"
+
+/**
+ * Arguments that identify a program.
+ */
+class JClientArguments final: public CHeapObj<mtJBooster> {
+ enum InitState {
+ NOT_INITIALIZED_FOR_SEREVR,
+ INITIALIZED_FOR_SEREVR,
+ INITIALIZED_FOR_CLIENT,
+ OBJECT_MOVED // all members are shallowly copied to another object
+ };
+
+public:
+ enum class CpuArch {
+ CPU_UNKOWN,
+ CPU_X86,
+ CPU_ARM,
+ CPU_AARCH64
+ };
+
+private:
+ InitState _state;
+ uint32_t _hash;
+
+ // ======== parameters used to identify a program ========
+ // All places with the comment "[FOR EACH ARG]" need to
+ // be updated if you want to add a new argument.
+ // [FOR EACH ARG]
+ CpuArch _cpu_arch;
+ uint32_t _jvm_version;
+ const char* _internal_jvm_info;
+ const char* _program_name;
+ const char* _program_entry;
+ bool _is_jar;
+ uint32_t _classpath_name_hash;
+ uint32_t _classpath_timestamp_hash;
+ uint32_t _agent_name_hash;
+ const char* _java_commands;
+ bool _jbooster_allow_clr;
+ bool _jbooster_allow_cds;
+ bool _jbooster_allow_aot;
+ JClientVMFlags* _related_flags;
+ // ========================= end =========================
+
+private:
+ void init_for_client();
+ uint32_t calc_hash();
+
+public:
+ JClientArguments(bool is_client);
+ ~JClientArguments();
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ // [FOR EACH ARG]
+ CpuArch cpu_arch() const { return _cpu_arch; }
+ uint32_t jvm_version() const { return _jvm_version; }
+ const char* internal_jvm_info() const { return _internal_jvm_info; }
+ const char* program_name() const { return _program_name; }
+ const char* program_entry() const { return _program_entry; }
+ bool is_jar() const { return _is_jar; }
+ uint32_t classpath_name_hash() const { return _classpath_name_hash; }
+ uint32_t classpath_timestamp_hash() const { return _classpath_timestamp_hash; }
+ uint32_t agent_name_hash() const { return _agent_name_hash; }
+ const char* java_commands() const { return _java_commands; }
+ bool jbooster_allow_clr() const { return _jbooster_allow_clr; }
+ bool jbooster_allow_cds() const { return _jbooster_allow_cds; }
+ bool jbooster_allow_aot() const { return _jbooster_allow_aot; }
+ bool jbooster_allow_pgo() const { return _jbooster_allow_pgo; }
+ JClientVMFlags* related_flags() const { return _related_flags; }
+
+ bool equals(const JClientArguments* that) const;
+ uint32_t hash() const { return _hash; }
+ void print_args(outputStream* st) const;
+
+ JClientArguments* move();
+
+ // This method is only used on the server.
+ bool check_compatibility_with_server(const char** reason);
+
+ static const char* cpu_arch_str(CpuArch cpu_arch);
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(JClientArguments);
+
+#endif // SHARE_JBOOSTER_JCLIENTARGUMENTS_HPP
diff --git a/src/hotspot/share/jbooster/jClientVMFlags.cpp b/src/hotspot/share/jbooster/jClientVMFlags.cpp
new file mode 100644
index 000000000..6e46aeb48
--- /dev/null
+++ b/src/hotspot/share/jbooster/jClientVMFlags.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/javaClasses.hpp"
+#include "jbooster/jClientVMFlags.hpp"
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "logging/log.hpp"
+#include "utilities/stringUtils.hpp"
+
+template <typename T>
+class FlagTypeHandler: public AllStatic {
+public:
+ static T constructor(T input) { return input; }
+ static void destructor(T flag) { /* do nothing */ }
+
+ static bool equals(T a, T b) { return a == b; }
+ static uint32_t hash(T flag) { return primitive_hash<T>(flag); }
+
+ static int serialize(MessageBuffer& buf, T flag) {
+ return buf.serialize_no_meta(flag);
+ }
+ static int deserialize(MessageBuffer& buf, T& flag) {
+ return buf.deserialize_ref_no_meta(flag);
+ }
+
+ static void print(outputStream* st, const char* name, T value) { fatal("please specialize it"); }
+};
+
+// ======================= template specialization of ccstr ========================
+
+template <>
+ccstr FlagTypeHandler<ccstr>::constructor(ccstr input) {
+ return StringUtils::copy_to_heap(input, mtJBooster);
+}
+
+template <>
+void FlagTypeHandler<ccstr>::destructor(ccstr flag) {
+ StringUtils::free_from_heap(flag);
+}
+
+template <>
+bool FlagTypeHandler<ccstr>::equals(ccstr a, ccstr b) {
+ return StringUtils::compare(a, b) == 0;
+}
+
+template <>
+uint32_t FlagTypeHandler<ccstr>::hash(ccstr flag) {
+ return StringUtils::hash_code(flag);
+}
+
+template <>
+int FlagTypeHandler<ccstr>::serialize(MessageBuffer& buf, ccstr flag) {
+ return buf.serialize_with_meta(&flag);
+}
+
+template <>
+int FlagTypeHandler<ccstr>::deserialize(MessageBuffer& buf, ccstr& flag) {
+ StringWrapper sw;
+ JB_RETURN(buf.deserialize_with_meta(&sw));
+ flag = sw.export_string();
+ return 0;
+}
+
+template <>
+void FlagTypeHandler<ccstr>::print(outputStream* st, const char* name, ccstr value) {
+ st->print_cr(" %s: %s", name, value);
+}
+
+// ==================== template specialization of other types =====================
+
+template <>
+void FlagTypeHandler<bool>::print(outputStream* st, const char* name, bool value) {
+ st->print_cr(" %s: %s", name, BOOL_TO_STR(value));
+}
+
+template <>
+void FlagTypeHandler<intx>::print(outputStream* st, const char* name, intx value) {
+ st->print_cr(" %s: " INTX_FORMAT, name, value);
+}
+
+template <>
+void FlagTypeHandler<size_t>::print(outputStream* st, const char* name, size_t value) {
+ st->print_cr(" %s: " SIZE_FORMAT, name, value);
+}
+
+// ==================================== macros =====================================
+
+JClientVMFlags::JClientVMFlags(bool is_client) {
+ if (is_client) {
+#define INIT_FLAG(type, flag) v_##flag = FlagTypeHandler<type>::constructor(flag);
+ JCLIENT_VM_FLAGS(INIT_FLAG)
+#undef INIT_FLAG
+
+ _state = INITIALIZED_FOR_CLIENT;
+ } else {
+ _state = NOT_INITIALIZED_FOR_SEREVR;
+ }
+}
+
+JClientVMFlags::~JClientVMFlags() {
+ guarantee(_state != NOT_INITIALIZED_FOR_SEREVR, "sanity");
+#define FREE_FLAG(type, flag) FlagTypeHandler<type>::destructor(v_##flag);
+ JCLIENT_VM_FLAGS(FREE_FLAG)
+#undef FREE_FLAG
+}
+
+bool JClientVMFlags::equals(JClientVMFlags* that, bool allow_clr, bool allow_cds, bool allow_aot) {
+#define CMP_FLAG(type, flag) if (!FlagTypeHandler<type>::equals(this->v_##flag, that->v_##flag)) return false;
+ if (allow_cds) {
+ JCLIENT_CDS_VM_FLAGS(CMP_FLAG)
+ }
+ if (allow_aot) {
+ JCLIENT_AOT_VM_FLAGS(CMP_FLAG)
+ }
+#undef CMP_FLAG
+
+ return true;
+}
+
+uint32_t JClientVMFlags::hash(bool allow_clr, bool allow_cds, bool allow_aot) {
+ uint32_t result = 1;
+#define CALC_FLAG_HASH(type, flag) result = 31 * result + FlagTypeHandler<type>::hash(v_##flag);
+ if (allow_cds) {
+ JCLIENT_CDS_VM_FLAGS(CALC_FLAG_HASH)
+ }
+ if (allow_aot) {
+ JCLIENT_AOT_VM_FLAGS(CALC_FLAG_HASH)
+ }
+#undef CALC_FLAG_HASH
+ return result;
+}
+
+int JClientVMFlags::serialize(MessageBuffer& buf) const {
+#define SERIALIZE_FLAG(type, flag) JB_RETURN(FlagTypeHandler<type>::serialize(buf, v_##flag));
+ JCLIENT_VM_FLAGS(SERIALIZE_FLAG)
+#undef SERIALIZE_FLAG
+ return 0;
+}
+
+int JClientVMFlags::deserialize(MessageBuffer& buf) {
+ guarantee(_state == NOT_INITIALIZED_FOR_SEREVR, "init twice?");
+
+#define DESERIALIZE_FLAG(type, flag) JB_RETURN(FlagTypeHandler<type>::deserialize(buf, v_##flag));
+ JCLIENT_VM_FLAGS(DESERIALIZE_FLAG)
+#undef DESERIALIZE_FLAG
+
+ _state = INITIALIZED_FOR_SEREVR;
+ return 0;
+}
+
+void JClientVMFlags::print_flags(outputStream* st) {
+ if (!log_is_enabled(Trace, jbooster)) return;
+#define PRINT_FLAG(type, flag) FlagTypeHandler<type>::print(st, #flag, v_##flag);
+ JCLIENT_VM_FLAGS(PRINT_FLAG)
+#undef PRINT_FLAG
+}
diff --git a/src/hotspot/share/jbooster/jClientVMFlags.hpp b/src/hotspot/share/jbooster/jClientVMFlags.hpp
new file mode 100644
index 000000000..3cdbc59cf
--- /dev/null
+++ b/src/hotspot/share/jbooster/jClientVMFlags.hpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_JCLIENTVMFLAGS_HPP
+#define SHARE_JBOOSTER_JCLIENTVMFLAGS_HPP
+
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/serialization.hpp"
+#include "runtime/globals_extension.hpp"
+
+#define JCLIENT_CDS_VM_FLAGS(f) \
+ f(bool, DynamicDumpSharedSpaces ) \
+ f(bool, DumpSharedSpaces ) \
+ f(bool, UseSharedSpaces ) \
+ f(ccstr, SharedArchiveFile ) \
+ f(ccstr, SharedArchiveConfigFile ) \
+ f(ccstr, ArchiveClassesAtExit ) \
+ f(size_t, MaxMetaspaceSize ) \
+ f(bool, UseAggressiveCDS ) \
+ f(size_t, SharedBaseAddress ) \
+ f(bool, UseCompressedOops ) \
+ f(bool, UseCompressedClassPointers ) \
+ f(intx, ObjectAlignmentInBytes ) \
+
+#define JCLIENT_AOT_VM_FLAGS(f) \
+ f(intx, CodeEntryAlignment ) \
+ f(bool, EnableContended ) \
+ f(bool, RestrictContended ) \
+ f(intx, ContendedPaddingWidth ) \
+ f(bool, VerifyOops ) \
+ f(bool, DontCompileHugeMethods ) \
+ f(intx, HugeMethodLimit ) \
+ f(bool, Inline ) \
+ f(bool, ForceUnreachable ) \
+ f(bool, FoldStableValues ) \
+ f(intx, MaxVectorSize ) \
+ f(bool, UseTLAB ) \
+ f(bool, UseG1GC ) \
+
+#define JCLIENT_VM_FLAGS(f) \
+ JCLIENT_CDS_VM_FLAGS(f) \
+ JCLIENT_AOT_VM_FLAGS(f) \
+
+class JClientVMFlags final: public CHeapObj<mtJBooster> {
+ enum InitState {
+ NOT_INITIALIZED_FOR_SEREVR,
+ INITIALIZED_FOR_SEREVR,
+ INITIALIZED_FOR_CLIENT
+ };
+
+ InitState _state;
+
+#define DEF_VARIABLES(type, flag) type v_##flag;
+ JCLIENT_VM_FLAGS(DEF_VARIABLES)
+#undef DEF_VARIABLES
+
+public:
+ JClientVMFlags(bool is_client);
+ ~JClientVMFlags();
+
+#define DEF_GETTERS(type, flag) type get_##flag() const { return v_##flag; }
+ JCLIENT_VM_FLAGS(DEF_GETTERS)
+#undef DEF_GETTERS
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ bool equals(JClientVMFlags* that, bool allow_clr, bool allow_cds, bool allow_aot);
+ uint32_t hash(bool allow_clr, bool allow_cds, bool allow_aot);
+
+ void print_flags(outputStream* st);
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(JClientVMFlags);
+
+#endif // SHARE_JBOOSTER_JCLIENTVMFLAGS_HPP
diff --git a/src/hotspot/share/jbooster/jbooster_globals.hpp b/src/hotspot/share/jbooster/jbooster_globals.hpp
new file mode 100644
index 000000000..45aa00db5
--- /dev/null
+++ b/src/hotspot/share/jbooster/jbooster_globals.hpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_JBOOSTER_GLOBALS_HPP
+#define SHARE_JBOOSTER_JBOOSTER_GLOBALS_HPP
+
+#include "runtime/globals_shared.hpp"
+
+#define JBOOSTER_FLAGS(develop, \
+ develop_pd, \
+ product, \
+ product_pd, \
+ notproduct, \
+ range, \
+ constraint) \
+ \
+ product(bool, UseJBooster, false, EXPERIMENTAL, \
+ "Use and connect to a JBooster server.") \
+ \
+ product(bool, AsJBooster, false, EXPERIMENTAL, \
+ "Play the role of the JBooster server. " \
+ "This flag is automatically set in VM.") \
+ \
+ product(ccstr, JBoosterAddress, "127.0.0.1", \
+ "Address of the JBooster server. Default: '127.0.0.1'.") \
+ \
+ product(ccstr, JBoosterPort, NULL, \
+ "Port of the JBooster server.") \
+ \
+ product(uint, JBoosterTimeout, 4'000, \
+ "Timeout of the JBooster connection. Default: 4,000 ms.") \
+ \
+ product(bool, JBoosterExitIfUnsupported, true, \
+ "Exit the VM if the client uses features " \
+ "that are not supported by the server.") \
+ \
+ product(bool, JBoosterCrashIfNoServer, false, DIAGNOSTIC, \
+ "Exit the VM if the server is not available.") \
+ \
+ product(ccstr, JBoosterProgramName, NULL, \
+ "Unique name of current app.") \
+ \
+ product(ccstr, JBoosterCachePath, NULL, \
+ "The directory path for JBooster caches " \
+ "(default: $HOME/.jbooster/client).") \
+ \
+ product(bool, JBoosterLocalMode, false, \
+ "No connection to the server and uses only the local cache.") \
+ \
+ product(ccstr, JBoosterStartupSignal, NULL, \
+ "The first invocation of the signal method means the end of " \
+ "the client start-up phase. " \
+ "The relevant logic is executed at exit if it's not set.") \
+ \
+ product(int, JBoosterStartupMaxTime, 600, \
+ "Max seconds required for the start-up phase (0 means off). " \
+ "A plan B when JBoosterStartupSignal fails.") \
+ range(0, max_jint) \
+ \
+ product(int, BoostStopAtLevel, 3, \
+ "0 for no optimization; 1 with class loader resource cache; " \
+ "2 with aggressive CDS; 3 with lazy AOT; 4 with PGO.") \
+ range(0, 4) \
+ \
+ product(ccstr, UseBoostPackages, "all", DIAGNOSTIC, \
+ "\"all\" means \"aot+cds+clr\".") \
+ \
+ product(bool, JBoosterClientStrictMatch, false, DIAGNOSTIC, \
+ "Be strict when matching the client data.") \
+ \
+ product(bool, PrintAllClassInfo, false, DIAGNOSTIC, \
+ "Print info of all class loaders and all classes " \
+ "at startup or at exit.") \
+ \
+ product(bool, UseAggressiveCDS, false, EXPERIMENTAL, \
+ "An aggressive stratage to improve start-up " \
+ "because we avoid decoding the classfile.") \
+ \
+ product(bool, CheckClassFileTimeStamp, true, EXPERIMENTAL, \
+ "Check whether the modification time of the" \
+ "class file is changed during UseAggressiveCDS.") \
+ \
+ product(bool, UseClassLoaderResourceCache, false, EXPERIMENTAL, \
+ "Cache and share the name-url pairs in " \
+ "java.net.URLClassLoader#findResource.") \
+ \
+ product(ccstr, DumpClassLoaderResourceCacheFile, NULL, \
+ "The file path to dump class loader resource cache.") \
+ \
+ product(ccstr, LoadClassLoaderResourceCacheFile, NULL, \
+ "The file path to laod class loader resource cache.") \
+ \
+ product(uint, ClassLoaderResourceCacheSizeEachLoader, 2000, \
+ "Max number of entries that can be cached in each " \
+ "class loader (delete old values based on LRU).") \
+ \
+ product(bool, ClassLoaderResourceCacheVerboseMode, false, DIAGNOSTIC, \
+ "Dump/load more data for verification and debugging.") \
+
+
+// end of JBOOSTER_FLAGS
+
+DECLARE_FLAGS(JBOOSTER_FLAGS)
+
+#endif // SHARE_JBOOSTER_JBOOSTER_GLOBALS_HPP
diff --git a/src/hotspot/share/jbooster/lazyAot.cpp b/src/hotspot/share/jbooster/lazyAot.cpp
new file mode 100644
index 000000000..ca79f1816
--- /dev/null
+++ b/src/hotspot/share/jbooster/lazyAot.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/classLoaderData.inline.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmClasses.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/lazyAot.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/debugUtils.hpp"
+#include "jbooster/utilities/ptrHashSet.inline.hpp"
+#include "memory/resourceArea.inline.hpp"
+#include "oops/instanceKlass.inline.hpp"
+#include "oops/method.inline.hpp"
+#include "oops/methodData.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/timerTrace.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/growableArray.hpp"
+
+enum {
+ ALL_KLASSES,
+ RESOLVED_KLASSES,
+ INITIALIZED_KLASSES
+};
+
+InstanceKlass* LazyAOT::get_klass_from_class_tag(const constantPoolHandle& cp,
+ int index,
+ Handle loader,
+ int which_klasses,
+ TRAPS) {
+ Klass* klass = nullptr;
+ if (which_klasses == ALL_KLASSES) {
+ // resolve class if not resolved
+ klass = cp->klass_at(index, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ ResourceMark rm;
+ CLEAR_PENDING_EXCEPTION;
+ CPKlassSlot kslot = cp->klass_slot_at(index);
+ int name_index = kslot.name_index();
+ Symbol* name = cp->symbol_at(name_index);
+ klass = SystemDictionary::resolve_or_fail(name, loader, Handle(), true, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ log_trace(jbooster, compilation)("Class in constant pool not found: "
+ "class=\"%s\", loader=\"%s\", holder=\"%s\"",
+ name->as_C_string(),
+ ClassLoaderData::class_loader_data_or_null(loader())
+ ->loader_name(),
+ cp->pool_holder()->internal_name());
+ CLEAR_PENDING_EXCEPTION;
+ return nullptr;
+ }
+ }
+ if (klass == nullptr || !klass->is_instance_klass()) return nullptr;
+ return InstanceKlass::cast(klass);
+ } else { // RESOLVED_KLASSES or INITIALIZED_KLASSES
+ klass = ConstantPool::klass_at_if_loaded(cp, index);
+ if (klass == nullptr || !klass->is_instance_klass()) return nullptr;
+ InstanceKlass* ik = InstanceKlass::cast(klass);
+ if (which_klasses == INITIALIZED_KLASSES && !ik->is_initialized()) {
+ return nullptr;
+ }
+ return ik;
+ }
+}
+
+void LazyAOT::get_klasses_from_name_and_type_tag(const constantPoolHandle& cp,
+ int index,
+ Handle loader,
+ GrowableArray<InstanceKlass*> &res,
+ int which_klasses,
+ TRAPS) {
+ int sym_index = cp->signature_ref_index_at(index);
+ Symbol* sym = cp->symbol_at(sym_index);
+ // The sym could be like "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
+ // or "Ljava/lang/Object;".
+ for (SignatureStream ss(sym, Signature::is_method(sym)); !ss.is_done(); ss.next()) {
+ if (ss.is_array()) ss.skip_array_prefix();
+ if (ss.is_reference()) {
+ assert(!ss.is_array(), "sanity");
+ SignatureStream::FailureMode mode = which_klasses == ALL_KLASSES
+ ? SignatureStream::ReturnNull
+ : SignatureStream::CachedOrNull;
+ Klass* klass = ss.as_klass(loader, Handle(), mode, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ ResourceMark rm;
+ log_trace(jbooster, compilation)("Class in constant pool not found: "
+ "method=\"%s\", loader=\"%s\", holder=\"%s\"",
+ sym->as_C_string(),
+ ClassLoaderData::class_loader_data_or_null(loader())
+ ->loader_name(),
+ cp->pool_holder()->internal_name());
+ CLEAR_PENDING_EXCEPTION;
+ continue;
+ }
+ if (klass == nullptr || !klass->is_instance_klass()) continue;
+ InstanceKlass* ik = InstanceKlass::cast(klass);
+ if (which_klasses == INITIALIZED_KLASSES && !ik->is_initialized()) {
+ continue;
+ }
+ res.append(ik);
+ }
+ }
+}
+
+void LazyAOT::collect_klasses_by_inheritance(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ InstanceKlass* ik,
+ TRAPS) {
+ if (!vis->add(ik)) return;
+ InstanceKlass* super = ik->java_super();
+ if (super != nullptr) {
+ collect_klasses_by_inheritance(dst, vis, super, THREAD);
+ }
+ Array<InstanceKlass*>* interfaces = ik->local_interfaces();
+ for (int i = 0; i < interfaces->length(); ++i) {
+ InstanceKlass* interface = interfaces->at(i);
+ collect_klasses_by_inheritance(dst, vis, interface, THREAD);
+ }
+ dst->append(ik);
+}
+
+void LazyAOT::collect_klasses_in_constant_pool(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ InstanceKlass* ik,
+ int which_klasses,
+ TRAPS) {
+ constantPoolHandle cp(THREAD, ik->constants());
+ Handle class_loader(THREAD, ik->class_loader());
+ for (int i = 0; i < cp->length(); ++i) {
+ constantTag tag = cp->tag_at(i);
+ if (tag.is_unresolved_klass() || tag.is_klass()) {
+ InstanceKlass* next = get_klass_from_class_tag(cp, i, class_loader, which_klasses, THREAD);
+ if (next != nullptr) { // next is null when it is not a InstanceKlass
+ collect_klasses_by_inheritance(dst, vis, next, THREAD);
+ }
+ } else if (tag.is_name_and_type()) {
+ GrowableArray<InstanceKlass*> next_list;
+ get_klasses_from_name_and_type_tag(cp, i, class_loader, next_list, which_klasses, THREAD);
+ for (GrowableArrayIterator<InstanceKlass*> iter = next_list.begin();
+ iter != next_list.end();
+ ++iter) {
+ collect_klasses_by_inheritance(dst, vis, *iter, THREAD);
+ }
+ }
+ }
+}
+
+void LazyAOT::collect_klasses_in_constant_pool(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ TRAPS) {
+ int last_len = 0;
+ for (int lvl = _compilation_related_klasses_layers; lvl > 0; --lvl) {
+ int len = dst->length();
+ for (int i = last_len; i < len; ++i) {
+ ThreadInVMfromNative tiv(THREAD);
+ collect_klasses_in_constant_pool(dst, vis, dst->at(i), ALL_KLASSES, THREAD);
+ }
+ last_len = len;
+ }
+}
+
+void LazyAOT::collect_klasses_in_method_data(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ PtrHashSet<InstanceKlass*>* ik_vis,
+ TRAPS) {
+ int last_len = 0;
+ PtrHashSet<ArrayKlass*> ak_vis;
+ for (int lvl = _compilation_related_klasses_layers; lvl > 0; --lvl) {
+ int len = dst_ik->length();
+ for (int i = last_len; i < len; ++i) {
+ Array<Method*>* methods = dst_ik->at(i)->methods();
+ for (int j = 0; j < methods->length(); j++) {
+ MethodData* method_data = methods->at(j)->method_data();
+ if (method_data != nullptr) {
+ collect_klasses_in_method_data(dst_ik, dst_ak, ik_vis, &ak_vis,
+ method_data, ALL_KLASSES, THREAD);
+ }
+ }
+ }
+ last_len = len;
+ }
+}
+
+void LazyAOT::collect_klasses_in_method_data(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ PtrHashSet<InstanceKlass*>* ik_vis,
+ PtrHashSet<ArrayKlass*>* ak_vis,
+ MethodData* method_data,
+ int which_klasses,
+ TRAPS) {
+ int position = 0;
+ while (position < method_data->data_size()) {
+ ProfileData* profile_data = method_data->data_at(position);
+ GrowableArray<InstanceKlass*> ik_array;
+ GrowableArray<ArrayKlass*> ak_array;
+ profile_data->collect_klass(&ik_array, &ak_array);
+ for (GrowableArrayIterator<InstanceKlass*> iter = ik_array.begin();
+ iter != ik_array.end();
+ ++iter) {
+ if (which_klasses == INITIALIZED_KLASSES && !(*iter)->is_initialized()) {
+ continue;
+ }
+ collect_klasses_by_inheritance(dst_ik, ik_vis, *iter, THREAD);
+ }
+ for (GrowableArrayIterator<ArrayKlass*> iter = ak_array.begin();
+ iter != ak_array.end();
+ ++iter) {
+ if (!ak_vis->add(*iter)) continue;
+ dst_ak->append(*iter);
+ }
+ position += profile_data->size_in_bytes();
+ }
+}
+
+bool LazyAOT::sort_klasses_by_inheritance(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ GrowableArray<InstanceKlass*>* src,
+ bool reverse_scan_src,
+ TRAPS) {
+ PtrHashSet<InstanceKlass*> visited;
+ if (reverse_scan_src) {
+ for (int i = src->length() - 1; i >= 0; --i) {
+ collect_klasses_by_inheritance(dst_ik, &visited, src->at(i), THREAD);
+ }
+ } else {
+ for (GrowableArrayIterator<InstanceKlass*> iter = src->begin();
+ iter != src->end();
+ ++iter) {
+ collect_klasses_by_inheritance(dst_ik, &visited, *iter, THREAD);
+ }
+ }
+ collect_klasses_in_constant_pool(dst_ik, &visited, THREAD);
+ collect_klasses_in_method_data(dst_ik, dst_ak, &visited, THREAD);
+ return true;
+}
+
+bool LazyAOT::can_be_compiled(const methodHandle& mh) {
+ ResourceMark rm;
+ InstanceKlass* ik = mh->method_holder();
+ return can_be_compiled(ik);
+}
+
+bool LazyAOT::can_be_compiled(InstanceKlass* ik, bool check_cld) {
+ if (check_cld && !can_be_compiled(ik->class_loader_data())) {
+ return false;
+ }
+
+ // AOT does not support it
+ if (ik->is_hidden()) return false;
+
+ // Error occurs when proxy classes are transferred to the server.
+ if (ik->is_dynamic_proxy()) return false;
+
+ return true;
+}
+
+bool LazyAOT::can_be_compiled(ClassLoaderData* cld) {
+ oop loader_oop = cld->class_loader();
+
+ // Skip the bootstrap loaders used by LambdaForm.
+ // Each LambdaForm has its own bootstrap class loader.
+ // @see ClassLoaderData::is_boot_class_loader_data()
+ if (cld->is_the_null_class_loader_data()) return true;
+ if (loader_oop == nullptr) return false;
+
+ // Skip klasses like GeneratedMethodAccessor, GeneratedConstructorAccessor
+ // and GeneratedSerializationConstructorAccessor.
+ if (loader_oop->is_a(vmClasses::reflect_DelegatingClassLoader_klass())) {
+ return false;
+ }
+
+ return true;
+}
+
+class KlassGetAllInstanceKlassesClosure: public KlassClosure {
+ GrowableArray<InstanceKlass*>* _klasses;
+ GrowableArray<Method*>* _methods_to_compile;
+ GrowableArray<Method*>* _methods_not_compile;
+
+public:
+ KlassGetAllInstanceKlassesClosure(GrowableArray<InstanceKlass*>* klasses,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile):
+ _klasses(klasses),
+ _methods_to_compile(methods_to_compile),
+ _methods_not_compile(methods_not_compile) {}
+
+ void do_klass(Klass* k) override {
+ if (!k->is_instance_klass()) return;
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ if (!ik->is_loaded()) return;
+
+ if (PrintAllClassInfo) {
+ ResourceMark rm;
+ Symbol* class_name = ik->name();
+ const char* class_name_c = class_name == nullptr ? nullptr
+ : class_name->as_C_string();
+ tty->print_cr(" %s:%p", class_name_c, ik);
+ }
+
+ // Maybe we should add "if (!ik->is_initialized()) return;".
+ if (!LazyAOT::can_be_compiled(ik, /* check_cld */ false)) return;
+
+ bool should_append_klass = false;
+ Array<Method*>* methods = ik->methods();
+ int len = methods->length();
+ for (int i = 0; i < len; i++) {
+ Method* m = methods->at(i);
+
+ bool should_compile = m->has_compiled_code();
+
+ if (should_compile) {
+ _methods_to_compile->append(m);
+ should_append_klass = true;
+ }
+
+ if (m->is_rewrite_invokehandle()) {
+ _methods_not_compile->append(m);
+ }
+ }
+
+ if (should_append_klass) {
+ _klasses->append(ik);
+ }
+ }
+};
+
+class CLDGetAllInstanceKlassesClosure: public CLDClosure {
+ GrowableArray<ClassLoaderData*>* _loaders;
+ GrowableArray<InstanceKlass*>* _klasses;
+ GrowableArray<Method*>* _methods_to_compile;
+ GrowableArray<Method*>* _methods_not_compile;
+
+private:
+ void for_each(ClassLoaderData* cld) {
+ if (PrintAllClassInfo) {
+ ResourceMark rm;
+ Klass* loader_class = cld->class_loader_klass();
+ Symbol* loader_class_name = loader_class == nullptr ? nullptr : loader_class->name();
+ Symbol* loader_name = cld->name();
+ const char* loader_class_name_c = loader_class_name == nullptr ? nullptr
+ : loader_class_name->as_C_string();
+ const char* loader_name_c = loader_name == nullptr ? nullptr
+ : loader_name->as_C_string();
+ tty->print_cr("Class loader: \"%s:%s:%p\", can_be_compiled=%s.",
+ loader_class_name_c, loader_name_c, cld,
+ BOOL_TO_STR(LazyAOT::can_be_compiled(cld)));
+ tty->print_cr(" -");
+ }
+ if (LazyAOT::can_be_compiled(cld)) {
+ if (_loaders != nullptr) _loaders->append(cld);
+ KlassGetAllInstanceKlassesClosure cl(_klasses, _methods_to_compile, _methods_not_compile);
+ cld->classes_do(&cl);
+ }
+ }
+
+public:
+ CLDGetAllInstanceKlassesClosure(GrowableArray<ClassLoaderData*>* all_loaders,
+ GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ bool no_boot_platform = false):
+ _loaders(all_loaders),
+ _klasses(klasses_to_compile),
+ _methods_to_compile(methods_to_compile),
+ _methods_not_compile(methods_not_compile) {}
+
+ void do_cld(ClassLoaderData* cld) override { for_each(cld); }
+};
+
+void LazyAOT::collect_all_klasses_to_compile(GrowableArray<ClassLoaderData*>* all_loaders,
+ GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ GrowableArray<InstanceKlass*>* all_sorted_klasses,
+ GrowableArray<ArrayKlass*>* array_klasses,
+ TRAPS) {
+ {
+ {
+ TraceTime tt("Collect all classes", TRACETIME_LOG(Info, jbooster, aot));
+ CLDGetAllInstanceKlassesClosure cl(all_loaders, klasses_to_compile, methods_to_compile, methods_not_compile);
+ ThreadInVMfromNative tivm(THREAD);
+ MutexLocker ml(THREAD, ClassLoaderDataGraph_lock);
+ ClassLoaderDataGraph::cld_do(&cl);
+ }
+ TraceTime tt("Sort klasses", TRACETIME_LOG(Info, jbooster, aot));
+ sort_klasses_by_inheritance(all_sorted_klasses, array_klasses, klasses_to_compile,
+ /* reverse_scan_src */ true, CATCH);
+ }
+ log_info(jbooster, compilation)("Klasses to send: %d", all_sorted_klasses->length());
+ log_info(jbooster, compilation)("Klasses to compile: %d", klasses_to_compile->length());
+}
+
+static Handle create_hash_set_instance(InstanceKlass** ik, TRAPS) {
+ TempNewSymbol hash_set_name = SymbolTable::new_symbol("java/util/HashSet");
+ Klass* hash_set_k = SystemDictionary::resolve_or_fail(hash_set_name, Handle(), Handle(), true, CHECK_NH);
+ assert(hash_set_k != nullptr && hash_set_k->is_instance_klass(), "sanity");
+ InstanceKlass* hash_set_ik = InstanceKlass::cast(hash_set_k);
+ *ik = hash_set_ik;
+ Handle hash_set_h = JavaCalls::construct_new_instance(hash_set_ik,
+ vmSymbols::void_method_signature(),
+ CHECK_NH);
+ return hash_set_h;
+}
+
+static Handle add_klasses_to_java_hash_set(GrowableArray<InstanceKlass*>* klasses, TRAPS) {
+ DebugUtils::assert_thread_in_vm();
+
+ // create a HashSet object
+ InstanceKlass* hash_set_ik = nullptr;
+ Handle hash_set_h = create_hash_set_instance(&hash_set_ik, CHECK_NH);
+
+ // add all klasses to the hash set
+ for (GrowableArrayIterator<InstanceKlass*> iter = klasses->begin(); iter != klasses->end(); ++iter) {
+ JavaValue result(T_BOOLEAN);
+ JavaCalls::call_virtual(&result, hash_set_h, hash_set_ik,
+ vmSymbols::add_method_name(),
+ vmSymbols::object_boolean_signature(),
+ Handle(THREAD, (*iter)->java_mirror()),
+ CHECK_NH);
+ guarantee((bool)result.get_jboolean() == true, "sanity");
+ }
+ return hash_set_h;
+}
+
+static Handle add_methods_names_to_java_hash_set(GrowableArray<Method*>* methods, TRAPS) {
+ DebugUtils::assert_thread_in_vm();
+
+ // create a HashSet object
+ InstanceKlass* hash_set_ik = nullptr;
+ Handle hash_set_h = create_hash_set_instance(&hash_set_ik, CHECK_NH);
+
+ // add all names of methods to the hash set
+ for (GrowableArrayIterator<Method*> iter = methods->begin(); iter != methods->end(); ++iter) {
+ ResourceMark rm(THREAD);
+ Method* m = *iter;
+ const char* s = m->name_and_sig_as_C_string();
+ Handle s_h = java_lang_String::create_from_str(s, CHECK_NH);
+
+ JavaValue result(T_BOOLEAN);
+ JavaCalls::call_virtual(&result, hash_set_h, hash_set_ik,
+ vmSymbols::add_method_name(),
+ vmSymbols::object_boolean_signature(),
+ s_h, CHECK_NH);
+ guarantee((bool)result.get_jboolean() == true, "sanity");
+ }
+ return hash_set_h;
+}
+
+bool LazyAOT::compile_classes_by_graal(int session_id,
+ const char* file_path,
+ GrowableArray<InstanceKlass*>* klasses,
+ TRAPS) {
+ DebugUtils::assert_thread_in_vm();
+
+ // arg2
+ Handle file_path_h = java_lang_String::create_from_str(file_path, CHECK_false);
+
+ // arg3
+ Handle hash_set_h = add_klasses_to_java_hash_set(klasses, CHECK_false);
+
+ JavaValue result(T_BOOLEAN);
+ JavaCallArguments java_args;
+ java_args.push_int(session_id);
+ java_args.push_oop(file_path_h);
+ java_args.push_oop(hash_set_h);
+
+ TempNewSymbol compile_classes_name = SymbolTable::new_symbol("compileClasses");
+ TempNewSymbol compile_classes_signature = SymbolTable::new_symbol("(ILjava/lang/String;Ljava/util/Set;)Z");
+ JavaCalls::call_static(&result, ServerDataManager::get().main_klass(),
+ compile_classes_name,
+ compile_classes_signature,
+ &java_args, CHECK_false);
+ return (bool) result.get_jboolean();
+}
+
+bool LazyAOT::compile_methods_by_graal(int session_id,
+ const char* file_path,
+ GrowableArray<InstanceKlass*>* klasses,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ TRAPS) {
+ DebugUtils::assert_thread_in_vm();
+
+ // arg2
+ Handle file_path_h = java_lang_String::create_from_str(file_path, CHECK_false);
+
+ // arg3
+ Handle klass_set_h = add_klasses_to_java_hash_set(klasses, CHECK_false);
+
+ // arg4
+ Handle method_name_set_h = add_methods_names_to_java_hash_set(methods_to_compile, CHECK_false);
+
+ // arg5
+ Handle not_method_name_set_h = add_methods_names_to_java_hash_set(methods_not_compile, CHECK_false);
+
+ JavaValue result(T_BOOLEAN);
+ JavaCallArguments java_args;
+ java_args.push_int(session_id);
+ java_args.push_oop(file_path_h);
+ java_args.push_oop(klass_set_h);
+ java_args.push_oop(method_name_set_h);
+ java_args.push_oop(not_method_name_set_h);
+
+ TempNewSymbol compile_methods_name = SymbolTable::new_symbol("compileMethods");
+ TempNewSymbol compile_methods_signature = SymbolTable::new_symbol("(ILjava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)Z");
+ JavaCalls::call_static(&result, ServerDataManager::get().main_klass(),
+ compile_methods_name,
+ compile_methods_signature,
+ &java_args, CHECK_false);
+ return (bool) result.get_jboolean();
+}
diff --git a/src/hotspot/share/jbooster/lazyAot.hpp b/src/hotspot/share/jbooster/lazyAot.hpp
new file mode 100644
index 000000000..f9adc6596
--- /dev/null
+++ b/src/hotspot/share/jbooster/lazyAot.hpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_LAZYAOT_HPP
+#define SHARE_JBOOSTER_LAZYAOT_HPP
+
+#include "jbooster/utilities/ptrHashSet.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/handles.hpp"
+#include "runtime/thread.hpp"
+
+class AOTCodeHeap;
+class ArrayKlass;
+class ClassLoaderData;
+template <class T> class GrowableArray;
+class InstanceKlass;
+class Method;
+class MethodData;
+
+class LazyAOT: public AllStatic {
+private:
+ static const int _compilation_related_klasses_layers = 2;
+
+private:
+ static InstanceKlass* get_klass_from_class_tag(const constantPoolHandle& cp,
+ int index,
+ Handle loader,
+ int which_klasses,
+ TRAPS);
+ static void get_klasses_from_name_and_type_tag(const constantPoolHandle& cp,
+ int index,
+ Handle loader,
+ GrowableArray<InstanceKlass*> &res,
+ int which_klasses,
+ TRAPS);
+
+ static void collect_klasses_by_inheritance(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ InstanceKlass* ik,
+ TRAPS);
+ static void collect_klasses_in_constant_pool(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ InstanceKlass* ik,
+ int which_klasses,
+ TRAPS);
+ static void collect_klasses_in_constant_pool(GrowableArray<InstanceKlass*>* dst,
+ PtrHashSet<InstanceKlass*>* vis,
+ TRAPS);
+ static void collect_klasses_in_method_data(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ PtrHashSet<InstanceKlass*>* ik_vis,
+ TRAPS);
+ static void collect_klasses_in_method_data(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ PtrHashSet<InstanceKlass*>* ik_vis,
+ PtrHashSet<ArrayKlass*>* ak_vis,
+ MethodData* method_data,
+ int which_klasses,
+ TRAPS);
+ static bool sort_klasses_by_inheritance(GrowableArray<InstanceKlass*>* dst_ik,
+ GrowableArray<ArrayKlass*>* dst_ak,
+ GrowableArray<InstanceKlass*>* src,
+ bool reverse_scan_src,
+ TRAPS);
+
+public:
+ static bool can_be_compiled(const methodHandle& mh);
+ static bool can_be_compiled(InstanceKlass* ik, bool check_cld = true);
+ static bool can_be_compiled(ClassLoaderData* cld);
+
+ static void collect_all_klasses_to_compile(GrowableArray<ClassLoaderData*>* all_loaders,
+ GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ GrowableArray<InstanceKlass*>* all_sorted_klasses,
+ GrowableArray<ArrayKlass*>* array_klasses,
+ TRAPS);
+
+ static bool compile_classes_by_graal(int session_id,
+ const char* file_path,
+ GrowableArray<InstanceKlass*>* klasses,
+ TRAPS);
+ static bool compile_methods_by_graal(int session_id,
+ const char* file_path,
+ GrowableArray<InstanceKlass*>* klasses,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_LAZYAOT_HPP
diff --git a/src/hotspot/share/jbooster/net/clientStream.cpp b/src/hotspot/share/jbooster/net/clientStream.cpp
new file mode 100644
index 000000000..d72b796d5
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/clientStream.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/client/clientDataManager.hpp"
+#include "jbooster/net/clientStream.hpp"
+#include "jbooster/net/rpcCompatibility.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "runtime/java.hpp"
+#include "runtime/thread.hpp"
+
+ClientStream::ClientStream(const char* address, const char* port, uint32_t timeout_ms):
+ CommunicationStream(Thread::current_or_null()),
+ _server_address(address),
+ _server_port(port),
+ _timeout_ms(timeout_ms),
+ _inform_before_close(true) {}
+
+ClientStream::ClientStream(const char* address, const char* port, uint32_t timeout_ms, Thread* thread):
+ CommunicationStream(thread),
+ _server_address(address),
+ _server_port(port),
+ _timeout_ms(timeout_ms),
+ _inform_before_close(true) {}
+
+ClientStream::~ClientStream() {
+ if (!can_close_safely() && _inform_before_close) {
+ send_no_more_compilation_tasks();
+ }
+}
+
+int ClientStream::connect_to_server() {
+ close_stream();
+ const int retries = 3;
+ int last_err = 0;
+ for (int i = 0; i < retries; ++i) {
+ JB_TRY_BREAKABLE {
+ int conn_fd;
+ JB_THROW(try_to_connect_once(&conn_fd, _server_address, _server_port, _timeout_ms));
+ assert(conn_fd >= 0 && errno == 0, "sanity");
+ init_stream(conn_fd);
+ return 0;
+ } JB_TRY_BREAKABLE_END
+ JB_CATCH(ECONNREFUSED, EBADF) {
+ last_err = JB_ERR;
+ } JB_CATCH_REST() {
+ last_err = JB_ERR;
+ break;
+ } JB_CATCH_END;
+ }
+
+ if (last_err == ECONNREFUSED || last_err == EBADF) {
+ constexpr const char* fmt = "Failed to connect to the JBooster server! Retried %d times.";
+ if (JBoosterCrashIfNoServer) {
+ fatal(fmt, retries);
+ } else {
+ log_error(jbooster, rpc)(fmt, retries);
+ }
+ } else {
+ constexpr const char* fmt = "Unexpected exception when connecting to the JBooster server: error=%s(\"%s\").";
+ if (JBoosterCrashIfNoServer) {
+ fatal(fmt, JBErr::err_name(last_err), JBErr::err_message(last_err));
+ } else {
+ log_error(jbooster, rpc)(fmt, JBErr::err_name(last_err), JBErr::err_message(last_err));
+ }
+ }
+
+ return last_err;
+}
+
+int ClientStream::request_cache_file(bool* use_it,
+ bool allowed_to_use,
+ bool local_cache_exists,
+ bool remote_cache_exists,
+ const char* file_path,
+ MessageType msg_type) {
+ if (!allowed_to_use) {
+ *use_it = false;
+ } else if (local_cache_exists) {
+ *use_it = true;
+ } else if (remote_cache_exists) {
+ FileWrapper file(file_path, SerializationMode::DESERIALIZE);
+ if (file.is_tmp_file_already_exists()) {
+ *use_it = file.wait_for_file_deserialization();
+ } else {
+ JB_RETURN(send_request(msg_type));
+ JB_RETURN(file.recv_file(this));
+ *use_it = !file.is_null();
+ }
+ } else {
+ *use_it = false;
+ }
+ return 0;
+}
+
+int ClientStream::sync_session_meta__client(bool* has_remote_clr, bool* has_remote_cds, bool* has_remote_aot) {
+ ClientDataManager& cdm = ClientDataManager::get();
+
+ RpcCompatibility comp;
+ uint64_t client_random_id = cdm.random_id();
+ JClientArguments* program_args = cdm.program_args();
+ JB_RETURN(send_request(MessageType::ClientSessionMeta, &comp, &client_random_id, program_args));
+
+ uint64_t server_random_id;
+ uint32_t session_id, program_id;
+
+ JB_TRY {
+ JB_THROW(recv_response(stream_id_addr(), &server_random_id, &session_id, &program_id,
+ has_remote_clr, has_remote_cds, has_remote_aot));
+ } JB_TRY_END
+ JB_CATCH(JBErr::BAD_MSG_TYPE) {
+ if (recv_msg_type() == MessageType::UnsupportedClient) {
+ char unsupport_reason[64];
+ parse_request(&unsupport_reason);
+ log_error(jbooster, rpc)("The server does not support this client because the \"%s\" of the two are different!",
+ unsupport_reason);
+ }
+ return JB_ERR;
+ } JB_CATCH_REST() {
+ return JB_ERR;
+ } JB_CATCH_END;
+
+ cdm.set_server_random_id(server_random_id);
+ cdm.set_session_id(session_id);
+ cdm.set_program_id(program_id);
+ log_info(jbooster, rpc)("Client meta: session_id=%u, program_id=%u, server_random_id=" UINT64_FORMAT_X ".",
+ session_id, program_id, server_random_id);
+ return 0;
+}
+
+int ClientStream::sync_stream_meta__client() {
+ ClientDataManager& cdm = ClientDataManager::get();
+
+ uint32_t session_id = cdm.session_id();
+ uint64_t client_random_id = cdm.random_id();
+ uint64_t server_random_id = cdm.server_random_id();
+ JB_RETURN(send_request(MessageType::ClientStreamMeta, &session_id, &client_random_id, &server_random_id));
+ JB_RETURN(recv_response(stream_id_addr()));
+ log_trace(jbooster, rpc)("New ClientStream: session_id=%u, stream_id=%u.", session_id, stream_id());
+ return 0;
+}
+
+int ClientStream::resync_session_and_stream_meta__client() {
+ bool has_remote_clr, has_remote_cds, has_remote_aot; // unused here
+ JB_RETURN(sync_session_meta__client(&has_remote_clr, &has_remote_cds, &has_remote_aot));
+ return 0;
+}
+
+int ClientStream::connect_and_init_session(bool* use_clr, bool* use_cds, bool* use_aot) {
+ _inform_before_close = false;
+ ClientDataManager& cdm = ClientDataManager::get();
+
+ JB_TRY {
+ JB_THROW(connect_to_server());
+ bool has_remote_clr, has_remote_cds, has_remote_aot;
+ JB_THROW(sync_session_meta__client(&has_remote_clr, &has_remote_cds, &has_remote_aot));
+
+ JB_THROW(request_cache_file(use_clr,
+ cdm.is_clr_allowed(),
+ FileUtils::is_file(cdm.cache_clr_path()),
+ has_remote_clr,
+ cdm.cache_clr_path(),
+ MessageType::GetClassLoaderResourceCache));
+
+ JB_THROW(request_cache_file(use_cds,
+ cdm.is_cds_allowed(),
+ FileUtils::is_file(cdm.cache_cds_path()),
+ has_remote_cds,
+ cdm.cache_cds_path(),
+ MessageType::GetAggressiveCDSCache));
+
+ JB_THROW(request_cache_file(use_aot,
+ cdm.is_aot_allowed(),
+ FileUtils::is_file(cdm.cache_aot_path()),
+ has_remote_aot,
+ cdm.cache_aot_path(),
+ MessageType::GetLazyAOTCache));
+
+ JB_THROW(send_request(MessageType::EndOfCurrentPhase));
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ if (JBoosterExitIfUnsupported && JB_ERR == JBErr::BAD_MSG_TYPE
+ && recv_msg_type() == MessageType::UnsupportedClient) {
+ vm_exit_during_initialization("The JBooster server does not support this client.");
+ }
+ LOG_OR_CRASH();
+
+ if (!*use_clr) *use_clr = FileUtils::is_file(cdm.cache_clr_path());
+ if (!*use_cds) *use_cds = FileUtils::is_file(cdm.cache_cds_path());
+ if (!*use_aot) *use_aot = FileUtils::is_file(cdm.cache_aot_path());
+
+ return JB_ERR;
+ } JB_CATCH_END;
+ return 0;
+}
+
+int ClientStream::connect_and_init_stream() {
+ JB_TRY {
+ JB_THROW(connect_to_server());
+ JB_THROW(sync_stream_meta__client());
+ } JB_TRY_END
+ JB_CATCH(JBErr::BAD_MSG_TYPE) {
+ if (msg_recv().msg_type() == MessageType::ClientSessionMetaAgain) {
+ JB_TRY {
+ JB_THROW(resync_session_and_stream_meta__client());
+ JB_THROW(sync_stream_meta__client());
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ LOG_OR_CRASH();
+ return JB_ERR;
+ } JB_CATCH_END;
+ return 0;
+ }
+ LOG_OR_CRASH();
+ return JB_ERR;
+ } JB_CATCH_REST() {
+ LOG_OR_CRASH();
+ return JB_ERR;
+ } JB_CATCH_END;
+ return 0;
+}
+
+void ClientStream::send_no_more_compilation_tasks() {
+ if (is_stream_closed()) return;
+ JB_TRY {
+ bool no_more = true;
+ JB_THROW(send_request(MessageType::NoMoreRequests, &no_more));
+ set_can_close_safely();
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ log_warning(jbooster, rpc)("Exception [%s] at ClientStream::send_no_more_compilation_tasks(). stream_id=%u.",
+ JBErr::err_name(JB_ERR), stream_id());
+ } JB_CATCH_END;
+}
+
+void ClientStream::log_or_crash(const char* file, int line, int err_code) {
+ constexpr const char* fmt = "Unhandled exception found at %s:%d: error=%s(\"%s\"), stream_id=%u.";
+ if (JBoosterCrashIfNoServer) {
+ fatal(fmt, file, line,
+ JBErr::err_name(err_code), JBErr::err_message(err_code),
+ stream_id());
+ } else {
+ log_error(jbooster, rpc)(fmt, file, line,
+ JBErr::err_name(err_code), JBErr::err_message(err_code),
+ stream_id());
+ }
+}
diff --git a/src/hotspot/share/jbooster/net/clientStream.hpp b/src/hotspot/share/jbooster/net/clientStream.hpp
new file mode 100644
index 000000000..1f185644f
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/clientStream.hpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_CLIENTSTREAM_HPP
+#define SHARE_JBOOSTER_NET_CLIENTSTREAM_HPP
+
+#include "jbooster/net/communicationStream.inline.hpp"
+
+class ClientStream: public CommunicationStream {
+private:
+ const char* const _server_address;
+ const char* const _server_port;
+ const uint32_t _timeout_ms;
+
+ bool _inform_before_close;
+
+private:
+ static int try_to_connect_once(int* res_fd, const char* address, const char* port, uint32_t timeout_ms);
+
+ int request_cache_file(bool* use_it,
+ bool allowed_to_use,
+ bool local_cache_exists,
+ bool remote_cache_exists,
+ const char* file_path,
+ MessageType msg_type);
+
+ int connect_to_server();
+
+ int sync_session_meta__client(bool* use_clr, bool* use_cds, bool* use_aot);
+ int sync_stream_meta__client();
+ int resync_session_and_stream_meta__client();
+
+public:
+ ClientStream(const char* address, const char* port, uint32_t timeout_ms);
+ ClientStream(const char* address, const char* port, uint32_t timeout_ms, Thread* thread);
+ ~ClientStream();
+
+ void set_inform_before_close(bool should) { _inform_before_close = should; }
+
+ int connect_and_init_session(bool* use_clr, bool* use_cds, bool* use_aot);
+ int connect_and_init_stream();
+ void send_no_more_compilation_tasks();
+
+ void log_or_crash(const char* file, int line, int err_code);
+};
+
+#define LOG_OR_CRASH() log_or_crash(__FILE__, __LINE__, JB_ERR)
+
+#endif // SHARE_JBOOSTER_NET_CLIENTSTREAM_HPP
diff --git a/src/hotspot/share/jbooster/net/communicationStream.cpp b/src/hotspot/share/jbooster/net/communicationStream.cpp
new file mode 100644
index 000000000..f0898170c
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/communicationStream.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/net/communicationStream.inline.hpp"
+#include "runtime/os.inline.hpp"
+#ifdef ASSERT
+#include "runtime/thread.inline.hpp"
+#endif // ASSERT
+
+void CommunicationStream::handle_net_err(int comm_size, bool is_recv) {
+ if (comm_size > 0) return;
+ const char* rw = is_recv ? "recv" : "send";
+ if (comm_size == 0) {
+ set_errno(JBErr::CONN_CLOSED_BY_PEER);
+ log_debug(jbooster, rpc)("Failed to %s as the connection is closed by peer. stream_id=%u.",
+ rw, stream_id());
+ close_stream();
+ return;
+ }
+ // comm_size < 0
+ if (is_stream_closed()) {
+ set_errno(JBErr::CONN_CLOSED);
+ log_debug(jbooster, rpc)("Failed to %s as the connection has been closed. stream_id=%u.",
+ rw, stream_id());
+ return;
+ }
+ int err = errno;
+ errno = 0;
+ set_errno(err);
+ log_debug(jbooster, rpc)("Failed to %s: error=%s(\"%s\"), stream_id=%u.",
+ rw, JBErr::err_name(err), JBErr::err_message(err), stream_id());
+ // We don't give special treatment to EAGAIN (yet).
+ close_stream();
+ return;
+}
+
+uint32_t CommunicationStream::read_once_from_stream(char* buf, uint32_t size) {
+ int read_size = os::recv(_conn_fd, buf, (size_t) size, 0);
+ if (read_size <= 0) {
+ handle_net_err(read_size, true);
+ return 0;
+ }
+ return (uint32_t) read_size;
+}
+
+uint32_t CommunicationStream::read_all_from_stream(char* buf, uint32_t size) {
+ uint32_t total_read_size = 0;
+ while (total_read_size < size) {
+ int read_size = os::recv(_conn_fd, buf + total_read_size, (size_t) (size - total_read_size), 0);
+ if (read_size <= 0) {
+ handle_net_err(read_size, true);
+ break;
+ }
+ total_read_size += read_size;
+ }
+ return total_read_size;
+}
+
+uint32_t CommunicationStream::write_once_to_stream(char* buf, uint32_t size) {
+ int written_size = os::send(_conn_fd, buf, size, 0);
+ if (written_size <= 0) {
+ handle_net_err(written_size, false);
+ return 0;
+ }
+ return (uint32_t) written_size;
+}
+
+uint32_t CommunicationStream::write_all_to_stream(char* buf, uint32_t size) {
+ uint32_t total_written_size = 0;
+ while (total_written_size < size) {
+ int written_size = os::send(_conn_fd, buf + total_written_size, (size_t) (size - total_written_size), 0);
+ if (written_size <= 0) {
+ handle_net_err(written_size, false);
+ break;
+ }
+ total_written_size += written_size;
+ }
+ return total_written_size;
+}
+
+void CommunicationStream::close_stream() {
+ if (_conn_fd >= 0) {
+ log_trace(jbooster, rpc)("Connection closed. stream_id=%u.", stream_id());
+ os::close(_conn_fd);
+ _conn_fd = -1;
+ }
+}
+
+#ifdef ASSERT
+void CommunicationStream::assert_current_thread() {
+ assert(Thread::current_or_null() == _cur_thread, "cur_thread=%p, stream_thread=%p",
+ Thread::current_or_null(), _cur_thread);
+}
+
+void CommunicationStream::assert_in_native() {
+ if (_cur_thread != nullptr && _cur_thread->is_Java_thread()) {
+ assert(_cur_thread->as_Java_thread()->thread_state() == _thread_in_native, "may affect safepoint");
+ }
+}
+#endif // ASSERT
+
+int CommunicationStream::recv_message() {
+ assert_current_thread();
+ assert_in_native();
+
+ Message& msg = _msg_recv;
+ // read once (or memmove from the overflowed buffer) to get message size
+ uint32_t read_size, msg_size;
+ if (msg.has_overflow()) {
+ read_size = msg.move_overflow();
+ if (read_size < sizeof(msg_size)) {
+ read_size += read_once_from_stream(msg.buf_beginning() + read_size, msg.buf_size() - read_size);
+ }
+ } else {
+ read_size = read_once_from_stream(msg.buf_beginning(), msg.buf_size());
+ }
+
+ if (read_size < sizeof(msg_size)) {
+ if (!is_stream_closed()) {
+ log_warning(jbooster, rpc)("Failed to read the size of the message (read_size=%d). stream_id=%u.",
+ read_size, stream_id());
+ }
+ return return_errno_or_flag(JBErr::BAD_MSG_SIZE);
+ }
+
+ msg_size = msg.deserialize_size_only();
+ if (read_size > msg_size) { // read too much
+ msg.set_overflow(msg_size, read_size - msg_size);
+ } else if (read_size < msg_size) { // read the rest then
+ uint32_t msg_left_size = msg_size - read_size;
+ msg.expand_buf_if_needed(msg_size, read_size);
+ uint32_t sz = read_all_from_stream(msg.buf_beginning() + read_size, msg_left_size);
+ if (sz < msg_left_size) {
+ log_warning(jbooster, rpc)("Failed to read the rest message: read_size=%d, msg_left_size=%d. stream_id=%u.",
+ sz, msg_left_size, stream_id());
+ return return_errno_or_flag(JBErr::BAD_MSG_SIZE);
+ }
+ }
+
+ msg.deserialize_meta();
+ log_debug(jbooster, rpc)("Recv %s, size=%u, thread=%p, stream_id=%u.",
+ msg_type_name(msg.msg_type()), msg.msg_size(),
+ Thread::current_or_null(), stream_id());
+ return 0;
+}
+
+int CommunicationStream::send_message() {
+ assert_current_thread();
+ assert_in_native();
+
+ Message& msg = _msg_send;
+ msg.serialize_meta();
+ uint32_t sz = write_all_to_stream(msg.buf_beginning(), msg.msg_size());
+ if (sz != msg.msg_size()) {
+ log_warning(jbooster, rpc)("Failed to send the full message: write_size=%d, msg_size=%d. stream_id=%u.",
+ sz, msg.msg_size(), stream_id());
+ return return_errno_or_flag(JBErr::BAD_MSG_SIZE);
+ }
+ log_debug(jbooster, rpc)("Send %s, size=%u, thread=%p, stream_id=%u.",
+ msg_type_name(msg.msg_type()), msg.msg_size(),
+ Thread::current_or_null(), stream_id());
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/net/communicationStream.hpp b/src/hotspot/share/jbooster/net/communicationStream.hpp
new file mode 100644
index 000000000..7b2cfd9db
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/communicationStream.hpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_HPP
+#define SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_HPP
+
+#include "jbooster/net/message.hpp"
+#include "jbooster/net/netCommon.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class Thread;
+
+/**
+ * Base class of ServerStream and ClientStream.
+ * We never use CommunicationStream directly. Instead, we specify ServerStream or ClientStream
+ * each time. So there's no need to define any method as vitrual.
+ */
+class CommunicationStream: public CHeapObj<mtJBooster> {
+private:
+ int _conn_fd; // generated by the OS
+ uint32_t _stream_id; // generated by the server
+ int _errno;
+
+ Message _msg_recv, _msg_send;
+
+ bool _can_close_safely;
+
+ // only to ensure that it's used by only one thread
+ Thread* _cur_thread;
+
+private:
+ void handle_net_err(int comm_size, bool is_read);
+
+ void set_errno(int eno) { _errno = eno; }
+ int get_errno() { return _errno; }
+ int get_and_clear_errno() { int eno = _errno; _errno = 0; return eno; }
+ int return_errno_or_flag(int flag) { return get_errno() ? get_and_clear_errno() : flag; }
+
+ uint32_t read_once_from_stream(char* buf, uint32_t size);
+ uint32_t read_all_from_stream(char* buf, uint32_t size);
+ uint32_t write_once_to_stream(char* buf, uint32_t size);
+ uint32_t write_all_to_stream(char* buf, uint32_t size);
+
+ bool check_received_message_type(MessageType expected);
+ bool check_received_message_size();
+
+ void assert_current_thread() NOT_DEBUG_RETURN;
+ void assert_in_native() NOT_DEBUG_RETURN;
+
+protected:
+ CommunicationStream(Thread* thread):
+ _conn_fd(-1),
+ _stream_id(0),
+ _errno(0),
+ _msg_recv(SerializationMode::DESERIALIZE, this),
+ _msg_send(SerializationMode::SERIALIZE, this),
+ _can_close_safely(false),
+ _cur_thread(thread) {}
+
+ virtual ~CommunicationStream() { close_stream(); }
+
+ void init_stream(int conn_fd) { _conn_fd = conn_fd; }
+ void close_stream();
+
+ int recv_message();
+ int send_message();
+
+ uint32_t* stream_id_addr() { return &_stream_id; }
+ void set_stream_id(uint32_t stream_id) { _stream_id = stream_id; }
+
+ void set_can_close_safely() { _can_close_safely = true; }
+
+public:
+ bool is_stream_closed() { return _conn_fd < 0; }
+ bool can_close_safely() { return _can_close_safely; }
+
+ int conn_fd() { return _conn_fd; }
+ uint32_t stream_id() { return _stream_id; }
+
+ const Message& msg_recv() const { return _msg_recv; }
+ const Message& msg_send() const { return _msg_send; }
+
+ // Usage of the following APIs:
+ // (Both the client and server can act as the requester and responder.)
+ //
+ // Requester: send_request() recv_response()
+ // | A
+ // V |
+ // Responder: recv_request() -> parse_request() -> send_response()
+ //
+ // Or, of course, more generally:
+ //
+ // Side A: send_request() recv_request() -> parse_request()
+ // | A
+ // V |
+ // Side B: recv_request() -> parse_request() -> send_request()
+ //
+ // @return: 0: fine; other values: error code
+ //
+ // **Note**: These operations (except parse_request()) may block the thread until
+ // JBoosterTimeout. So it's better not to invoke them in _thread_in_vm
+ // state of JavaThread, or the time to reach safepoints may be severely
+ // affected.
+ // @see ThreadToNativeFromVM, ThreadBlockInVM
+
+ template <typename... Args>
+ int send_request(MessageType type, const Args* const... args);
+
+ int recv_request(MessageType& type);
+
+ template <typename... Args>
+ int recv_request(MessageType type, Args* const&... args);
+
+ template <typename... Args>
+ int parse_request(Args* const&... args);
+
+ template <typename... Args>
+ int send_response(const Args* const... args);
+
+ template <typename... Args>
+ int recv_response(Args* const&... args);
+
+ MessageType recv_msg_type() { return _msg_recv.msg_type(); }
+
+ Thread* current_thread() { return _cur_thread; }
+ void set_current_thread(Thread* thread) { _cur_thread = thread; }
+
+ bool set_read_write_timeout(uint32_t timeout_ms);
+};
+
+#endif // SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_HPP
diff --git a/src/hotspot/share/jbooster/net/communicationStream.inline.hpp b/src/hotspot/share/jbooster/net/communicationStream.inline.hpp
new file mode 100644
index 000000000..45311c2be
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/communicationStream.inline.hpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_INLINE_HPP
+#define SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_INLINE_HPP
+
+#include "jbooster/net/communicationStream.hpp"
+#include "jbooster/net/message.inline.hpp"
+#include "logging/log.hpp"
+
+inline bool CommunicationStream::check_received_message_type(MessageType expected) {
+ if (expected != _msg_recv.msg_type()) {
+ log_warning(jbooster, rpc)("Failed to receive the message as wrong message type: "
+ "expected=%s, received=%s. stream_id=%u.",
+ msg_type_name(expected),
+ msg_type_name(_msg_recv.msg_type()),
+ stream_id());
+ return false;
+ }
+ return true;
+}
+
+inline bool CommunicationStream::check_received_message_size() {
+ if (_msg_recv.msg_size() != _msg_recv.cur_buf_offset()) {
+ log_warning(jbooster, rpc)("Failed to parse the message as the msg_size mismatch: "
+ "msg_size=%u, parsed_size=%u. stream_id=%u.",
+ _msg_recv.msg_size(),
+ _msg_recv.cur_buf_offset(),
+ stream_id());
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Send the mesaage with the message type and all the arguments.
+ */
+template <typename... Args>
+inline int CommunicationStream::send_request(MessageType type, const Args* const... args) {
+ _msg_send.set_msg_type(type);
+ _msg_send.set_cur_buf_offset_after_meta();
+ JB_RETURN(_msg_send.serialize(args...));
+ _msg_send.set_msg_size_based_on_cur_buf_offset();
+ return send_message();
+}
+
+/**
+ * Receive a message.
+ * The received message type is set to `type`.
+ */
+inline int CommunicationStream::recv_request(MessageType& type) {
+ int err_code = recv_message();
+ type = _msg_recv.msg_type();
+ return err_code;
+}
+
+/**
+ * Receive and parse a message.
+ * The received message type must be the same as `type`.
+ */
+template <typename... Args>
+inline int CommunicationStream::recv_request(MessageType type, Args* const&... args) {
+ JB_RETURN(recv_message());
+ if (!check_received_message_type(type)) {
+ return JBErr::BAD_MSG_TYPE;
+ }
+ return parse_request(args...);
+}
+
+/**
+ * Parse the received message.
+ * Invoke recv_request() before parse_request() to identify the message type.
+ */
+template <typename... Args>
+inline int CommunicationStream::parse_request(Args* const&... args) {
+ assert(_msg_recv.cur_buf_offset() == Message::meta_size, "address mismatch");
+ JB_RETURN(_msg_recv.deserialize(args...));
+ if (!check_received_message_size()) {
+ return JBErr::BAD_MSG_DATA;
+ }
+ return 0;
+}
+
+/**
+ * Send the response using the message type of the last received message.
+ */
+template <typename... Args>
+inline int CommunicationStream::send_response(const Args* const... args) {
+ return send_request(_msg_recv.msg_type(), args...);
+}
+
+
+/**
+ * Receive the response using the message type of the last sent message.
+ */
+template <typename... Args>
+inline int CommunicationStream::recv_response(Args* const&... args) {
+ JB_RETURN(recv_message());
+ if (!check_received_message_type(_msg_send.msg_type())) {
+ return JBErr::BAD_MSG_TYPE;
+ }
+ return parse_request(args...);
+}
+
+#endif // SHARE_JBOOSTER_NET_COMMUNICATIONSTREAM_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/net/errorCode.cpp b/src/hotspot/share/jbooster/net/errorCode.cpp
new file mode 100644
index 000000000..aa46e5b88
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/errorCode.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/net/errorCode.hpp"
+#include "runtime/os.hpp"
+#include "utilities/debug.hpp"
+
+#define REGISTER_JB_ERROR_CODE_NAMES_AND_MESSAGES(err_name, human_readable) #err_name, human_readable,
+
+static const char* jb_err_code_name_str_arr[] = {
+ "GOOD", "Nothing is wrong",
+ JB_ERROR_CODES(REGISTER_JB_ERROR_CODE_NAMES_AND_MESSAGES)
+ "END_OF_JB_ERROR_CODE", "Should not reach here!"
+};
+
+#undef REGISTER_JB_ERROR_CODE_NAMES_AND_MESSAGES
+
+const char* JBErr::err_name(int err_code) {
+ if (err_code > 0) return os::errno_name(err_code);
+ guarantee(-err_code <= err_cnt(), "unknown error code: %d", err_code);
+ int index = -err_code * 2;
+ return jb_err_code_name_str_arr[index];
+}
+
+const char* JBErr::err_message(int err_code) {
+ if (err_code > 0) return os::strerror(err_code);
+ guarantee(-err_code <= err_cnt(), "unknown error code: %d", err_code);
+ int index = -err_code * 2 + 1;
+ return jb_err_code_name_str_arr[index];
+}
diff --git a/src/hotspot/share/jbooster/net/errorCode.hpp b/src/hotspot/share/jbooster/net/errorCode.hpp
new file mode 100644
index 000000000..8dddd31f0
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/errorCode.hpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_ERRORCODE_HPP
+#define SHARE_JBOOSTER_NET_ERRORCODE_HPP
+
+#include <errno.h>
+
+#define JB_ERROR_CODES(f) \
+ f(CONN_CLOSED, "Connection has been closed" ) \
+ f(CONN_CLOSED_BY_PEER, "Connection is closed by the other end" ) \
+ f(BAD_MSG_SIZE, "Unexpected size of the received message" ) \
+ f(BAD_MSG_TYPE, "Unexpected message type of the received message" ) \
+ f(BAD_MSG_DATA, "Unexpected payload data of the received message" ) \
+ f(BAD_ARG_SIZE, "Unexpected size of the argument" ) \
+ f(BAD_ARG_DATA, "Unexpected payload data of the argument" ) \
+ f(INCOMPATIBLE_RPC, "Incompatible RPC version" ) \
+ f(DESER_TERMINATION, "Deserialization terminated early (not an error)" ) \
+ f(ABORT_CUR_PHRASE, "Abort current communication phrase (not an error)" ) \
+ f(THREAD_EXCEPTION, "Java exception from Thread::current()" ) \
+ f(UNKNOWN, "Unknown error" ) \
+
+
+#define REGISTER_JB_ERROR_CODE_OPPOSITE_IDS(err_name, human_readable) err_name,
+#define REGISTER_JB_ERROR_CODES(err_name, human_readable) err_name = -(int)ErrCodeOpposite::err_name,
+
+/**
+ * Defines all the possible error codes for the jbooster network communication.
+ * We define all the jbooster error codes as negative integers as C standard
+ * already defines some positive errnos.
+ *
+ * @see static const char* errno_to_string (int e, bool short_text)
+ */
+class JBErr {
+private:
+ enum class ErrCodeOpposite: int {
+ PLACE_HOLDER_OF_ZERO,
+ JB_ERROR_CODES(REGISTER_JB_ERROR_CODE_OPPOSITE_IDS)
+ PLACE_HOLDER_END
+ };
+
+ static const int _err_cnt = (int) ErrCodeOpposite::PLACE_HOLDER_END - 1;
+
+public:
+ enum: int {
+ JB_ERROR_CODES(REGISTER_JB_ERROR_CODES)
+ PLACE_HOLDER_END
+ };
+
+ static int err_cnt() { return _err_cnt; }
+
+ static const char* err_name(int err_code);
+ static const char* err_message(int err_code);
+};
+
+#undef REGISTER_JB_ERROR_CODE_OPPOSITE_IDS
+#undef REGISTER_JB_ERROR_CODES
+
+#endif // SHARE_JBOOSTER_NET_ERRORCODE_HPP
diff --git a/src/hotspot/share/jbooster/net/message.hpp b/src/hotspot/share/jbooster/net/message.hpp
new file mode 100644
index 000000000..47d2634e2
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/message.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_MESSAGE_HPP
+#define SHARE_JBOOSTER_NET_MESSAGE_HPP
+
+#include "jbooster/net/messageBuffer.hpp"
+#include "jbooster/net/messageType.hpp"
+#include "jbooster/net/netCommon.hpp"
+
+class Message: public MessageConst {
+ // see MessageConst for the message format
+private:
+ struct {
+ uint32_t msg_size;
+ MessageType msg_type;
+ } _meta;
+
+ MessageBuffer _buf;
+
+ uint32_t _overflow_offset;
+ uint32_t _overflow_size;
+
+private:
+ int serialize_inner();
+ int deserialize_inner();
+ template <typename Arg, typename... Args>
+ int serialize_inner(const Arg* const arg, const Args* const... args);
+ template <typename Arg, typename... Args>
+ int deserialize_inner(Arg* const& arg, Args* const&... args);
+
+public:
+ Message(SerializationMode smode, CommunicationStream* stream = nullptr):
+ _buf(smode, stream),
+ _overflow_offset(0),
+ _overflow_size(0) {}
+
+ uint32_t msg_size() const { return _meta.msg_size; }
+ void set_msg_size(uint32_t size) { _meta.msg_size = size; }
+ void set_msg_size_based_on_cur_buf_offset() { set_msg_size(cur_buf_offset()); }
+ MessageType msg_type() const { return _meta.msg_type; }
+ void set_msg_type(MessageType type) { _meta.msg_type = type; }
+
+ char* buf_beginning() const { return _buf.buf(); }
+ uint32_t buf_size() const { return _buf.buf_size(); }
+
+ uint32_t cur_buf_offset() { return _buf.cur_offset(); }
+ void set_cur_buf_offset_after_meta() { _buf.set_cur_offset(meta_size); }
+
+ bool has_overflow() { return _overflow_size > 0; }
+ void set_overflow(uint32_t offset, uint32_t size);
+ uint32_t move_overflow();
+
+ void expand_buf_if_needed(uint32_t required_size, uint32_t copy_size) {
+ _buf.expand_if_needed(required_size, copy_size);
+ }
+
+ uint32_t deserialize_size_only() { return *((uint32_t*)_buf.buf()); }
+
+ template <typename... Args>
+ int serialize(const Args* const... args);
+ template <typename... Args>
+ int deserialize(Args* const&... args);
+
+ void serialize_meta();
+ void deserialize_meta();
+};
+
+#endif // SHARE_JBOOSTER_NET_MESSAGE_HPP
diff --git a/src/hotspot/share/jbooster/net/message.inline.hpp b/src/hotspot/share/jbooster/net/message.inline.hpp
new file mode 100644
index 000000000..5b5add47f
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/message.inline.hpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_MESSAGE_INLINE_HPP
+#define SHARE_JBOOSTER_NET_MESSAGE_INLINE_HPP
+
+#include <string.h>
+
+#include "jbooster/net/message.hpp"
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serialization.hpp"
+
+inline void Message::set_overflow(uint32_t offset, uint32_t size) {
+ guarantee(!has_overflow(), "handle the existing overflow first");
+ assert(size > 0 && offset + size <= _buf.buf_size(), "sanity");
+ _overflow_offset = offset;
+ _overflow_size = size;
+}
+
+inline uint32_t Message::move_overflow() {
+ assert(has_overflow(), "sanity");
+ uint32_t size = _overflow_size;
+ memmove(_buf.buf(), _buf.buf() + _overflow_offset, _overflow_size);
+ _overflow_offset = _overflow_size = 0;
+ return size;
+}
+
+inline int Message::serialize_inner() {
+ return 0;
+}
+
+inline int Message::deserialize_inner() {
+ return 0;
+}
+
+template <typename Arg, typename... Args>
+inline int Message::serialize_inner(const Arg* const arg, const Args* const... args) {
+ JB_RETURN(_buf.serialize_with_meta(arg));
+ return serialize_inner(args...);
+}
+
+template <typename Arg, typename... Args>
+inline int Message::deserialize_inner(Arg* const& arg, Args* const&... args) {
+ JB_RETURN(_buf.deserialize_with_meta(arg));
+ return deserialize_inner(args...);
+}
+
+template <typename... Args>
+inline int Message::serialize(const Args* const... args) {
+ return serialize_inner(args...);
+}
+
+template <typename... Args>
+inline int Message::deserialize(Args* const&... args) {
+ return deserialize_inner(args...);
+}
+
+inline void Message::serialize_meta() {
+ _buf.set_cur_offset(0);
+ _buf.serialize_no_meta(_meta.msg_size);
+ _buf.serialize_no_meta(_meta.msg_type);
+ assert(cur_buf_offset() == meta_size, "sanity");
+}
+
+inline void Message::deserialize_meta() {
+ _buf.set_cur_offset(0);
+ _buf.deserialize_ref_no_meta(_meta.msg_size);
+ _buf.deserialize_ref_no_meta(_meta.msg_type);
+ assert(cur_buf_offset() == meta_size, "sanity");
+}
+
+#endif // SHARE_JBOOSTER_NET_MESSAGE_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/net/messageBuffer.cpp b/src/hotspot/share/jbooster/net/messageBuffer.cpp
new file mode 100644
index 000000000..4673bb784
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/messageBuffer.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/net/messageBuffer.inline.hpp"
+
+MessageBuffer::MessageBuffer(SerializationMode smode, CommunicationStream* stream):
+ _smode(smode),
+ _buf_size(_default_buf_size),
+ _buf(NEW_C_HEAP_ARRAY(char, _buf_size, mtJBooster)),
+ _cur_offset(0),
+ _stream(stream) {}
+
+MessageBuffer::~MessageBuffer() {
+ FREE_C_HEAP_ARRAY(char, _buf);
+}
+
+/**
+ * Round capacity to power of 2, at most limit.
+ * Make sure that 0 < _buf_size < required_size <= 0x80000000.
+ */
+uint32_t MessageBuffer::calc_new_buf_size(uint32_t required_size) {
+ guarantee(required_size <= 0x80000000, "Message size is too big");
+ uint32_t v = required_size - 1;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return v + 1;
+}
+
+void MessageBuffer::expand_buf(uint32_t required_size, uint32_t copy_size) {
+ char* old_buf = _buf;
+ uint32_t new_buf_size = calc_new_buf_size(required_size);
+ char* new_buf = NEW_C_HEAP_ARRAY(char, new_buf_size, mtJBooster);
+ memcpy(new_buf, old_buf, copy_size);
+
+ _buf = new_buf;
+ _buf_size = new_buf_size;
+ FREE_C_HEAP_ARRAY(char, old_buf);
+}
diff --git a/src/hotspot/share/jbooster/net/messageBuffer.hpp b/src/hotspot/share/jbooster/net/messageBuffer.hpp
new file mode 100644
index 000000000..aaa8e7c3b
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/messageBuffer.hpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_MESSAGEBUFFER_HPP
+#define SHARE_JBOOSTER_NET_MESSAGEBUFFER_HPP
+
+#include "jbooster/net/netCommon.hpp"
+#include "memory/allocation.hpp"
+
+class CommunicationStream;
+
+class SerializationMode {
+public:
+ enum Value: uint8_t {
+ BOTH,
+ SERIALIZE,
+ DESERIALIZE
+ };
+
+private:
+ Value _mode;
+
+public:
+ SerializationMode(Value mode): _mode(mode) {}
+
+ void set(Value mode) { _mode = mode; }
+
+ bool operator == (const SerializationMode other) const {
+ return _mode == other._mode;
+ }
+
+ bool can_serialize() const { return _mode != DESERIALIZE; }
+ bool can_deserialize() const { return _mode != SERIALIZE; }
+
+ void assert_can_serialize() const NOT_DEBUG_RETURN;
+ void assert_can_deserialize() const NOT_DEBUG_RETURN;
+};
+
+class MessageBuffer final: public StackObj {
+ friend class Message;
+
+private:
+ const uint32_t _default_buf_size = 4 * 1024; // 4k
+
+ SerializationMode _smode;
+ uint32_t _buf_size;
+ char* _buf;
+ uint32_t _cur_offset;
+ CommunicationStream* const _stream;
+
+private:
+ static uint32_t calc_new_buf_size(uint32_t required_size);
+ void expand_buf(uint32_t required_size, uint32_t copy_size);
+
+public:
+ MessageBuffer(SerializationMode smode, CommunicationStream* stream = nullptr);
+ ~MessageBuffer();
+
+ char* buf() const { return _buf; }
+ uint32_t buf_size() const { return _buf_size; }
+
+ uint32_t cur_offset() const { return _cur_offset; }
+ void set_cur_offset(uint32_t offset) { _cur_offset = offset; }
+ void skip_cur_offset(uint32_t offset) { _cur_offset += offset; }
+ void reset_cur_offset() { _cur_offset = 0u; }
+
+ char* cur_buf_ptr() const { return _buf + _cur_offset; }
+
+ CommunicationStream* stream() const { return _stream; }
+
+ void expand_if_needed(uint32_t required_size, uint32_t copy_size) {
+ if (_buf_size < required_size) {
+ expand_buf(required_size, copy_size);
+ }
+ }
+
+ // serializers
+ int serialize_memcpy(const void* from, uint32_t arg_size);
+ template <typename Arg>
+ int serialize_no_meta(const Arg& arg);
+ template <typename Arg>
+ int serialize_with_meta(const Arg* arg_ptr);
+
+ // deserializers
+ int deserialize_memcpy(void* to, uint32_t arg_size);
+ template <typename Arg>
+ int deserialize_ref_no_meta(Arg& arg);
+ template <typename Arg>
+ int deserialize_ptr_no_meta(Arg*& arg_ptr);
+ template <typename Arg>
+ int deserialize_with_meta(Arg* const& arg_ptr);
+};
+
+#endif // SHARE_JBOOSTER_NET_MESSAGEBUFFER_HPP
diff --git a/src/hotspot/share/jbooster/net/messageBuffer.inline.hpp b/src/hotspot/share/jbooster/net/messageBuffer.inline.hpp
new file mode 100644
index 000000000..c7dc0986f
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/messageBuffer.inline.hpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_MESSAGEBUFFER_INLINE_HPP
+#define SHARE_JBOOSTER_NET_MESSAGEBUFFER_INLINE_HPP
+
+#include "jbooster/net/messageBuffer.hpp"
+#include "jbooster/net/serialization.hpp"
+#include "logging/log.hpp"
+
+#ifdef ASSERT
+inline void SerializationMode::assert_can_serialize() const {
+ assert(can_serialize(), "serialization only");
+}
+
+inline void SerializationMode::assert_can_deserialize() const {
+ assert(can_deserialize(), "deserialization only");
+}
+#endif
+
+inline int MessageBuffer::serialize_memcpy(const void* from, uint32_t arg_size) {
+ _smode.assert_can_serialize();
+ assert(from != nullptr, "sanity");
+ uint32_t nxt_offset = _cur_offset + arg_size;
+ expand_if_needed(nxt_offset, _cur_offset);
+ memcpy((void*) (_buf + _cur_offset), from, arg_size);
+ _cur_offset = nxt_offset;
+ return 0;
+}
+
+template <typename Arg>
+inline int MessageBuffer::serialize_no_meta(const Arg& arg) {
+ _smode.assert_can_serialize();
+ return SerializationImpl<Arg>::serialize(*this, arg);
+}
+
+template <typename Arg>
+inline int MessageBuffer::serialize_with_meta(const Arg* arg_ptr) {
+ _smode.assert_can_serialize();
+ if (arg_ptr == nullptr) {
+ return serialize_no_meta(MessageConst::NULL_PTR);
+ }
+ const Arg& arg = *arg_ptr;
+ uint32_t meta_offset = _cur_offset;
+ skip_cur_offset(MessageConst::arg_meta_size);
+ expand_if_needed(_cur_offset, _cur_offset);
+ JB_RETURN(serialize_no_meta(arg));
+
+ // fill arg meta at last
+ uint32_t arg_size = _cur_offset - meta_offset - MessageConst::arg_meta_size;
+ memcpy((void*) (_buf + meta_offset), &arg_size, sizeof(arg_size));
+ return 0;
+}
+
+inline int MessageBuffer::deserialize_memcpy(void* to, uint32_t arg_size) {
+ _smode.assert_can_deserialize();
+ assert(to != nullptr, "sanity");
+ uint32_t nxt_offset = _cur_offset + arg_size;
+ if (_buf_size < nxt_offset) {
+ log_warning(jbooster, rpc)("The size to parse is longer than the msg size: "
+ "arg_size=%u, cur_offset=%u, nxt_offset=%u, buf_size=%u",
+ arg_size, _cur_offset, nxt_offset, _buf_size);
+ return JBErr::BAD_MSG_DATA;
+ }
+ memcpy(to, (void*) (_buf + _cur_offset), arg_size);
+ _cur_offset = nxt_offset;
+ return 0;
+}
+
+template <typename Arg>
+inline int MessageBuffer::deserialize_ref_no_meta(Arg& arg) {
+ _smode.assert_can_deserialize();
+ return SerializationImpl<Arg>::deserialize_ref(*this, arg);
+}
+
+template <typename Arg>
+inline int MessageBuffer::deserialize_ptr_no_meta(Arg*& arg_ptr) {
+ _smode.assert_can_deserialize();
+ assert(arg_ptr == nullptr, "memory will be allocated by this deserializer");
+ return SerializationImpl<Arg>::deserialize_ptr(*this, arg_ptr);
+}
+
+template <typename Arg>
+inline int MessageBuffer::deserialize_with_meta(Arg* const& arg_ptr) {
+ _smode.assert_can_deserialize();
+ uint32_t arg_size;
+ JB_RETURN(deserialize_ref_no_meta(arg_size));
+ uint32_t arg_begin = _cur_offset;
+ int jb_err;
+ if (arg_ptr == nullptr) {
+ if (arg_size == MessageConst::NULL_PTR) {
+ return 0;
+ }
+ Arg*& nonconst_ptr = const_cast<Arg*&>(arg_ptr);
+ jb_err = deserialize_ptr_no_meta(nonconst_ptr);
+ if (jb_err != JBErr::DESER_TERMINATION) JB_RETURN(jb_err);
+ } else {
+ assert(arg_size != MessageConst::NULL_PTR, "nullptr cannot be assigned to \"Args* const&\"");
+ jb_err = deserialize_ref_no_meta(*arg_ptr);
+ if (jb_err != JBErr::DESER_TERMINATION) JB_RETURN(jb_err);
+ }
+ if (_cur_offset - arg_begin != arg_size) {
+ if (jb_err == JBErr::DESER_TERMINATION && (_cur_offset - arg_begin < arg_size)) {
+ _cur_offset = arg_begin + arg_size;
+ return 0;
+ }
+ const char* type_name = DebugUtils::type_name<Arg>();
+ log_warning(jbooster, rpc)("The arg size does match the parsed size: "
+ "arg=%s, arg_size=%u, (cur_size - arg_begin)=%u.",
+ type_name, arg_size, _cur_offset - arg_begin);
+ FREE_C_HEAP_ARRAY(char, type_name);
+ return JBErr::BAD_ARG_SIZE;
+ }
+ return 0;
+}
+
+#endif // SHARE_JBOOSTER_NET_MESSAGEBUFFER_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/net/messageType.cpp b/src/hotspot/share/jbooster/net/messageType.cpp
new file mode 100644
index 000000000..3d0065f2b
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/messageType.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/net/messageType.hpp"
+
+#define REGISTER_MESSAGE_TYPE_STR(type_name, human_readable) #type_name,
+
+static const char* msg_type_name_arr[] = {
+ MESSAGE_TYPES(REGISTER_MESSAGE_TYPE_STR)
+ "END_OF_MESSAGE_TYPE"
+};
+
+const char* msg_type_name(MessageType meg_type) {
+ int mt = (int)meg_type;
+ if (mt < 0) return "UNKNOWN_NEGATIVE";
+ if (mt >= (int)(sizeof(msg_type_name_arr) / sizeof(char*))) return "UNKNOWN_POSITIVE";
+ return msg_type_name_arr[(int)meg_type];
+}
+
+#undef REGISTER_MESSAGE_TYPE_STR
diff --git a/src/hotspot/share/jbooster/net/messageType.hpp b/src/hotspot/share/jbooster/net/messageType.hpp
new file mode 100644
index 000000000..f8cb8f3e6
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/messageType.hpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_MESSAGETYPE_HPP
+#define SHARE_JBOOSTER_NET_MESSAGETYPE_HPP
+
+#include "utilities/globalDefinitions.hpp" // for uint16_t
+
+#define MESSAGE_TYPES(f) \
+ /* session/stream meta related */ \
+ f(ClientSessionMeta, "from client" ) \
+ f(ClientStreamMeta, "from client" ) \
+ f(ClientSessionMetaAgain, "from client" ) \
+ \
+ /* task related */ \
+ f(EndOfCurrentPhase, "from both" ) \
+ f(NoMoreRequests, "from client" ) \
+ f(ClientDaemonTask, "from client" ) \
+ f(CacheFilesSyncTask, "from client" ) \
+ f(LazyAOTCompilationTask, "from client" ) \
+ \
+ /* cache file related */ \
+ f(GetLazyAOTCache, "from client" ) \
+ f(GetAggressiveCDSCache, "from client" ) \
+ f(GetClassLoaderResourceCache, "from client" ) \
+ f(CacheAggressiveCDS, "from server" ) \
+ f(CacheClassLoaderResource, "from server" ) \
+ \
+ /* Lazy AOT related */ \
+ f(ClassLoaderLocators, "from server" ) \
+ f(DataOfClassLoaders, "from server" ) \
+ f(KlassLocators, "from server" ) \
+ f(Profilinginfo, "from server" ) \
+ f(ArrayKlasses, "from server" ) \
+ f(DataOfKlasses, "from server" ) \
+ f(MethodLocators, "from server" ) \
+ \
+ /* others */ \
+ f(FileSegment, "from both" ) \
+ f(Heartbeat, "from both" ) \
+ f(AOTRelatedClassNames, "from client" ) \
+ f(AOTCompilationResult, "from server" ) \
+ f(AbortCompilation, "from both" ) \
+ f(CompilationFailure, "from both" ) \
+ f(UnexpectedMessageType, "from both" ) \
+ f(UnsupportedClient, "from server" ) \
+ f(Unknown, "from both" ) \
+
+
+#define REGISTER_MESSAGE_TYPE_ENUM(type_name, human_readable) type_name,
+
+enum class MessageType: uint16_t {
+ MESSAGE_TYPES(REGISTER_MESSAGE_TYPE_ENUM)
+ END_OF_MESSAGE_TYPE
+};
+
+#undef REGISTER_MESSAGE_TYPE_ENUM
+
+const char* msg_type_name(MessageType meg_type);
+
+#endif // SHARE_JBOOSTER_NET_MESSAGETYPE_HPP
diff --git a/src/hotspot/share/jbooster/net/netCommon.hpp b/src/hotspot/share/jbooster/net/netCommon.hpp
new file mode 100644
index 000000000..8706b7f22
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/netCommon.hpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_NETCOMMON_HPP
+#define SHARE_JBOOSTER_NET_NETCOMMON_HPP
+
+#include "jbooster/net/errorCode.hpp"
+#include "jbooster/net/messageType.hpp"
+
+// C++ exceptions are disabled in Hotspot (see hotspot-style.md).
+// And Thread::current() is not always available in our cases so we do not choose
+// to use the tools in exceptions.hpp.
+// Instead we choose to return the error code just like ZGC does (see zErrno.hpp).
+//
+// Here is a simple implementation of the "try&catch" statements. They are used
+// only for CommunicationStream. How to use:
+//
+// // try (no support for "break" or "continue" when in a for/switch block)
+// JB_TRY {
+// JB_THROW(func());
+// if (...) JB_THROW(err_code1);
+// while (...) {...} JB_THROW(JB_ERR);
+// } JB_TRY_END
+//
+// // or use this (supports "break" and "continue" when in a for/switch block))
+// JB_TRY_BREAKABLE {
+// JB_THROW(func());
+// if (...) JB_THROW(err_code1);
+// while (...) {...} JB_THROW(JB_ERR);
+// if (...) continue; else break;
+// } JB_TRY_BREAKABLE_END
+//
+// // catch (do not insert any code between "JB_TRY_END" and "JB_CATCH")
+// JB_CATCH(err_code2) {...}
+// JB_CATCH(err_code3, err_code4, err_code5) {...}
+// JB_CATCH_REST() {
+// if (JB_ERR != err_code6) break;
+// else continue;
+// } JB_CATCH_END;
+//
+// Note1: Use JB_TRY_BREAKABLE if you are in "while/for/switch" statements and want
+// to use "break" or "continue" in our "try" block. Use JB_TRY if not.
+// Note2: Always use JB_THROW in a JB_TRY or JB_TRY_BREAKABLE block!
+// It does not support being thrown to the upper layer.
+// Note3: We use "do {...} while (false);" to implement the "try/catch" statements.
+// So if you have another "while/for/switch" statement inside the JB_TRY,
+// insert a JB_THROW(JB_ERR) after the statement.
+#define JB_ERR __jbooster_errcode__
+
+// do not call JB_INNER_ macros
+#define JB_INNER_CONTROL __jbooster_control__
+#define JB_INNER_NORMAL 0
+#define JB_INNER_CONTINUE 1
+#define JB_INNER_BREAK 2
+
+#define JB_INNER_CMP_1(e) JB_ERR == (e)
+#define JB_INNER_CMP_2(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_1(__VA_ARGS__)
+#define JB_INNER_CMP_3(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_2(__VA_ARGS__)
+#define JB_INNER_CMP_4(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_3(__VA_ARGS__)
+#define JB_INNER_CMP_5(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_4(__VA_ARGS__)
+#define JB_INNER_CMP_6(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_5(__VA_ARGS__)
+#define JB_INNER_CMP_7(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_6(__VA_ARGS__)
+#define JB_INNER_CMP_8(e, ...) JB_INNER_CMP_1(e) || JB_INNER_CMP_7(__VA_ARGS__)
+
+#define JB_INNER_CMP_GET(_1, _2, _3, _4, _5, _6, _7, _8, V, ...) V
+#define JB_INNER_CMP(...) JB_INNER_CMP_GET(__VA_ARGS__, \
+ JB_INNER_CMP_8, \
+ JB_INNER_CMP_7, \
+ JB_INNER_CMP_6, \
+ JB_INNER_CMP_5, \
+ JB_INNER_CMP_4, \
+ JB_INNER_CMP_3, \
+ JB_INNER_CMP_2, \
+ JB_INNER_CMP_1) \
+ (__VA_ARGS__)
+
+// the try/catch statements
+#define JB_TRY { int JB_ERR = 0; do
+#define JB_TRY_END while (false); \
+ if (JB_ERR == 0) { /* do nothing */ }
+
+#define JB_TRY_BREAKABLE { int JB_ERR = 0; \
+ uint8_t JB_INNER_CONTROL = JB_INNER_BREAK; \
+ do {
+#define JB_TRY_BREAKABLE_END JB_INNER_CONTROL = JB_INNER_NORMAL; \
+ break; \
+ } while ((JB_INNER_CONTROL = JB_INNER_CONTINUE) \
+ != JB_INNER_CONTINUE); \
+ if (JB_ERR == 0) { \
+ if (JB_INNER_CONTROL != JB_INNER_NORMAL) { \
+ if (JB_INNER_CONTROL == JB_INNER_BREAK) break; \
+ else continue; \
+ } \
+ }
+
+// Here we use "expression" instead of "(expression)" to be compatible with "CHECK"
+// in exceptions.hpp.
+#define JB_THROW(expression) { JB_ERR = expression; \
+ if (JB_ERR != 0) break; \
+ }
+
+#define JB_CATCH(...) else if (JB_INNER_CMP(__VA_ARGS__))
+#define JB_CATCH_REST() else
+#define JB_CATCH_END }
+
+// Here we use "expression" instead of "(expression)" to be compatible with "CHECK"
+// in exceptions.hpp.
+#define JB_RETURN(expression) { int JB_ERR = expression; \
+ if (JB_ERR != 0) return JB_ERR; \
+ }
+
+// End of the definition of JB_TRY/JB_CATCH.
+
+class MessageConst {
+public:
+ enum: uint32_t {
+ /**
+ * The layout of the message in the buffer:
+ * | msg_size | msg_type | ... (all of the arguments) ... |
+ * | 4 bytes | 2 bytes | msg_size - 4 - 2 bytes |
+ */
+ meta_size = sizeof(uint32_t) + sizeof(MessageType),
+ /**
+ * The layout of each argument in the buffer:
+ * | arg_size | ... (payload of the argument) ... |
+ * | 4 bytes | arg_size bytes |
+ */
+ arg_meta_size = sizeof(uint32_t),
+
+ NULL_PTR = 0xffffffff // @see ArrayWrapper::NULL_PTR
+ };
+};
+
+#endif // SHARE_JBOOSTER_NET_NETCOMMON_HPP
diff --git a/src/hotspot/share/jbooster/net/rpcCompatibility.cpp b/src/hotspot/share/jbooster/net/rpcCompatibility.cpp
new file mode 100644
index 000000000..7f849a6fe
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/rpcCompatibility.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/dataTransmissionUtils.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/jClientArguments.hpp"
+#include "jbooster/jClientVMFlags.hpp"
+#include "jbooster/net/clientStream.hpp"
+#include "jbooster/net/communicationStream.hpp"
+#include "jbooster/net/errorCode.hpp"
+#include "jbooster/net/message.hpp"
+#include "jbooster/net/messageBuffer.hpp"
+#include "jbooster/net/rpcCompatibility.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/net/serverStream.hpp"
+
+static constexpr uint32_t calc_new_hash(uint32_t old_hash, uint32_t ele_hash) {
+ return 31 * old_hash + ele_hash;
+}
+
+template <typename T>
+static constexpr uint32_t calc_class_hash() {
+ uint32_t hash = (uint32_t) sizeof(T);
+ return hash ^ (hash >> 3);
+}
+
+template <typename Last>
+static constexpr uint32_t calc_classes_hash() {
+ return calc_class_hash<Last>();
+}
+
+template <typename First, typename Second, typename... Rest>
+static constexpr uint32_t calc_classes_hash() {
+ return calc_new_hash(calc_classes_hash<Second, Rest...>(), calc_class_hash<First>());
+}
+
+/**
+ * Returns a magic number computed at compile time based on the sizes of some classes.
+ * It is just an crude way to check compatibility for now. More policies can be added later.
+ */
+static constexpr uint32_t calc_magic_num() {
+ return calc_classes_hash<
+ JBoosterManager, JClientVMFlags, JClientArguments,
+ JBErr, Message, MessageBuffer,
+ CommunicationStream, ClientStream, ServerStream,
+ ArrayWrapper<int>, MemoryWrapper, StringWrapper, FileWrapper,
+ ClassLoaderKey, ClassLoaderChain, ClassLoaderLocator, KlassLocator, MethodLocator, ProfileDataCollector
+ >();
+}
+
+uint32_t RpcCompatibility::magic_num() {
+ return calc_magic_num();
+}
diff --git a/src/hotspot/share/jbooster/net/rpcCompatibility.hpp b/src/hotspot/share/jbooster/net/rpcCompatibility.hpp
new file mode 100644
index 000000000..da4283d41
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/rpcCompatibility.hpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_COMPATIBILITY_HPP
+#define SHARE_JBOOSTER_NET_COMPATIBILITY_HPP
+
+#include "jbooster/net/serializationWrappers.hpp"
+#include "memory/allocation.hpp"
+
+/**
+ * Check whether the server is compatible with the client.
+ * If the magic numbers are different, then the RPC formats may be different.
+ */
+class RpcCompatibility: public StackObj {
+public:
+ static uint32_t magic_num();
+
+ int serialize(MessageBuffer& buf) const {
+ return buf.serialize_no_meta(magic_num());
+ }
+ int deserialize(MessageBuffer& buf) {
+ uint32_t hash;
+ JB_RETURN(buf.deserialize_ref_no_meta(hash));
+ return ((hash == magic_num()) ? 0 : JBErr::INCOMPATIBLE_RPC);
+ }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(RpcCompatibility);
+
+#endif // SHARE_JBOOSTER_NET_COMPATIBILITY_HPP
diff --git a/src/hotspot/share/jbooster/net/serialization.cpp b/src/hotspot/share/jbooster/net/serialization.cpp
new file mode 100644
index 000000000..b0db65e68
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serialization.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/classFileStream.hpp"
+#include "classfile/classLoadInfo.hpp"
+#include "classfile/classLoaderData.inline.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmClasses.hpp"
+#include "jbooster/dataTransmissionUtils.hpp"
+#include "jbooster/jbooster_globals.hpp"
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serialization.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/arrayKlass.hpp"
+#include "oops/instanceKlass.hpp"
+#include "prims/jvmtiClassFileReconstituter.hpp"
+#include "runtime/handles.inline.hpp"
+#include "utilities/stringUtils.hpp"
+
+// ------------------ Serializer of C-style String (i.e., char*) -------------------
+
+int SerializationImpl<const char*>::serialize(MessageBuffer& buf, const Arg& arg) {
+ if (arg == nullptr) {
+ return buf.serialize_no_meta(MessageConst::NULL_PTR);
+ }
+ uint32_t str_len = strlen(arg);
+ JB_RETURN(buf.serialize_no_meta(str_len));
+ return buf.serialize_memcpy(arg, str_len);
+}
+
+int SerializationImpl<const char*>::deserialize_ref(MessageBuffer& buf, Arg& arg) {
+ char* str = nullptr;
+ JB_RETURN(buf.deserialize_ref_no_meta<char*>(str));
+ arg = str;
+ return 0;
+}
+
+int SerializationImpl<char*>::serialize(MessageBuffer& buf, const Arg& arg) {
+ const char* str = arg;
+ return buf.serialize_no_meta<const char*>(arg);
+}
+
+int SerializationImpl<char*>::deserialize_ref(MessageBuffer& buf, Arg& arg) {
+ guarantee(arg == nullptr, "do not pre-allocate the string");
+ uint32_t str_len;
+ JB_RETURN(buf.deserialize_ref_no_meta(str_len));
+ if (str_len == MessageConst::NULL_PTR) {
+ return 0;
+ }
+ char* s = NEW_RESOURCE_ARRAY(char, str_len + 1);
+ JB_RETURN(buf.deserialize_memcpy(s, str_len));
+ s[str_len] = '\0';
+ arg = s;
+ return 0;
+}
+
+// ----------------------------- Serializer for Symbol -----------------------------
+
+int SerializationImpl<Symbol>::serialize(MessageBuffer& buf, const Symbol& arg) {
+ JB_RETURN(buf.serialize_no_meta(arg.utf8_length()));
+ return buf.serialize_memcpy(arg.base(), arg.utf8_length());
+}
+
+int SerializationImpl<Symbol>::deserialize_ptr(MessageBuffer& buf, Symbol*& arg_ptr) {
+ int len;
+ JB_RETURN(buf.deserialize_ref_no_meta(len));
+ arg_ptr = SymbolTable::new_symbol(buf.cur_buf_ptr(), len);
+ buf.skip_cur_offset((uint32_t) len);
+ return 0;
+};
+
+// ------------------------- Serializer for InstanceKlass --------------------------
+// We rebuild the InstanceKlass based on the following method:
+// InstanceKlass* SystemDictionary::resolve_from_stream(
+// ClassFileStream* st, Symbol* class_name, Handle class_loader,
+// const ClassLoadInfo& cl_info, TRAPS)
+
+int SerializationImpl<InstanceKlass>::serialize(MessageBuffer& buf, const InstanceKlass& arg) {
+ assert(UseJBooster, "only for client klass");
+
+ // client-side klass pointer
+ JB_RETURN(buf.serialize_no_meta((uintptr_t) &arg));
+
+ // [arg of resolve_from_stream] Symbol* class_name
+ JB_RETURN(buf.serialize_with_meta(arg.name()));
+
+ // [arg of resolve_from_stream] Handle class_loader
+ ClassLoaderLocator cll(arg.class_loader_data());
+ JB_RETURN(buf.serialize_no_meta(cll));
+
+ // [arg of resolve_from_stream] const ClassLoadInfo& cl_info (ignored for now)
+ JB_RETURN(buf.serialize_no_meta((int) 0x1c2d3e4f));
+
+ // [arg of resolve_from_stream] ClassFileStream* st
+ {
+ JavaThread* THREAD = JavaThread::current();
+ ResourceMark rm(THREAD);
+ HandleMark hm(THREAD);
+
+ char* cf_buf = nullptr;
+ uint32_t cf_size = 0;
+
+ bool should_send_class_file = !arg.class_loader_data()->is_boot_class_loader_data();
+ if (should_send_class_file) {
+ InstanceKlass* ik = const_cast<InstanceKlass*>(&arg);
+ JvmtiClassFileReconstituter reconstituter(ik);
+ if (reconstituter.get_error() == JVMTI_ERROR_NONE) {
+ cf_buf = (char*) reconstituter.class_file_bytes();
+ cf_size = (uint32_t) reconstituter.class_file_size();
+ }
+ }
+ MemoryWrapper mw((void*) cf_buf, cf_size);
+ JB_RETURN(buf.serialize_no_meta(mw));
+ }
+
+ return 0;
+}
+
+static InstanceKlass* resolve_instance_klass(Symbol* class_name,
+ ClassLoaderData* class_loader_data,
+ Handle protection_domain,
+ TRAPS) {
+ Handle class_loader(THREAD, class_loader_data->class_loader());
+ Klass* result = SystemDictionary::resolve_or_null(class_name, class_loader,
+ protection_domain, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ log_warning(jbooster, serialization)("Failed to deserialize and resolve InstanceKlass \"%s\" "
+ "(loader=\"%s:%p\"): see stack trace.",
+ class_name->as_C_string(),
+ class_loader_data->loader_name(),
+ class_loader_data);
+ LogTarget(Warning, jbooster, serialization) lt;
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ return nullptr;
+ } else if (result == nullptr) {
+ log_warning(jbooster, serialization)("Failed to deserialize InstanceKlass \"%s\" "
+ "(loader=\"%s:%p\"): no class file found.",
+ class_name->as_C_string(),
+ class_loader_data->loader_name(),
+ class_loader_data);
+ return nullptr;
+ }
+ return InstanceKlass::cast(result);
+}
+
+static InstanceKlass* rebuild_instance_klass(Symbol* class_name,
+ ClassLoaderData* class_loader_data,
+ ClassLoadInfo& cl_info,
+ u1* class_file_buf,
+ int class_file_size,
+ TRAPS) {
+ Handle class_loader(THREAD, class_loader_data->class_loader());
+ ClassFileStream st(class_file_buf, class_file_size, "__VM_JBoosterDeserialization__");
+ InstanceKlass* result = SystemDictionary::resolve_from_stream(&st, class_name, class_loader,
+ cl_info, THREAD);
+ if (result == nullptr || HAS_PENDING_EXCEPTION) {
+ assert(HAS_PENDING_EXCEPTION, "sanity");
+ Handle first_ex(THREAD, THREAD->pending_exception());
+ if (PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass())) {
+ // The class loader attempted duplicate class definition for the target class.
+ Handle ex(THREAD, PENDING_EXCEPTION);
+ const char* efile = ((ThreadShadow*)THREAD)->exception_file();
+ int eline = ((ThreadShadow*)THREAD)->exception_line();
+ CLEAR_PENDING_EXCEPTION;
+ Klass* found = SystemDictionary::find_instance_klass(class_name, class_loader, Handle());
+ if (found != nullptr) {
+ result = InstanceKlass::cast(found);
+ }
+ }
+ // still null
+ if (result == nullptr) {
+ LogTarget(Warning, jbooster, serialization) lt;
+ if (lt.is_enabled()) {
+ lt.print("Failed to deserialize and rebuild InstanceKlass \"%s\" "
+ "(loader=\"%s:%p\"): see stack trace.",
+ class_name->as_C_string(),
+ class_loader_data->loader_name(),
+ class_loader_data);
+ if (HAS_PENDING_EXCEPTION) {
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ } else {
+ lt.print("(A LinkageError but not caused by duplicate definition)");
+ LogStream ls(lt);
+ java_lang_Throwable::print_stack_trace(first_ex, &ls);
+ }
+ }
+ }
+ CLEAR_PENDING_EXCEPTION;
+ }
+ return result;
+}
+
+int SerializationImpl<InstanceKlass>::deserialize_ptr(MessageBuffer& buf, InstanceKlass*& arg_ptr) {
+ assert(AsJBooster, "only called on the server");
+ JavaThread* THREAD = JavaThread::current();
+ ResourceMark rm(THREAD);
+
+ // client-side InstanceKlass pointer
+ uintptr_t client_klass;
+ JB_RETURN(buf.deserialize_ref_no_meta(client_klass));
+
+ // [arg of resolve_from_stream] Symbol* class_name
+ Symbol* class_name = nullptr;
+ JB_RETURN(buf.deserialize_with_meta(class_name));
+ TempNewSymbol class_name_wrapper = class_name;
+
+ // [arg of resolve_from_stream] Handle class_loader
+ ClassLoaderLocator cll;
+ JB_RETURN(buf.deserialize_ref_no_meta(cll));
+ if (cll.class_loader_data() == nullptr) {
+ log_warning(jbooster, serialization)("Failed to deserialize InstanceKlass \"%s\": "
+ "cannot deserialize class loader " INTPTR_FORMAT ".",
+ class_name->as_C_string(), cll.client_cld_address());
+ return JBErr::DESER_TERMINATION;
+ }
+ ClassLoaderData* class_loader_data = cll.class_loader_data();
+
+ // [arg of resolve_from_stream] const ClassLoadInfo& cl_info (ignored for now)
+ int placeholder_num;
+ JB_RETURN(buf.deserialize_ref_no_meta(placeholder_num));
+ if (placeholder_num != 0x1c2d3e4f) {
+ log_warning(jbooster, serialization)("Failed to deserialize klass \"%s\": "
+ "wrong placeholder_num: %d.",
+ class_name->as_C_string(), placeholder_num);
+ return JBErr::BAD_ARG_DATA;
+ }
+ Handle protection_domain;
+ ClassLoadInfo cl_info(protection_domain);
+
+ // [arg of resolve_from_stream] ClassFileStream* st
+ MemoryWrapper mw;
+ JB_RETURN(buf.deserialize_ref_no_meta(mw));
+
+ InstanceKlass* res = nullptr;
+ if (mw.is_null()) {
+ res = resolve_instance_klass(class_name, class_loader_data,
+ cl_info.protection_domain(), THREAD);
+ } else {
+ res = rebuild_instance_klass(class_name, class_loader_data, cl_info,
+ (u1*) mw.get_memory(), (int) mw.size(), THREAD);
+ }
+ if (res != nullptr) {
+ JClientSessionData* session_data = ((ServerStream*) buf.stream())->session_data();
+ session_data->add_klass_address((address) client_klass, (address) res, THREAD);
+ arg_ptr = res;
+ }
+ return 0;
+}
+
+// --------------------------- Serializer for ArrayKlass ---------------------------
+// We rebuild the klass by:
+// SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader,
+// Handle protection_domain, TRAPS)
+
+int SerializationImpl<ArrayKlass>::serialize(MessageBuffer& buf, const ArrayKlass& arg) {
+ assert(UseJBooster, "only for client klass");
+
+ // client-side klass pointer
+ JB_RETURN(buf.serialize_no_meta((uintptr_t) &arg));
+
+ // class_name
+ JB_RETURN(buf.serialize_with_meta(arg.name()));
+
+ // class loader
+ ClassLoaderLocator cll(arg.class_loader_data());
+ JB_RETURN(buf.serialize_no_meta(cll));
+
+ return 0;
+}
+
+int SerializationImpl<ArrayKlass>::deserialize_ptr(MessageBuffer& buf, ArrayKlass*& arg_ptr) {
+ assert(AsJBooster, "only called on the server");
+ JavaThread* THREAD = JavaThread::current();
+ ResourceMark rm(THREAD);
+
+ // client-side ArrayKlass pointer
+ uintptr_t client_klass;
+ JB_RETURN(buf.deserialize_ref_no_meta(client_klass));
+
+ // class_name
+ Symbol* class_name = nullptr;
+ JB_RETURN(buf.deserialize_with_meta(class_name));
+ TempNewSymbol class_name_wrapper = class_name;
+
+ // class loader
+ ClassLoaderLocator cll;
+ JB_RETURN(buf.deserialize_ref_no_meta(cll));
+ if (cll.class_loader_data() == nullptr) {
+ log_warning(jbooster, serialization)("Failed to deserialize ArrayKlass \"%s\": "
+ "cannot deserialize class loader " INTPTR_FORMAT ".",
+ class_name->as_C_string(), cll.client_cld_address());
+ return JBErr::DESER_TERMINATION;
+ }
+ Handle class_loader(THREAD, cll.class_loader_data()->class_loader());
+
+ Klass* res = SystemDictionary::resolve_or_null(class_name, class_loader, Handle(), THREAD);
+ if (res == nullptr || HAS_PENDING_EXCEPTION) {
+ log_warning(jbooster, serialization)("Failed to deserialize ArrayKlass \"%s\" (loader={%s}): see stack trace.",
+ class_name->as_C_string(), cll.class_loader_data()->loader_name());
+ LogTarget(Warning, jbooster, serialization) lt;
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ } else {
+ JClientSessionData* session_data = ((ServerStream*) buf.stream())->session_data();
+ session_data->add_klass_address((address) client_klass, (address) res, THREAD);
+ arg_ptr = ArrayKlass::cast(res);
+ }
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/net/serialization.hpp b/src/hotspot/share/jbooster/net/serialization.hpp
new file mode 100644
index 000000000..f9a2996c5
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serialization.hpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+// ---------------------------------------------------------------------------------
+// This file implements serialization for the C++ classes in Hotspot.
+// This file has nothing to do with the serialization of Java classes.
+// ---------------------------------------------------------------------------------
+
+#ifndef SHARE_JBOOSTER_NET_SERIALIZATION_HPP
+#define SHARE_JBOOSTER_NET_SERIALIZATION_HPP
+
+#include <string.h>
+
+#include "jbooster/net/messageBuffer.hpp"
+#include "jbooster/net/netCommon.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "memory/allocation.hpp"
+#include "memory/resourceArea.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// ----------------------------- Serializer Implements -----------------------------
+// template <typename Arg>
+// struct CppClassSerializerImpl {
+// // Serialize the argument (invoked only if the arg is not null).
+// static int serialize(MessageBuffer& buf, const Arg& arg);
+//
+// // There are two deserialization modes. You don't need to implement both.
+// // * Implement the first one if the memory space of the arg has been
+// // pre-allocated.
+// // * Implement the second one if the memory space of the arg should be
+// // allocated inside deserialization.
+// static int deserialize_ref(MessageBuffer& buf, Arg& arg);
+// static int deserialize_ptr(MessageBuffer& buf, Arg*& arg_ptr);
+// };
+
+// ------------------------ Unsupported Deserializer Macro -------------------------
+
+#define CANNOT_DESERIALIZE_REFERENCE(Arg) \
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg) { \
+ fatal("Reference deserialization is not supported: %s,", \
+ DebugUtils::type_name<Arg>()); \
+ return 0; \
+ }
+
+#define CANNOT_DESERIALIZE_POINTER(Arg) \
+ static int deserialize_ptr(MessageBuffer& buf, Arg*& arg_ptr) { \
+ fatal("Pointer deserialization is not supported: %s,", \
+ DebugUtils::type_name<Arg>()); \
+ return 0; \
+ }
+
+// ------------------------------ Default Serializer -------------------------------
+// The types it support for serialization/deserialization:
+// - Base types: bool, int, long long, size_t, uint64_t, and so on.
+// - POD classes without pointers.
+//
+// memcpy() is good enough in most cases. Even for base types such as char and size_t,
+// memcpy() has the similar performance as assignment (`=`) according to our tests.
+// It's also a choice to use assignment here. But if a class overloads the operator `=`
+// and allocates something on the heap, it can cause a memory leak.
+
+template <typename Arg>
+struct SerializationImpl {
+ static int serialize(MessageBuffer& buf, const Arg& arg) {
+ return buf.serialize_memcpy(&arg, sizeof(Arg));
+ }
+
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg) {
+ return buf.deserialize_memcpy(&arg, sizeof(Arg));
+ }
+
+ CANNOT_DESERIALIZE_POINTER(Arg);
+};
+
+// ----------------------- Intrusive Serializer Declaration ------------------------
+
+#define DECLARE_SERIALIZER_INTRUSIVE(Arg) \
+template <> struct SerializationImpl<Arg> { \
+ static int serialize(MessageBuffer& buf, const Arg& arg) { \
+ return arg.serialize(buf); \
+ } \
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg) { \
+ return arg.deserialize(buf); \
+ } \
+ CANNOT_DESERIALIZE_POINTER(Arg); \
+};
+
+// --------------------- Non-Intrusive Serializer Declaration ----------------------
+
+#define DECLARE_SERIALIZER_ONLY_REFERENCE(Arg) \
+class Arg; \
+template <> struct SerializationImpl<Arg> { \
+ static int serialize(MessageBuffer& buf, const Arg& arg); \
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg); \
+ CANNOT_DESERIALIZE_POINTER(Arg); \
+};
+
+#define DECLARE_SERIALIZER_ONLY_POINTER(Arg) \
+class Arg; \
+template <> struct SerializationImpl<Arg> { \
+ static int serialize(MessageBuffer& buf, const Arg& arg); \
+ static int deserialize_ptr(MessageBuffer& buf, Arg*& arg_ptr); \
+ CANNOT_DESERIALIZE_REFERENCE(Arg); \
+};
+
+// Declare the classes that do not belong to JBooster below.
+DECLARE_SERIALIZER_ONLY_POINTER(Symbol);
+DECLARE_SERIALIZER_ONLY_POINTER(InstanceKlass);
+DECLARE_SERIALIZER_ONLY_POINTER(ArrayKlass);
+
+// ------------------ Serializer of C-style String (i.e., char*) -------------------
+// Threre are 4 specializations of the template:
+// - const char [arr_size] : serialize() only
+// - char [arr_size] : both serialize() and deserialize()
+// - const char * : both serialize() and deserialize()
+// - char * : both serialize() and deserialize()
+// They and StringWrapper are compatible with each other.
+//
+// **ATTENTION** Type `address` will be identified as `char*`.
+// So if you want to send an `address`, cast it to an `uintptr_t`.
+
+template <> struct SerializationImpl<const char*> {
+ using Arg = const char*;
+ static int serialize(MessageBuffer& buf, const Arg& arg);
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg);
+ CANNOT_DESERIALIZE_POINTER(Arg);
+};
+
+template <> struct SerializationImpl<char*> {
+ using Arg = char*;
+ static int serialize(MessageBuffer& buf, const Arg& arg);
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg);
+ CANNOT_DESERIALIZE_POINTER(Arg);
+};
+
+template <size_t arr_size> struct SerializationImpl<const char[arr_size]> {
+ using Arg = const char[arr_size];
+ static int serialize(MessageBuffer& buf, const Arg& arg) {
+ return buf.serialize_no_meta<const char*>(arg);
+ }
+
+ // Should not implement deserialize() here as the arg cannot be const.
+};
+
+template <size_t arr_size> struct SerializationImpl<char[arr_size]> {
+ using Arg = char[arr_size];
+ static int serialize(MessageBuffer& buf, const Arg& arg) {
+ return buf.serialize_no_meta<const char*>(arg);
+ }
+
+ static int deserialize_ref(MessageBuffer& buf, Arg& arg) {
+ uint32_t str_len;
+ JB_RETURN(buf.deserialize_ref_no_meta(str_len));
+ guarantee(arr_size >= (size_t) (str_len + 1),
+ "array index out of bounds: arr_size=" SIZE_FORMAT ", str_len=%u",
+ arr_size, str_len + 1);
+ guarantee(str_len != MessageConst::NULL_PTR, "cannot set array to null");
+ char* p = arg;
+ JB_RETURN(buf.deserialize_memcpy(p, str_len));
+ p[str_len] = '\0';
+ return 0;
+ }
+
+ CANNOT_DESERIALIZE_POINTER(Arg);
+};
+
+#endif // SHARE_JBOOSTER_NET_SERIALIZATION_HPP
diff --git a/src/hotspot/share/jbooster/net/serializationWrappers.cpp b/src/hotspot/share/jbooster/net/serializationWrappers.cpp
new file mode 100644
index 000000000..13db948f6
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serializationWrappers.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/communicationStream.inline.hpp"
+#include "jbooster/net/serializationWrappers.inline.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "runtime/os.inline.hpp"
+#include "utilities/stringUtils.hpp"
+
+// ------------------------- MemoryWrapper for Serializer --------------------------
+
+MemoryWrapper::MemoryWrapper(const void* base, uint32_t size):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(base),
+ _size(base == nullptr ? MessageConst::NULL_PTR : size),
+ _allocater(nullptr) {
+}
+
+MemoryWrapper::MemoryWrapper(MemoryAllocater* allocater):
+ WrapperBase(SerializationMode::DESERIALIZE),
+ _base(nullptr),
+ _size(MessageConst::NULL_PTR),
+ _allocater(allocater) {}
+
+MemoryWrapper::~MemoryWrapper() {
+ if (!_free_memory || _base == nullptr) return;
+ FREE_C_HEAP_ARRAY(char*, _base);
+}
+
+int MemoryWrapper::serialize(MessageBuffer& buf) const {
+ _smode.assert_can_serialize();
+ JB_RETURN(buf.serialize_no_meta(_size));
+ if (_base == nullptr) {
+ assert(_size == MessageConst::NULL_PTR, "sanity");
+ return 0;
+ }
+ return buf.serialize_memcpy(_base, _size);
+}
+
+int MemoryWrapper::deserialize(MessageBuffer& buf) {
+ _smode.assert_can_deserialize();
+ assert(_base == nullptr, "can only be deserialized once");
+ JB_RETURN(buf.deserialize_ref_no_meta(_size));
+ if (_size == MessageConst::NULL_PTR) {
+ return 0;
+ }
+ void* mem;
+ if (_allocater != nullptr) {
+ mem = _allocater->alloc(_size);
+ } else {
+ _free_memory = true;
+ mem = (void*) NEW_C_HEAP_ARRAY(char, _size, mtJBooster);
+ }
+ buf.deserialize_memcpy(mem, _size);
+ _base = mem;
+ return 0;
+}
+
+// ------------------------- StringWrapper for Serializer --------------------------
+
+StringWrapper::StringWrapper(const char* str):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(str),
+ _size(str == nullptr ? (uint32_t) MessageConst::NULL_PTR : (uint32_t) strlen(str)) {}
+
+StringWrapper::StringWrapper(const char* str, uint32_t size):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(str),
+ _size(str == nullptr ? (uint32_t) MessageConst::NULL_PTR : size) {
+#ifdef ASSERT
+ if (str != nullptr) {
+ assert(strlen(str) >= size, "sanity");
+ }
+#endif
+}
+
+StringWrapper::StringWrapper():
+ WrapperBase(SerializationMode::DESERIALIZE),
+ _base(nullptr),
+ _size(MessageConst::NULL_PTR) {}
+
+StringWrapper::~StringWrapper() {
+ if (!_free_memory || _base == nullptr) return;
+ FREE_C_HEAP_ARRAY(char, _base);
+}
+
+int StringWrapper::serialize(MessageBuffer& buf) const {
+ _smode.assert_can_serialize();
+ JB_RETURN(buf.serialize_no_meta(_size));
+ if (_base == nullptr) {
+ assert(_size == MessageConst::NULL_PTR, "sanity");
+ return 0;
+ }
+ assert(_base[_size] == '\0', "sanity");
+ return buf.serialize_memcpy(_base, _size);
+}
+
+int StringWrapper::deserialize(MessageBuffer& buf) {
+ _smode.assert_can_deserialize();
+ assert(_base == nullptr, "can only be deserialized once");
+ JB_RETURN(buf.deserialize_ref_no_meta(_size));
+ if (_size == MessageConst::NULL_PTR) {
+ return 0;
+ }
+ char* mem = NEW_C_HEAP_ARRAY(char, _size + 1, mtJBooster);
+ JB_RETURN(buf.deserialize_memcpy(mem, _size));
+ mem[_size] = '\0';
+ _base = mem;
+ return 0;
+}
+
+// -------------------------- FileWrapper for Serializer ---------------------------
+
+FileWrapper::FileWrapper(const char* file_path, SerializationMode smode):
+ WrapperBase(smode),
+ _file_path(file_path), // don't have to be copied
+ _file_size(MessageConst::NULL_PTR),
+ _tmp_file_path(nullptr),
+ _fd(-1),
+ _errno(0),
+ _handled_file_size(0),
+ _handled_once(false) {
+ if (smode == SerializationMode::SERIALIZE) {
+ init_for_serialize();
+ } else if (smode == SerializationMode::DESERIALIZE) {
+ init_for_deserialize();
+ } else {
+ fatal("smode should not be SerializationMode::BOTH");
+ }
+}
+
+FileWrapper::~FileWrapper() {
+ if (has_file_err()) {
+ if (_smode.can_serialize()) {
+ log_trace(jbooster, serialization)("The file \"%s\" is serialized as null: error=%s(\"%s\").",
+ _file_path, os::errno_name(_errno), os::strerror(_errno));
+ } else {
+ log_trace(jbooster, serialization)("The file \"%s\" is not deserialized (by this thread): error=%s(\"%s\").",
+ _file_path, os::errno_name(_errno), os::strerror(_errno));
+ }
+ } else if (!is_file_all_handled()) {
+ if (_smode.can_serialize()) {
+ log_warning(jbooster, serialization)("The file \"%s\" is not fully serialized.",
+ _file_path);
+ } else {
+ log_warning(jbooster, serialization)("The file \"%s\" is not fully deserialized and will be deleted.",
+ _tmp_file_path);
+ }
+ if (_fd >= 0) {
+ os::close(_fd);
+ _fd = -1;
+ if (_smode.can_deserialize()) {
+ FileUtils::remove(_tmp_file_path);
+ }
+ }
+ }
+ guarantee(_fd < 0, "sanity");
+ StringUtils::free_from_heap(_tmp_file_path);
+}
+
+void FileWrapper::init_for_serialize() {
+ errno = 0;
+ _fd = os::open(_file_path, O_BINARY | O_RDONLY, 0);
+ _errno = errno;
+ // We use lseek() instead of stat() to get the file size since we have opened the file.
+ if (_errno == 0 && _fd >= 0) {
+ _file_size = (uint32_t) os::lseek(_fd, 0, SEEK_END);
+ os::lseek(_fd, 0, SEEK_SET);
+ } else {
+ assert(_fd < 0 && _errno != 0, "sanity");
+ }
+}
+
+void FileWrapper::on_deser_tmp_file_opened() {
+ // The tmp file is successfully created and opened.
+ guarantee(_fd >= 0, "sanity");
+ // Recheck if the target file already exists. Skip deserialization if the target file exists.
+ if (FileUtils::is_file(_file_path)) {
+ os::close(_fd);
+ _fd = -1;
+ FileUtils::remove(_tmp_file_path);
+ log_info(jbooster, serialization)("The target file of \"%s\" already exists. Skipped deserialization.",
+ _file_path);
+ // Use is_tmp_file_already_exists() to check later.
+ _errno = EEXIST;
+ }
+}
+
+void FileWrapper::on_deser_tmp_file_exist() {
+ // The tmp file already exists. Failed to open the file.
+ // Use is_tmp_file_already_exists() to check later.
+ log_info(jbooster, serialization)("The tmp file of \"%s\" already exists. Skipped deserialization.",
+ _file_path);
+}
+
+void FileWrapper::on_deser_dir_not_exist() {
+ // The folder does not exist. Create it.
+ assert(_fd < 0, "sanity");
+ const int dir_buf_len = strlen(_file_path) + 1;
+ char* dir = NEW_C_HEAP_ARRAY(char, dir_buf_len, mtJBooster);
+ const char* r = strrchr(_file_path, FileUtils::separator()[0]);
+ if (r == nullptr) {
+ snprintf(dir, dir_buf_len, ".%s", FileUtils::separator());
+ } else {
+ int len = r - _file_path;
+ memcpy(dir, _file_path, len);
+ dir[len] = '\0';
+ }
+ FileUtils::mkdirs(dir);
+ FREE_C_HEAP_ARRAY(char, dir);
+}
+
+void FileWrapper::init_for_deserialize() {
+ // Write to file "<file-path>.tmp" and then rename to "<file-path>".
+ _tmp_file_path = JBoosterManager::calc_tmp_cache_path(_file_path);
+
+ const int max_retry_times = 2;
+ int retry_times = 0;
+ while (++retry_times <= max_retry_times) {
+ errno = 0;
+ _fd = JBoosterManager::create_and_open_tmp_cache_file(_tmp_file_path);
+ _errno = errno;
+ switch (_errno) {
+ case 0:
+ on_deser_tmp_file_opened();
+ return;
+ case EEXIST:
+ on_deser_tmp_file_exist();
+ return;
+ case ENOENT:
+ // No need to try to make dirs twice.
+ guarantee(retry_times == 1, "Failed to make dirs.");
+ on_deser_dir_not_exist();
+ break;
+ default:
+ fatal("failed to open file \"%s\": %s", _tmp_file_path, os::strerror(_errno));
+ break;
+ }
+ }
+}
+
+uint32_t FileWrapper::get_size_to_send_this_time() const {
+ assert(_fd >= 0, "sanity");
+ return MIN2(MAX_SIZE_PER_TRANS, _file_size - _handled_file_size);
+}
+
+uint32_t FileWrapper::get_arg_meta_size_if_not_null() {
+ return sizeof(_file_size) + sizeof(MAX_SIZE_PER_TRANS);
+}
+
+int FileWrapper::serialize(MessageBuffer& buf) const {
+ _smode.assert_can_serialize();
+ guarantee(!is_file_all_handled(), "the file is all parsed");
+
+ // file size
+ JB_RETURN(buf.serialize_no_meta(_file_size));
+ if (_file_size == MessageConst::NULL_PTR) {
+ assert(_fd < 0, "sanity");
+ _handled_file_size = _file_size;
+ _handled_once = true;
+ return 0;
+ }
+
+ // size to handle this time
+ uint32_t size_to_send = get_size_to_send_this_time();
+ JB_RETURN(buf.serialize_no_meta(size_to_send));
+
+ // content (use low-level APIs to save a memcpy)
+ buf.expand_if_needed(buf.cur_offset() + size_to_send, buf.cur_offset());
+ uint32_t left = size_to_send;
+ do {
+ ssize_t read_size_ret = os::read(_fd, buf.cur_buf_ptr(), left);
+ if (read_size_ret == -1) {
+ return errno;
+ }
+ uint32_t read_size = (uint32_t) read_size_ret;
+ buf.skip_cur_offset(read_size);
+ left -= read_size;
+ } while (left > 0);
+
+ // update status
+ _handled_file_size += size_to_send;
+ _handled_once = true;
+ if (is_file_all_handled()) {
+ os::close(_fd);
+ _fd = -1;
+ }
+ return 0;
+}
+
+/**
+ * The file to deserialize is null.
+ */
+void FileWrapper::on_deser_null() {
+ if (!_handled_once && _handled_file_size == 0) {
+ assert(_fd >= 0, "sanity");
+ _handled_file_size = _file_size;
+ _handled_once = true;
+ os::close(_fd);
+ _fd = -1;
+ FileUtils::remove(_tmp_file_path);
+ } else {
+ guarantee(_handled_once && _handled_file_size == _file_size && _fd == -1, "sanity");
+ }
+}
+
+/**
+ * The whole file is all deserialized.
+ */
+void FileWrapper::on_deser_end() {
+ assert(_fd >= 0, "sanity");
+ os::close(_fd);
+ _fd = -1;
+ bool rename_successful = false;
+ if (!FileUtils::is_file(_file_path)) {
+ // We rename the tmp file to the target file finally if the file is deserialized successfully.
+ // FileUtils::rename() may replace the target file by the tmp file if the target file exists,
+ // which is not what we want.
+ // Instead, we want the rename function to fail (atomically) when the target file already exists.
+ // But we don't have to use some atomic function like renameat2 with RENAME_NOREPLACE here.
+ // Because every target file is renamed from the tmp file so a target file cannot be created after
+ // its tmp file is locked.
+ chmod(_tmp_file_path, S_IREAD);
+ rename_successful = FileUtils::rename(_tmp_file_path, _file_path);
+ if (!rename_successful) {
+ log_warning(jbooster, serialization)("Failed to rename the tmp file of \"%s\" "
+ "and the tmp file will be removed. errno=%s(\"%s\").",
+ _file_path, os::errno_name(errno), os::strerror(errno));
+ }
+ } else {
+ // This branch should not be walked on.
+ log_error(jbooster, serialization)("File \"%s\" already exists and will not be overwritten.", _file_path);
+ }
+ if (!rename_successful) {
+ FileUtils::remove(_tmp_file_path);
+ }
+}
+
+int FileWrapper::deserialize(MessageBuffer& buf) {
+ _smode.assert_can_deserialize();
+ assert(_fd >= 0, "the tmp file");
+ guarantee(!is_file_all_handled(), "the deserialization is ended");
+
+ // file size
+ uint32_t file_size;
+ JB_RETURN(buf.deserialize_ref_no_meta(file_size));
+ if (_file_size != file_size) {
+ if (_file_size == MessageConst::NULL_PTR) { // first transmission
+ _file_size = file_size;
+ } else {
+ log_warning(jbooster, serialization)("File size mismatch while deserializing file \"%s\": "
+ "expect_file_size=%u, actual_file_size=%u",
+ _file_path, _file_size, file_size);
+ return JBErr::BAD_ARG_DATA;
+ }
+ }
+ if (_file_size == MessageConst::NULL_PTR) {
+ on_deser_null();
+ return 0;
+ }
+
+ // size to handle this time
+ uint32_t size_to_recv;
+ JB_RETURN(buf.deserialize_ref_no_meta(size_to_recv));
+
+ // content (use low-level APIs to save a memcpy)
+ uint32_t left = size_to_recv;
+ do {
+ uint32_t write_size = (uint32_t) os::write(_fd, buf.cur_buf_ptr(), left);
+ buf.skip_cur_offset(write_size);
+ left -= write_size;
+ } while (left > 0);
+
+ // update status
+ _handled_file_size += size_to_recv;
+ _handled_once = true;
+ if (is_file_all_handled()) {
+ on_deser_end();
+ }
+ return 0;
+}
+
+bool FileWrapper::wait_for_file_deserialization(int timeout_millisecond) {
+ jlong start = os::javaTimeMillis();
+ do {
+ if (FileUtils::is_file(_file_path)) {
+ return true;
+ }
+ os::naked_short_sleep(50);
+ } while ((os::javaTimeMillis() - start) < timeout_millisecond);
+ log_warning(jbooster, serialization)("File \"%s\" is still being deserialized by another thread or process "
+ "after %dms. Stop waiting.",
+ _file_path, timeout_millisecond);
+ return false;
+}
+
+int FileWrapper::send_file(CommunicationStream* stream) {
+ // No need to check has_file_err() here because it's not an exception.
+ // It's OK that the target file failed to be opened.
+ while (!is_file_all_handled()) {
+ bool ok = false;
+ JB_RETURN(stream->send_request(MessageType::FileSegment, this));
+ JB_RETURN(stream->recv_request(MessageType::FileSegment, &ok));
+ guarantee(ok, "sanity");
+ }
+ return 0;
+}
+
+int FileWrapper::recv_file(CommunicationStream* stream) {
+ // Check has_file_err() here and treat the err as an exception.
+ // The tmp file should be opened.
+ if (has_file_err()) return _errno;
+ while (!is_file_all_handled()) {
+ bool ok = true;
+ JB_RETURN(stream->recv_request(MessageType::FileSegment, this));
+ JB_RETURN(stream->send_request(MessageType::FileSegment, &ok));
+ }
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/net/serializationWrappers.hpp b/src/hotspot/share/jbooster/net/serializationWrappers.hpp
new file mode 100644
index 000000000..cc7f96c15
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serializationWrappers.hpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_HPP
+#define SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_HPP
+
+#include "jbooster/net/serialization.hpp"
+
+template <typename T> class Array;
+class ClassLoaderData;
+class CommunicationStream;
+template <typename T> class GrowableArray;
+class InstanceKlass;
+class Method;
+
+// Here are some wrappers defined for serialization and deserialization.
+
+class WrapperBase: public StackObj {
+public:
+ // signed-int version of unsigned-int MessageConst::NULL_PTR
+ enum: int {
+ INT_NULL_PTR = static_cast<int>(MessageConst::NULL_PTR)
+ };
+
+protected:
+ SerializationMode _smode;
+
+ // should free the memory for deserialization at ~WrapperBase()
+ bool _free_memory;
+
+ WrapperBase(SerializationMode smode): _smode(smode), _free_memory(false) {}
+
+public:
+ // The APIs that must be implemented by each wrapper:
+ // int serialize(MessageBuffer& buf) const;
+ // int deserialize(MessageBuffer& buf);
+ //
+ // And don't forget to register the wrapper to SerializationImpl:
+ // DECLARE_SERIALIZER_INTRUSIVE(TheWrapper);
+};
+
+/**
+ * ArrayWrapper supports serialization/deserialization of both pointers or values,
+ * that is, it can (de)serialize Arg* and Arg**.
+ *
+ * Layout of the serialized array:
+ * | array meta | ...... elements ...... |
+ * | element count | arg0 meta | arg0 payload | arg1 meta | arg1 payload | ... |
+ */
+template <typename Arg>
+class ArrayWrapper final: public WrapperBase {
+public:
+ using ConstArgPtr = const Arg*;
+
+ template <typename Type>
+ struct ArrayAllocater {
+ virtual void* alloc_val_container(int size) { ShouldNotReachHere(); return nullptr; }
+ virtual void* alloc_ptr_container(int size) { ShouldNotReachHere(); return nullptr; }
+ };
+
+private:
+ const void* _base; // start of the list
+ int _size; // count of elements
+ bool _is_ptr; // whether the elements are pointers or values
+
+ // Used to allocate third-party arrays such as GrowableArray and Array.
+ // This API is not implemented yet.
+ ArrayAllocater<Arg>* const _allocater;
+
+ template <typename T>
+ void check_arr_type();
+
+ Arg* cast_to_non_const_val_base(); // for deserialization only
+ Arg** cast_to_non_const_ptr_base(); // for deserialization only
+
+ void* alloc_val_container();
+ void* alloc_ptr_container();
+
+private:
+ template <typename ArrLike>
+ static const void* arr_base(ArrLike* array);
+
+ template <typename ArrLike>
+ static int arr_size(ArrLike* array);
+
+public:
+ ArrayWrapper(bool is_ptr, ArrayAllocater<Arg>* allocater = nullptr); // for deserialization only
+
+ ArrayWrapper(const Arg* val_base, int size); // for serialization only
+ ArrayWrapper(const ConstArgPtr* ptr_base, int size); // for serialization only
+
+ ArrayWrapper(const Array<Arg>* array); // for serialization only
+ ArrayWrapper(const Array<Arg*>* array); // for serialization only
+ ArrayWrapper(const GrowableArray<Arg>* array); // for serialization only
+ ArrayWrapper(const GrowableArray<Arg*>* array); // for serialization only
+
+ ~ArrayWrapper();
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ bool is_null() const { return _base == nullptr; }
+ int size() const { return _size; }
+
+ // Attention: it always return "Arg*" whether it is an array of "Arg*" or "Arg".
+ // Use get_array() if you care about the performance.
+ const Arg* get(int index) const;
+
+ // This function is for serialization, And it's irreversible.
+ int set_sub_arr(int start, int size);
+
+ // after deserialization
+
+ // The array will still be destroyed (if in deserialization mode) in ~ArrayWrapper.
+ template <typename T>
+ T* get_array();
+ // Now the array will not be destroyed in ~ArrayWrapper.
+ template <typename T>
+ T* export_array();
+};
+
+template <typename Arg>
+struct SerializationImpl<ArrayWrapper<Arg>> {
+ static int serialize(MessageBuffer& buf, const ArrayWrapper<Arg>& arg);
+ static int deserialize_ref(MessageBuffer& buf, ArrayWrapper<Arg>& arg);
+ CANNOT_DESERIALIZE_POINTER(ArrayWrapper<Arg>);
+};
+
+/**
+ * Serializing contiguous memory (using memcpy())
+ */
+class MemoryWrapper final: public WrapperBase {
+public:
+ struct MemoryAllocater {
+ virtual void* alloc(int size) { ShouldNotReachHere(); return nullptr; }
+ };
+
+private:
+ const void* _base; // start of the memory
+ uint32_t _size; // bytes of the memory
+
+ MemoryAllocater* const _allocater;
+
+public:
+ MemoryWrapper(const void* base, uint32_t size); // for serialization only
+ MemoryWrapper(MemoryAllocater* allocater = nullptr); // for deserialization only
+
+ ~MemoryWrapper();
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ // after deserialization
+ bool is_null() const { return _base == nullptr; }
+ uint32_t size() const { return _size; }
+
+ // The memory will still be destroyed (if in deserialization mode) in ~MemoryWrapper.
+ void* get_memory() const { return const_cast<void*>(_base); }
+ // Now the memory will not be destroyed in ~MemoryWrappe.
+ void* export_memory() {
+ _free_memory = false;
+ return get_memory();
+ }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(MemoryWrapper);
+
+/**
+ * Compatible with the serializers of `char*` and `char[]`, but supports RAII.
+ */
+class StringWrapper final: public WrapperBase {
+private:
+ const char* _base;
+ uint32_t _size; // not including '\0'
+
+public:
+ StringWrapper(const char* str); // for serialization only
+ StringWrapper(const char* str, uint32_t size); // for serialization only
+ StringWrapper(); // for deserialization only
+ ~StringWrapper();
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ uint32_t size() { return _size; }
+
+ // The string will still be destroyed (if in deserialization mode) in ~StringWrapper.
+ const char* get_string() const { return _base; }
+ // Now the string will not be destroyed in ~StringWrapper.
+ const char* export_string() {
+ _free_memory = false;
+ return _base;
+ }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(StringWrapper);
+
+class FileWrapper final: public WrapperBase {
+public:
+ static constexpr uint32_t MAX_SIZE_PER_TRANS = 40 * 1024 * 1024; // 40 MB
+
+private:
+ const char* _file_path;
+ uint32_t _file_size;
+ const char* _tmp_file_path;
+
+ mutable int _fd;
+ mutable int _errno;
+ mutable uint32_t _handled_file_size;
+ mutable bool _handled_once; // to cover _file_size = 0
+
+private:
+ void init_for_serialize();
+ void init_for_deserialize();
+
+ void on_deser_tmp_file_opened();
+ void on_deser_tmp_file_exist();
+ void on_deser_dir_not_exist();
+
+ void on_deser_null();
+ void on_deser_end();
+
+ uint32_t get_size_to_send_this_time() const;
+
+public:
+ FileWrapper(const char* file_path, SerializationMode smode);
+ ~FileWrapper();
+
+ static uint32_t get_arg_meta_size_if_not_null();
+
+ int serialize(MessageBuffer& buf) const;
+ int deserialize(MessageBuffer& buf);
+
+ bool is_null() const { return _file_size == MessageConst::NULL_PTR; }
+ bool is_file_all_handled() const {
+ assert(_file_size >= _handled_file_size, "sanity");
+ return _handled_once && _file_size == _handled_file_size;
+ }
+
+ const char* file_path() const { return _file_path; }
+ uint32_t file_size() const { return _file_size; }
+ int fd() const { return _fd; }
+ int has_file_err() const { return _errno != 0; }
+ int file_err() const { return _errno; }
+ bool is_tmp_file_already_exists() { _smode.assert_can_deserialize(); return _errno == EEXIST; }
+
+ // Wait for other threads or processes to generate files.
+ bool wait_for_file_deserialization(int timeout_millisecond = 2000);
+
+ int send_file(CommunicationStream* stream);
+ int recv_file(CommunicationStream* stream);
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(FileWrapper);
+
+#endif // SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_HPP
diff --git a/src/hotspot/share/jbooster/net/serializationWrappers.inline.hpp b/src/hotspot/share/jbooster/net/serializationWrappers.inline.hpp
new file mode 100644
index 000000000..b1ff6b35c
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serializationWrappers.inline.hpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_INLINE_HPP
+#define SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_INLINE_HPP
+
+#include <type_traits>
+
+#include "jbooster/net/serializationWrappers.hpp"
+#include "memory/metadataFactory.hpp"
+#include "oops/array.hpp"
+#include "runtime/thread.hpp"
+#include "utilities/growableArray.hpp"
+
+// -------------------------- ArrayWrapper for Serializer --------------------------
+
+template <typename Arg>
+template <typename ArrLike>
+inline const void* ArrayWrapper<Arg>::arr_base(ArrLike* array) {
+ if (array == nullptr) return nullptr;
+ if (array->is_empty()) {
+ // adr_at() requires 0 <= idx < len. So just use a non-null placeholder here.
+ // We use 0x1 because if the program accesses it, it crashes.
+ return (const void*) ((uintptr_t) 0x1);
+ }
+ return static_cast<const void*>(array->adr_at(0));
+}
+
+template <typename Arg>
+template <typename ArrLike>
+inline int ArrayWrapper<Arg>::arr_size(ArrLike* array) {
+ if (array == nullptr) return INT_NULL_PTR;
+ return array->length();
+}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(bool is_ptr, ArrayAllocater<Arg>* allocater):
+ WrapperBase(SerializationMode::DESERIALIZE),
+ _base(nullptr),
+ _size(0),
+ _is_ptr(is_ptr),
+ _allocater(allocater) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const Arg* val_base, int size):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(static_cast<const void*>(val_base)),
+ _size(val_base == nullptr ? INT_NULL_PTR : size),
+ _is_ptr(false),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const ConstArgPtr* ptr_base, int size):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(static_cast<const void*>(ptr_base)),
+ _size(ptr_base == nullptr ? INT_NULL_PTR : size),
+ _is_ptr(true),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const Array<Arg>* array):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(arr_base(array)),
+ _size(arr_size(array)),
+ _is_ptr(false),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const Array<Arg*>* array):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(arr_base(array)),
+ _size(arr_size(array)),
+ _is_ptr(true),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const GrowableArray<Arg>* array):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(arr_base(array)),
+ _size(arr_size(array)),
+ _is_ptr(false),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::ArrayWrapper(const GrowableArray<Arg*>* array):
+ WrapperBase(SerializationMode::SERIALIZE),
+ _base(arr_base(array)),
+ _size(arr_size(array)),
+ _is_ptr(true),
+ _allocater(nullptr) {}
+
+template <typename Arg>
+inline ArrayWrapper<Arg>::~ArrayWrapper() {
+ if (!_free_memory || _base == nullptr) return;
+ if (!_is_ptr) {
+ Arg* base = cast_to_non_const_val_base();
+ for (int i = 0; i < _size; ++i) base[i].~Arg();
+ FREE_C_HEAP_ARRAY(Arg, _base);
+ }
+ else {
+ // We do not delete the value of pointers here.
+ FREE_C_HEAP_ARRAY(ConstArgPtr, _base);
+ }
+}
+
+template <typename Arg>
+inline int ArrayWrapper<Arg>::set_sub_arr(int start, int max_size) {
+ _smode.assert_can_serialize();
+ _size = MIN2(max_size, MAX2(0, _size - start));
+ // _base may be a illegal address when _size is small than max_size.
+ // But it's ok because _size is 0 in such condition.
+ if (!_is_ptr) {
+ const Arg* base = static_cast<const Arg*>(_base);
+ _base = base + start;
+ } else {
+ const ConstArgPtr* base = static_cast<const ConstArgPtr*>(_base);
+ _base = base + start;
+ }
+ return _size;
+}
+
+template <typename Arg>
+template <typename T>
+inline void ArrayWrapper<Arg>::check_arr_type() {
+ if (!_is_ptr) {
+ assert((std::is_same<T, Arg>::value), "wrong type");
+ } else {
+ assert((std::is_same<T, Arg*>::value), "wrong type");
+ }
+}
+
+template <typename Arg>
+inline Arg* ArrayWrapper<Arg>::cast_to_non_const_val_base() {
+ _smode.assert_can_deserialize();
+ assert(!_is_ptr, "elements should be values");
+ void* base_non_const = const_cast<void*>(_base);
+ return static_cast<Arg*>(base_non_const);
+}
+template <typename Arg>
+inline Arg** ArrayWrapper<Arg>::cast_to_non_const_ptr_base() {
+ _smode.assert_can_deserialize();
+ assert(_is_ptr, "elements should be pointers");
+ void* base_non_const = const_cast<void*>(_base);
+ return static_cast<Arg**>(base_non_const);
+}
+
+template <typename Arg>
+inline void* ArrayWrapper<Arg>::alloc_val_container() {
+ assert(_base == nullptr, "sanity");
+ if (_allocater != nullptr) {
+ return _allocater->alloc_val_container(_size);
+ }
+ _free_memory = true;
+ void* res = (void*)NEW_C_HEAP_ARRAY(Arg, _size, mtJBooster);
+ // We do not initialize the values here.
+ return res;
+}
+
+template <typename Arg>
+inline void* ArrayWrapper<Arg>::alloc_ptr_container() {
+ assert(_base == nullptr, "sanity");
+ if (_allocater != nullptr) {
+ return _allocater->alloc_ptr_container(_size);
+ }
+ _free_memory = true;
+ void* res = (void*)NEW_C_HEAP_ARRAY(Arg*, _size, mtJBooster);
+ // We do not initialize the pointers here.
+ return res;
+}
+
+template <typename Arg>
+inline int ArrayWrapper<Arg>::serialize(MessageBuffer& buf) const {
+ _smode.assert_can_serialize();
+ if (_base == nullptr) {
+ return buf.serialize_no_meta(INT_NULL_PTR);
+ }
+ JB_RETURN(buf.serialize_no_meta(_size));
+ if (!_is_ptr) {
+ const Arg* base = static_cast<const Arg*>(_base);
+ for (int i = 0; i < _size; ++i) {
+ JB_RETURN(buf.serialize_with_meta(base + i));
+ }
+ } else {
+ const ConstArgPtr* base = static_cast<const ConstArgPtr*>(_base);
+ for (int i = 0; i < _size; ++i) {
+ JB_RETURN(buf.serialize_with_meta(base[i]));
+ }
+ }
+ return 0;
+}
+
+template <typename C, typename Enable = void>
+class ArrayWrapperUtils: public AllStatic {
+public:
+ static C* placement_new_if_possible(void* base) {
+ return ::new (base) C();
+ }
+};
+
+template <typename C>
+class ArrayWrapperUtils<C, typename std::enable_if<std::is_abstract<C>::value || !std::is_default_constructible<C>::value>::type>: public AllStatic {
+public:
+ static C* placement_new_if_possible(void* base) {
+ if (std::is_abstract<C>::value) {
+ fatal("Unable to preallocate memory for abstract classes!");
+ }
+ if (!std::is_default_constructible<C>::value) {
+ fatal("Unable to preallocate memory for classes with no default constructors!");
+ }
+ return nullptr;
+ }
+};
+
+template <typename Arg>
+inline int ArrayWrapper<Arg>::deserialize(MessageBuffer& buf) {
+ _smode.assert_can_deserialize();
+ assert(_base == nullptr, "can only deserialize once");
+ JB_RETURN(buf.deserialize_ref_no_meta(_size));
+ if (_size == INT_NULL_PTR) {
+ _size = 0;
+ return 0;
+ }
+
+ if (!_is_ptr) {
+ _base = alloc_val_container();
+ Arg* base = cast_to_non_const_val_base();
+ for (int i = 0; i < _size; ++i) {
+ // initialize each element
+ ArrayWrapperUtils<Arg>::placement_new_if_possible((void*) (base + i));
+ JB_RETURN(buf.deserialize_with_meta(base + i));
+ }
+ } else {
+ _base = alloc_ptr_container();
+ Arg** base = cast_to_non_const_ptr_base();
+ // set all Arg* to nullptr
+ memset(base, 0, _size * sizeof(Arg*));
+ for (int i = 0; i < _size; ++i) {
+ JB_RETURN(buf.deserialize_with_meta(base[i]));
+ }
+ }
+ return 0;
+}
+
+template <typename Arg>
+inline const Arg* ArrayWrapper<Arg>::get(int index) const {
+ assert(_base != nullptr && index >= 0 && index < size(), "out-of-bounds");
+ if (!_is_ptr) {
+ const Arg* base = static_cast<const Arg*>(_base);
+ return base + index;
+ } else {
+ const ConstArgPtr* base = static_cast<const ConstArgPtr*>(_base);
+ return base[index];
+ }
+}
+
+template <typename Arg>
+template <typename T>
+inline T* ArrayWrapper<Arg>::get_array() {
+ check_arr_type<T>();
+ return (T*)const_cast<void*>(_base);
+}
+
+template <typename Arg>
+template <typename T>
+inline T* ArrayWrapper<Arg>::export_array() {
+ _free_memory = false;
+ return get_array<T>();
+}
+
+// --------------------- Serializer register for ArrayWrapper ----------------------
+
+template <typename Arg>
+inline int SerializationImpl<ArrayWrapper<Arg>>::serialize(MessageBuffer& buf, const ArrayWrapper<Arg>& arg) {
+ return arg.serialize(buf);
+}
+
+template <typename Arg>
+inline int SerializationImpl<ArrayWrapper<Arg>>::deserialize_ref(MessageBuffer& buf, ArrayWrapper<Arg>& arg) {
+ return arg.deserialize(buf);
+}
+
+#endif // SHARE_JBOOSTER_NET_SERIALIZATIONWRAPPERS_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/net/serverListeningThread.cpp b/src/hotspot/share/jbooster/net/serverListeningThread.cpp
new file mode 100644
index 000000000..0f7b5dc8d
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serverListeningThread.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/javaClasses.inline.hpp"
+#include "classfile/vmClasses.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/net/serverListeningThread.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/server/serverMessageHandler.hpp"
+#include "logging/log.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/thread.inline.hpp"
+
+ServerListeningThread* ServerListeningThread::_singleton = nullptr;
+
+/**
+ * This function is called in the main thread.
+ */
+ServerListeningThread* ServerListeningThread::start_thread(const char* address,
+ uint16_t port,
+ uint32_t timeout_ms,
+ TRAPS) {
+ JavaThread* new_thread = new JavaThread(&server_listener_thread_entry);
+ guarantee(new_thread != nullptr && new_thread->osthread() != nullptr, "sanity");
+ guarantee(_singleton == nullptr, "sanity");
+ _singleton = new ServerListeningThread(new_thread, address, port, timeout_ms);
+
+ Handle name = java_lang_String::create_from_str("JBooster Listening Thread", CHECK_NULL);
+ Handle thread_group(THREAD, Universe::main_thread_group());
+ Handle thread_oop = JavaCalls::construct_new_instance(
+ vmClasses::Thread_klass(),
+ vmSymbols::threadgroup_string_void_signature(),
+ thread_group,
+ name,
+ CHECK_NULL);
+
+ Klass* group = vmClasses::ThreadGroup_klass();
+ JavaValue result(T_VOID);
+ JavaCalls::call_special(&result,
+ thread_group,
+ group,
+ vmSymbols::add_method_name(),
+ vmSymbols::thread_void_signature(),
+ thread_oop,
+ THREAD);
+
+ JavaThread::start_internal_daemon(THREAD, new_thread, thread_oop, NearMaxPriority);
+ return _singleton;
+}
+
+void ServerListeningThread::server_listener_thread_entry(JavaThread* thread, TRAPS) {
+ JB_TRY {
+ JB_THROW(_singleton->run_listener(thread));
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ log_error(jbooster, rpc)("ServerListeningThread failed to listen on the port: error=%s(\"%s\").",
+ JBErr::err_name(JB_ERR), JBErr::err_message(JB_ERR));
+ vm_exit(1);
+ } JB_CATCH_END;
+ vm_exit(0);
+}
+
+ServerListeningThread::ServerListeningThread(JavaThread* the_java_thread,
+ const char* address,
+ uint16_t port,
+ uint32_t timeout_ms):
+ _the_java_thread(the_java_thread),
+ _address(address),
+ _port(port),
+ _timeout_ms(timeout_ms),
+ _stream_id_for_alloc(0),
+ _exit_flag(false) {
+}
+
+ServerListeningThread::~ServerListeningThread() {
+ log_debug(jbooster, rpc)("The JBooster server listener thread is destroyed.");
+}
+
+uint32_t ServerListeningThread::new_stream_id() {
+ return Atomic::add(&_stream_id_for_alloc, 1U);
+}
+
+/**
+ * Add the new connection to the connection thread pool.
+ */
+void ServerListeningThread::handle_new_connection(int conn_fd, TRAPS) {
+ ThreadInVMfromNative tiv(THREAD);
+ ResourceMark rm(THREAD);
+ HandleMark hm(THREAD);
+
+ JavaValue result(T_BOOLEAN);
+ JavaCallArguments args;
+ args.push_int(conn_fd);
+ JavaCalls::call_static(&result, ServerDataManager::get().main_klass(),
+ vmSymbols::receiveConnection_name(),
+ vmSymbols::int_bool_signature(),
+ &args, CATCH);
+ if (!result.get_jboolean()) {
+ log_warning(jbooster, rpc)("Failed to handle the new connection as the thread pool is full.");
+ os::close(conn_fd);
+ }
+}
+
+/**
+ * Handle the connection obtained from the connection thread pool.
+ *
+ * This function is called in another java thread (not in ServerListeningThread thread).
+ * So do not use `this` here.
+ */
+void ServerListeningThread::handle_connection(int conn_fd) {
+ JavaThread* THREAD = JavaThread::current();
+ ThreadToNativeFromVM ttn(THREAD);
+
+ ServerStream* server_stream = new ServerStream(conn_fd, THREAD);
+ ThreadServerStreamMark tssm(server_stream, true, THREAD);
+ server_stream->handle_meta_request(new_stream_id());
+ if (server_stream->is_stream_closed()) return;
+
+ ServerMessageHandler msg_handler(server_stream);
+ JB_TRY {
+ JB_THROW(msg_handler.handle_tasks_from_client(THREAD));
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ log_warning(jbooster, rpc)("Unhandled exception at ServerListeningThread::handle_connection(): "
+ "error=%s(\"%s\"), session_id=%u, stream_id=%u.",
+ JBErr::err_name(JB_ERR), JBErr::err_message(JB_ERR),
+ server_stream->session_id(), server_stream->stream_id());
+ } JB_CATCH_END;
+}
diff --git a/src/hotspot/share/jbooster/net/serverListeningThread.hpp b/src/hotspot/share/jbooster/net/serverListeningThread.hpp
new file mode 100644
index 000000000..49f5efb3f
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serverListeningThread.hpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_SERVERLISTENINGTHREAD_HPP
+#define SHARE_JBOOSTER_NET_SERVERLISTENINGTHREAD_HPP
+
+#include "runtime/thread.hpp"
+
+class ServerListeningThread : public CHeapObj<mtJBooster> {
+private:
+ static ServerListeningThread* _singleton;
+
+ JavaThread* const _the_java_thread;
+ const char* const _address;
+ const uint16_t _port;
+ const uint32_t _timeout_ms;
+
+ volatile uint32_t _stream_id_for_alloc;
+
+ volatile bool _exit_flag;
+
+private:
+ static void server_listener_thread_entry(JavaThread* thread, TRAPS);
+
+ ServerListeningThread(JavaThread* the_java_thread, const char* address, uint16_t port, uint32_t timeout_ms);
+
+ uint32_t new_stream_id();
+
+ void handle_new_connection(int conn_fd, TRAPS);
+
+ int run_listener(TRAPS);
+
+public:
+ static ServerListeningThread* start_thread(const char* address,
+ uint16_t port,
+ uint32_t timeout_ms,
+ TRAPS);
+
+ ~ServerListeningThread();
+
+ bool get_exit_flag() { return _exit_flag; }
+ void set_exit_flag() { _exit_flag = true; }
+
+ void handle_connection(int conn_fd);
+};
+
+#endif // SHARE_JBOOSTER_NET_SERVERLISTENINGTHREAD_HPP
diff --git a/src/hotspot/share/jbooster/net/serverStream.cpp b/src/hotspot/share/jbooster/net/serverStream.cpp
new file mode 100644
index 000000000..a3e7fa5c6
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serverStream.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "compiler/compileTask.hpp"
+#include "jbooster/net/rpcCompatibility.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/timerTrace.hpp"
+
+ServerStream::ServerStream(int conn_fd):
+ CommunicationStream(Thread::current_or_null()),
+ _session_data(nullptr) {
+ init_stream(conn_fd);
+}
+
+ServerStream::ServerStream(int conn_fd, Thread* thread):
+ CommunicationStream(thread),
+ _session_data(nullptr) {
+ init_stream(conn_fd);
+}
+
+ServerStream::~ServerStream() {
+ set_session_data(nullptr);
+}
+
+uint32_t ServerStream::session_id() {
+ return session_data()->session_id();
+}
+
+void ServerStream::set_session_data(JClientSessionData* sd) {
+ JClientSessionData* old_sd = _session_data;
+ if (sd == old_sd) return;
+ // Do not call sd->ref_cnt().inc() here as it has been inc when obtained.
+ if (old_sd != nullptr) {
+ old_sd->ref_cnt().dec_and_update_time();
+ }
+ _session_data = sd;
+}
+
+void ServerStream::handle_meta_request(uint32_t stream_id) {
+ set_stream_id(stream_id);
+ MessageType type;
+ JB_TRY {
+ JB_THROW(recv_request(type));
+ switch (type) {
+ case MessageType::ClientSessionMeta: {
+ JB_THROW(sync_session_meta__server());
+ close_stream();
+ break;
+ }
+ case MessageType::ClientStreamMeta: {
+ JB_THROW(sync_stream_meta__server());
+ break;
+ }
+ default: {
+ JB_THROW(JBErr::BAD_MSG_TYPE);
+ break;
+ }
+ // Recheck JB_ERR because the JB_THROWs above are intercepted
+ // by the switch statement.
+ JB_THROW(JB_ERR);
+ }
+ } JB_TRY_END
+ JB_CATCH(JBErr::INCOMPATIBLE_RPC) {
+ const char* unsupport_reason = "RPC version";
+ log_warning(jbooster)("The server does not support this client because the \"%s\" of the two are different! stream_id=%u.",
+ unsupport_reason, stream_id);
+ send_request(MessageType::UnsupportedClient, &unsupport_reason);
+ close_stream();
+ } JB_CATCH_REST() {
+ log_warning(jbooster, rpc)("Unhandled exception at ServerStream::handle_meta_request(): "
+ "error=%s(\"%s\"), first_msg_type=%s, stream_id=%u.",
+ JBErr::err_name(JB_ERR), JBErr::err_message(JB_ERR),
+ msg_type_name(type), stream_id);
+ close_stream();
+ } JB_CATCH_END;
+}
+
+int ServerStream::sync_session_meta__server() {
+ ServerDataManager& sdm = ServerDataManager::get();
+
+ RpcCompatibility comp;
+ uint64_t client_random_id;
+ JClientArguments program_args(false); // on-stack allocation to prevent memory leakage
+ JB_RETURN(parse_request(&comp, &client_random_id, &program_args));
+
+ const char* unsupport_reason = nullptr;
+ if (!program_args.check_compatibility_with_server(&unsupport_reason)) {
+ log_warning(jbooster)("The server does not support this client because the \"%s\" of the two are different! stream_id=%u.",
+ unsupport_reason, stream_id());
+ JB_RETURN(send_request(MessageType::UnsupportedClient, &unsupport_reason));
+ JB_RETURN(JBErr::ABORT_CUR_PHRASE);
+ }
+
+ JClientSessionData* sd = sdm.create_session(client_random_id, &program_args, Thread::current());
+ JClientProgramData* pd = sd->program_data();
+ set_session_data(sd);
+
+ uint64_t server_random_id = sdm.random_id();
+ uint32_t session_id = sd->session_id();
+ uint32_t program_id = pd->program_id();
+ bool has_remote_clr = pd->clr_cache_state().is_cached();
+ bool has_remote_cds = pd->cds_cache_state().is_cached();
+ bool has_remote_aot = pd->aot_cache_state().is_cached();
+ JB_RETURN(send_response(stream_id_addr(), &server_random_id, &session_id, &program_id,
+ &has_remote_clr, &has_remote_cds, &has_remote_aot));
+ log_info(jbooster, rpc)("New client: session_id=%u, program_id=%u, "
+ "random_id=" UINT64_FORMAT_X ", program_name=%s, "
+ "has_clr=%s, has_cds=%s, has_aot=%s, "
+ "stream_id=%u.",
+ session_id, program_id,
+ client_random_id, program_args.program_name(),
+ BOOL_TO_STR(has_remote_clr),
+ BOOL_TO_STR(has_remote_cds),
+ BOOL_TO_STR(has_remote_aot),
+ stream_id());
+
+ return handle_sync_requests(pd);
+}
+
+int ServerStream::handle_sync_requests(JClientProgramData* pd) {
+ bool not_end = true;
+ do {
+ MessageType type;
+ JB_RETURN(recv_request(type));
+ switch (type) {
+ case MessageType::GetClassLoaderResourceCache: {
+ TraceTime tt("Send clr", TRACETIME_LOG(Info, jbooster));
+ FileWrapper file(pd->clr_cache_state().file_path(), SerializationMode::SERIALIZE);
+ JB_RETURN(file.send_file(this));
+ break;
+ }
+ case MessageType::GetAggressiveCDSCache: {
+ TraceTime tt("Send cds", TRACETIME_LOG(Info, jbooster));
+ FileWrapper file(pd->cds_cache_state().file_path(), SerializationMode::SERIALIZE);
+ JB_RETURN(file.send_file(this));
+ break;
+ }
+ case MessageType::GetLazyAOTCache: {
+ TraceTime tt("Send aot", TRACETIME_LOG(Info, jbooster));
+ FileWrapper file(pd->aot_cache_state().file_path(), SerializationMode::SERIALIZE);
+ JB_RETURN(file.send_file(this));
+ break;
+ }
+ default: not_end = false; break;
+ }
+ } while (not_end);
+ return 0;
+}
+
+int ServerStream::sync_stream_meta__server() {
+ ServerDataManager& sdm = ServerDataManager::get();
+
+ uint32_t session_id;
+ uint64_t client_random_id, server_random_id;
+ JB_RETURN(parse_request(&session_id, &client_random_id, &server_random_id));
+ set_session_data(sdm.get_session(session_id, Thread::current()));
+
+ if (_session_data == nullptr
+ || client_random_id != _session_data->random_id()
+ || server_random_id != sdm.random_id()) {
+ // The server has been restarted, and this client was connected to the previous server instance.
+ log_warning(jbooster, rpc)("Unrecognized session! Sync again. "
+ "obtained_session_id=%u, "
+ "obtained_client_random_id=" UINT64_FORMAT_X ", "
+ "obtained_server_random_id=" UINT64_FORMAT_X ", "
+ "stream_id=%u.",
+ session_id, client_random_id, server_random_id,
+ stream_id());
+ JB_RETURN(resync_session_and_stream_meta__server());
+ JB_RETURN(sync_stream_meta__server());
+ } else {
+ JB_RETURN(send_response(stream_id_addr()));
+ log_trace(jbooster, rpc)("New ServerStream: session_id=%u, stream_id=%u.", session_id, stream_id());
+ }
+ return 0;
+}
+
+int ServerStream::resync_session_and_stream_meta__server() {
+ JB_RETURN(send_request(MessageType::ClientSessionMetaAgain));
+ MessageType type;
+ JB_RETURN(recv_request(type));
+ if (type != MessageType::ClientSessionMeta) {
+ JB_RETURN(JBErr::BAD_MSG_TYPE);
+ }
+ JB_RETURN(sync_session_meta__server());
+ return 0;
+}
+
+// ============================ ThreadServerStreamMark =============================
+
+ThreadServerStreamMark::ThreadServerStreamMark(ServerStream* server_stream, bool should_delete):
+ _server_stream(server_stream),
+ _delete(should_delete),
+ _thread(Thread::current_or_null()) {
+ guarantee(_thread == _server_stream->current_thread(), "sanity");
+}
+
+ThreadServerStreamMark::ThreadServerStreamMark(ServerStream* server_stream, bool should_delete, TRAPS):
+ _server_stream(server_stream),
+ _delete(should_delete),
+ _thread(THREAD) {
+ guarantee(_thread == _server_stream->current_thread(), "sanity");
+}
+
+ThreadServerStreamMark::~ThreadServerStreamMark() {
+ if (_delete && (_thread == _server_stream->current_thread())) {
+ delete _server_stream;
+ log_debug(jbooster, rpc)("A ServerStream is automatically deleted.");
+ }
+}
diff --git a/src/hotspot/share/jbooster/net/serverStream.hpp b/src/hotspot/share/jbooster/net/serverStream.hpp
new file mode 100644
index 000000000..56d05dabb
--- /dev/null
+++ b/src/hotspot/share/jbooster/net/serverStream.hpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_NET_SERVERSTREAM_HPP
+#define SHARE_JBOOSTER_NET_SERVERSTREAM_HPP
+
+#include "jbooster/net/communicationStream.inline.hpp"
+#include "memory/allocation.hpp"
+
+class JClientSessionData;
+class JClientProgramData;
+
+class ServerStream: public CommunicationStream {
+ JClientSessionData* _session_data;
+
+private:
+ void set_session_data(JClientSessionData* sd);
+
+ int sync_session_meta__server();
+ int sync_stream_meta__server();
+ int resync_session_and_stream_meta__server();
+
+ int handle_sync_requests(JClientProgramData* pd);
+
+public:
+ ServerStream(int conn_fd);
+ ServerStream(int conn_fd, Thread* thread);
+ ~ServerStream();
+
+ void handle_meta_request(uint32_t stream_id);
+
+ JClientSessionData* session_data() { return _session_data; }
+ uint32_t session_id();
+}; // ServerStream
+
+/**
+ * Use RAII to auto deleting closed server stream.
+ * The server stream will be deleted only if it's handled by current thread.
+ */
+class ThreadServerStreamMark: public StackObj {
+ ServerStream* const _server_stream;
+ const bool _delete;
+ Thread* const _thread;
+
+public:
+ ThreadServerStreamMark(ServerStream* server_stream, bool should_delete);
+ ThreadServerStreamMark(ServerStream* server_stream, bool should_delete, TRAPS);
+ ~ThreadServerStreamMark();
+};
+
+#endif // SHARE_JBOOSTER_NET_SERVERSTREAM_HPP
diff --git a/src/hotspot/share/jbooster/server/serverControlThread.cpp b/src/hotspot/share/jbooster/server/serverControlThread.cpp
new file mode 100644
index 000000000..9b4ff6857
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverControlThread.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/javaClasses.inline.hpp"
+#include "classfile/vmClasses.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverControlThread.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/server/serverMessageHandler.hpp"
+#include "jbooster/utilities/concurrentHashMap.inline.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "logging/log.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.hpp"
+#include "runtime/thread.inline.hpp"
+
+uint32_t ServerControlThread::_unused_shared_data_cleanup_timeout = -1; // set in java code
+uint32_t ServerControlThread::_heartbeat_read_write_timeout = 4 * 1000;
+uint32_t ServerControlThread::_session_no_ref_timeout = 16 * 1000;
+
+ServerControlThread* ServerControlThread::_singleton = nullptr;
+
+ServerControlThread::ClientSessionState::ClientSessionState(ServerStream* server_stream):
+ _server_stream(server_stream),
+ _heartbeat_failed_times(0) {}
+
+ServerControlThread* ServerControlThread::start_thread(TRAPS) {
+ JavaThread* new_thread = new JavaThread(&server_control_thread_entry);
+ guarantee(new_thread != nullptr && new_thread->osthread() != nullptr, "sanity");
+ guarantee(_singleton == nullptr, "sanity");
+ _singleton = new ServerControlThread(new_thread);
+
+ Handle string = java_lang_String::create_from_str("JBooster Serevr Control", CHECK_NULL);
+ Handle thread_group(THREAD, Universe::main_thread_group());
+ Handle thread_oop = JavaCalls::construct_new_instance(
+ vmClasses::Thread_klass(),
+ vmSymbols::threadgroup_string_void_signature(),
+ thread_group,
+ string,
+ CHECK_NULL);
+
+ Klass* group = vmClasses::ThreadGroup_klass();
+ JavaValue result(T_VOID);
+ JavaCalls::call_special(&result,
+ thread_group,
+ group,
+ vmSymbols::add_method_name(),
+ vmSymbols::thread_void_signature(),
+ thread_oop,
+ THREAD);
+
+ JavaThread::start_internal_daemon(THREAD, new_thread, thread_oop, NormPriority);
+ return _singleton;
+}
+
+void ServerControlThread::server_control_thread_entry(JavaThread* thread, TRAPS) {
+ _singleton->control_loop(thread);
+}
+
+ServerControlThread::ServerControlThread(JavaThread* java_thread):
+ _the_java_thread(java_thread),
+ _client_daemons(),
+ _sleep_lock(Mutex::leaf, "JBooster Server Control", true, Mutex::_safepoint_check_never) {
+}
+
+/**
+ * This method is invoked in other thread.
+ * So pay attention to the difference between THREAD and this.
+ */
+void ServerControlThread::add_client_daemon_connection(ServerStream* server_stream, TRAPS) {
+ uint32_t session_id = server_stream->session_id();
+ server_stream->set_current_thread(_the_java_thread);
+ server_stream->set_read_write_timeout(_heartbeat_read_write_timeout);
+ ClientSessionState state(server_stream);
+ ClientSessionState* in_map = _client_daemons.put_if_absent(session_id, state, THREAD);
+ if (in_map->server_stream() != server_stream) {
+ server_stream->set_current_thread(THREAD);
+ log_error(jbooster)("Duplicate client daemon connection, ignored: session_id=%u, stream_id=%u.",
+ session_id, server_stream->stream_id());
+ return;
+ }
+}
+
+/**
+ * Send and receive the heartbeat.
+ */
+int ServerControlThread::send_heartbeat(ServerStream* server_stream) {
+ int magic = 0xa1b2c3;
+ JB_RETURN(server_stream->send_request(MessageType::Heartbeat, &magic));
+ int num;
+ JB_RETURN(server_stream->recv_response(&num));
+ if (num != magic) JB_RETURN(JBErr::BAD_ARG_DATA);
+ return 0;
+}
+
+/**
+ * Try to delete a session data that meets:
+ * - It has a daemon stream and the stream has been closed;
+ * - It has no other stream.
+ */
+void ServerControlThread::try_remove_session_data(uint32_t session_id, ClientSessionState* state, TRAPS) {
+ ServerStream* server_stream = state->server_stream();
+ JClientSessionData* sd = server_stream->session_data();
+ assert(sd->session_id() == session_id, "sanity");
+
+ // the last server stream should be this one
+ if (sd->ref_cnt().get() > 1) {
+ return;
+ }
+ assert(sd->ref_cnt().get() == 1, "sanity");
+
+ guarantee(server_stream->current_thread() == THREAD, "sanity");
+
+ log_info(jbooster)("The client has exited, so its session data is cleared: session_id=%u, stream_id=%u.",
+ session_id, server_stream->stream_id());
+
+ _client_daemons.remove(session_id, THREAD);
+ state = nullptr; // deleted in _client_daemons
+ delete server_stream;
+ assert(sd->ref_cnt().get() == 0, "sanity");
+ ServerDataManager::get().try_remove_session(session_id, THREAD);
+}
+
+/**
+ * Update the heart beat of the session data.
+ * Delete the session data if the client has exited (or lost contact).
+ */
+void ServerControlThread::update_session_state(uint32_t session_id, ClientSessionState* state, TRAPS) {
+ ServerStream* server_stream = state->server_stream();
+
+ if (server_stream->is_stream_closed()) {
+ try_remove_session_data(session_id, state, THREAD);
+ return;
+ }
+
+ JB_TRY {
+ JB_THROW(send_heartbeat(server_stream));
+ } JB_TRY_END
+ JB_CATCH(JBErr::CONN_CLOSED_BY_PEER) {
+ try_remove_session_data(session_id, state, THREAD);
+ return;
+ } JB_CATCH_REST() {
+ int times = state->inc_heartbeat_failed_times();
+ log_warning(jbooster)("Heartbeat sync failed: error=%s(\"%s\"), "
+ "failed_times=%d, session_id=%u, stream_id=%u.",
+ JBErr::err_name(JB_ERR), JBErr::err_message(JB_ERR),
+ state->heartbeat_failed_times(),
+ session_id, server_stream->stream_id());
+ if (times >= 4) {
+ try_remove_session_data(session_id, state, THREAD);
+ }
+ return;
+ } JB_CATCH_END;
+
+ state->reset_heartbeat_failed_times();
+}
+
+/**
+ * Update the states of all sessions by heart beat.
+ */
+void ServerControlThread::update_session_states(TRAPS) {
+ GrowableArray<ServerControlThread::ClientSessionState*> sessions_with_daemon;
+ auto scan_func = [&sessions_with_daemon] (ServerControlThread::SessionStateMap::KVNode* p) -> bool {
+ assert(p->key() == p->value().server_stream()->session_id(), "sanity");
+ sessions_with_daemon.append(&p->value());
+ return true;
+ };
+ _client_daemons.for_each(scan_func, THREAD);
+ for (GrowableArrayIterator<ClientSessionState*> iter = sessions_with_daemon.begin();
+ iter != sessions_with_daemon.end();
+ ++iter) {
+ // Don't do the following logic in the scan_func above, because that's in the
+ // critical section, where the map is locked. We should avoid locking the map
+ // for too long.
+ update_session_state((*iter)->server_stream()->session_id(), *iter, THREAD);
+ }
+}
+
+/**
+ * Delete session datas with closed daemon stream.
+ * Most unused sessions are cleared in update_session_state(). This function is
+ * mainly used to clear some special cases.
+ * So it doesn't have to be invoked very often.
+ */
+void ServerControlThread::clear_unused_sessions(jlong current_time, TRAPS) {
+ auto eval_func = [current_time] (ServerDataManager::JClientSessionDataMap::KVNode* p) -> bool {
+ JClientSessionData* sd = p->value();
+ if (sd->ref_cnt().no_ref_time(current_time) < (jlong) session_no_ref_timeout()) {
+ return false;
+ }
+ return sd->ref_cnt().get() == 0;
+ };
+
+ auto del_func = [&keys = _client_daemons, THREAD] (ServerDataManager::JClientSessionDataMap::KVNode* p) {
+ uint32_t session_id = p->key();
+ // collect sessions without daemon stream
+ guarantee(!keys.contains(session_id, THREAD), "ref cnt mismatch");
+
+ log_info(jbooster)("A session data is deleted due to no daemon stream: "
+ "session_id=%u.", session_id);
+ // The real deletion of session data will be done in JClientSessionDataMapEvents::on_del().
+ };
+
+ ServerDataManager& sdm = ServerDataManager::get();
+ sdm.sessions()->bulk_remove_if(eval_func, del_func, THREAD);
+}
+
+/**
+ * Delete program datas with no sessions for a long time.
+ */
+void ServerControlThread::clear_unused_programs(jlong current_time, TRAPS) {
+ ResourceMark rm;
+ GrowableArray<uint32_t> deleted_program_ids;
+
+ auto eval_func = [current_time] (ServerDataManager::JClientProgramDataMap::KVNode* p) -> bool {
+ JClientProgramData* pd = p->value();
+ if (pd->ref_cnt().no_ref_time(current_time) < (jlong) unused_shared_data_cleanup_timeout()) {
+ return false;
+ }
+ return pd->ref_cnt().get() == 0;
+ };
+
+ auto del_func = [&deleted_program_ids] (ServerDataManager::JClientProgramDataMap::KVNode* p) {
+ deleted_program_ids.append(p->value()->program_id());
+ log_info(jbooster)("A program data is deleted due to no sessions for a long time: "
+ "program_id=%u.", p->value()->program_id());
+ // The real deletion of program data will be done in JClientProgramDataMapEvents::on_del().
+ };
+
+ ServerDataManager& sdm = ServerDataManager::get();
+ sdm.programs()->bulk_remove_if(eval_func, del_func, THREAD);
+
+ // Call the java methods after the critical region of bulk_remove_if().
+ for (GrowableArrayIterator<uint32_t> iter = deleted_program_ids.begin();
+ iter != deleted_program_ids.end(); ++iter) {
+ ServerDataManager::get().remove_java_side_program_data(*iter, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ LogTarget(Error, jbooster) lt;
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ }
+ }
+}
+
+/**
+ * Main loop of ServerControlThread.
+ */
+void ServerControlThread::control_loop(TRAPS) {
+ ThreadToNativeFromVM ttn(THREAD);
+ jlong program_last_check_time = os::javaTimeMillis();
+
+ while (true) {
+ { MonitorLocker locker(&_sleep_lock, Mutex::_no_safepoint_check_flag);
+ locker.wait(JBoosterManager::heartbeat_timeout() / 4);
+ }
+ jlong current_time = os::javaTimeMillis();
+ ResourceMark rm(THREAD);
+
+ update_session_states(THREAD);
+
+ if (current_time - program_last_check_time > (jlong) (unused_shared_data_cleanup_timeout() >> 1)) {
+ program_last_check_time = current_time;
+
+ clear_unused_sessions(current_time, THREAD);
+ clear_unused_programs(current_time, THREAD);
+ }
+ }
+}
diff --git a/src/hotspot/share/jbooster/server/serverControlThread.hpp b/src/hotspot/share/jbooster/server/serverControlThread.hpp
new file mode 100644
index 000000000..0acfa38b3
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverControlThread.hpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_SERVER_SERVERCONTROLTHREAD_HPP
+#define SHARE_JBOOSTER_SERVER_SERVERCONTROLTHREAD_HPP
+
+#include "jbooster/utilities/concurrentHashMap.hpp"
+#include "runtime/mutex.hpp"
+#include "runtime/thread.hpp"
+
+class ServerStream;
+
+/**
+ * Goals of this thread:
+ * - Sync heartbeat with the clients;
+ * - Manage the client sessions and client programs (clear out-of-date data);
+ */
+class ServerControlThread : public CHeapObj<mtJBooster> {
+public:
+ static uint32_t _unused_shared_data_cleanup_timeout;
+ static uint32_t _heartbeat_read_write_timeout;
+ static uint32_t _session_no_ref_timeout;
+
+ class ClientSessionState: public StackObj {
+ ServerStream* _server_stream;
+ int _heartbeat_failed_times;
+
+ public:
+ ClientSessionState(ServerStream* server_stream);
+
+ ServerStream* server_stream() { return _server_stream; }
+
+ int heartbeat_failed_times() { return _heartbeat_failed_times; }
+ int inc_heartbeat_failed_times() { return ++_heartbeat_failed_times; }
+ int reset_heartbeat_failed_times() { return _heartbeat_failed_times = 0; }
+ };
+
+ // key: session id
+ using SessionStateMap = ConcurrentHashMap<uint32_t, ClientSessionState, mtJBooster>;
+
+private:
+ static ServerControlThread* _singleton;
+
+ JavaThread* const _the_java_thread;
+ SessionStateMap _client_daemons;
+ Monitor _sleep_lock;
+
+private:
+ static void server_control_thread_entry(JavaThread* thread, TRAPS);
+
+ ServerControlThread(JavaThread* java_thread);
+
+ int send_heartbeat(ServerStream* server_stream);
+ void try_remove_session_data(uint32_t session_id, ClientSessionState* state, TRAPS);
+ void update_session_state(uint32_t session_id, ClientSessionState* state, TRAPS);
+ void update_session_states(TRAPS);
+ void clear_unused_sessions(jlong current_time, TRAPS);
+ void clear_unused_programs(jlong current_time, TRAPS);
+ void control_loop(TRAPS);
+
+public:
+ static ServerControlThread* start_thread(TRAPS);
+
+ static uint32_t unused_shared_data_cleanup_timeout() { return _unused_shared_data_cleanup_timeout; }
+ static void set_unused_shared_data_cleanup_timeout(uint32_t timeout) {
+ _unused_shared_data_cleanup_timeout = timeout;
+ }
+
+ static uint32_t heartbeat_read_write_timeout() { return _heartbeat_read_write_timeout; }
+ static void set_heartbeat_read_write_timeout(uint32_t timeout) {
+ _heartbeat_read_write_timeout = timeout;
+ }
+
+ static uint32_t session_no_ref_timeout() { return _session_no_ref_timeout; }
+ static void set_session_no_ref_timeout(uint32_t timeout) {
+ _session_no_ref_timeout = timeout;
+ }
+
+ void add_client_daemon_connection(ServerStream* server_stream, TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_SERVER_SERVERCONTROLTHREAD_HPP
diff --git a/src/hotspot/share/jbooster/server/serverDataManager.cpp b/src/hotspot/share/jbooster/server/serverDataManager.cpp
new file mode 100644
index 000000000..b4a11dc5c
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverDataManager.cpp
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/javaClasses.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/net/serialization.hpp"
+#include "jbooster/net/serverListeningThread.hpp"
+#include "jbooster/server/serverControlThread.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/concurrentHashMap.inline.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "logging/log.hpp"
+#include "oops/instanceKlass.inline.hpp"
+#include "oops/methodData.hpp"
+#include "oops/symbol.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/thread.hpp"
+#include "utilities/formatBuffer.hpp"
+#include "utilities/stringUtils.hpp"
+
+static InstanceKlass* get_jbooster_class_loader_klass(TRAPS) {
+ assert(THREAD->thread_state() == _thread_in_vm, "sanity");
+
+ TempNewSymbol loader_k_name = SymbolTable::new_symbol("jdk/jbooster/JBoosterClassLoader");
+ Handle class_loader(THREAD, SystemDictionary::java_system_loader());
+ Klass* loader_k = SystemDictionary::resolve_or_fail(loader_k_name, class_loader, Handle(), true, CHECK_NULL);
+ guarantee(loader_k != nullptr && loader_k->is_instance_klass(), "sanity");
+ InstanceKlass* loader_ik = InstanceKlass::cast(loader_k);
+ loader_ik->initialize(CHECK_NULL);
+ return loader_ik;
+}
+
+static ClassLoaderData* create_jbooster_class_loader(uint32_t program_id,
+ const ClassLoaderKey& key,
+ ClassLoaderData* parent,
+ TRAPS) {
+ ThreadInVMfromNative tiv(THREAD);
+
+ // get the class
+ InstanceKlass* loader_ik = get_jbooster_class_loader_klass(CHECK_NULL);
+
+ // get the method
+ TempNewSymbol method_name = SymbolTable::new_symbol("create");
+ TempNewSymbol method_sig = SymbolTable::new_symbol("(ILjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)"
+ "Ljdk/jbooster/JBoosterClassLoader;");
+
+ // invoke it and get a new loader obj
+ JavaValue result(T_OBJECT);
+ JavaCallArguments java_args;
+ int arg1 = (int) program_id;
+ Handle arg2 = key.loader_class_name() == nullptr ? Handle()
+ : java_lang_String::create_from_symbol(key.loader_class_name(), CHECK_NULL);
+ Handle arg3 = key.loader_name() == nullptr ? Handle()
+ : java_lang_String::create_from_symbol(key.loader_name(), CHECK_NULL);
+ Handle arg4(THREAD, parent->class_loader());
+ java_args.push_int(arg1);
+ java_args.push_oop(arg2);
+ java_args.push_oop(arg3);
+ java_args.push_oop(arg4);
+ JavaCalls::call_static(&result, loader_ik, method_name, method_sig, &java_args, CHECK_NULL);
+ Handle new_loader(THREAD, result.get_oop());
+
+ // register and get ClassLoaderData
+ ClassLoaderData* res_data = SystemDictionary::register_loader(new_loader);
+ guarantee(res_data != nullptr, "sanity");
+
+ ResourceMark rm;
+ log_trace(jbooster, compilation)("New custom class loader:\n - loader: \"%s:%p\"\n - parent: \"%s:%p\"",
+ res_data->loader_name(), res_data, parent->loader_name(), parent);
+
+ return res_data;
+}
+
+static void destroy_jbooster_class_loader(uint32_t program_id, TRAPS) {
+ ThreadInVMfromNative tiv(THREAD);
+
+ // get the class
+ InstanceKlass* loader_ik = get_jbooster_class_loader_klass(CHECK);
+
+ // get the method
+ TempNewSymbol method_name = SymbolTable::new_symbol("destroy");
+ TempNewSymbol method_sig = vmSymbols::int_void_signature();
+
+ // invoke it
+ JavaValue result(T_VOID);
+ JavaCallArguments java_args;
+ int arg1 = (int) program_id;
+ java_args.push_int(arg1);
+ JavaCalls::call_static(&result, loader_ik, method_name, method_sig, &java_args, CHECK);
+}
+
+// -------------------------------------- RefCnt --------------------------------------
+
+int RefCnt::get() const {
+ return Atomic::load_acquire(&_ref_cnt);
+}
+
+/**
+ * If the returned value is < 0, this ref cnt has been
+ * locked by another thread and is no longer avaliable.
+ */
+int RefCnt::inc() const {
+ int res;
+ do {
+ res = Atomic::load_acquire(&_ref_cnt);
+ if (res == LOCKED) return res;
+ } while (Atomic::cmpxchg(&_ref_cnt, res, res + 1) != res);
+ return res + 1;
+}
+
+/**
+ * This function should be invoked after inc()
+ * (and the returned value of inc() must be >= 0).
+ */
+int RefCnt::dec() const {
+ return Atomic::add(&_ref_cnt, (int) -1);
+}
+
+bool RefCnt::try_lock() const {
+ return Atomic::cmpxchg(&_ref_cnt, 0, LOCKED) == 0;
+}
+
+RefCntWithTime::RefCntWithTime(int init_ref_cnt): RefCnt(init_ref_cnt),
+ _no_ref_begin_time(os::javaTimeMillis()) {}
+
+int RefCntWithTime::dec_and_update_time() const {
+ if (get() == 1) {
+ Atomic::release_store(&_no_ref_begin_time, os::javaTimeMillis());
+ }
+ return dec();
+}
+
+jlong RefCntWithTime::no_ref_time() const {
+ return no_ref_time(os::javaTimeMillis());
+}
+
+jlong RefCntWithTime::no_ref_time(jlong current_time) const {
+ return current_time - Atomic::load_acquire(&_no_ref_begin_time);
+}
+
+// -------------------------------- JClientCacheState ---------------------------------
+
+JClientCacheState::JClientCacheState(): _is_allowed(false),
+ _state(NOT_GENERATED),
+ _file_path(nullptr),
+ _file_timestamp(0) {
+ // The real assignment is in JClientProgramData::JClientProgramData().
+}
+
+void JClientCacheState::init(bool allow, const char* file_path) {
+ _is_allowed = allow;
+ _file_path = file_path;
+ bool file_exists = FileUtils::is_file(file_path);
+ _state = file_exists ? GENERATED : NOT_GENERATED;
+ update_file_timestamp();
+}
+
+JClientCacheState::~JClientCacheState() {
+ remove_file();
+ StringUtils::free_from_heap(_file_path);
+}
+
+void JClientCacheState::update_file_timestamp() {
+ if (is_not_generated()) {
+ _file_timestamp = 0;
+ } else {
+ _file_timestamp = FileUtils::modify_time(_file_path);
+ }
+}
+
+void JClientCacheState::remove_file_and_set_not_generated_sync() {
+ if (set_being_generated_from_generated()) {
+ FileUtils::remove(file_path());
+ update_file_timestamp();
+ set_not_generated_from_being_generated();
+ }
+}
+
+bool JClientCacheState::is_not_generated() {
+ return Atomic::load_acquire(&_state) == NOT_GENERATED;
+}
+
+bool JClientCacheState::is_being_generated() {
+ return Atomic::load_acquire(&_state) == BEING_GENERATED;
+}
+
+bool JClientCacheState::is_generated() {
+ return Atomic::load_acquire(&_state) == GENERATED;
+}
+
+bool JClientCacheState::set_being_generated() {
+ return Atomic::cmpxchg(&_state, NOT_GENERATED, BEING_GENERATED) == NOT_GENERATED;
+}
+
+void JClientCacheState::set_generated() {
+ update_file_timestamp();
+ bool success = Atomic::cmpxchg(&_state, BEING_GENERATED, GENERATED) == BEING_GENERATED;
+ guarantee(success, "sanity");
+}
+
+void JClientCacheState::set_not_generated() {
+ bool success = Atomic::cmpxchg(&_state, BEING_GENERATED, NOT_GENERATED) == BEING_GENERATED;
+ guarantee(success, "sanity");
+}
+
+bool JClientCacheState::set_being_generated_from_generated() {
+ return Atomic::cmpxchg(&_state, GENERATED, BEING_GENERATED) == GENERATED;
+}
+
+void JClientCacheState::set_not_generated_from_being_generated() {
+ bool success = Atomic::cmpxchg(&_state, BEING_GENERATED, NOT_GENERATED) == BEING_GENERATED;
+ guarantee(success, "sanity");
+}
+
+bool JClientCacheState::is_cached() {
+ if (is_generated()) {
+ bool is_file = FileUtils::is_file(file_path());
+ if (is_file) {
+ uint64_t timestamp = FileUtils::modify_time(file_path());
+ if (timestamp == _file_timestamp) return true;
+ log_warning(jbooster)("Cache file \"%s\" is tampered with so will be deleted: "
+ "actual=" UINT64_FORMAT ", expect=" UINT64_FORMAT ".",
+ file_path(), timestamp, _file_timestamp);
+ } else {
+ log_warning(jbooster)("Cache file \"%s\" does not exist! Reset state to NOT_GENERATED.", file_path());
+ }
+ remove_file_and_set_not_generated_sync();
+ }
+ return false;
+}
+
+void JClientCacheState::remove_file() {
+ if (is_cached()) {
+ remove_file_and_set_not_generated_sync();
+ }
+}
+
+// ------------------------------ JClientProgramData -------------------------------
+
+JClientProgramData::JClientProgramData(uint32_t program_id, JClientArguments* program_args):
+ _program_id(program_id),
+ _program_args(program_args->move()),
+ _class_loaders(),
+ _ref_cnt(1) {
+ const char* sd = ServerDataManager::get().cache_dir_path();
+ _program_str_id = JBoosterManager::calc_program_string_id(_program_args->program_name(),
+ _program_args->program_entry(),
+ _program_args->is_jar(),
+ _program_args->hash());
+ bool allow_clr = _program_args->jbooster_allow_clr();
+ bool allow_cds = _program_args->jbooster_allow_cds();
+ bool allow_aot = _program_args->jbooster_allow_aot();
+ const char* clr_path = JBoosterManager::calc_cache_path(sd, _program_str_id, "clr.log");
+ const char* cds_path = JBoosterManager::calc_cache_path(sd, _program_str_id, "cds.jsa");
+ const char* aot_path = JBoosterManager::calc_cache_path(sd, _program_str_id, "aot.so");
+ clr_cache_state().init(allow_clr, clr_path);
+ cds_cache_state().init(allow_cds, cds_path);
+ aot_cache_state().init(allow_aot, aot_path);
+}
+
+JClientProgramData::~JClientProgramData() {
+ guarantee(ref_cnt().get() == 0, "ref=%d, pd=%u", ref_cnt().get(), program_id());
+ delete _program_args;
+ StringUtils::free_from_heap(_program_str_id);
+}
+
+ClassLoaderData* JClientProgramData::class_loader_data(const ClassLoaderKey& key, Thread* thread) {
+ ClassLoaderData** res = _class_loaders.get(key, thread);
+ return res == nullptr ? nullptr : *res;
+}
+
+ClassLoaderData* JClientProgramData::add_class_loader_if_absent(const ClassLoaderKey& key,
+ const ClassLoaderKey& parent_key,
+ Thread* thread) {
+ // return the existing cld if if exists
+ ClassLoaderData* res_data = class_loader_data(key, thread);
+ if (res_data != nullptr) return res_data;
+
+ // get/create the cld to insert
+ ClassLoaderData* new_data;
+ if (key.is_boot_loader() && parent_key.is_boot_loader()) {
+ new_data = ClassLoaderData::the_null_class_loader_data();
+ } else if (key.is_platform_loader() && parent_key.is_boot_loader()) {
+ new_data = ServerDataManager::get().platform_class_loader_data();
+ } else {
+ ClassLoaderData* parent_data = class_loader_data(parent_key, thread);
+ JavaThread* THREAD = thread->as_Java_thread();
+ new_data = create_jbooster_class_loader(program_id(), key, parent_data, CATCH);
+ }
+
+ // try to insert
+ ClassLoaderData** res = _class_loaders.put_if_absent(key, new_data, thread);
+ guarantee(res != nullptr, "sanity");
+ res_data = *res;
+
+ // duplicate insertion, so delete the duplicate cld
+ if (res_data != new_data) {
+ if (!new_data->is_builtin_class_loader_data()) {
+ // [JBOOSTER TODO] failed to insert so unregister the `res_data` above
+ }
+ }
+
+ return res_data;
+}
+
+// ------------------------------ JClientSessionData -------------------------------
+
+class AddressMapKVArray: public StackObj {
+ GrowableArray<address>* _key_array;
+ GrowableArray<address>* _value_array;
+public:
+ AddressMapKVArray(GrowableArray<address>* key_array,
+ GrowableArray<address>* value_array) :
+ _key_array(key_array),
+ _value_array(value_array) {}
+
+ bool operator () (JClientSessionData::AddressMap::KVNode* kv_node) {
+ assert(kv_node->key() != nullptr && kv_node->value() != nullptr, "sanity");
+ _key_array->append(kv_node->key());
+ _value_array->append(kv_node->value());
+ return true;
+ }
+};
+
+JClientSessionData::JClientSessionData(uint32_t session_id,
+ uint64_t client_random_id,
+ JClientProgramData* program_data):
+ _session_id(session_id),
+ _random_id(client_random_id),
+ _program_data(program_data),
+ _cl_s2c(),
+ _cl_c2s(),
+ _k_c2s(),
+ _m2md(),
+ _ref_cnt(1) {}
+
+JClientSessionData::~JClientSessionData() {
+ guarantee(ref_cnt().get() == 0, "sanity");
+ _program_data->ref_cnt().dec_and_update_time();
+}
+
+address JClientSessionData::get_address(AddressMap& table, address key, Thread* thread) {
+ address* res = table.get(key, thread);
+ if (res == nullptr) return nullptr;
+ return *res;
+}
+
+bool JClientSessionData::put_address(AddressMap& table, address key, address value, Thread* thread) {
+ address* res = table.put_if_absent(key, value, thread);
+ return res != nullptr && *res == value;
+}
+
+bool JClientSessionData::remove_address(AddressMap& table, address key, Thread* thread) {
+ return table.remove(key, thread);
+}
+
+address JClientSessionData::client_cld_address(ClassLoaderData* server_data, Thread* thread) {
+ return get_address(_cl_s2c, (address) server_data, thread);
+}
+
+ClassLoaderData* JClientSessionData::server_cld_address(address client_data, Thread* thread) {
+ return (ClassLoaderData*) get_address(_cl_c2s, client_data, thread);
+}
+
+Klass* JClientSessionData::server_klass_address(address client_klass_addr, Thread* thread) {
+ return (Klass*) get_address(_k_c2s, client_klass_addr, thread);
+}
+
+void JClientSessionData::add_klass_address(address client_klass_addr,
+ address server_cld_addr,
+ Thread* thread) {
+ put_address(_k_c2s, client_klass_addr, server_cld_addr, thread);
+}
+
+void JClientSessionData::klass_array(GrowableArray<address>* key_array,
+ GrowableArray<address>* value_array,
+ Thread* thread) {
+ AddressMapKVArray array(key_array, value_array);
+ _k_c2s.for_each(array, thread);
+}
+
+void JClientSessionData::klass_pointer_map_to_server(GrowableArray<address>* klass_array, Thread* thread) {
+ for (GrowableArrayIterator<address> iter = klass_array->begin();
+ iter != klass_array->end(); ++iter) {
+ InstanceKlass* klass = (InstanceKlass*) (*iter);
+ Array<Method*>* methods = klass->methods();
+ for (int method_index = 0; method_index < methods->length(); method_index++) {
+ MethodData* method_data = method_data_address((address)(methods->at(method_index)), thread);
+ if (method_data == nullptr) continue;
+ // relocate klass pointer in method_data
+ int position = 0;
+ while (position < method_data->data_size()) {
+ ProfileData* profile_data = method_data->data_at(position);
+ profile_data->klass_pointer_map_to_server(this, thread);
+ position += profile_data->size_in_bytes();
+ }
+ }
+ }
+}
+
+void JClientSessionData::add_method_data(address method, address method_data, Thread* thread) {
+ put_address(_m2md, method, method_data, thread);
+}
+
+bool JClientSessionData::remove_method_data(address method, Thread* thread) {
+ return remove_address(_m2md, method, thread);
+}
+
+MethodData* JClientSessionData::method_data_address(address method, Thread* thread) {
+ return (MethodData*) get_address(_m2md, method, thread);
+}
+
+ClassLoaderData* JClientSessionData::add_class_loader_if_absent(address client_cld_addr,
+ const ClassLoaderKey& key,
+ const ClassLoaderKey& parent_key,
+ Thread* thread) {
+ // has been registered
+ address server_cld_addr = get_address(_cl_c2s, client_cld_addr, thread);
+ if (server_cld_addr != nullptr) return (ClassLoaderData*) server_cld_addr;
+
+ // register
+ ClassLoaderData* data = _program_data->add_class_loader_if_absent(key, parent_key, thread);
+ server_cld_addr = (address) data;
+ bool success = put_address(_cl_s2c, server_cld_addr, client_cld_addr, thread);
+ if (success) {
+ success = put_address(_cl_c2s, client_cld_addr, server_cld_addr, thread);
+ }
+ if (!success) {
+ ResourceMark rm(thread);
+ log_trace(jbooster)("Duplicate class loader key: loader_class=%s, first_loaded_class=%s, "
+ "client_cld_addr=%p, server_cld_addr=%p.",
+ StringUtils::str(key.loader_class_name()),
+ StringUtils::str(key.first_loaded_class_name()),
+ client_cld_addr, server_cld_addr);
+ }
+ return success ? data : nullptr;
+}
+
+// ------------------------------- ServerDataManager -------------------------------
+
+ServerDataManager* ServerDataManager::_singleton = nullptr;
+
+ServerDataManager::ServerDataManager(TRAPS): _programs(Mutex::leaf + 1),
+ _sessions(Mutex::leaf + 1),
+ _next_program_allocation_id(0),
+ _next_session_allocation_id(0) {
+ _cache_dir_path = JBoosterCachePath;
+ _random_id = JBoosterManager::calc_random_id();
+
+ TempNewSymbol jbooster_main_name = SymbolTable::new_symbol("jdk/jbooster/JBooster");
+ Klass* main_k = SystemDictionary::resolve_or_null(
+ jbooster_main_name,
+ Handle(THREAD, SystemDictionary::java_platform_loader()),
+ Handle(), THREAD);
+ guarantee(main_k != nullptr && main_k->is_instance_klass(), "sanity");
+ _main_klass = InstanceKlass::cast(main_k);
+
+ Handle platform_loader(THREAD, SystemDictionary::java_platform_loader());
+ _platform_loader_data = ClassLoaderData::class_loader_data(platform_loader());
+}
+
+jint ServerDataManager::init_server_vm_options() {
+#define DO_NOT_SET_FLAG(flag) \
+ if (FLAG_IS_CMDLINE(flag)) { \
+ vm_exit_during_initialization("The flag " #flag " is client-only!"); \
+ }
+
+ // Check some typical flags.
+ // In fact, all flags should not be used (manually) on the server.
+ DO_NOT_SET_FLAG(UseJBooster);
+ DO_NOT_SET_FLAG(JBoosterPort);
+ DO_NOT_SET_FLAG(JBoosterTimeout);
+ DO_NOT_SET_FLAG(JBoosterCachePath);
+
+#undef DO_NOT_SET_FLAG
+ if (FLAG_SET_CMDLINE(TypeProfileWidth, 8) != JVMFlag::SUCCESS) {
+ return JNI_EINVAL;
+ }
+ return JNI_OK;
+}
+
+jint ServerDataManager::init_phase1() {
+ JBoosterManager::server_only();
+ return init_server_vm_options();
+}
+
+void ServerDataManager::init_cache_path(const char* optional_cache_path) {
+ if (optional_cache_path == nullptr) {
+ FLAG_SET_DEFAULT(JBoosterCachePath, JBoosterManager::calc_cache_dir_path(false));
+ } else {
+ FLAG_SET_CMDLINE(JBoosterCachePath, StringUtils::copy_to_heap(optional_cache_path, mtJBooster));
+ }
+ FileUtils::mkdirs(JBoosterCachePath);
+}
+
+void ServerDataManager::init_phase3(int server_port, int connection_timeout, int cleanup_timeout, const char* cache_path, TRAPS) {
+ JBoosterManager::server_only();
+ init_cache_path(cache_path);
+
+ _singleton = new ServerDataManager(CHECK);
+
+ ServerControlThread::set_unused_shared_data_cleanup_timeout(cleanup_timeout);
+ _singleton->_control_thread = ServerControlThread::start_thread(CHECK);
+ _singleton->_listening_thread = ServerListeningThread::start_thread(JBoosterAddress, (uint16_t) server_port, connection_timeout, CHECK);
+}
+
+/**
+ * The ref_cnt of the returned JClientProgramData will +1.
+ */
+JClientProgramData* ServerDataManager::get_program(JClientArguments* program_args, Thread* thread) {
+ JClientProgramData** res = _programs.get(program_args, thread);
+ if (res == nullptr) return nullptr;
+ return *res;
+}
+
+/**
+ * The ref_cnt of the returned JClientProgramData will +1.
+ */
+JClientProgramData* ServerDataManager::get_or_create_program(JClientArguments* program_args, Thread* thread) {
+ JClientProgramData** res = _programs.get(program_args, thread);
+ if (res != nullptr) return *res;
+
+ uint32_t program_id = Atomic::add(&_next_program_allocation_id, 1u);
+ JClientProgramData* new_pd = new JClientProgramData(program_id, program_args);
+ res = _programs.put_if_absent(new_pd->program_args(), new_pd, thread);
+ guarantee(res != nullptr, "sanity");
+ JClientProgramData* res_pd = *res;
+
+ if (res_pd != new_pd) {
+ new_pd->ref_cnt().dec();
+ delete new_pd;
+ }
+
+ return res_pd;
+}
+
+bool ServerDataManager::try_remove_program(JClientArguments* program_args, Thread* thread) {
+ auto eval_func = [](const JClientProgramDataKey& key, JClientProgramData* pd) -> bool {
+ return pd->ref_cnt().get() == 0;
+ };
+ return _programs.remove_if(program_args, eval_func, thread);
+}
+
+/**
+ * We can't call a java method with a lock held by this thread.
+ * So we destroy the java-side data after JClientProgramDataMapEvents::on_del().
+ */
+void ServerDataManager::remove_java_side_program_data(uint32_t program_id, TRAPS) {
+ destroy_jbooster_class_loader(program_id, THREAD);
+}
+
+/**
+ * The ref_cnt of the returned JClientSessionData will +1.
+ */
+JClientSessionData* ServerDataManager::get_session(uint32_t session_id, Thread* thread) {
+ JClientSessionData** res = _sessions.get(session_id, thread);
+ if (res == nullptr) return nullptr;
+ return *res;
+}
+
+/**
+ * The ref_cnt of the returned JClientSessionData will +1.
+ */
+JClientSessionData* ServerDataManager::create_session(uint64_t client_random_id,
+ JClientArguments* program_args,
+ Thread* thread) {
+ uint32_t session_id = Atomic::add(&_next_session_allocation_id, 1u);
+ JClientProgramData* pd = get_or_create_program(program_args, thread);
+ JClientSessionData* sd = new JClientSessionData(session_id, client_random_id, pd);
+
+ JClientSessionData** res = _sessions.put_if_absent(session_id, sd, thread);
+ guarantee(res != nullptr && *res == sd, "sanity");
+ return sd;
+}
+
+bool ServerDataManager::try_remove_session(uint32_t session_id, Thread* thread) {
+ auto eval_func = [](uint32_t session_id, JClientSessionData* sd) -> bool {
+ return sd->ref_cnt().get() == 0;
+ };
+ return _sessions.remove_if(session_id, eval_func, thread);
+}
diff --git a/src/hotspot/share/jbooster/server/serverDataManager.hpp b/src/hotspot/share/jbooster/server/serverDataManager.hpp
new file mode 100644
index 000000000..22b416cd9
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverDataManager.hpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_SERVER_SERVERDATAMANAGER_HPP
+#define SHARE_JBOOSTER_SERVER_SERVERDATAMANAGER_HPP
+
+#include "jbooster/dataTransmissionUtils.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/jClientArguments.hpp"
+#include "jbooster/net/serialization.hpp"
+#include "jbooster/utilities/concurrentHashMap.hpp"
+
+// Every client has a JClientSessionData and a JClientProgramData on the server.
+//
+// The JClientSessionData maintains the runtime info of the client session (a
+// session represents a run of java program), such as the pointers of the class
+// loaders.
+//
+// The JClientProgramData maintains the immutable data of a program (a program
+// can be a .jar or a .class), especially the keys of the class loaders.
+//
+// Each JClientSessionData has one JClientProgramData and different JClientSessionData
+// can share the same JClientProgramData, so the program data such as the class
+// loaders can be shared between different client sessions.
+//
+// On the server the ServerDataManager manages the JClientSessionData list and
+// the JClientProgramData list.
+//
+// +--------------------------------------------------------------------------+
+// | ServerDataManager |
+// | +------------------+ +------------------+ |
+// | | ClassLoaderTable | .--------------->| ClassLoaderTable | |
+// | +------------------+ | +------------------+ |
+// | A \ | / A |
+// | | +--------------------+ | +--------------------+ | |
+// | | | JClientProgramData | | | JClientProgramData | | |
+// | | | task1.class | | | task2.jar | | |
+// | | +--------------------+ | +--------------------+ | |
+// | | / | / \ | |
+// | | / | / \ | |
+// | +--------------------+ +--------------------+ +--------------------+ |
+// | | JClientSessionData | | JClientSessionData | | JClientSessionData | |
+// | | 192.168.0.11 (1) | | 192.168.0.11 (2) | | 192.168.0.12 | |
+// | +--------------------+ +--------------------+ +--------------------+ |
+// | |
+// +--------------------------------------------------------------------------+
+
+// For each class loader on the client, we generate a corresponding fake class
+// loader on the server.
+//
+// +---------------------+
+// | bootstrap loader |
+// +---------------------+
+// A (share boot loader
+// | and platform loader)
+// +---------------------+
+// | PlatformClassLoader |
+// +---------------------+
+// A A
+// +-----------------------+ | | +----------------------------------------------+
+// | ClassLoaderTable1 | | | | ClassLoaderTable2 |
+// | | | | | |
+// | +-------------------+ | | | | +--------------------+ |
+// | | FakeAppLoader1 |---+ +--------------| FakeAppLoader2 | |
+// | +-------------------+ | | +--------------------+ |
+// | A | | A A |
+// | | | | | | |
+// | +-------------------+ | | +-------------------+ +-------------------+ |
+// | | FakeCustomLoader1 | | | | FakeCustomLoader3 | | FakeCustomLoader4 | |
+// | +-------------------+ | | +-------------------+ +-------------------+ |
+// | A | | |
+// | | | +----------------------------------------------+
+// | +-------------------+ |
+// | | FakeCustomLoader2 | |
+// | +-------------------+ |
+// | |
+// +-----------------------+
+
+class ClassLoaderData;
+class InstanceKlass;
+class ServerControlThread;
+class ServerListeningThread;
+
+class RefCnt: public StackObj {
+ static const int LOCKED = -1000;
+
+ mutable volatile int _ref_cnt;
+
+public:
+ RefCnt(int init_ref_cnt = 0): _ref_cnt(init_ref_cnt) {}
+
+ int get() const;
+ int inc() const;
+ int dec() const;
+ bool try_lock() const;
+ bool is_locked() const { return get() == LOCKED; }
+};
+
+class RefCntWithTime: public RefCnt {
+ mutable volatile jlong _no_ref_begin_time;
+
+public:
+ RefCntWithTime(int init_ref_cnt = 0);
+ int dec_and_update_time() const;
+ jlong no_ref_time() const;
+ jlong no_ref_time(jlong current_time) const;
+};
+
+class JClientCacheState final: public StackObj {
+ friend class JClientProgramData;
+
+ static const int NOT_GENERATED = 0;
+ static const int BEING_GENERATED = 1;
+ static const int GENERATED = 2;
+
+ bool _is_allowed;
+ volatile int _state;
+ const char* _file_path;
+ uint64_t _file_timestamp;
+
+private:
+ NONCOPYABLE(JClientCacheState);
+ void update_file_timestamp();
+ void remove_file_and_set_not_generated_sync();
+ bool set_being_generated_from_generated();
+ void set_not_generated_from_being_generated();
+
+public:
+ JClientCacheState();
+ ~JClientCacheState();
+
+ void init(bool allow, const char* file_path);
+
+ bool is_allowed() { return _is_allowed; }
+
+ // <cache_dir>/server/cache-<parogram_str_id>-<cache-name>
+ const char* file_path() { return _file_path; }
+ uint64_t file_timestamp() { return _file_timestamp; }
+
+ bool is_not_generated();
+ bool is_being_generated();
+ bool is_generated();
+ bool set_being_generated(); // from state of not_generated
+ void set_generated(); // from state of being_generated
+ void set_not_generated(); // from state of being_generate
+
+ // Unlike the APIs above, the APIs above only change the atomic variables,
+ // while the following APIs checks whether the cache file exists.
+ bool is_cached();
+
+ void remove_file();
+};
+
+/**
+ * The data of the program (usually a .class or .jar file) executed by the client.
+ */
+class JClientProgramData final: public CHeapObj<mtJBooster> {
+public:
+ using ClassLoaderTable = ConcurrentHashMap<ClassLoaderKey, ClassLoaderData*, mtJBooster>;
+
+private:
+ uint32_t _program_id; // generated by the server
+ const char* _program_str_id; // generated by the client
+ JClientArguments* _program_args; // used to uniquely identify a client program
+
+ ClassLoaderTable _class_loaders;
+
+ RefCntWithTime _ref_cnt;
+
+ JClientCacheState _clr_cache_state;
+ JClientCacheState _cds_cache_state;
+ JClientCacheState _aot_cache_state;
+
+ NONCOPYABLE(JClientProgramData);
+
+public:
+ JClientProgramData(uint32_t program_id, JClientArguments* program_args);
+ ~JClientProgramData();
+
+ uint32_t program_id() const { return _program_id; }
+ JClientArguments* program_args() const { return _program_args; }
+
+ ClassLoaderTable* class_loaders() { return &_class_loaders; }
+
+ ClassLoaderData* class_loader_data(const ClassLoaderKey& key, Thread* thread);
+ ClassLoaderData* add_class_loader_if_absent(const ClassLoaderKey& key,
+ const ClassLoaderKey& parent_key, Thread* thread);
+
+ RefCntWithTime& ref_cnt() { return _ref_cnt; }
+
+ JClientCacheState& clr_cache_state() { return _clr_cache_state; }
+ JClientCacheState& cds_cache_state() { return _cds_cache_state; }
+ JClientCacheState& aot_cache_state() { return _aot_cache_state; }
+};
+
+/**
+ * @see TempNewSymbol
+ */
+class TempJClientProgramData : public StackObj {
+ JClientProgramData* _temp;
+
+public:
+ TempJClientProgramData(JClientProgramData *s) : _temp(s) {}
+ TempJClientProgramData(const TempJClientProgramData& rhs) : _temp(rhs._temp) { if (_temp != nullptr) _temp->ref_cnt().inc(); }
+ ~TempJClientProgramData() { if (_temp != nullptr) _temp->ref_cnt().dec(); }
+
+ void operator=(TempJClientProgramData rhs) = delete;
+ bool operator == (JClientProgramData* o) const = delete;
+ operator JClientProgramData*() { return _temp; }
+ JClientProgramData* operator -> () const { return _temp; }
+};
+
+/**
+ * The session data of a client.
+ */
+class JClientSessionData final: public CHeapObj<mtJBooster> {
+ friend class ServerStream;
+
+public:
+ using AddressMap = ConcurrentHashMap<address, address, mtJBooster>;
+
+private:
+ uint32_t _session_id; // generated by the server
+ uint64_t _random_id; // generated by the client
+
+ JClientProgramData* const _program_data;
+
+ // server-side CLD pointer -> client-side CLD pointer
+ AddressMap _cl_s2c;
+ // client-side CLD pointer -> server-side CLD pointer
+ AddressMap _cl_c2s;
+
+ // client-side klass pointer -> server-side klass pointer
+ AddressMap _k_c2s;
+
+ // method pointer -> method data pointer (both server-side)
+ AddressMap _m2md;
+
+ RefCntWithTime _ref_cnt;
+
+private:
+ NONCOPYABLE(JClientSessionData);
+
+ static address get_address(AddressMap& table, address key, Thread* thread);
+ static bool put_address(AddressMap& table, address key, address value, Thread* thread);
+ static bool remove_address(AddressMap& table, address key, Thread* thread);
+
+public:
+ JClientSessionData(uint32_t session_id, uint64_t client_random_id, JClientProgramData* program_data);
+ ~JClientSessionData();
+
+ uint32_t session_id() const { return _session_id; }
+
+ uint64_t random_id() const { return _random_id; }
+
+ JClientProgramData* program_data() const { return _program_data; }
+
+ address client_cld_address(ClassLoaderData* server_data, Thread* thread);
+ ClassLoaderData* server_cld_address(address client_data, Thread* thread);
+ ClassLoaderData* add_class_loader_if_absent(address client_cld_addr,
+ const ClassLoaderKey& key,
+ const ClassLoaderKey& parent_key,
+ Thread* thread);
+
+ Klass* server_klass_address(address client_klass_addr, Thread* thread);
+ void add_klass_address(address client_klass_addr, address server_cld_addr, Thread* thread);
+
+ void klass_array(GrowableArray<address>* key_array, GrowableArray<address>* value_array, Thread* thread);
+
+ void klass_pointer_map_to_server(GrowableArray<address>* klass_array, Thread* thread);
+
+ void add_method_data(address method, address method_data, Thread* thread);
+ bool remove_method_data(address method, Thread* thread);
+ MethodData* method_data_address(address method, Thread* thread);
+
+ RefCntWithTime& ref_cnt() { return _ref_cnt; }
+};
+
+/**
+ * @see TempNewSymbol
+ */
+class TempJClientSessionData : public StackObj {
+ JClientSessionData* _temp;
+
+public:
+ TempJClientSessionData(JClientSessionData *s) : _temp(s) {}
+ TempJClientSessionData(const TempJClientSessionData& rhs) : _temp(rhs._temp) { if (_temp != nullptr) _temp->ref_cnt().inc(); }
+ ~TempJClientSessionData() { if (_temp != nullptr) _temp->ref_cnt().dec(); }
+
+ void operator=(TempJClientSessionData rhs) = delete;
+ bool operator == (JClientSessionData* o) const = delete;
+ operator JClientSessionData*() { return _temp; }
+ JClientSessionData* operator -> () const { return _temp; }
+};
+
+/**
+ * The manager of JClientSessionData and JClientProgramData.
+ */
+class ServerDataManager: public CHeapObj<mtJBooster> {
+public:
+ class JClientProgramDataKey: public StackObj {
+ private:
+ // The lifetime of _args is managed by the value of JClientProgramDataMap.
+ // So make sure the lifetime of JClientProgramData is outlive its JClientProgramDataKey.
+ JClientArguments* _args;
+ public:
+ JClientProgramDataKey(JClientArguments* args): _args(args) {}
+ uintx hash() const { return (uintx) _args->hash(); }
+ bool equals(const JClientProgramDataKey& other) const {
+ return _args->equals(other._args);
+ }
+ };
+
+ struct JClientProgramDataMapEvents {
+ static bool is_dead(const JClientProgramDataKey& key, JClientProgramData*& value) { return false; }
+ static void on_get(const JClientProgramDataKey& key, JClientProgramData*& value) { value->ref_cnt().inc(); }
+ static void on_del(const JClientProgramDataKey& key, JClientProgramData*& value) {
+ if (value->ref_cnt().get() == 0) {
+ // Do not invoke destroy_jbooster_class_loader() here as we are now in the lock of the map.
+ delete value;
+ } else {
+ // The thread lost a race to insert this newly created object.
+ guarantee(value->ref_cnt().get() == 1, "sanity");
+ }
+ }
+ };
+
+ struct JClientSessionDataMapEvents {
+ static bool is_dead(const uint32_t& key, JClientSessionData*& value) { return false; }
+ static void on_get(const uint32_t& key, JClientSessionData*& value) { value->ref_cnt().inc(); }
+ static void on_del(const uint32_t& key, JClientSessionData*& value) {
+ // There shouldn't be a insert race failure here, so the ref won't be 1.
+ assert(value->ref_cnt().get() == 0, "sanity");
+ delete value;
+ }
+ };
+
+ using JClientProgramDataMap = ConcurrentHashMap<JClientProgramDataKey, JClientProgramData*, mtJBooster, JClientProgramDataMapEvents>;
+ using JClientSessionDataMap = ConcurrentHashMap<uint32_t, JClientSessionData*, mtJBooster, JClientSessionDataMapEvents>;
+
+private:
+ static ServerDataManager* _singleton;
+
+ const char* _cache_dir_path;
+
+ uint64_t _random_id;
+
+ JClientProgramDataMap _programs;
+ JClientSessionDataMap _sessions;
+
+ volatile uint32_t _next_program_allocation_id;
+ volatile uint32_t _next_session_allocation_id;
+
+ ServerListeningThread* _listening_thread;
+ ServerControlThread* _control_thread;
+
+ InstanceKlass* _main_klass;
+ ClassLoaderData* _platform_loader_data;
+
+private:
+ NONCOPYABLE(ServerDataManager);
+
+ ServerDataManager(TRAPS);
+
+ static jint init_server_vm_options();
+ static void init_cache_path(const char* optional_cache_path);
+
+ JClientProgramData* get_or_create_program(JClientArguments* program_args, Thread* thread);
+
+public:
+ static ServerDataManager& get() {
+ JBoosterManager::server_only();
+ assert(_singleton != nullptr, "sanity");
+ return *_singleton;
+ }
+
+ static jint init_phase1();
+ static void init_phase2(TRAPS) { /* do nothing */ }
+ static void init_phase3(int server_port, int connection_timeout, int cleanup_timeout, const char* cache_path, TRAPS);
+
+ // $HOME/.jbooster/server
+ const char* cache_dir_path() { return _cache_dir_path; }
+
+ uint64_t random_id() { return _random_id; }
+
+ JClientProgramDataMap* programs() { return &_programs; }
+ JClientSessionDataMap* sessions() { return &_sessions; }
+
+ JClientProgramData* get_program(JClientArguments* program_args, Thread* thread);
+ bool try_remove_program(JClientArguments* program_args, Thread* thread);
+ void remove_java_side_program_data(uint32_t program_id, TRAPS);
+
+ JClientSessionData* get_session(uint32_t session_id, Thread* thread);
+ JClientSessionData* create_session(uint64_t client_random_id,
+ JClientArguments* program_args,
+ Thread* thread);
+ bool try_remove_session(uint32_t session_id, Thread* thread);
+
+ ServerListeningThread* listening_thread() { return _listening_thread; }
+ ServerControlThread* control_thread() { return _control_thread; }
+
+ InstanceKlass* main_klass() { return _main_klass; };
+ ClassLoaderData* platform_class_loader_data() { return _platform_loader_data; }
+
+ void log_all_state(bool print_all = false);
+};
+
+#endif // SHARE_JBOOSTER_SERVER_SERVERDATAMANAGER_HPP
diff --git a/src/hotspot/share/jbooster/server/serverDataManagerLog.cpp b/src/hotspot/share/jbooster/server/serverDataManagerLog.cpp
new file mode 100644
index 000000000..47e9c6b01
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverDataManagerLog.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "classfile/classLoaderData.inline.hpp"
+#include "classfile/javaClasses.hpp"
+#include "jbooster/jBoosterManager.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/utilities/concurrentHashMap.inline.hpp"
+#include "memory/allocation.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/globals.hpp"
+#include "utilities/growableArray.hpp"
+
+// log mark of ServerDataManager
+#define OUT_0(format, args...) tty->print_cr("" format, ##args)
+#define OUT_1(format, args...) tty->print_cr(" " format, ##args)
+#define OUT_2(format, args...) tty->print_cr(" " format, ##args)
+#define OUT_3(format, args...) tty->print_cr(" " format, ##args)
+#define OUT_4(format, args...) tty->print_cr(" " format, ##args)
+#define OUT_5(format, args...) tty->print_cr(" " format, ##args)
+
+class LogClassLoaderNodeIterator: public StackObj {
+ bool _print_details;
+
+public:
+ LogClassLoaderNodeIterator(bool print_details): _print_details(print_details) {}
+
+ bool operator () (JClientProgramData::ClassLoaderTable::KVNode* p) {
+ const ClassLoaderKey& key = p->key();
+ ClassLoaderData* cld = p->value();
+
+ ResourceMark rm;
+ Symbol* s1 = key.loader_class_name();
+ Symbol* s2 = key.first_loaded_class_name();
+
+ OUT_3("-");
+
+ int cnt = 0;
+ for (Klass* loaded_k = cld->klasses(); loaded_k != nullptr; loaded_k = loaded_k->next_link()) {
+ guarantee(loaded_k->class_loader_data() == cld, "sanity");
+ ++cnt;
+ }
+
+ if (cld->is_builtin_class_loader_data()) {
+ OUT_4("loader_name: %s", cld->loader_name());
+ OUT_4("loaded_class_cnt: %d", cnt);
+ return true;
+ } else {
+ OUT_4("client_loader_class: %s", s1 == nullptr ? "<null>" : s1->as_C_string());
+ OUT_4("first_loaded_class: %s", s2 == nullptr ? "<null>" : s2->as_C_string());
+ OUT_4("parent: %s", ClassLoaderData::class_loader_data_or_null(
+ java_lang_ClassLoader::parent(cld->class_loader()))->loader_name());
+ OUT_4("loaded_class_cnt: %d", cnt);
+ if (_print_details && cnt > 0) {
+ OUT_4("loaded_classes:");
+ for (Klass* loaded_k = cld->klasses(); loaded_k != nullptr; loaded_k = loaded_k->next_link()) {
+ guarantee(loaded_k->class_loader_data() == cld, "sanity");
+ OUT_5("class_name: %s", loaded_k->internal_name());
+ }
+ }
+ }
+ return true;
+ }
+};
+
+class LogJClientProgramDataIterator: public StackObj {
+ bool _print_details;
+
+public:
+ LogJClientProgramDataIterator(bool print_details): _print_details(print_details) {}
+
+ bool operator () (ServerDataManager::JClientProgramDataMap::KVNode* kv_node) {
+ JClientProgramData* pd = kv_node->value();
+ JClientCacheState& clr = pd->clr_cache_state();
+ JClientCacheState& cds = pd->cds_cache_state();
+ JClientCacheState& aot = pd->aot_cache_state();
+ const char* clr_stat = (clr.is_cached() ? "generated" : (clr.is_being_generated() ? "generating" : "none"));
+ const char* cds_stat = (cds.is_cached() ? "generated" : (cds.is_being_generated() ? "generating" : "none"));
+ const char* aot_stat = (aot.is_cached() ? "generated" : (aot.is_being_generated() ? "generating" : "none"));
+ OUT_1("-");
+ OUT_2("program_id: %u", pd->program_id());
+ OUT_2("program_name: %s", pd->program_args()->program_name());
+ OUT_2("program_hash: %x", pd->program_args()->hash());
+ OUT_2("ref_cnt: %d", pd->ref_cnt().get());
+ OUT_2("clr_cache: %s", clr_stat);
+ OUT_2("cds_cache: %s", cds_stat);
+ OUT_2("aot_cache: %s", aot_stat);
+ OUT_2("class_loader_size: " SIZE_FORMAT, pd->class_loaders()->size());
+ if (pd->class_loaders()->size() > 0) {
+ OUT_2("class_loaders:");
+ LogClassLoaderNodeIterator ci(_print_details);
+ pd->class_loaders()->for_each(ci, Thread::current());
+ }
+ return true;
+ }
+};
+
+class LogJClientSessionDataIterator: public StackObj {
+public:
+ bool operator () (ServerDataManager::JClientSessionDataMap::KVNode* kv_node) {
+ JClientSessionData* sd = kv_node->value();
+ OUT_1("-");
+ OUT_2("random_id: " UINT64_FORMAT, sd->random_id());
+ OUT_2("session_id: %u", sd->session_id());
+ OUT_2("program_id: %u", sd->program_data()->program_id());
+ OUT_2("ref_cnt: %d", sd->ref_cnt().get());
+ return true;
+ }
+};
+
+void ServerDataManager::log_all_state(bool print_details) {
+ Thread* THREAD = Thread::current();
+ ResourceMark rm(THREAD);
+ LogJClientSessionDataIterator ri;
+ LogJClientProgramDataIterator pi(print_details);
+
+ OUT_0("JClientSessionData:");
+ _sessions.for_each(ri, THREAD);
+ OUT_0("");
+
+ OUT_0("JClientProgramData:");
+ _programs.for_each(pi, THREAD);
+ OUT_0("");
+}
diff --git a/src/hotspot/share/jbooster/server/serverMessageHandler.cpp b/src/hotspot/share/jbooster/server/serverMessageHandler.cpp
new file mode 100644
index 000000000..9e66b6d4c
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverMessageHandler.cpp
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/lazyAot.hpp"
+#include "jbooster/net/serializationWrappers.inline.hpp"
+#include "jbooster/net/serverStream.hpp"
+#include "jbooster/server/serverControlThread.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#include "jbooster/server/serverMessageHandler.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/method.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+
+ServerMessageHandler::ServerMessageHandler(ServerStream* server_stream): _server_stream(server_stream),
+ _no_more_client_request(false) {}
+
+int ServerMessageHandler::handle_a_task_from_client(MessageType msg_type, TRAPS) {
+ _no_more_client_request = false;
+ switch (msg_type) {
+ case MessageType::ClientDaemonTask:
+ ServerDataManager::get().control_thread()->add_client_daemon_connection(&ss(), THREAD);
+ _no_more_client_request = true;
+ break;
+ case MessageType::CacheFilesSyncTask:
+ JB_RETURN(handle_cache_file_sync_task(THREAD));
+ break;
+ case MessageType::LazyAOTCompilationTask:
+ JB_RETURN(handle_lazy_aot_compilation_task(THREAD));
+ break;
+ case MessageType::NoMoreRequests:
+ _no_more_client_request = true;
+ break;
+ default:
+ JB_RETURN(JBErr::BAD_MSG_TYPE);
+ break;
+ }
+ return 0;
+}
+
+int ServerMessageHandler::handle_tasks_from_client(TRAPS) {
+ MessageType msg_type;
+ do {
+ JB_RETURN(ss().recv_request(msg_type));
+ JB_RETURN(handle_a_task_from_client(msg_type, THREAD));
+ } while (!_no_more_client_request);
+ return 0;
+}
+
+int ServerMessageHandler::request_class_loaders(GrowableArray<ClassLoaderLocator> *loaders, TRAPS) {
+ if (loaders->is_empty()) return 0;
+ ArrayWrapper<ClassLoaderChain> chains_aw(false);
+ int size = loaders->length();
+ ArrayWrapper<ClassLoaderLocator> aw(loaders);
+ JB_RETURN(ss().send_request(MessageType::DataOfClassLoaders, &aw));
+ JB_RETURN(ss().recv_response(&chains_aw));
+ guarantee(size == chains_aw.size(), "sanity");
+
+ JClientSessionData* sd = ss().session_data();
+ ClassLoaderChain* chains = chains_aw.get_array<ClassLoaderChain>();
+ for (int i = 0; i < size; ++i) {
+ ClassLoaderChain& loader_chain = chains[i];
+ GrowableArray<ClassLoaderChain::Node>* chain = loader_chain.chain();
+ ClassLoaderChain::Node& last_node = chain->at(chain->length() - 1);
+ guarantee(last_node.key.is_boot_loader(), "sanity");
+ sd->add_class_loader_if_absent((address) last_node.client_cld_addr,
+ last_node.key,
+ last_node.key,
+ CHECK_(JBErr::THREAD_EXCEPTION));
+ for (int j = chain->length() - 2; j >= 0; --j) {
+ ClassLoaderChain::Node& node = chain->at(j);
+ ClassLoaderChain::Node& parent_node = chain->at(j + 1);
+ ClassLoaderData* cld = sd->add_class_loader_if_absent((address) node.client_cld_addr,
+ node.key,
+ parent_node.key,
+ CHECK_(JBErr::THREAD_EXCEPTION));
+ if (cld == nullptr) {
+ // Failed to register the class loader. So skip the loaders above too.
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int ServerMessageHandler::request_klasses(GrowableArray<KlassLocator>* klasses, TRAPS) {
+ const int max_req_cnt = 1000;
+ const int klass_cnt = klasses->length();
+ for (int recv_cnt = 0; recv_cnt < klass_cnt; recv_cnt += max_req_cnt) {
+ int req_cnt;
+ { ArrayWrapper<KlassLocator> kl_aw(klasses);
+ req_cnt = kl_aw.set_sub_arr(recv_cnt, max_req_cnt);
+ JB_RETURN(ss().send_request(MessageType::DataOfKlasses, &kl_aw));
+ // Cannot use recv_response() here because deserialization of InstanceKlass must be in
+ // _threa_in_vm state. So we split it into recv_request() and parse_request().
+ MessageType msg_type;
+ JB_RETURN(ss().recv_request(msg_type));
+ if (msg_type != MessageType::DataOfKlasses) {
+ JB_RETURN(JBErr::BAD_MSG_TYPE);
+ }
+ }
+ ThreadInVMfromNative tiv(THREAD);
+ ArrayWrapper<InstanceKlass> ik_aw(true);
+ JB_RETURN(ss().parse_request(&ik_aw));
+ guarantee(ik_aw.size() == req_cnt, "sanity");
+ }
+ return 0;
+}
+
+int ServerMessageHandler::request_missing_class_loaders(TRAPS) {
+ JB_RETURN(ss().send_request(MessageType::ClassLoaderLocators));
+ ArrayWrapper<ClassLoaderLocator> cll_aw(false);
+ JB_RETURN(ss().recv_response(&cll_aw));
+ ClassLoaderLocator* cll_arr = cll_aw.get_array<ClassLoaderLocator>();
+
+ ResourceMark rm(THREAD);
+ GrowableArray<ClassLoaderLocator> missing_loaders;
+ for (int i = 0; i < cll_aw.size(); ++i) {
+ ClassLoaderLocator& cll = cll_arr[i];
+ if (cll.class_loader_data() != nullptr) continue;
+ missing_loaders.append(cll);
+ }
+
+ // get class loaders
+ JB_RETURN(request_class_loaders(&missing_loaders, THREAD));
+ return 0;
+}
+
+int ServerMessageHandler::request_missing_klasses(TRAPS) {
+ JB_RETURN(ss().send_request(MessageType::KlassLocators));
+ ArrayWrapper<KlassLocator> kl_aw(false);
+ JB_RETURN(ss().recv_response(&kl_aw));
+ KlassLocator* kl_arr = kl_aw.get_array<KlassLocator>();
+ log_debug(jbooster, compilation)("Related klasses: %d. session_id=%u.",
+ kl_aw.size(),
+ ss().session_id());
+
+ ResourceMark rm(THREAD);
+ GrowableArray<KlassLocator> missing_klasses;
+ {
+ JavaThread* java_thread = THREAD->as_Java_thread();
+ for (int i = 0; i < kl_aw.size(); ++i) {
+ KlassLocator& kl = kl_arr[i];
+ InstanceKlass* ik;
+ { ThreadInVMfromNative tivm(java_thread);
+ ik = kl.try_to_get_ik(THREAD);
+ }
+ if (ik == nullptr) missing_klasses.append(kl);
+ else {
+ assert(ik->has_stored_fingerprint(), "add -XX:+CalculateClassFingerprint please");
+ if (ik->get_stored_fingerprint() != kl.fingerprint()) {
+ missing_klasses.append(kl);
+ ResourceMark rm(THREAD);
+ log_warning(jbooster, compilation)("Bad fingerprint of \"%s\": client=%lu, server=%lu. session_id=%u.",
+ ik->internal_name(),
+ kl.fingerprint(), ik->get_stored_fingerprint(),
+ ss().session_id());
+ } else {
+ JClientSessionData* session_data = ss().session_data();
+ session_data->add_klass_address((address)(kl.client_klass()),
+ (address)ik,
+ CHECK_(JBErr::THREAD_EXCEPTION));
+ }
+ }
+ }
+ }
+ log_debug(jbooster, compilation)("Missing klasses: %d. session_id=%u.",
+ missing_klasses.length(),
+ ss().session_id());
+ if (!missing_klasses.is_empty()) {
+ JB_RETURN(request_klasses(&missing_klasses, THREAD));
+ }
+ return 0;
+}
+
+int ServerMessageHandler::request_method_data(TRAPS) {
+ ResourceMark rm(THREAD);
+ GrowableArray<address> client_klass_array;
+ GrowableArray<address> server_klass_array;
+ ss().session_data()->klass_array(&client_klass_array,
+ &server_klass_array,
+ THREAD);
+
+ {
+ ArrayWrapper<address> aw(&client_klass_array);
+ JB_RETURN(ss().send_request(MessageType::Profilinginfo, &aw));
+ InstanceKlass** klass_array_base = NULL;
+ if (server_klass_array.length() > 0) {
+ klass_array_base = (InstanceKlass**)server_klass_array.adr_at(0);
+ }
+ ProfileDataCollector data_collector(server_klass_array.length(), klass_array_base);
+ JB_RETURN(ss().recv_response(&data_collector));
+ }
+
+ {
+ JB_RETURN(ss().send_request(MessageType::ArrayKlasses));
+ MessageType msg_type;
+ JB_RETURN(ss().recv_request(msg_type));
+ if (msg_type != MessageType::ArrayKlasses) {
+ JB_RETURN(JBErr::BAD_MSG_TYPE);
+ }
+ ThreadInVMfromNative tivm(THREAD);
+ ArrayWrapper<ArrayKlass> klasses(true);
+ JB_RETURN(ss().parse_request(&klasses));
+ }
+
+ ss().session_data()->klass_pointer_map_to_server(&server_klass_array, THREAD);
+ return 0;
+}
+
+int ServerMessageHandler::request_methods_to_compile(GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ TRAPS) {
+ bool to_compile = true;
+ JB_RETURN(ss().send_request(MessageType::MethodLocators, &to_compile));
+ ArrayWrapper<MethodLocator> ml_aw(false);
+ JB_RETURN(ss().recv_response(&ml_aw));
+ InstanceKlass* last_ik = nullptr;
+ MethodLocator* ml_arr = ml_aw.get_array<MethodLocator>();
+ for (int i = 0; i < ml_aw.size(); ++i) {
+ Method* m = ml_arr[i].get_method();
+ if (m != nullptr) {
+ methods_to_compile->append(m);
+ InstanceKlass* ik = m->method_holder();
+ if (last_ik != ik) {
+ last_ik = ik;
+ klasses_to_compile->append(ik);
+ }
+ }
+ }
+ log_debug(jbooster, compilation)("To compile: klasses=%d, methods=%d, session_id=%u.",
+ klasses_to_compile->length(),
+ methods_to_compile->length(),
+ ss().session_id());
+ return 0;
+}
+
+int ServerMessageHandler::request_methods_not_compile(GrowableArray<Method*>* methods_not_compile, TRAPS) {
+ bool to_compile = false;
+ JB_RETURN(ss().send_request(MessageType::MethodLocators, &to_compile));
+ ArrayWrapper<MethodLocator> ml_aw(false);
+ JB_RETURN(ss().recv_response(&ml_aw));
+ MethodLocator* ml_arr = ml_aw.get_array<MethodLocator>();
+ for (int i = 0; i < ml_aw.size(); ++i) {
+ Method* m = ml_arr[i].get_method();
+ if (m != nullptr) {
+ methods_not_compile->append(m);
+ }
+ }
+ log_debug(jbooster, compilation)("Methods not compile: %d. session_id=%u.",
+ methods_not_compile->length(),
+ ss().session_id());
+ return 0;
+}
+
+int ServerMessageHandler::request_client_cache(MessageType msg_type, JClientCacheState& cache) {
+ if (cache.is_allowed() && !cache.is_cached() && cache.set_being_generated()) {
+ JB_TRY {
+ JB_THROW(ss().send_request(msg_type));
+ FileWrapper file(cache.file_path(), SerializationMode::DESERIALIZE);
+ JB_THROW(file.recv_file(&ss()));
+ if (file.is_null()) cache.set_not_generated();
+ else cache.set_generated();
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ cache.set_not_generated();
+ return JB_ERR;
+ } JB_CATCH_END;
+ }
+ return 0;
+}
+
+int ServerMessageHandler::handle_cache_file_sync_task(TRAPS) {
+ DebugUtils::assert_thread_nonjava_or_in_native();
+
+ JClientProgramData* pd = ss().session_data()->program_data();
+
+ JB_RETURN(request_client_cache(MessageType::CacheClassLoaderResource, pd->clr_cache_state()));
+ JB_RETURN(request_client_cache(MessageType::CacheAggressiveCDS, pd->cds_cache_state()));
+
+ JB_RETURN(ss().send_request(MessageType::EndOfCurrentPhase));
+ return 0;
+}
+
+int ServerMessageHandler::handle_lazy_aot_compilation_task(TRAPS) {
+ DebugUtils::assert_thread_in_native();
+
+ JClientProgramData* pd = ss().session_data()->program_data();
+ JClientCacheState& aot_cache_state = pd->aot_cache_state();
+ ResourceMark rm(THREAD);
+ GrowableArray<InstanceKlass*> klasses_to_compile;
+ GrowableArray<Method*> methods_to_compile;
+ GrowableArray<Method*> methods_not_compile;
+ bool compile_in_current_thread = false;
+
+ JB_TRY {
+ compile_in_current_thread = !aot_cache_state.is_cached() && aot_cache_state.set_being_generated();
+ JB_THROW(ss().send_response(&compile_in_current_thread));
+ if (compile_in_current_thread) {
+ JB_THROW(request_missing_class_loaders(THREAD));
+ JB_THROW(request_missing_klasses(THREAD));
+ JB_THROW(request_methods_to_compile(&klasses_to_compile, &methods_to_compile, THREAD));
+ JB_THROW(request_methods_not_compile(&methods_not_compile, THREAD));
+ JB_THROW(request_method_data(THREAD));
+ JB_THROW(ss().send_request(MessageType::EndOfCurrentPhase));
+ }
+ } JB_TRY_END JB_CATCH_REST() {
+ if (compile_in_current_thread) {
+ aot_cache_state.set_not_generated();
+ }
+ return JB_ERR;
+ } JB_CATCH_END;
+
+ if (compile_in_current_thread) {
+ JB_RETURN(try_to_compile_lazy_aot(&klasses_to_compile,
+ &methods_to_compile,
+ &methods_not_compile,
+ THREAD));
+ } else { // not compile in current thread
+ if (aot_cache_state.is_being_generated()) {
+ log_info(jbooster, compilation)("Skippd as this program is being compiled. session_id=%u.",
+ ss().session_id());
+ } else if (aot_cache_state.is_generated()) {
+ log_info(jbooster, compilation)("Skippd as this program has been compiled. session_id=%u.",
+ ss().session_id());
+ } else {
+ log_error(jbooster, compilation)("Unknown compile state. session_id=%u.",
+ ss().session_id());
+ }
+ }
+ guarantee(!(compile_in_current_thread && aot_cache_state.is_being_generated()), "some logic missing?");
+ return 0;
+}
+
+int ServerMessageHandler::try_to_compile_lazy_aot(GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ TRAPS) {
+ JClientProgramData* pd = ss().session_data()->program_data();
+ JClientCacheState& aot_cache_state = pd->aot_cache_state();
+ if (klasses_to_compile->is_empty()) {
+ aot_cache_state.set_not_generated();
+ log_error(jbooster, compilation)("Failed to compile as the compilation list is empty. session_id=%u.",
+ ss().session_id());
+ return 0;
+ }
+
+ bool successful;
+ int session_id = ss().session_id();
+ const char* file_path = aot_cache_state.file_path();
+
+ ThreadInVMfromNative tiv(THREAD);
+ if (methods_to_compile->is_empty()) {
+ successful = LazyAOT::compile_classes_by_graal(session_id, file_path, klasses_to_compile, use_pgo, THREAD);
+ } else {
+ successful = LazyAOT::compile_methods_by_graal(session_id, file_path, klasses_to_compile,
+ methods_to_compile, methods_not_compile, use_pgo, THREAD);
+ }
+
+ if (successful) {
+ guarantee(!HAS_PENDING_EXCEPTION, "sanity");
+ chmod(file_path, S_IREAD);
+ aot_cache_state.set_generated();
+ log_info(jbooster, compilation)("Successfully comiled %d classes. session_id=%u.",
+ klasses_to_compile->length(),
+ ss().session_id());
+ } else if (HAS_PENDING_EXCEPTION) {
+ aot_cache_state.set_not_generated();
+ LogTarget(Error, jbooster, compilation) lt;
+ if (lt.is_enabled()) {
+ lt.print("Failed to compile %d classes because:", klasses_to_compile->length());
+ }
+ DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
+ if (lt.is_enabled()) {
+ lt.print("session_id=%u.", ss().session_id());
+ }
+ } else {
+ aot_cache_state.set_not_generated();
+ log_error(jbooster, compilation)("Failed to compile %d classes. session_id=%u.",
+ klasses_to_compile->length(),
+ ss().session_id());
+ }
+
+ return 0;
+}
diff --git a/src/hotspot/share/jbooster/server/serverMessageHandler.hpp b/src/hotspot/share/jbooster/server/serverMessageHandler.hpp
new file mode 100644
index 000000000..6d510c6ca
--- /dev/null
+++ b/src/hotspot/share/jbooster/server/serverMessageHandler.hpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_SERVER_SERVERMESSAGEHANDLER_HPP
+#define SHARE_JBOOSTER_SERVER_SERVERMESSAGEHANDLER_HPP
+
+#include "jbooster/net/messageType.hpp"
+#include "memory/allocation.hpp"
+
+class ClassLoaderLocator;
+template <class T> class GrowableArray;
+class InstanceKlass;
+class KlassLocator;
+class Method;
+class ServerStream;
+
+/**
+ * Unlike in ClientMessageHandler, the use of TRAPS is encouraged here because these
+ * logics are all executed in JavaThread and ServerDataManager uses THREAD a lot.
+ */
+class ServerMessageHandler: public StackObj {
+ ServerStream* _server_stream;
+ bool _no_more_client_request;
+
+private:
+ NONCOPYABLE(ServerMessageHandler);
+
+ int request_class_loaders(GrowableArray<ClassLoaderLocator> *loaders, TRAPS);
+ int request_klasses(GrowableArray<KlassLocator>* klasses, TRAPS);
+ int request_missing_class_loaders(TRAPS);
+ int request_missing_klasses(TRAPS);
+ int request_method_data(TRAPS);
+ int request_methods_to_compile(GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ TRAPS);
+ int request_methods_not_compile(GrowableArray<Method*>* methods_not_compile, TRAPS);
+ int request_client_cache(MessageType msg_type, JClientCacheState& cache);
+
+ int try_to_compile_lazy_aot(GrowableArray<InstanceKlass*>* klasses_to_compile,
+ GrowableArray<Method*>* methods_to_compile,
+ GrowableArray<Method*>* methods_not_compile,
+ TRAPS);
+public:
+ ServerMessageHandler(ServerStream* server_stream);
+
+ ServerStream& ss() { return *_server_stream; }
+
+ int handle_a_task_from_client(MessageType msg_type, TRAPS);
+ int handle_tasks_from_client(TRAPS);
+
+ int handle_cache_file_sync_task(TRAPS);
+ int handle_lazy_aot_compilation_task(TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_SERVER_SERVERMESSAGEHANDLER_HPP
diff --git a/src/hotspot/share/jbooster/utilities/concurrentHashMap.hpp b/src/hotspot/share/jbooster/utilities/concurrentHashMap.hpp
new file mode 100644
index 000000000..199d30e53
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/concurrentHashMap.hpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_HPP
+#define SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/concurrentHashTable.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+template <typename K, typename V>
+struct DefaultConcurrentHashMapEvents {
+ static bool is_dead(const K& key, V& value) { return false; }
+
+ static void on_get(const K& key, V& value) {}
+
+ /**
+ * This function will be invoked in two cases:
+ * 1. The element is being removed from the map;
+ * 2. The element is failed to be inserted into the map (lost a race).
+ * Consider both cases when implementing this function.
+ *
+ * @see SymbolTableConfig::free_node
+ * @see ServerDataManager::get_or_create_program
+ */
+ static void on_del(const K& key, V& value) {}
+};
+
+/**
+ * A simple and user-friendly concurrent hash map based on ConcurrentHashTable.
+ *
+ * About the `hash` of the key:
+ * - If K is a scalar type (arithmetic, enum, pointer and nullptr types), then
+ * the hash is calculated by primitive_hash();
+ * - Or please implement the member function `uintx hash() const` for your
+ * custom K class.
+ *
+ * About the `equals` of the key:
+ * - Always use the operater `==`. Please overload the operator `==` in your
+ * custom K class.
+ */
+template <typename K, typename V, MEMFLAGS F, typename Events = DefaultConcurrentHashMapEvents<K, V>>
+class ConcurrentHashMap: public CHeapObj<F> {
+public:
+ class KVNode {
+ const K _key;
+ mutable V _value;
+ public:
+ KVNode(const K& key, const V& value): _key(key), _value(value) {}
+ const K& key() const { return _key; }
+ V& value() const { return _value; }
+ };
+
+private:
+ class KVNodeConfig : public AllStatic {
+ public:
+ typedef KVNode Value;
+ static uintx get_hash(KVNode const& node, bool* is_dead);
+ static void* allocate_node(void* context, size_t size, KVNode const& node);
+ static void free_node(void* context, void* memory, KVNode const& node);
+ };
+
+ typedef ConcurrentHashTable<KVNodeConfig, F> KVNodeTable;
+
+ class Lookup: public StackObj {
+ protected:
+ const K& _key;
+ const uintx _hash;
+ public:
+ Lookup(const K& key);
+ uintx get_hash() const { return _hash; }
+ bool equals(KVNode* kv_node) const;
+ bool is_dead(KVNode* value) const { return Events::is_dead(value->key(), value->value()); }
+ };
+
+ template <typename EVALUATE_FUNC>
+ class RmLookup: public Lookup {
+ EVALUATE_FUNC& _eval_f;
+ public:
+ RmLookup(const K& key, EVALUATE_FUNC& eval_f): Lookup(key), _eval_f(eval_f) {}
+ bool equals(KVNode* kv_node) const;
+ };
+
+ class Get: public StackObj {
+ KVNode* _res;
+ public:
+ Get() : _res(nullptr) {}
+ void operator () (KVNode* kv_node);
+ KVNode* res() const { return _res; }
+ };
+
+private:
+ const float PREF_AVG_LIST_LEN = 2.0F;
+
+ KVNodeTable _table;
+ volatile size_t _items_count;
+
+private:
+ void init_lock(int lock_rank) NOT_DEBUG_RETURN;
+
+ size_t item_added();
+ size_t item_removed();
+ size_t table_size(Thread* thread);
+ float load_factor(Thread* thread);
+ void grow_if_needed(Thread* thread);
+
+public:
+ ConcurrentHashMap();
+ ConcurrentHashMap(int lock_rank);
+ virtual ~ConcurrentHashMap();
+
+ size_t size();
+
+ bool contains(const K& key, Thread* thread);
+
+ /**
+ * Returns the value if found, or returns null.
+ */
+ V* get(const K& key, Thread* thread);
+
+ /**
+ * Returns the input value if the insertion is successful,
+ * or returns the previously inserted value.
+ */
+ V* put_if_absent(const K& key, V& value, Thread* thread);
+
+ /**
+ * Returns whether the key is deleted.
+ * * We cannot return the pointer of the removed value because
+ * it has been deleted.
+ */
+ bool remove(const K& key, Thread* thread);
+
+ template <typename EVALUATE_FUNC>
+ bool remove_if(const K& key, EVALUATE_FUNC& eval_f, Thread* thread);
+
+ template <typename SCAN_FUNC>
+ void for_each(SCAN_FUNC& scan_f, Thread* thread);
+
+ template <typename EVALUATE_FUNC, typename DELETE_FUNC>
+ void bulk_remove_if(EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f, Thread* thread);
+};
+
+#endif // SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_HPP
diff --git a/src/hotspot/share/jbooster/utilities/concurrentHashMap.inline.hpp b/src/hotspot/share/jbooster/utilities/concurrentHashMap.inline.hpp
new file mode 100644
index 000000000..e69da290f
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/concurrentHashMap.inline.hpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_INLINE_HPP
+#define SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_INLINE_HPP
+
+#include <type_traits>
+
+#include "jbooster/utilities/concurrentHashMap.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "utilities/concurrentHashTable.inline.hpp"
+
+template <typename K, typename Enable = void>
+struct ConcurrentKeyHashEquals: public AllStatic {
+ static uintx hash(const K& k) { return k.hash(); }
+ static bool equals(const K& k1, const K& k2) { return k1.equals(k2); }
+};
+
+template <typename K>
+struct ConcurrentKeyHashEquals<K, typename std::enable_if<std::is_scalar<K>::value>::type>: public AllStatic {
+ static uintx hash(const K& key) { return primitive_hash(key); }
+ static bool equals(const K& k1, const K& k2) { return k1 == k2; }
+};
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline uintx ConcurrentHashMap<K, V, F, Events>::KVNodeConfig::get_hash(KVNode const& kv_node, bool* is_dead) {
+ *is_dead = Events::is_dead(kv_node.key(), kv_node.value());
+ if (*is_dead) return 0;
+ return ConcurrentKeyHashEquals<K>::hash(kv_node.key());
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline void* ConcurrentHashMap<K, V, F, Events>::KVNodeConfig::allocate_node(void* context,
+ size_t size,
+ KVNode const& kv_node) {
+ static_cast<ConcurrentHashMap<K, V, F, Events>*>(context)->item_added();
+ return AllocateHeap(size, F);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline void ConcurrentHashMap<K, V, F, Events>::KVNodeConfig::free_node(void* context,
+ void* memory,
+ KVNode const& kv_node) {
+ Events::on_del(kv_node.key(), kv_node.value());
+ kv_node.~KVNode();
+ FreeHeap(memory);
+ static_cast<ConcurrentHashMap<K, V, F, Events>*>(context)->item_removed();
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline ConcurrentHashMap<K, V, F, Events>::Lookup::Lookup(const K& key):
+ _key(key),
+ _hash(ConcurrentKeyHashEquals<K>::hash(key)) {}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline bool ConcurrentHashMap<K, V, F, Events>::Lookup::equals(KVNode* kv_node) const {
+ if (ConcurrentKeyHashEquals<K>::equals(_key, kv_node->key())) {
+ Events::on_get(kv_node->key(), kv_node->value());
+ return true;
+ }
+ return false;
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+template <typename EVALUATE_FUNC>
+inline bool ConcurrentHashMap<K, V, F, Events>::RmLookup<EVALUATE_FUNC>::equals(KVNode* kv_node) const {
+ // Do not invoke Events::on_get() here.
+ return ConcurrentKeyHashEquals<K>::equals(Lookup::_key, kv_node->key())
+ && _eval_f(kv_node->key(), kv_node->value());
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline void ConcurrentHashMap<K, V, F, Events>::Get::operator () (KVNode* kv_node) {
+ _res = kv_node;
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline ConcurrentHashMap<K, V, F, Events>::ConcurrentHashMap(): _table(8), _items_count(0) {
+ _table._context = static_cast<void*>(this);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline ConcurrentHashMap<K, V, F, Events>::ConcurrentHashMap(int lock_rank): _table(8), _items_count(0) {
+ init_lock(lock_rank);
+ _table._context = static_cast<void*>(this);
+}
+
+#ifdef ASSERT
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline void ConcurrentHashMap<K, V, F, Events>::init_lock(int lock_rank) {
+ if (_table._resize_lock->rank() == lock_rank) return;
+ delete _table._resize_lock;
+ _table._resize_lock = new Mutex(lock_rank, "ConcurrentHashMap", true,
+ Mutex::_safepoint_check_never);
+}
+#endif // ASSERT
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline ConcurrentHashMap<K, V, F, Events>::~ConcurrentHashMap() {}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline size_t ConcurrentHashMap<K, V, F, Events>::item_added() {
+ return Atomic::add(&_items_count, (size_t) 1);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline size_t ConcurrentHashMap<K, V, F, Events>::item_removed() {
+ return Atomic::add(&_items_count, (size_t) -1);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline size_t ConcurrentHashMap<K, V, F, Events>::table_size(Thread* thread) {
+ return ((size_t) 1) << _table.get_size_log2(thread);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline float ConcurrentHashMap<K, V, F, Events>::load_factor(Thread* thread) {
+ return float(_items_count) / float(table_size(thread));
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline void ConcurrentHashMap<K, V, F, Events>::grow_if_needed(Thread* thread) {
+ if (load_factor(thread) > PREF_AVG_LIST_LEN && !_table.is_max_size_reached()) {
+ _table.grow(thread);
+ }
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline size_t ConcurrentHashMap<K, V, F, Events>::size() {
+ return Atomic::load_acquire(&_items_count);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline bool ConcurrentHashMap<K, V, F, Events>::contains(const K& key, Thread* thread) {
+ Lookup lookup(key);
+ Get get;
+ bool grow_hint = false;
+
+ return _table.get(thread, lookup, get, &grow_hint);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline V* ConcurrentHashMap<K, V, F, Events>::get(const K& key, Thread* thread) {
+ Lookup lookup(key);
+ Get get;
+ bool grow_hint = false;
+
+ V* res = nullptr;
+
+ if (_table.get(thread, lookup, get, &grow_hint)) {
+ KVNode& kv_node = *get.res();
+ res = &kv_node.value();
+ }
+
+ return res;
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline V* ConcurrentHashMap<K, V, F, Events>::put_if_absent(const K& key, V& value, Thread* thread) {
+ Lookup lookup(key);
+ Get get;
+ bool grow_hint = false;
+ bool clean_hint = false;
+
+ V* res = nullptr;
+
+ do {
+ if (_table.insert(thread, lookup, KVNode(key, value), &grow_hint, &clean_hint)) {
+ res = &value;
+ break;
+ }
+
+ if (_table.get(thread, lookup, get, &grow_hint)) {
+ KVNode& kv_node = *get.res();
+ res = &kv_node.value();
+ break;
+ }
+ } while (true);
+
+ if (grow_hint) {
+ grow_if_needed(thread);
+ }
+
+ return res;
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+inline bool ConcurrentHashMap<K, V, F, Events>::remove(const K& key, Thread* thread) {
+ Lookup lookup(key);
+ return _table.remove(thread, lookup);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+template <typename EVALUATE_FUNC>
+bool ConcurrentHashMap<K, V, F, Events>::remove_if(const K& key, EVALUATE_FUNC& eval_f, Thread* thread) {
+ RmLookup<EVALUATE_FUNC> lookup(key, eval_f);
+ return _table.remove(thread, lookup);
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+template <typename SCAN_FUNC>
+inline void ConcurrentHashMap<K, V, F, Events>::for_each(SCAN_FUNC& scan_f, Thread* thread) {
+ // Unlike ConcurrentHashTable, ConcurrentHashMap never do scan without resize lock.
+ // So we don't have to assert `!SafepointSynchronize::is_at_safepoint()`.
+ assert(_table._resize_lock_owner != thread, "Re-size lock held");
+ _table.lock_resize_lock(thread);
+ _table.do_scan_locked(thread, scan_f);
+ _table.unlock_resize_lock(thread);
+ assert(_table._resize_lock_owner != thread, "Re-size lock held");
+}
+
+template <typename K, typename V, MEMFLAGS F, typename Events>
+template <typename EVALUATE_FUNC, typename DELETE_FUNC>
+inline void ConcurrentHashMap<K, V, F, Events>::bulk_remove_if(EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f, Thread* thread) {
+ // Unlike ConcurrentHashTable, ConcurrentHashMap never do scan without resize lock.
+ // So we don't have to assert `!SafepointSynchronize::is_at_safepoint()`.
+ assert(_table._resize_lock_owner != thread, "Re-size lock held");
+ _table.lock_resize_lock(thread);
+ _table.do_bulk_delete_locked(thread, eval_f, del_f);
+ _table.unlock_resize_lock(thread);
+ assert(_table._resize_lock_owner != thread, "Re-size lock held");
+}
+
+#endif // SHARE_JBOOSTER_UTILITIES_CONCURRENTHASHMAP_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/utilities/debugUtils.cpp b/src/hotspot/share/jbooster/utilities/debugUtils.cpp
new file mode 100644
index 000000000..44c2fcca0
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/debugUtils.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "jbooster/utilities/debugUtils.inline.hpp"
+
+#ifdef ASSERT
+void DebugUtils::assert_thread_in_vm() {
+ JavaThread* THREAD = JavaThread::current();
+ assert(THREAD->thread_state() == _thread_in_vm, "sanity");
+}
+
+void DebugUtils::assert_thread_in_native() {
+ JavaThread* THREAD = JavaThread::current();
+ assert(THREAD->thread_state() == _thread_in_native, "sanity");
+}
+
+void DebugUtils::assert_thread_nonjava_or_in_native() {
+ Thread* THREAD = Thread::current();
+ assert(!THREAD->is_Java_thread() || THREAD->as_Java_thread()->thread_state() == _thread_in_native, "sanity");
+}
+#endif
diff --git a/src/hotspot/share/jbooster/utilities/debugUtils.hpp b/src/hotspot/share/jbooster/utilities/debugUtils.hpp
new file mode 100644
index 000000000..611a31e7f
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/debugUtils.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_HPP
+#define SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_HPP
+
+#include "logging/log.hpp"
+#include "memory/allStatic.hpp"
+#include "utilities/exceptions.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class DebugUtils: public AllStatic {
+private:
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define PLATFORM_FUNC_SIG __PRETTY_FUNCTION__
+ static const int _func_sig_prefix_len = 53; // "static const char* DebugUtils::type_name() [with T = "
+ static const int _func_sig_suffix_len = 1; // "]"
+#elif defined(_MSC_VER)
+#define PLATFORM_FUNC_SIG __FUNCSIG__
+ static const int _func_sig_prefix_len = 0; // not done yet
+ static const int _func_sig_suffix_len = 1; // not done yet
+#else
+#define PLATFORM_FUNC_SIG __func__
+ static const int _func_sig_prefix_len = 0; // not done yet
+ static const int _func_sig_suffix_len = 0; // not done yet
+#endif
+
+public:
+ static void assert_thread_in_vm() NOT_DEBUG_RETURN;
+ static void assert_thread_in_native() NOT_DEBUG_RETURN;
+ static void assert_thread_nonjava_or_in_native() NOT_DEBUG_RETURN;
+
+ template <typename T>
+ static const char* type_name();
+
+ template <LogLevelType level, LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG,
+ LogTagType T3 = LogTag::__NO_TAG, LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
+ static void clear_java_exception_and_print_stack_trace(LogTargetImpl<level, T0, T1, T2, T3, T4, GuardTag>& lt, TRAPS);
+};
+
+#endif // SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_HPP
diff --git a/src/hotspot/share/jbooster/utilities/debugUtils.inline.hpp b/src/hotspot/share/jbooster/utilities/debugUtils.inline.hpp
new file mode 100644
index 000000000..9c4e58ec4
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/debugUtils.inline.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_INLINE_HPP
+#define SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_INLINE_HPP
+
+#include "classfile/javaClasses.inline.hpp"
+#include "jbooster/utilities/debugUtils.hpp"
+#include "logging/logStream.hpp"
+#include "memory/allocation.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "utilities/vmError.hpp"
+
+/**
+ * Attention: the type name string is allocated on the heap!
+ */
+template <typename T>
+ const char* DebugUtils::type_name() {
+ const char* sig = PLATFORM_FUNC_SIG;
+ const int sig_len = strlen(sig);
+ const int res_len = sig_len - _func_sig_prefix_len - _func_sig_suffix_len;
+ char* res = NEW_C_HEAP_ARRAY(char, res_len + 1, mtJBooster);
+ memcpy(res, sig + _func_sig_prefix_len, res_len);
+ res[res_len] = '\0';
+ return res;
+}
+
+template <LogLevelType level, LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4, LogTagType GuardTag>
+ void DebugUtils::clear_java_exception_and_print_stack_trace(LogTargetImpl<level, T0, T1, T2, T3, T4, GuardTag>& lt, TRAPS) {
+ if (!HAS_PENDING_EXCEPTION) return;
+
+ if (lt.is_enabled()) {
+ ResourceMark rm(THREAD);
+ Handle ex(THREAD, THREAD->pending_exception());
+ CLEAR_PENDING_EXCEPTION;
+ LogStream ls(lt);
+ assert(THREAD->thread_state() == _thread_in_vm, "sanity");
+ java_lang_Throwable::print_stack_trace(ex, &ls);
+ } else {
+ CLEAR_PENDING_EXCEPTION;
+ }
+}
+
+#endif // SHARE_JBOOSTER_UTILITIES_DEBUGUTILS_INLINE_HPP
diff --git a/src/hotspot/share/jbooster/utilities/fileUtils.cpp b/src/hotspot/share/jbooster/utilities/fileUtils.cpp
new file mode 100644
index 000000000..f19bf8fb3
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/fileUtils.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serializationWrappers.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/os.inline.hpp"
+
+static const char* get_home_path() {
+ const char* res = ::getenv("HOME");
+ if ((res == nullptr) || (*res == '\0')) {
+ struct passwd* pwd = getpwuid(geteuid());
+ if (pwd != nullptr) res = pwd->pw_dir;
+ }
+ guarantee(res != nullptr && *res != '\0', "failed to get home");
+ return res;
+}
+
+const char* FileUtils::separator() {
+ return os::file_separator();
+}
+
+char FileUtils::separator_char() {
+ assert(strlen(separator()) == 1, "sanity");
+ return separator()[0];
+}
+
+const char* FileUtils::home_path() {
+ static const char* home_dir = get_home_path();
+ return home_dir;
+}
+
+bool FileUtils::exists(const char* path) {
+ struct stat st = {0};
+ return os::stat(path, &st) == 0;
+}
+
+bool FileUtils::is_file(const char* path) {
+ struct stat st = {0};
+ if (os::stat(path, &st) != 0) return false;
+ return S_ISREG(st.st_mode);
+}
+
+bool FileUtils::is_dir(const char* path) {
+ struct stat st = {0};
+ if (os::stat(path, &st) != 0) return false;
+ return S_ISDIR(st.st_mode);
+}
+
+uint64_t FileUtils::modify_time(const char* path) {
+ struct stat st = {0};
+ if (os::stat(path, &st) != 0) return 0;
+ return st.st_mtime;
+}
+
+bool FileUtils::mkdir(const char* path) {
+ if (::mkdir(path, 0777) == OS_ERR) {
+ if (errno == EEXIST) return false;
+ else fatal("Cannot mkdir: %s. Err: %s", path, os::strerror(errno));
+ }
+ return true;
+}
+
+bool FileUtils::mkdirs(const char* path) {
+ int len = strlen(path) + 1;
+ // We don't use NEW_RESOURCE_ARRAY here as Thread::current() may
+ // not be initialized yet.
+ char* each_path = NEW_C_HEAP_ARRAY(char, len, mtJBooster);
+ memcpy((void*)each_path, path, len);
+
+ const char* sp = separator();
+ int sp_len = strlen(sp);
+ for (char* p = each_path + 1, *end = each_path + len; p < end; ++p) {
+ if (strncmp(p, sp, sp_len) != 0) continue;
+ *p = '\0';
+ if (!exists(each_path)) mkdir(each_path);
+ *p = *sp;
+ }
+ FREE_C_HEAP_ARRAY(char, each_path);
+ return mkdir(path);
+}
+
+bool FileUtils::rename(const char* path_from, const char* path_to) {
+ return ::rename(path_from, path_to) == 0;
+}
+
+bool FileUtils::move(const char* path_from, const char* path_to) {
+ return rename(path_from, path_to);
+}
+
+bool FileUtils::remove(const char* path) {
+ return ::remove(path) == 0;
+}
+
+bool FileUtils::is_same(const char* path1, const char* path2) {
+ bool res = false;
+ char* buf1 = nullptr;
+ char* buf2 = nullptr;
+ int fd1 = os::open(path1, O_BINARY | O_RDONLY, 0);
+ int fd2 = os::open(path2, O_BINARY | O_RDONLY, 0);
+ do {
+ if (fd1 < 0 || fd2 < 0) break;
+ int64_t size1 = os::lseek(fd1, 0, SEEK_END);
+ int64_t size2 = os::lseek(fd2, 0, SEEK_END);
+ if (size1 != size2) break;
+ int64_t size = size1;
+ os::lseek(fd1, 0, SEEK_SET);
+ os::lseek(fd2, 0, SEEK_SET);
+ // We don't use NEW_RESOURCE_ARRAY here as Thread::current() may
+ // not be initialized yet.
+ buf1 = NEW_C_HEAP_ARRAY(char, (size_t) size, mtJBooster);
+ buf2 = NEW_C_HEAP_ARRAY(char, (size_t) size, mtJBooster);
+ size1 = (int64_t) os::read(fd1, buf1, size);
+ size2 = (int64_t) os::read(fd2, buf2, size);
+ guarantee(size1 == size && size2 == size, "sanity");
+ res = memcmp(buf1, buf2, size) == 0;
+ } while (false);
+ if (fd1 >= 0) os::close(fd1);
+ if (fd2 >= 0) os::close(fd2);
+ if (buf1 != nullptr) {
+ FREE_C_HEAP_ARRAY(char, buf1);
+ }
+ if (buf2 != nullptr) {
+ FREE_C_HEAP_ARRAY(char, buf2);
+ }
+ return res;
+}
+
+bool FileUtils::is_same(const char* path, const char* content, int64_t size) {
+ bool res = false;
+ char* buf = nullptr;
+ int fd = os::open(path, O_BINARY | O_RDONLY, 0);
+ do {
+ if (fd < 0) break;
+ int64_t fsize = os::lseek(fd, 0, SEEK_END);
+ if (fsize != size) break;
+ os::lseek(fd, 0, SEEK_SET);
+ // We don't use NEW_RESOURCE_ARRAY here as Thread::current() may
+ // not be initialized yet.
+ buf = NEW_C_HEAP_ARRAY(char, (size_t) size, mtJBooster);
+ fsize = (int64_t) os::read(fd, buf, size);
+ guarantee(fsize == size, "sanity");
+ res = memcmp(content, buf, size) == 0;
+ } while (false);
+ if (fd >= 0) os::close(fd);
+ if (buf != nullptr) {
+ FREE_C_HEAP_ARRAY(char, buf);
+ }
+ return res;
+}
diff --git a/src/hotspot/share/jbooster/utilities/fileUtils.hpp b/src/hotspot/share/jbooster/utilities/fileUtils.hpp
new file mode 100644
index 000000000..2b5734754
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/fileUtils.hpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_FILEUTILS_HPP
+#define SHARE_JBOOSTER_UTILITIES_FILEUTILS_HPP
+
+#ifdef LINUX
+#include <dirent.h>
+#endif // LINUX
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// @see src/hotspot/share/memory/filemap.cpp
+#ifndef O_BINARY // if defined (Win32) use binary files.
+#define O_BINARY 0 // otherwise do nothing.
+#endif
+
+class FileUtils: AllStatic {
+public:
+ static const char* separator();
+ static char separator_char();
+ static const char* home_path();
+ static bool exists(const char* path);
+ static bool is_file(const char* path);
+ static bool is_dir(const char* path);
+ static uint64_t modify_time(const char* path);
+ static bool mkdir(const char* path);
+ static bool mkdirs(const char* path);
+ static bool rename(const char* path_from, const char* path_to);
+ static bool move(const char* path_from, const char* path_to);
+ static bool remove(const char* path);
+ static bool is_same(const char* path1, const char* path2);
+ static bool is_same(const char* path, const char* content, int64_t size);
+
+ class ListDir: public StackObj {
+ const char* _path;
+
+#ifdef LINUX
+ DIR* _ds;
+ dirent* _cur;
+#endif // LINUX
+
+ public:
+ ListDir(const char* path);
+ ~ListDir();
+
+ bool next();
+
+ const char* name();
+ bool is_file();
+ bool is_dir();
+ };
+};
+
+#endif // SHARE_JBOOSTER_UTILITIES_FILEUTILS_HPP
diff --git a/src/hotspot/share/jbooster/utilities/scalarHashMap.hpp b/src/hotspot/share/jbooster/utilities/scalarHashMap.hpp
new file mode 100644
index 000000000..30b9ab605
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/scalarHashMap.hpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_HPP
+#define SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_HPP
+
+#include "utilities/hashtable.hpp"
+
+/**
+ * A simple hash map that does not manage the lifecycle of the storage elements.
+ */
+template <typename K, typename V, MEMFLAGS F = mtJBooster>
+class ScalarHashMap: protected KVHashtable<K, V, F> {
+ static_assert(std::is_scalar<K>::value, "K must be scalar because we use primitive_hash() and primitive_equals()");
+ static_assert(std::is_scalar<V>::value, "V must be scalar because we don't manage its lifecycle at all");
+
+ using Entry = typename KVHashtable<K, V, F>::KVHashtableEntry;
+
+ static const int _resize_load_trigger = 4;
+
+ bool resize_if_needed();
+
+public:
+ ScalarHashMap(int table_size = 107);
+ virtual ~ScalarHashMap();
+
+ V* get(K key) const {
+ return KVHashtable<K, V, F>::lookup(key);
+ }
+
+ V* add_if_absent(K key, V value, bool* p_created) {
+ V* res = KVHashtable<K, V, F>::add_if_absent(key, value, p_created);
+ if (*p_created) {
+ resize_if_needed();
+ }
+ return res;
+ }
+
+ void clear();
+
+ int size() const { return KVHashtable<K, V, F>::number_of_entries(); }
+};
+
+/**
+ * A simple hash set that does not manage the lifecycle of the storage elements.
+ * We do not choose to extend Hashtable because it uses hashtable.cpp, which leads
+ * to its poor availability (because of template).
+ */
+template <class T, MEMFLAGS F = mtJBooster>
+class ScalarHashSet: protected ScalarHashMap<T, bool, F> {
+
+public:
+ ScalarHashSet(int table_size = 107): ScalarHashMap<T, bool, F>(table_size) {}
+
+ /**
+ * true: existing; false: not found
+ */
+ bool has(T o) const {
+ return ScalarHashMap<T, bool, F>::get(o) != nullptr;
+ }
+
+ /**
+ * true: added; false: existing
+ */
+ bool add(T o) {
+ bool created = false;
+ ScalarHashMap<T, bool, F>::add_if_absent(o, true, &created);
+ return created;
+ }
+
+ void clear() { ScalarHashMap<T, bool, F>::clear(); }
+
+ int size() const { return ScalarHashMap<T, bool, F>::number_of_entries(); }
+};
+
+#endif // SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_HPP
diff --git a/src/hotspot/share/jbooster/utilities/scalarHashMap.inline.hpp b/src/hotspot/share/jbooster/utilities/scalarHashMap.inline.hpp
new file mode 100644
index 000000000..4564f08e9
--- /dev/null
+++ b/src/hotspot/share/jbooster/utilities/scalarHashMap.inline.hpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+#ifndef SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_INLINE_HPP
+#define SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_INLINE_HPP
+
+#include "jbooster/utilities/scalarHashMap.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/hashtable.inline.hpp"
+
+template <typename K, typename V, MEMFLAGS F>
+inline ScalarHashMap<K, V, F>::ScalarHashMap(int table_size): KVHashtable<K, V, F>(table_size) {}
+
+template <typename K, typename V, MEMFLAGS F>
+inline ScalarHashMap<K, V, F>::~ScalarHashMap() {
+ clear();
+ // Base class ~BasicHashtable deallocates the buckets.
+ // Note that ~BasicHashtable is not virtual. Be careful.
+}
+
+template <typename K, typename V, MEMFLAGS F>
+inline void ScalarHashMap<K, V, F>::clear() {
+ for (int i = 0; i < KVHashtable<K, V, F>::table_size(); ++i) {
+ for (Entry* e = KVHashtable<K, V, F>::bucket(i); e != nullptr;) {
+ Entry* cur = e;
+ // read next before freeing.
+ e = e->next();
+ KVHashtable<K, V, F>::free_entry(cur);
+ }
+ Entry** p = (Entry**) KVHashtable<K, V, F>::bucket_addr(i);
+ *p = nullptr; // clear out buckets.
+ }
+ assert((KVHashtable<K, V, F>::number_of_entries()) == 0, "sanity");
+}
+
+/**
+ * BasicHashtable<F>::maybe_grow is not good enough.
+ */
+template <typename K, typename V, MEMFLAGS F>
+inline bool ScalarHashMap<K, V, F>::resize_if_needed() {
+ if (KVHashtable<K, V, F>::number_of_entries() > (_resize_load_trigger * KVHashtable<K, V, F>::table_size())) {
+ int desired_size = KVHashtable<K, V, F>::calculate_resize(false);
+ if (desired_size == KVHashtable<K, V, F>::table_size()) {
+ return false;
+ }
+ assert(desired_size != 0, "sanity");
+ return KVHashtable<K, V, F>::resize(desired_size);
+ }
+ return false;
+}
+
+#endif // SHARE_JBOOSTER_UTILITIES_SCALARHASHMAP_INLINE_HPP
diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp
index 7f31b5e66..d8ae64bc8 100644
--- a/src/hotspot/share/logging/logTag.hpp
+++ b/src/hotspot/share/logging/logTag.hpp
@@ -36,6 +36,7 @@
LOG_TAG(age) \
LOG_TAG(alloc) \
LOG_TAG(annotation) \
+ JBOOSTER_ONLY(LOG_TAG(aot)) \
LOG_TAG(arguments) \
LOG_TAG(attach) \
LOG_TAG(barrier) \
@@ -91,6 +92,7 @@
LOG_TAG(install) \
LOG_TAG(interpreter) \
LOG_TAG(itables) \
+ JBOOSTER_ONLY(LOG_TAG(jbooster)) \
LOG_TAG(jfr) \
LOG_TAG(jit) \
LOG_TAG(jni) \
@@ -154,10 +156,12 @@
LOG_TAG(reloc) \
LOG_TAG(remset) \
LOG_TAG(resolve) \
+ JBOOSTER_ONLY(LOG_TAG(rpc)) \
LOG_TAG(safepoint) \
LOG_TAG(sampling) \
LOG_TAG(scavenge) \
LOG_TAG(sealed) \
+ JBOOSTER_ONLY(LOG_TAG(serialization)) \
LOG_TAG(setting) \
LOG_TAG(smr) \
LOG_TAG(stackbarrier) \
diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp
index e881b577b..4efce9d97 100644
--- a/src/hotspot/share/memory/allocation.hpp
+++ b/src/hotspot/share/memory/allocation.hpp
@@ -146,6 +146,7 @@ class AllocatedObj {
f(mtMetaspace, "Metaspace") \
f(mtStringDedup, "String Deduplication") \
f(mtObjectMonitor, "Object Monitors") \
+ JBOOSTER_ONLY(f(mtJBooster, "JBooster")) \
f(mtNone, "Unknown") \
//end
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index fdee66a88..47c4d31c1 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -4244,6 +4244,18 @@ void InstanceKlass::log_to_classlist() const {
#endif // INCLUDE_CDS
}
+#if INCLUDE_JBOOSTER
+static Klass* _proxy_klass = nullptr;
+bool InstanceKlass::is_dynamic_proxy() const {
+ if (_proxy_klass == nullptr) {
+ TempNewSymbol sym = SymbolTable::new_symbol("java/lang/reflect/Proxy", 23);
+ _proxy_klass = SystemDictionary::find_instance_klass(sym, Handle(), Handle());
+ }
+ if (_proxy_klass == nullptr) return false;
+ return is_subclass_of(_proxy_klass);
+}
+#endif // INCLUDE_JBOOSTER
+
// Make a step iterating over the class hierarchy under the root class.
// Skips subclasses if requested.
void ClassHierarchyIterator::next() {
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 23b773d01..c111a6ab5 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -1274,6 +1274,16 @@ public:
void print_class_load_logging(ClassLoaderData* loader_data,
const ModuleEntry* module_entry,
const ClassFileStream* cfs) const;
+
+#if INCLUDE_JBOOSTER
+ bool is_dynamic_proxy() const;
+
+ // [JBOOSTER TODO] aot
+ bool should_store_fingerprint() const { return true; }
+ bool has_stored_fingerprint() const { return true; }
+ uint64_t get_stored_fingerprint() const { return 0u; }
+ void store_fingerprint(uint64_t fingerprint) {}
+#endif // INCLUDE_JBOOSTER
};
// for adding methods
diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp
index 93fcbd139..d3e77ba70 100644
--- a/src/hotspot/share/oops/method.hpp
+++ b/src/hotspot/share/oops/method.hpp
@@ -91,6 +91,10 @@ class Method : public Metadata {
_intrinsic_candidate = 1 << 5,
_reserved_stack_access = 1 << 6,
_scoped = 1 << 7
+#if INCLUDE_JBOOSTER
+ ,
+ _rewrite_invokehandle = 1 << 8
+#endif // INCLUDE_JBOOSTER
};
mutable u2 _flags;
@@ -992,6 +996,17 @@ public:
// Inlined elements
address* native_function_addr() const { assert(is_native(), "must be native"); return (address*) (this+1); }
address* signature_handler_addr() const { return native_function_addr() + 1; }
+
+#if INCLUDE_JBOOSTER
+ public:
+ bool is_rewrite_invokehandle() {
+ return (_flags & _rewrite_invokehandle) != 0;
+ }
+
+ void set_rewrite_invokehandle(bool x) {
+ _flags = x ? (_flags | _rewrite_invokehandle) : (_flags & ~_rewrite_invokehandle);
+ }
+#endif // INCLUDE_JBOOSTER
};
diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp
index 101081748..266c48a5e 100644
--- a/src/hotspot/share/oops/methodData.cpp
+++ b/src/hotspot/share/oops/methodData.cpp
@@ -43,6 +43,9 @@
#include "runtime/signature.hpp"
#include "utilities/align.hpp"
#include "utilities/copy.hpp"
+#if INCLUDE_JBOOSTER
+#include "jbooster/server/serverDataManager.hpp"
+#endif // INCLUDE_JBOOSTER
// ==================================================================
// DataLayout
@@ -366,6 +369,46 @@ void ReturnTypeEntry::print_data_on(outputStream* st) const {
st->cr();
}
+#if INCLUDE_JBOOSTER
+
+static void collect_a_klass(GrowableArray<InstanceKlass*>* ik_array,
+ GrowableArray<ArrayKlass*>* ak_array,
+ Klass* k) {
+ if (k != NULL) {
+ if (k->is_array_klass()) {
+ ArrayKlass* ak = ArrayKlass::cast(k);
+ ak_array->append(ak);
+ return;
+ }
+ guarantee(k->is_instance_klass(), "sanity");
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ if (ik->is_hidden() || ik->is_dynamic_proxy()) return;
+ Klass* loader_klass = ik->class_loader_data()->class_loader_klass();
+ if (loader_klass != NULL &&
+ loader_klass->is_subtype_of(vmClasses::reflect_DelegatingClassLoader_klass())) {
+ return;
+ }
+ ik_array->append(ik);
+ }
+}
+
+void ReturnTypeEntry::klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) {
+ intptr_t p = type();
+ Klass* client_klass = (Klass*)klass_part(p);
+ if (client_klass != NULL) {
+ Klass* server_klass = session_data->server_klass_address((address)client_klass, thread);
+ set_type(with_status(server_klass, p));
+ }
+}
+
+void ReturnTypeEntry::collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) {
+ intptr_t p = type();
+ Klass* k = (Klass*)klass_part(p);
+ collect_a_klass(ik_array, ak_array, k);
+}
+
+#endif // INCLUDE_JBOOSTER
+
void CallTypeData::print_data_on(outputStream* st, const char* extra) const {
CounterData::print_data_on(st, extra);
if (has_arguments()) {
@@ -441,6 +484,38 @@ void ReceiverTypeData::print_data_on(outputStream* st, const char* extra) const
print_receiver_data_on(st);
}
+#if INCLUDE_JBOOSTER
+
+void ReceiverTypeData::clean_untrusted_entries() {
+ for (uint row = 0; row < row_limit(); row++) {
+ set_receiver(row, (Klass*)NULL);
+ }
+}
+
+void ReceiverTypeData::klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) {
+ uint row = 0;
+ for (row = 0; row < row_limit(); row++) {
+ Klass* client_klass = receiver_no_check(row);
+ if (client_klass != NULL) {
+ Klass* server_klass = session_data->server_klass_address((address)client_klass, thread);
+ if (server_klass == nullptr) break;
+ set_receiver(row, server_klass);
+ }
+ }
+ if (row != row_limit()) {
+ clean_untrusted_entries();
+ }
+}
+
+void ReceiverTypeData::collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) {
+ for (uint row = 0; row < row_limit(); row++) {
+ Klass* k = receiver_no_check(row);
+ collect_a_klass(ik_array, ak_array, k);
+ }
+}
+
+#endif // INCLUDE_JBOOSTER
+
void VirtualCallData::print_data_on(outputStream* st, const char* extra) const {
print_shared(st, "VirtualCallData", extra);
print_receiver_data_on(st);
@@ -628,6 +703,41 @@ int ParametersTypeData::compute_cell_count(Method* m) {
return 0;
}
+#if INCLUDE_JBOOSTER
+
+void TypeStackSlotEntries::clean_untrusted_entries() {
+ for (int i = 0; i < _number_of_entries; i++) {
+ intptr_t p = type(i);
+ set_type(i, with_status((Klass*)NULL, p));
+ }
+}
+
+void TypeStackSlotEntries::klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) {
+ int index = 0;
+ for (int index = 0; index < _number_of_entries; index++) {
+ intptr_t p = type(index);
+ Klass* client_klass = (Klass*)klass_part(p);
+ if (client_klass != NULL) {
+ Klass* server_klass = session_data->server_klass_address((address)client_klass, thread);
+ if (server_klass == nullptr) break;
+ set_type(index, with_status(server_klass, p));
+ }
+ }
+ if (index != _number_of_entries) {
+ clean_untrusted_entries();
+ }
+}
+
+void TypeStackSlotEntries::collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) {
+ for (int i = 0; i < _number_of_entries; i++) {
+ intptr_t p = type(i);
+ Klass* k = (Klass*)klass_part(p);
+ collect_a_klass(ik_array, ak_array, k);
+ }
+}
+
+#endif // INCLUDE_JBOOSTER
+
void ParametersTypeData::post_initialize(BytecodeStream* stream, MethodData* mdo) {
_parameters.post_initialize(mdo->method()->signature(), !mdo->method()->is_static(), true);
}
@@ -1749,7 +1859,7 @@ void MethodData::clean_extra_data(CleanExtraDataClosure* cl) {
SpeculativeTrapData* data = new SpeculativeTrapData(dp);
Method* m = data->method();
assert(m != NULL, "should have a method");
- if (!cl->is_live(m)) {
+ if (!cl->is_live(m) JBOOSTER_ONLY(|| AsJBooster)) {
// "shift" accumulates the number of cells for dead
// SpeculativeTrapData entries that have been seen so
// far. Following entries must be shifted left by that many
@@ -1832,3 +1942,46 @@ void MethodData::clean_weak_method_links() {
clean_extra_data(&cl);
verify_extra_data_clean(&cl);
}
+
+#if INCLUDE_JBOOSTER
+/**
+ * The "bool* ignored" is just to distinguish it from another constructor.
+ */
+MethodData::MethodData(const methodHandle& method, const bool* ignored)
+ : _method(method()),
+ _extra_data_lock(Mutex::leaf, "MDO extra data lock"),
+ _compiler_counters(),
+ _parameters_type_data_di(parameters_uninitialized) {
+ // part of initialize()
+ Thread* thread = Thread::current();
+ NoSafepointVerifier no_safepoint;
+ ResourceMark rm(thread);
+ init();
+ set_creation_mileage(mileage_of(method()));
+ int data_size = 0;
+ int empty_bc_count = 0;
+ _data[0] = 0;
+}
+
+MethodData* MethodData::create_instance_for_jbooster(Method* method, int size, char* mem, TRAPS) {
+ ClassLoaderData* loader_data = method->method_holder()->class_loader_data();
+ MethodData* res = new (loader_data, size, MetaspaceObj::MethodDataType, THREAD)
+ MethodData(methodHandle(THREAD, method), (const bool*) nullptr);
+
+ // backup
+ char lock_bak[sizeof(res->_extra_data_lock)];
+ memcpy((void*) lock_bak, &res->_extra_data_lock, sizeof(res->_extra_data_lock));
+ FailedSpeculation* fs_bak = res->_failed_speculations;
+
+ // memcpy
+ int start = in_bytes(byte_offset_of(MethodData, _method)) + sizeof(res->_method);
+ memcpy((void*) (((char*) res) + start), mem + start, size);
+
+ // restore
+ memcpy((void*) &res->_extra_data_lock, lock_bak, sizeof(res->_extra_data_lock));
+ res->_failed_speculations = fs_bak;
+ res->set_size(size);
+
+ return res;
+}
+#endif // INCLUDE_JBOOSTER
diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp
index 55967e9ee..a83b373a8 100644
--- a/src/hotspot/share/oops/methodData.hpp
+++ b/src/hotspot/share/oops/methodData.hpp
@@ -35,6 +35,10 @@
#include "utilities/align.hpp"
#include "utilities/copy.hpp"
+#if INCLUDE_JBOOSTER
+class JClientSessionData;
+#endif // INCLUDE_JBOOSTER
+
class BytecodeStream;
// The MethodData object collects counts and other profile information
@@ -476,6 +480,11 @@ public:
void print_shared(outputStream* st, const char* name, const char* extra) const;
void tab(outputStream* st, bool first = false) const;
+
+#if INCLUDE_JBOOSTER
+ virtual void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) {}
+ virtual void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) {}
+#endif // INCLUDE_JBOOSTER
};
// BitData
@@ -750,6 +759,15 @@ public:
void set_profile_data(ProfileData* pd) {
_pd = pd;
}
+
+#if INCLUDE_JBOOSTER
+ virtual void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) {
+ assert(false, "override it");
+ }
+ virtual void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) {
+ assert(false, "override it");
+ }
+#endif // INCLUDE_JBOOSTER
};
// Type entries used for arguments passed at a call and parameters on
@@ -839,6 +857,12 @@ public:
void clean_weak_klass_links(bool always_clean);
void print_data_on(outputStream* st) const;
+
+#if INCLUDE_JBOOSTER
+ void clean_untrusted_entries();
+ void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) override;
+ void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) override;
+#endif // INCLUDE_JBOOSTER
};
// Type entry used for return from a call. A single cell to record the
@@ -882,6 +906,11 @@ public:
void clean_weak_klass_links(bool always_clean);
void print_data_on(outputStream* st) const;
+
+#if INCLUDE_JBOOSTER
+ void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) override;
+ void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) override;
+#endif // INCLUDE_JBOOSTER
};
// Entries to collect type information at a call: contains arguments
@@ -1073,6 +1102,25 @@ public:
}
virtual void print_data_on(outputStream* st, const char* extra = NULL) const;
+
+#if INCLUDE_JBOOSTER
+ void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) override {
+ if (has_arguments()) {
+ _args.klass_pointer_map_to_server(session_data, thread);
+ }
+ if (has_return()) {
+ _ret.klass_pointer_map_to_server(session_data, thread);
+ }
+ }
+ void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) override {
+ if (has_arguments()) {
+ _args.collect_klass(ik_array, ak_array);
+ }
+ if (has_return()) {
+ _ret.collect_klass(ik_array, ak_array);
+ }
+ }
+#endif // INCLUDE_JBOOSTER
};
// ReceiverTypeData
@@ -1213,6 +1261,17 @@ public:
void print_receiver_data_on(outputStream* st) const;
void print_data_on(outputStream* st, const char* extra = NULL) const;
+
+#if INCLUDE_JBOOSTER
+ Klass* receiver_no_check(uint row) const {
+ assert(row < row_limit(), "oob");
+ Klass* recv = (Klass*)intptr_at(receiver_cell_index(row));
+ return recv;
+ }
+ void clean_untrusted_entries();
+ void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) override;
+ void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) override;
+#endif // INCLUDE_JBOOSTER
};
// VirtualCallData
@@ -1784,6 +1843,15 @@ public:
static ByteSize type_offset(int i) {
return cell_offset(type_local_offset(i));
}
+
+#if INCLUDE_JBOOSTER
+ void klass_pointer_map_to_server(JClientSessionData* session_data, Thread* thread) override {
+ _parameters.klass_pointer_map_to_server(session_data, thread);
+ }
+ void collect_klass(GrowableArray<InstanceKlass*>* ik_array, GrowableArray<ArrayKlass*>* ak_array) override {
+ _parameters.collect_klass(ik_array, ak_array);
+ }
+#endif // INCLUDE_JBOOSTER
};
// SpeculativeTrapData
@@ -2476,6 +2544,13 @@ public:
void clean_method_data(bool always_clean);
void clean_weak_method_links();
Mutex* extra_data_lock() { return &_extra_data_lock; }
+
+#if INCLUDE_JBOOSTER
+private:
+ MethodData(const methodHandle& method, const bool* ignored);
+public:
+ static MethodData* create_instance_for_jbooster(Method* method, int size, char* mem, TRAPS);
+#endif // INCLUDE_JBOOSTER
};
#endif // SHARE_OOPS_METHODDATA_HPP
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 9e29c3300..8ceca7cd3 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -104,6 +104,12 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
+#if INCLUDE_JBOOSTER
+#include "jbooster/client/clientDataManager.hpp"
+#include "jbooster/client/clientMessageHandler.hpp"
+#include "jbooster/net/serverListeningThread.hpp"
+#include "jbooster/server/serverDataManager.hpp"
+#endif // INCLUDE_JBOOSTER
#include <errno.h>
@@ -3850,3 +3856,39 @@ JVM_END
JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name))
return os::get_signal_number(name);
JVM_END
+
+// JBooster ////////////////////////////////////////////////////////////////////////
+
+JVM_ENTRY(void, JVM_JBoosterInitVM(JNIEnv *env, jint server_port, jint connection_timeout, jint cleanup_timeout, jstring cache_path))
+#if INCLUDE_JBOOSTER
+ ResourceMark rm(THREAD);
+ const char* cache_path_c = NULL;
+ if (cache_path != NULL) {
+ cache_path_c = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(cache_path));
+ }
+ ServerDataManager::init_phase3(server_port, connection_timeout, cleanup_timeout, cache_path_c, THREAD);
+#endif // INCLUDE_JBOOSTER
+JVM_END
+
+JVM_ENTRY(void, JVM_JBoosterHandleConnection(JNIEnv *env, jint connection_fd))
+#if INCLUDE_JBOOSTER
+ ServerDataManager::get().listening_thread()->handle_connection(connection_fd);
+#endif // INCLUDE_JBOOSTER
+JVM_END
+
+JVM_ENTRY(void, JVM_JBoosterPrintStoredClientData(JNIEnv *env, jboolean print_all))
+#if INCLUDE_JBOOSTER
+ ServerDataManager::get().log_all_state(print_all);
+#endif // INCLUDE_JBOOSTER
+JVM_END
+
+JVM_ENTRY(void, JVM_JBoosterStartupNativeCallback(JNIEnv *env))
+#if INCLUDE_JBOOSTER
+ if (!UseJBooster) return;
+ ThreadToNativeFromVM ttn(THREAD->as_Java_thread());
+ log_debug(jbooster, start)("Reached the startup signal point of this program.");
+ ClientDataManager::get().set_startup_end();
+ ClientMessageHandler::trigger_cache_generation_tasks(ClientMessageHandler::TriggerTaskPhase::ON_STARTUP, THREAD);
+ log_debug(jbooster, start)("End of the startup callback.");
+#endif // INCLUDE_JBOOSTER
+JVM_END
\ No newline at end of file
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 60f639795..a1a737915 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -62,6 +62,9 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
+#if INCLUDE_JBOOSTER
+#include "jbooster/jBoosterManager.hpp"
+#endif // INCLUDE_JBOOSTER
#define DEFAULT_JAVA_LAUNCHER "generic"
@@ -3998,6 +4001,21 @@ jint Arguments::apply_ergo() {
GCConfig::arguments()->initialize();
+#if INCLUDE_JBOOSTER
+ // The jbooster server identifies the client based on the client VM flags.
+ // After identifying the client, the server sends the corresponding caches
+ // to the client (or no cache to send).
+ // Depending on whether the caches are present, different flags will be set
+ // by JBoosterManager, especially for Dynamic CDS.
+ // So the time for sending client VM flags to the server should be later
+ // than most VM flags are initialized, but earlier than the time for CDS
+ // initialization.
+ if (UseJBooster || AsJBooster) {
+ result = JBoosterManager::init_phase1();
+ if (result != JNI_OK) return result;
+ }
+#endif // INCLUDE_JBOOSTER
+
result = set_shared_spaces_flags_and_archive_paths();
if (result != JNI_OK) return result;
@@ -4318,3 +4336,38 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
*b = '\0';
return (p == src_end); // return false if not all of the source was copied
}
+
+#if INCLUDE_JBOOSTER
+jint Arguments::init_jbooster_startup_signal_properties(const char* klass_name,
+ const char* method_name,
+ const char* method_signature) {
+ bool added;
+ int jio_res;
+ const int buf_len = 4096;
+ char buffer[buf_len];
+
+ if (klass_name != nullptr) {
+ jio_res = jio_snprintf(buffer, buf_len, "jdk.jbooster.startup_klass_name=%s", klass_name);
+ if (jio_res < 0) return JNI_ENOMEM;
+ added = add_property(buffer, UnwriteableProperty, InternalProperty);
+ if (!added) return JNI_ENOMEM;
+ }
+
+ if (method_name != nullptr) {
+ jio_res = jio_snprintf(buffer, buf_len, "jdk.jbooster.startup_method_name=%s", method_name);
+ if (jio_res < 0) return JNI_ENOMEM;
+ added = add_property(buffer, UnwriteableProperty, InternalProperty);
+ if (!added) return JNI_ENOMEM;
+ }
+
+ if (method_signature != nullptr) {
+ jio_res = jio_snprintf(buffer, buf_len, "jdk.jbooster.startup_method_signature=%s", method_signature);
+ if (jio_res < 0) return JNI_ENOMEM;
+ added = add_property(buffer, UnwriteableProperty, InternalProperty);
+ if (!added) return JNI_ENOMEM;
+ }
+
+ return JNI_OK;
+}
+
+#endif // INCLUDE_JBOOSTER
\ No newline at end of file
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 3876be4a9..7f52419a5 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -641,6 +641,12 @@ class Arguments : AllStatic {
assert(Arguments::is_dumping_archive(), "dump time only");
}
+#if INCLUDE_JBOOSTER
+ static jint init_jbooster_startup_signal_properties(const char* klass_name,
+ const char* method_name,
+ const char* method_signature);
+#endif // INCLUDE_JBOOSTER
+
DEBUG_ONLY(static bool verify_special_jvm_flags(bool check_globals);)
};
diff --git a/src/hotspot/share/runtime/flags/allFlags.hpp b/src/hotspot/share/runtime/flags/allFlags.hpp
index 7d644e9a8..b0e7465cb 100644
--- a/src/hotspot/share/runtime/flags/allFlags.hpp
+++ b/src/hotspot/share/runtime/flags/allFlags.hpp
@@ -30,6 +30,9 @@
#include "gc/shared/tlab_globals.hpp"
#include "runtime/flags/debug_globals.hpp"
#include "runtime/globals.hpp"
+#if INCLUDE_JBOOSTER
+#include "jbooster/jbooster_globals.hpp"
+#endif // INCLUDE_JBOOSTER
// Put LP64/ARCH/JVMCI/COMPILER1/COMPILER2 at the top,
// as they are processed by jvmFlag.cpp in that order.
@@ -138,7 +141,17 @@
product_pd, \
notproduct, \
range, \
- constraint)
+ constraint) \
+ \
+ JBOOSTER_ONLY( \
+ JBOOSTER_FLAGS( \
+ develop, \
+ develop_pd, \
+ product, \
+ product_pd, \
+ notproduct, \
+ range, \
+ constraint))
#define ALL_CONSTRAINTS(f) \
COMPILER_CONSTRAINTS(f) \
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 054482383..8eae5e5cc 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2008,6 +2008,11 @@ const intx ObjectAlignmentInBytes = 8;
product(ccstr, ArchiveClassesAtExit, NULL, \
"The path and name of the dynamic archive file") \
\
+ product(bool, SkipSharedClassPathCheck, false, DIAGNOSTIC, \
+ "Skips SharedClassPath check in DynamicCDS, which allows" \
+ "non-empty directories to exist in classpath when DynamicDS" \
+ "is used") \
+ \
product(ccstr, ExtraSharedClassListFile, NULL, \
"Extra classlist for building the CDS archive file") \
\
diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
index 5dc47c7c2..d7b1c5ed6 100644
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -93,6 +93,9 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
+#if INCLUDE_JBOOSTER
+#include "jbooster/client/clientMessageHandler.hpp"
+#endif // INCLUDE_JBOOSTER
GrowableArray<Method*>* collected_profiled_methods;
@@ -510,7 +513,7 @@ void before_exit(JavaThread* thread, bool halt) {
os::terminate_signal_thread();
#if INCLUDE_CDS
- if (DynamicDumpSharedSpaces) {
+ if (DynamicDumpSharedSpaces JBOOSTER_ONLY(&& !UseJBooster)) {
ExceptionMark em(thread);
DynamicArchive::dump();
if (thread->has_pending_exception()) {
@@ -523,6 +526,14 @@ void before_exit(JavaThread* thread, bool halt) {
}
#endif
+#if INCLUDE_JBOOSTER
+ if (UseJBooster) {
+ ThreadToNativeFromVM ttn(thread);
+ bool should_compile = JBoosterStartupSignal == nullptr;
+ ClientMessageHandler::trigger_cache_generation_tasks(ClientMessageHandler::TriggerTaskPhase::ON_SHUTDOWN, thread);
+ }
+#endif // INCLUDE_JBOOSTER
+
print_statistics();
Universe::heap()->print_tracing_info();
diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp
index ece43e350..c057b1ef5 100644
--- a/src/hotspot/share/runtime/thread.cpp
+++ b/src/hotspot/share/runtime/thread.cpp
@@ -149,6 +149,9 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
+#if INCLUDE_JBOOSTER
+#include "jbooster/jBoosterManager.hpp"
+#endif // INCLUDE_JBOOSTER
// Initialization after module runtime initialization
void universe_post_module_init(); // must happen after call_initPhase2
@@ -3150,6 +3153,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
ShouldNotReachHere();
}
+#if INCLUDE_JBOOSTER
+ if (UseJBooster || AsJBooster) {
+ JBoosterManager::init_phase2(CHECK_JNI_ERR);
+ }
+#endif // INCLUDE_JBOOSTER
+
return JNI_OK;
}
diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp
index a94d3ed9a..d3ca8acb5 100644
--- a/src/hotspot/share/utilities/concurrentHashTable.hpp
+++ b/src/hotspot/share/utilities/concurrentHashTable.hpp
@@ -40,6 +40,10 @@ class Mutex;
template <typename CONFIG, MEMFLAGS F>
class ConcurrentHashTable : public CHeapObj<F> {
+#if INCLUDE_JBOOSTER
+ template <typename KK, typename VV, MEMFLAGS FF, typename EE> friend class ConcurrentHashMap;
+#endif // INCLUDE_JBOOSTER
+
typedef typename CONFIG::Value VALUE;
private:
// This is the internal node structure.
diff --git a/src/hotspot/share/utilities/hashtable.cpp b/src/hotspot/share/utilities/hashtable.cpp
index 5b99bba8b..ad0ab01f9 100644
--- a/src/hotspot/share/utilities/hashtable.cpp
+++ b/src/hotspot/share/utilities/hashtable.cpp
@@ -280,6 +280,9 @@ template class BasicHashtable<mtCompiler>;
template class BasicHashtable<mtTracing>;
template class BasicHashtable<mtServiceability>;
template class BasicHashtable<mtLogging>;
+#if INCLUDE_JBOOSTER
+template class BasicHashtable<mtJBooster>;
+#endif // INCLUDE_JBOOSTER
template void BasicHashtable<mtClass>::verify_table<DictionaryEntry>(char const*);
template void BasicHashtable<mtModule>::verify_table<ModuleEntry>(char const*);
diff --git a/src/hotspot/share/utilities/hashtable.hpp b/src/hotspot/share/utilities/hashtable.hpp
index 87bd65e05..8bbb20e68 100644
--- a/src/hotspot/share/utilities/hashtable.hpp
+++ b/src/hotspot/share/utilities/hashtable.hpp
@@ -228,6 +228,7 @@ template<
bool (*EQUALS)(K const&, K const&) = primitive_equals<K>
>
class KVHashtable : public BasicHashtable<F> {
+protected:
class KVHashtableEntry : public BasicHashtableEntry<F> {
public:
K _key;
@@ -237,7 +238,6 @@ class KVHashtable : public BasicHashtable<F> {
}
};
-protected:
KVHashtableEntry* bucket(int i) const {
return (KVHashtableEntry*)BasicHashtable<F>::bucket(i);
}
diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp
index 33ecfe089..fde84e048 100644
--- a/src/hotspot/share/utilities/macros.hpp
+++ b/src/hotspot/share/utilities/macros.hpp
@@ -119,6 +119,16 @@
#define NOT_CDS_RETURN_(code) { return code; }
#endif // INCLUDE_CDS
+#ifndef INCLUDE_JBOOSTER
+#define INCLUDE_JBOOSTER 1
+#endif
+
+#if INCLUDE_JBOOSTER
+#define JBOOSTER_ONLY(x) x
+#else
+#define JBOOSTER_ONLY(x)
+#endif // INCLUDE_JBOOSTER
+
#ifndef INCLUDE_MANAGEMENT
#define INCLUDE_MANAGEMENT 1
#endif // INCLUDE_MANAGEMENT
diff --git a/src/hotspot/share/utilities/stringUtils.cpp b/src/hotspot/share/utilities/stringUtils.cpp
index 21fb7a6e8..04883b4f1 100644
--- a/src/hotspot/share/utilities/stringUtils.cpp
+++ b/src/hotspot/share/utilities/stringUtils.cpp
@@ -25,6 +25,11 @@
#include "precompiled.hpp"
#include "utilities/debug.hpp"
#include "utilities/stringUtils.hpp"
+#if INCLUDE_JBOOSTER
+#include "classfile/javaClasses.hpp"
+#include "memory/allocation.hpp"
+#include "oops/symbol.hpp"
+#endif // INCLUDE_JBOOSTER
int StringUtils::replace_no_expand(char* string, const char* from, const char* to) {
int replace_count = 0;
@@ -65,3 +70,61 @@ double StringUtils::similarity(const char* str1, size_t len1, const char* str2,
return 2.0 * (double) hit / (double) total;
}
+
+#if INCLUDE_JBOOSTER
+
+uint32_t StringUtils::hash_code(const char* str) {
+ if (str == nullptr) return 0u;
+ return hash_code(str, strlen(str) + 1);
+}
+
+uint32_t StringUtils::hash_code(const char* str, int len) {
+ assert(str != nullptr && str[len - 1] == '\0', "sanity");
+ return java_lang_String::hash_code((const jbyte*) str, len - 1);
+}
+
+uint32_t StringUtils::hash_code(Symbol* sym) {
+ if (sym == nullptr) return 0u;
+ return java_lang_String::hash_code((const jbyte*) sym->base(), sym->utf8_length());
+}
+
+int StringUtils::compare(const char* str1, const char* str2) {
+ if (str1 == nullptr) {
+ return str2 == nullptr ? 0 : -1;
+ }
+ if (str2 == nullptr) {
+ return str1 == nullptr ? 0 : +1;
+ }
+ return strcmp(str1, str2);
+}
+
+char* StringUtils::copy_to_resource(const char* str) {
+ if (str == nullptr) return nullptr;
+ return copy_to_resource(str, strlen(str) + 1);
+}
+
+char* StringUtils::copy_to_resource(const char* str, int len) {
+ assert(str != nullptr && str[len - 1] == '\0', "sanity");
+ return (char*) memcpy(NEW_RESOURCE_ARRAY(char, len), str, len);
+}
+
+char* StringUtils::copy_to_heap(const char* str, MEMFLAGS mt) {
+ if (str == nullptr) return nullptr;
+ return copy_to_heap(str, strlen(str) + 1, mt);
+}
+
+char* StringUtils::copy_to_heap(const char* str, int len, MEMFLAGS mt) {
+ assert(str != nullptr && str[len - 1] == '\0', "sanity");
+ return (char*) memcpy(NEW_C_HEAP_ARRAY(char, len, mt), str, len);
+}
+
+void StringUtils::free_from_heap(const char* str) {
+ if (str == nullptr) return;
+ FREE_C_HEAP_ARRAY(char, str);
+}
+
+const char* StringUtils::str(Symbol* sym) {
+ return sym == nullptr ? "<null>" : sym->as_C_string();
+}
+
+#endif // INCLUDE_JBOOSTER
diff --git a/src/hotspot/share/utilities/stringUtils.hpp b/src/hotspot/share/utilities/stringUtils.hpp
index 372222d7c..abc7ef361 100644
--- a/src/hotspot/share/utilities/stringUtils.hpp
+++ b/src/hotspot/share/utilities/stringUtils.hpp
@@ -27,6 +27,10 @@
#include "memory/allocation.hpp"
+#if INCLUDE_JBOOSTER
+class Symbol;
+#endif // INCLUDE_JBOOSTER
+
class StringUtils : AllStatic {
public:
// Replace the substring <from> with another string <to>. <to> must be
@@ -40,6 +44,30 @@ public:
// Compute string similarity based on Dice's coefficient
static double similarity(const char* str1, size_t len1, const char* str2, size_t len2);
+
+#if INCLUDE_JBOOSTER
+ // A null-pointer-supported version of strcmp.
+ static int compare(const char* str1, const char* str2);
+
+ // Return 0 if str is null.
+ static uint32_t hash_code(const char* str);
+ static uint32_t hash_code(const char* str, int len);
+ static uint32_t hash_code(Symbol* sym);
+
+ // Do nothing and return null if the str is null.
+ static char* copy_to_resource(const char* str);
+ static char* copy_to_resource(const char* str, int len);
+
+ // Do nothing and return null if the str is null.
+ static char* copy_to_heap(const char* str, MEMFLAGS mt);
+ static char* copy_to_heap(const char* str, int len, MEMFLAGS mt);
+
+ // Do nothing if the str is null.
+ static void free_from_heap(const char* str);
+
+ // Return "<null>" if sym is null.
+ static const char* str(Symbol* sym);
+#endif // INCLUDE_JBOOSTER
};
#endif // SHARE_UTILITIES_STRINGUTILS_HPP
diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
index 03b7afff8..560a0c05f 100644
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -177,6 +177,7 @@ module java.base {
java.logging;
exports jdk.internal.org.objectweb.asm to
jdk.jartool,
+ jdk.jbooster,
jdk.jfr,
jdk.jlink;
exports jdk.internal.org.objectweb.asm.tree to
diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy
index af5df34b5..1c58b7797 100644
--- a/src/java.base/share/lib/security/default.policy
+++ b/src/java.base/share/lib/security/default.policy
@@ -185,6 +185,15 @@ grant codeBase "jrt:/jdk.internal.vm.compiler.management" {
permission java.lang.RuntimePermission "accessClassInPackage.org.graalvm.compiler.serviceprovider";
};
+grant codeBase "jrt:/jdk.jbooster" {
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.org.objectweb.asm";
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.tools.jaotc";
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.tools.jaotc.collect";
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.tools.jaotc.collect.module";
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.vm.ci.hotspot";
+ permission java.lang.RuntimePermission "accessClassInPackage.jdk.vm.ci.jbooster";
+};
+
grant codeBase "jrt:/jdk.jsobject" {
permission java.security.AllPermission;
};
diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/JBoosterCompilationContext.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/JBoosterCompilationContext.java
new file mode 100644
index 000000000..aa18682e9
--- /dev/null
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/JBoosterCompilationContext.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.vm.ci.jbooster;
+
+import java.util.HashMap;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Compilation context interface for JBooster.
+ * Defined in jdk.vm.ci for all modules to access.
+ */
+public interface JBoosterCompilationContext {
+ /**
+ * The context is thread-local.
+ * Please access it through get() & set().
+ */
+ ThreadLocal<JBoosterCompilationContext> CTX = new ThreadLocal<>();
+
+ /**
+ * Get thread-local context.
+ *
+ * @return JBoosterCompilationContext
+ */
+ static JBoosterCompilationContext get() {
+ return CTX.get();
+ }
+
+ /**
+ * Set thread-local context.
+ *
+ * @param ctx JBoosterCompilationContext
+ */
+ static void set(JBoosterCompilationContext ctx) {
+ CTX.set(ctx);
+ }
+
+ /**
+ * Get the ID of the client session.
+ */
+ int getSessionId();
+
+ /**
+ * Get the file path to store the compiled AOT lib.
+ */
+ String getFilePath();
+
+ /**
+ * Get the classes to compile.
+ */
+ Set<Class<?>> getClassesToCompile();
+
+ /**
+ * Get the methods to compile.
+ * The methods should be in getClassesToCompile().
+ *
+ * @return names & signatures of the methods
+ */
+ Set<String> getMethodsToCompile();
+
+ /**
+ * Get the methods that should never be compiled.
+ * These methods have special invoke handles in them so they are not
+ * compatible with PGO.
+ *
+ * @return names & signatures of the methods
+ */
+ Set<String> getMethodsNotToCompile();
+
+ /**
+ * return true if this context use PGO
+ */
+ boolean usePGO();
+
+ /**
+ * Get methodCount of CompiledMethodInfo.
+ * (To support multi-task concurrent compilation of AOT)
+ */
+ AtomicInteger getCompiledMethodInfoMethodsCount();
+
+ /**
+ * Get dynoStore of AOTCompiledClass.
+ * (To support multi-task concurrent compilation of AOT)
+ *
+ * @return a AOTDynamicTypeStore
+ * (AOTDynamicTypeStore is not visible here)
+ */
+ Object getAOTCompiledClassAOTDynamicTypeStore();
+
+ /**
+ * Set dynoStore of AOTCompiledClass.
+ * (To support multi-task concurrent compilation of AOT)
+ */
+ void setAotCompiledClassAOTDynamicTypeStore(Object dynoStore);
+
+ /**
+ * Get classesCount of AOTCompiledClass.
+ * (To support multi-task concurrent compilation of AOT)
+ */
+ AtomicInteger getAOTCompiledClassClassesCount();
+
+ /**
+ * Get klassData of AOTCompiledClass.
+ * (To support multi-task concurrent compilation of AOT)
+ *
+ * @return a HashMap<String, AOTKlassData>
+ * (AOTKlassData is not visible here)
+ */
+ HashMap<String, ?> getAOTCompiledClassKlassData();
+
+ /**
+ * Get sectNameTab of ElfSection.
+ * (To support multi-task concurrent compilation of AOT)
+ */
+ StringBuilder getElfSectionSectNameTab();
+
+ /**
+ * Get shStrTabNrOfBytes of ElfSection.
+ * (To support multi-task concurrent compilation of AOT)
+ */
+ AtomicInteger getElfSectionShStrTabNrOfBytes();
+
+ /**
+ * Get the number of remaining tasks that have not been compiled.
+ * (To support concurrent compilation for different tasks)
+ */
+ CountDownLatch getAOTCompilerRemainingTaskCount();
+
+ /**
+ * Set the number of tasks that are successfully enqueued.
+ * (To support concurrent compilation for different tasks)
+ */
+ void setAOTCompilerTotalTaskCount(int taskCnt);
+
+ /**
+ * Get successfulMethodCount of AOTCompiler#CompileQueue.
+ * (To support concurrent compilation for different tasks)
+ */
+ AtomicInteger getCompileQueueSuccessfulMethodCount();
+
+ /**
+ * Get failedMethodCount of AOTCompiler#CompileQueue.
+ * (To support concurrent compilation for different tasks)
+ */
+ AtomicInteger getCompileQueueFailedMethodCount();
+
+ /**
+ * Should the method be excluded for inline.
+ * (To support PGO)
+ *
+ * @param methodName method name
+ * @return is excluded for inline
+ */
+ boolean isInlineExcluded(String methodName);
+
+ /**
+ * Get the real metaspace method data of client session.
+ * (To support PGO)
+ *
+ * @param metaspaceMethod the metaspace method
+ * @return the address of method data
+ */
+ long getMetaspaceMethodData(long metaspaceMethod);
+}
diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/package-info.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/package-info.java
new file mode 100644
index 000000000..e1e0bad5f
--- /dev/null
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/jbooster/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+/**
+ * The JBooster of the JVMCI API
+ */
+package jdk.vm.ci.jbooster;
\ No newline at end of file
diff --git a/src/jdk.internal.vm.ci/share/classes/module-info.java b/src/jdk.internal.vm.ci/share/classes/module-info.java
index ed1976957..631cc8af9 100644
--- a/src/jdk.internal.vm.ci/share/classes/module-info.java
+++ b/src/jdk.internal.vm.ci/share/classes/module-info.java
@@ -33,6 +33,7 @@ module jdk.internal.vm.ci {
exports jdk.vm.ci.meta to jdk.internal.vm.compiler;
exports jdk.vm.ci.code to jdk.internal.vm.compiler;
exports jdk.vm.ci.hotspot to jdk.internal.vm.compiler;
+ exports jdk.vm.ci.jbooster to jdk.jbooster, jdk.internal.vm.compiler;
uses jdk.vm.ci.services.JVMCIServiceLocator;
uses jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory;
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/Commands.java b/src/jdk.jbooster/share/classes/jdk/jbooster/Commands.java
new file mode 100644
index 000000000..1333a5907
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/Commands.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Scanner;
+
+import static java.lang.System.Logger.Level.INFO;
+import static java.lang.System.Logger.Level.WARNING;
+import static jdk.jbooster.JBooster.LOGGER;
+
+/**
+ * The interactive commands of the JBooster server.
+ */
+public final class Commands {
+ private static final Command[] COMMANDS = {new Command(of("h", "help"),
+ "Print this help message of JBooster interactive commands.") {
+ @Override
+ void process(Commands commands, String[] args) {
+ printHelp();
+ }
+ }, new Command(of("q", "quit", "exit"),
+ "Exit the JBooster server.") {
+ @Override
+ void process(Commands commands, String[] args) {
+ commands.exit = true;
+ }
+ }, new Command(of("c", "conn", "connection"),
+ "Print network connections with the clients.") {
+ @Override
+ void process(Commands commands, String[] args) {
+ int workingThreads = JBooster.getConnectionPool().getExecutor().getActiveCount();
+ LOGGER.log(INFO, "Working threads for connections: {0}", workingThreads);
+ }
+ }, new Command(of("d", "data"),
+ "Print managed client data and cache state (add arg \"all\" for more details).") {
+ @Override
+ void process(Commands commands, String[] args) {
+ boolean printAll = (args.length == 1 && "all".equals(args[0]));
+ JBooster.printStoredClientData(printAll);
+ }
+ }};
+
+ private boolean exit = false;
+
+ /**
+ * Open the scanner and receive the interactive commands.
+ */
+ public boolean interact() {
+ try (Scanner scanner = new Scanner(System.in)) {
+ do {
+ String cmdLine = scanner.nextLine();
+ parseLine(cmdLine);
+ } while (!exit);
+ } catch (NoSuchElementException e) {
+ LOGGER.log(WARNING, "Looks like jbooster is running in the background so the interaction "
+ + "is unsupported here. Add \"-b\" or \"--background\" to the command line to "
+ + "suppress this warning.");
+ return false;
+ }
+ return true;
+ }
+
+ private void parseLine(String cmdLine) {
+ if (cmdLine.isEmpty()) {
+ return;
+ }
+ String[] cmdAndArgs = Arrays.stream(cmdLine.split(" ")).filter(s -> !s.isEmpty()).toArray(String[]::new);
+ if (cmdAndArgs.length == 0) {
+ return;
+ }
+ String cmd = cmdAndArgs[0];
+ String[] args = Arrays.stream(cmdAndArgs).skip(1).toArray(String[]::new);
+
+ Optional<Command> command = findCommand(cmd);
+ if (command.isPresent()) {
+ command.get().process(this, args);
+ } else {
+ LOGGER.log(WARNING, "Unknown command: \"{0}\"", cmd);
+ printHelp();
+ }
+ }
+
+ private static Optional<Command> findCommand(String cmd) {
+ for (Command command : COMMANDS) {
+ if (command.matches(cmd)) {
+ return Optional.of(command);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private static void printHelp() {
+ int maxLenOfAliases = 0;
+ for (Command command : COMMANDS) {
+ int lenAliases = Arrays.stream(command.aliases).mapToInt(String::length).sum()
+ + (2 * (command.aliases.length - 1));
+ maxLenOfAliases = Math.max(maxLenOfAliases, lenAliases);
+ }
+
+ final int margin = 4;
+ final String sep = System.lineSeparator();
+ StringBuilder sbAll = new StringBuilder("Help of JBooster commands:" + sep);
+ for (Command command : COMMANDS) {
+ sbAll.append(" ");
+ StringBuilder sb = new StringBuilder();
+ for (String alias : command.aliases) {
+ sb.append(alias).append(", ");
+ }
+ sb.setLength(sb.length() - 2);
+ sbAll.append(sb);
+ sbAll.append(" ".repeat(maxLenOfAliases + margin - sb.length()));
+ sbAll.append(command.comment);
+ sbAll.append(sep);
+ }
+ LOGGER.log(INFO, sbAll);
+ }
+
+ private static String[] of(String... aliases) {
+ return aliases;
+ }
+
+ abstract static class Command {
+ final String[] aliases;
+ final String comment;
+
+ protected Command(String[] aliases, String comment) {
+ this.aliases = aliases;
+ this.comment = comment;
+ }
+
+ boolean matches(String cmd) {
+ for (String alias : aliases) {
+ if (alias.equals(cmd)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ abstract void process(Commands commands, String[] args);
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/ConnectionPool.java b/src/jdk.jbooster/share/classes/jdk/jbooster/ConnectionPool.java
new file mode 100644
index 000000000..3d703de1c
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/ConnectionPool.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.System.Logger.Level.INFO;
+import static jdk.jbooster.JBooster.LOGGER;
+
+/**
+ * The network connection thread pool of the JBooster server.
+ */
+public final class ConnectionPool {
+ private static final int CORE_POOL_SIZE = Math.min(25, Math.max(8, 2 * Runtime.getRuntime().availableProcessors()));
+ private static final int MAX_POOL_SIZE = 200;
+ private static final long KEEP_ALIVE_SECONDS = 60L;
+
+ private final ThreadPoolExecutor executor;
+
+ ConnectionPool() {
+ executor = new ConnectionPoolExecutor();
+ }
+
+ public ThreadPoolExecutor getExecutor() {
+ return executor;
+ }
+
+ public boolean execute(Runnable command) {
+ try {
+ executor.execute(command);
+ } catch (RejectedExecutionException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private static class ConnectionPoolExecutor extends ThreadPoolExecutor {
+
+ public ConnectionPoolExecutor() {
+ super(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+ new SynchronousQueue<>(), new ThreadPoolExecutor.AbortPolicy());
+ }
+
+ @Override
+ protected void terminated() {
+ LOGGER.log(INFO, "JBooster connection pool terminated.");
+ }
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/JBooster.java b/src/jdk.jbooster/share/classes/jdk/jbooster/JBooster.java
new file mode 100644
index 000000000..49cdbb28a
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/JBooster.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.lang.System.Logger;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
+import sun.misc.Signal;
+import sun.misc.SignalHandler;
+
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.INFO;
+import static java.lang.System.Logger.Level.WARNING;
+
+/**
+ * The entry of the JBooster server.
+ */
+public final class JBooster {
+ static {
+ System.setProperty("java.util.logging.SimpleFormatter.format",
+ "[%1$tF %1$tT][%4$-7s][%2$s] %5$s%n");
+ System.loadLibrary("jbooster");
+ }
+
+ public static final Logger LOGGER = System.getLogger("jbooster");
+
+ private static final Options options = new Options();
+
+ private static final Commands commands = new Commands();
+
+ private static ConnectionPool connectionPool;
+
+ public static void main(String[] args) {
+ try {
+ options.parseArgs(args);
+ } catch (IllegalArgumentException e) {
+ if (LOGGER.isLoggable(ERROR)) {
+ LOGGER.log(ERROR, "Failed to start because of the bad CMD args:");
+ LOGGER.log(ERROR, "{0}", e.getMessage());
+ } else {
+ System.err.println("Failed to start because of the bad CMD args:");
+ System.err.println(e.getMessage());
+ }
+ Options.printHelp();
+ return;
+ }
+
+ init();
+ LOGGER.log(INFO, "The JBooster server is ready!");
+
+ try {
+ if (!options.isInteractive() || !commands.interact()) {
+ loop();
+ }
+ } finally {
+ terminateAndJoin();
+ }
+ }
+
+ static Options getOptions() {
+ return options;
+ }
+
+ static Commands getCommands() {
+ return commands;
+ }
+
+ static ConnectionPool getConnectionPool() {
+ return connectionPool;
+ }
+
+ private static void init() {
+ BackgroundScannerSignalHandler.register();
+ connectionPool = new ConnectionPool();
+ Main.initForJBooster();
+ initInVM(options.getServerPort(), options.getConnectionTimeout(),
+ options.getCleanupTimeout(), options.getCachePath());
+ }
+
+ private static void loop() {
+ while (true) {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+
+ private static void terminateAndJoin() {
+ final long maxTimeForWaiting = 8000L;
+ final long maxTimeForInterrupting = 2000L;
+
+ LOGGER.log(INFO, "Shutting down the connection pools (it may take some time)...",
+ maxTimeForWaiting);
+ ThreadPoolExecutor pool1 = getConnectionPool().getExecutor();
+ ThreadPoolExecutor pool2 = Main.getJBoosterGlobalCompileQueue();
+ List<ThreadPoolExecutor> pools = List.of(pool1, pool2);
+ try {
+ LOGGER.log(INFO, "Waiting (up to {0} ms) for the existing pool tasks to finish.", maxTimeForWaiting);
+ for (ThreadPoolExecutor pool : pools) {
+ pool.shutdown();
+ }
+ if (!waitThreadPoolsToTerminate(pools, maxTimeForWaiting)) {
+ LOGGER.log(INFO, "Waiting (up to {0} ms) for interrupting pool tasks.", maxTimeForInterrupting);
+ for (ThreadPoolExecutor pool : pools) {
+ pool.shutdownNow();
+ }
+ if (!waitThreadPoolsToTerminate(pools, maxTimeForInterrupting)) {
+ LOGGER.log(WARNING, "Failed to stop the pool properly. Force exit. Bye~");
+ System.exit(1);
+ }
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ LOGGER.log(INFO, "Bye~");
+ }
+
+ /**
+ * Wait for all thread pools to stop working.
+ * The total wait time does not exceed waitMillis.
+ */
+ private static boolean waitThreadPoolsToTerminate(List<ThreadPoolExecutor> pools, long waitMillis)
+ throws InterruptedException {
+ boolean allTerminated = true;
+ long timeBegin = System.currentTimeMillis();
+ for (ThreadPoolExecutor pool : pools) {
+ if (pool.isTerminated()) {
+ continue;
+ }
+ long timeToWait = Math.max(0, waitMillis - (System.currentTimeMillis() - timeBegin));
+ if (!pool.awaitTermination(timeToWait, TimeUnit.MILLISECONDS)) {
+ allTerminated = false;
+ LOGGER.log(INFO, "The pool {0} is still running after {1} ms.",
+ pool.getClass().getSimpleName(), waitMillis);
+ }
+ }
+ return allTerminated;
+ }
+
+ /**
+ * This method is invoked only in C++.
+ */
+ private static boolean receiveConnection(int connectionFd) {
+ return connectionPool.execute(() -> handleConnection(connectionFd));
+ }
+
+ /**
+ * This method is invoked only in C++.
+ */
+ private static boolean compileClasses(int sessionId, String filePath, Set<Class<?>> classes) {
+ return compileMethods(sessionId, filePath, classes, null, null);
+ }
+
+ /**
+ * This method is invoked only in C++.
+ */
+ private static boolean compileMethods(int sessionId, String filePath, Set<Class<?>> classes,
+ Set<String> methodsToCompile, Set<String> methodsNotToCompile) {
+ LOGGER.log(INFO, "Compilation task received: classes_to_compile={0}, methods_to_compile={1}, methods_not_compile={2}, session_id={3}.",
+ classes.size(),
+ (methodsToCompile == null ? "all" : String.valueOf(methodsToCompile.size())),
+ (methodsNotToCompile == null ? "none" : String.valueOf(methodsNotToCompile.size())),
+ sessionId);
+ try {
+ // [JBOOSTER TODO] jaotc logic
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ private static native void initInVM(int serverPort, int connectionTimeout, int cleanupTimeout, String cachePath);
+
+ private static native void handleConnection(int connectionFd);
+
+ static native void printStoredClientData(boolean printAll);
+
+ private static final class BackgroundScannerSignalHandler implements SignalHandler {
+ public static void register() {
+ // The returned value (i.e. the old, default handler) is ignored because
+ // the default handler will make the program "stop" instead of "exit".
+ // So do not use it.
+ Signal.handle(new Signal("TTIN"), new BackgroundScannerSignalHandler());
+ }
+
+ private boolean handled;
+
+ private BackgroundScannerSignalHandler() {
+ handled = false;
+ }
+
+ @Override
+ public void handle(Signal sig) {
+ if (!handled) {
+ handled = true;
+ LOGGER.log(ERROR, "A SIGTTIN is detected! Looks like this program is running in background "
+ + "so Scanner is not supported here. Add \"-b\" or \"--background\" to the command "
+ + "line of jbooster to disable interactive commands.");
+ System.exit(1);
+ }
+ }
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterClassLoader.java b/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterClassLoader.java
new file mode 100644
index 000000000..efbf6f673
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterClassLoader.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static java.lang.System.Logger.Level.INFO;
+import static jdk.jbooster.JBooster.LOGGER;
+
+public class JBoosterClassLoader extends ClassLoader {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ /**
+ * To keep the classes from being GC'ed.
+ */
+ public static final Map<Integer, Collection<JBoosterClassLoader>> loaders = new ConcurrentHashMap<>();
+
+ /**
+ * This method is only invoked in Hotspot C++ side.
+ */
+ private static JBoosterClassLoader create(int programId,
+ String loaderClassName, String loaderName, ClassLoader parent) {
+ String name = loaderClassName + ":" + loaderName;
+ JBoosterClassLoader newLoader = new JBoosterClassLoader(name, parent);
+ Collection<JBoosterClassLoader> que = loaders.computeIfAbsent(programId, id -> new ConcurrentLinkedQueue<>());
+ que.add(newLoader);
+ return newLoader;
+ }
+
+ /**
+ * This method is only invoked in Hotspot C++ side.
+ */
+ private static void destroy(int programId) {
+ Collection<JBoosterClassLoader> que = loaders.remove(programId);
+ LOGGER.log(INFO, "Class loaders and classes that belong to the program will be GCed later: "
+ + "program_id={0}, loader_cnt={1}.", programId, (que == null ? -1 : que.size()));
+ }
+
+ private JBoosterClassLoader(String name, ClassLoader parent) {
+ super(name, parent);
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterCompilationContextImpl.java b/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterCompilationContextImpl.java
new file mode 100644
index 000000000..65f28e141
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/JBoosterCompilationContextImpl.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.util.HashMap;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.vm.ci.jbooster.JBoosterCompilationContext;
+
+/**
+ * The only implement of JBoosterCompilationContext.
+ */
+public class JBoosterCompilationContextImpl implements JBoosterCompilationContext {
+ // These values are related to JBooster compilation.
+ private final int sessionId;
+ private final String filePath;
+ private final Set<Class<?>> classesToCompile;
+ private final Set<String> methodsToCompile;
+ private final Set<String> methodsNotToCompile;
+ private final boolean usePGO;
+
+ // These values are used to replace the static values in AOT classes.
+ private final AtomicInteger compiledMethodInfoMethodsCount = new AtomicInteger(0);
+ private Object aotCompiledClassAOTDynamicTypeStore = null;
+ private final AtomicInteger aotCompiledClassClassesCount = new AtomicInteger(0);
+ private final HashMap<String, ?> aotCompiledClassKlassData = new HashMap<>();
+ private final StringBuilder elfSectionSectNameTab = new StringBuilder();
+ private final AtomicInteger elfSectionShStrTabNrOfBytes = new AtomicInteger(0);
+ private final AtomicInteger compileQueueSuccessfulMethodCount = new AtomicInteger(0);
+ private final AtomicInteger compileQueueFailedMethodCount = new AtomicInteger(0);
+
+ private CountDownLatch aotCompilerRemainingTaskCount = null;
+
+ public JBoosterCompilationContextImpl(
+ int sessionId,
+ String filePath,
+ Set<Class<?>> classesToCompile,
+ Set<String> methodsToCompile,
+ Set<String> methodsNotCompile,
+ boolean usePGO) {
+ this.sessionId = sessionId;
+ this.filePath = filePath;
+ this.classesToCompile = classesToCompile;
+ this.methodsToCompile = methodsToCompile;
+ this.methodsNotToCompile = methodsNotCompile;
+ this.usePGO = usePGO;
+ }
+
+ @Override
+ public int getSessionId() {
+ return sessionId;
+ }
+
+ @Override
+ public String getFilePath() {
+ return filePath;
+ }
+
+ @Override
+ public Set<Class<?>> getClassesToCompile() {
+ return classesToCompile;
+ }
+
+ @Override
+ public Set<String> getMethodsToCompile() {
+ return methodsToCompile;
+ }
+
+ @Override
+ public Set<String> getMethodsNotToCompile() {
+ return methodsNotToCompile;
+ }
+
+ @Override
+ public boolean usePGO() {
+ return usePGO;
+ }
+
+ @Override
+ public AtomicInteger getCompiledMethodInfoMethodsCount() {
+ return compiledMethodInfoMethodsCount;
+ }
+
+ @Override
+ public Object getAOTCompiledClassAOTDynamicTypeStore() {
+ return aotCompiledClassAOTDynamicTypeStore;
+ }
+
+ @Override
+ public void setAotCompiledClassAOTDynamicTypeStore(Object dynoStore) {
+ this.aotCompiledClassAOTDynamicTypeStore = dynoStore;
+ }
+
+ @Override
+ public AtomicInteger getAOTCompiledClassClassesCount() {
+ return aotCompiledClassClassesCount;
+ }
+
+ @Override
+ public HashMap<String, ?> getAOTCompiledClassKlassData() {
+ return aotCompiledClassKlassData;
+ }
+
+ @Override
+ public StringBuilder getElfSectionSectNameTab() {
+ return elfSectionSectNameTab;
+ }
+
+ @Override
+ public AtomicInteger getElfSectionShStrTabNrOfBytes() {
+ return elfSectionShStrTabNrOfBytes;
+ }
+
+ @Override
+ public CountDownLatch getAOTCompilerRemainingTaskCount() {
+ return aotCompilerRemainingTaskCount;
+ }
+
+ @Override
+ public void setAOTCompilerTotalTaskCount(int taskCnt) {
+ aotCompilerRemainingTaskCount = new CountDownLatch(taskCnt);
+ }
+
+ @Override
+ public AtomicInteger getCompileQueueSuccessfulMethodCount() {
+ return compileQueueSuccessfulMethodCount;
+ }
+
+ @Override
+ public AtomicInteger getCompileQueueFailedMethodCount() {
+ return compileQueueFailedMethodCount;
+ }
+
+ @Override
+ public boolean isInlineExcluded(String methodName) {
+ return methodsNotToCompile.contains(methodName);
+ }
+
+ @Override
+ public long getMetaspaceMethodData(long metaspaceMethod) {
+ return getMetaspaceMethodData(sessionId, metaspaceMethod);
+ }
+
+ private static native long getMetaspaceMethodData(int sessionId, long metaspaceMethod);
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/Options.java b/src/jdk.jbooster/share/classes/jdk/jbooster/Options.java
new file mode 100644
index 000000000..869e5ca97
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/Options.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Properties;
+
+import static java.lang.System.Logger.Level.INFO;
+import static jdk.jbooster.JBooster.LOGGER;
+
+/**
+ * Options of the JBooster server.
+ */
+public final class Options {
+ private static final int UNSET_PORT = 0;
+ private static final int CONNECTION_TIMEOUT = 32 * 1000;
+ private static final int CLEANUP_TIMEOUT = 2 * 24 * 60 * 60 * 1000;
+
+ private static final Option[] OPTIONS = {new Option(of("-h", "-?", "-help", "--help"), null,
+ "Print this help message of the JBooster server.") {
+ @Override
+ protected void process(Options options, String arg) {
+ printHelp();
+ System.exit(0);
+ }
+ }, new Option(of("-p", "--server-port"), "port-num",
+ "The listening port of JBooster server (1024~65535). No default value.") {
+ @Override
+ protected void process(Options options, String arg) {
+ int port = -1;
+ try {
+ port = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Failed to convert the arg \"" + arg + "\" to a int!");
+ }
+ if (port < 1024 || port > 65535) {
+ throw new IllegalArgumentException("Port should be in 1024~65535!");
+ }
+ options.serverPort = port;
+ }
+ }, new Option(of("-t", "--connection-timeout"), "timeout-ms",
+ "The connection timeout of JBooster server. Default: " + CONNECTION_TIMEOUT + " ms.") {
+ @Override
+ protected void process(Options options, String arg) {
+ int timeout = Integer.parseInt(arg);
+ if (timeout <= 0) {
+ throw new IllegalArgumentException("Timeout should be greater than 0!");
+ }
+ options.connectionTimeout = timeout;
+ }
+ }, new Option(of("--unused-cleanup-timeout"), "timeout-ms",
+ "The cleanup timeout for unused shared data. Default: " + CLEANUP_TIMEOUT + " ms (2 days).") {
+ @Override
+ protected void process(Options options, String arg) {
+ int timeout = Integer.parseInt(arg);
+ if (timeout <= 0) {
+ throw new IllegalArgumentException("Timeout should be greater than 0!");
+ }
+ options.cleanupTimeout = timeout;
+ }
+ }, new Option(of("--cache-path"), "dir-path",
+ "The directory path for JBooster caches. Default: \"$HOME/.jbooster/server\".") {
+ @Override
+ protected void process(Options options, String arg) {
+ // verification is on c++ side
+ options.cachePath = arg;
+ }
+ }, new Option(of("-b", "--background"), null,
+ "Disable the scanner for inputting commands (use it if running in the background).") {
+ @Override
+ protected void process(Options options, String arg) {
+ options.interactive = false;
+ }
+ }};
+
+ private int serverPort = UNSET_PORT;
+ private int connectionTimeout = CONNECTION_TIMEOUT;
+ private int cleanupTimeout = CLEANUP_TIMEOUT;
+ private String cachePath = null; // set on C++ side
+ private boolean interactive = true;
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public int getConnectionTimeout() {
+ return connectionTimeout;
+ }
+
+ public int getCleanupTimeout() {
+ return cleanupTimeout;
+ }
+
+ public String getCachePath() {
+ return cachePath;
+ }
+
+ public boolean isInteractive() {
+ return interactive;
+ }
+
+ /**
+ * Parse the args of main().
+ *
+ * @param args args of main()
+ */
+ public void parseArgs(String[] args) {
+ for (int i = 0; i < args.length; ++i) {
+ String[] tmp = args[i].split("=", 2);
+ String op = tmp[0];
+ String arg = tmp.length == 2 ? tmp[1] : null;
+ Optional<Option> opt = findOption(op);
+
+ if (opt.isEmpty()) {
+ onEmptyArg(op, args[i]);
+ return;
+ }
+
+ Option option = opt.get();
+ if (option.needArg()) {
+ if (arg == null) {
+ ++i;
+ if (i >= args.length) {
+ throw new IllegalArgumentException("The option \"" + op + "\" needs argument!");
+ }
+ arg = args[i];
+ }
+ } else if (arg != null) {
+ throw new IllegalArgumentException("The option \"" + op + "\" does not need any argument!");
+ }
+
+ option.process(this, arg);
+ }
+
+ checkIllegalArgruments();
+ onAllCmdParsed();
+ }
+
+ public static void printHelp() {
+ StringBuilder res = new StringBuilder();
+ res.append("Usage:");
+ res.append(System.lineSeparator());
+
+ final int argBorder = 2; // '<' & '>'
+ int maxLenOfAliases = 0;
+ int maxLenOfArg = 0;
+ for (Option option : OPTIONS) {
+ int lenAliases = Arrays.stream(option.aliases).mapToInt(String::length).sum()
+ + (2 * (option.aliases.length - 1));
+ if (option.aliases[0].startsWith("--")) { // no short alias
+ lenAliases += 4;
+ }
+ maxLenOfAliases = Math.max(maxLenOfAliases, lenAliases);
+ maxLenOfArg = Math.max(maxLenOfArg, option.arg == null ? 0 : (option.arg.length() + argBorder));
+ }
+
+ final int margin1 = 1;
+ final int margin2 = 2;
+ for (Option option : OPTIONS) {
+ res.append(" ");
+ StringBuilder sb = new StringBuilder();
+ if (option.aliases[0].startsWith("--")) {
+ sb.append(" ");
+ }
+ for (String alias : option.aliases) {
+ sb.append(alias).append(", ");
+ }
+ sb.setLength(sb.length() - 2); // the last ", "
+ res.append(sb);
+ res.append(" ".repeat(maxLenOfAliases + margin1 - sb.length()));
+ res.append(option.arg == null ? "" : ("<" + option.arg + ">")); // argBorder
+ int spacePadding = maxLenOfArg + margin2 - (option.arg == null ? 0 : (option.arg.length() + argBorder));
+ res.append(" ".repeat(spacePadding));
+ res.append(option.comment);
+ res.append(System.lineSeparator());
+ }
+ if (LOGGER.isLoggable(INFO)) {
+ LOGGER.log(INFO, "{0}", res);
+ } else {
+ System.out.println(res);
+ }
+ }
+
+ private void onEmptyArg(String op, String mainArg) {
+ if (op.startsWith("-X") || op.startsWith("-D")) {
+ String msg = "The JVM option \"" + mainArg + "\" should be written as "
+ + "\"-J" + mainArg + "\" on jbooster!";
+ throw new IllegalArgumentException(msg);
+ } else {
+ throw new IllegalArgumentException("Unknown option \"" + mainArg + "\" on jbooster!");
+ }
+ }
+
+ private void checkIllegalArgruments() {
+ Properties properties = System.getProperties();
+ for (String key : properties.stringPropertyNames()) {
+ String value = properties.getProperty(key, "");
+ String msg = "The graal option -J-D" + key + " should not set on jbooster!";
+ if (key.startsWith("graal.")) {
+ if ("graal.RemoveNeverExecutedCode".equals(key) ||
+ "graal.UseExceptionProbability".equals(key)) {
+ if (!"false".equals(value)) {
+ throw new IllegalArgumentException(msg);
+ }
+ } else {
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ }
+ }
+
+ private void onAllCmdParsed() {
+ if (serverPort == UNSET_PORT) {
+ throw new IllegalArgumentException("The option \"--server-port\" (or \"-p\") must be set!");
+ }
+ }
+
+ private static Optional<Option> findOption(String arg) {
+ for (Option option : OPTIONS) {
+ if (option.matches(arg)) {
+ return Optional.of(option);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private static String[] of(String... aliases) {
+ return aliases;
+ }
+
+ abstract static class Option {
+ final String[] aliases;
+ final String arg;
+ final String comment;
+
+ Option(String[] aliases, String arg, String comment) {
+ if (aliases == null || aliases.length == 0) {
+ throw new IllegalArgumentException("aliases should contain at least one alias");
+ }
+ this.aliases = aliases;
+ this.arg = arg;
+ this.comment = comment;
+ }
+
+ boolean matches(String op) {
+ for (String alias : aliases) {
+ if (alias.equals(op)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean needArg() {
+ return arg != null;
+ }
+
+ abstract void process(Options options, String arg);
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/jdk/jbooster/api/JBoosterStartupSignal.java b/src/jdk.jbooster/share/classes/jdk/jbooster/api/JBoosterStartupSignal.java
new file mode 100644
index 000000000..af6177a08
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/jdk/jbooster/api/JBoosterStartupSignal.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+package jdk.jbooster.api;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+
+import java.lang.System.Logger;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static java.lang.System.Logger.Level.ERROR;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * This class is client-side only. All the other classes in jdk.jbooster are
+ * server-side only. So please keep this class independent of other classes
+ * in jdk.jbooster.
+ */
+public class JBoosterStartupSignal {
+ private static final Logger LOGGER = System.getLogger(JBoosterStartupSignal.class.getName());
+
+ private static final AtomicBoolean invoked = new AtomicBoolean(false);
+
+ private JBoosterStartupSignal() {}
+
+ /**
+ * Invoke this method after the program startup phase is complete.
+ * @see CallbackMethodVisitor#visitInsn(int)
+ */
+ public static void startupCallback() {
+ // can only be invoked once
+ if (!invoked.compareAndSet(false, true)) {
+ return;
+ }
+
+ // recheck the caller
+ Optional<StackWalker.StackFrame> frameOptional = StackWalker.getInstance()
+ .walk(stream -> stream.skip(1).findFirst());
+ if (frameOptional.isEmpty()) {
+ return;
+ }
+ StackWalker.StackFrame frame = frameOptional.get();
+
+ String expectedClassName = System.getProperty("jdk.jbooster.startup_klass_name");
+ String expectedMethodName = System.getProperty("jdk.jbooster.startup_method_name");
+ String expectedMethodSignature = System.getProperty("jdk.jbooster.startup_method_signature");
+ if (expectedClassName != null) {
+ expectedClassName = expectedClassName.replace('/', '.');
+ }
+
+ if (!Objects.equals(expectedClassName, frame.getClassName())
+ || !Objects.equals(expectedMethodName, frame.getMethodName())) {
+ return;
+ }
+ if (expectedMethodSignature != null && !expectedMethodSignature.equals(frame.getDescriptor())) {
+ return;
+ }
+
+ // callback logic in the new thread
+ Thread thread = new Thread(() -> {
+ System.loadLibrary("jbooster");
+ startupNativeCallback();
+ });
+ thread.setName("JBooster Startup");
+ thread.setDaemon(true);
+ thread.setUncaughtExceptionHandler(((t, e) -> {
+ LOGGER.log(ERROR, "Failed to call the startup callback.");
+ }));
+ thread.start();
+ }
+
+ /**
+ * Append the static invocation of startupCallBack() at the end of the target method.
+ * This method is invoked only in C++.
+ * @param originBytecode the origin bytecode bytes of the target class
+ * @param methodName the name of the target method
+ * @param methodSignature the signature of the method (something like "(IIILjava/lang/Object;)Z").
+ * PS: In Hotspot it is called "signature" but in ASM it is called "descriptor".
+ * @return the modified bytecode bytes
+ */
+ private static byte[] injectStartupCallback(byte[] originBytecode, String methodName, String methodSignature) {
+ ClassReader cr = new ClassReader(originBytecode);
+ ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ CallbackClassVisitor cv = new CallbackClassVisitor(cw, methodName, methodSignature);
+ cr.accept(cv, 0);
+ return cv.isInjected() ? cw.toByteArray() : null;
+ }
+
+ private static native void startupNativeCallback();
+
+ private static final class CallbackClassVisitor extends ClassVisitor {
+ private final String methodName;
+ private final String methodSignature;
+
+ private boolean injected;
+
+ public CallbackClassVisitor(ClassWriter classVisitor, String methodName, String methodSignature) {
+ super(ASM7, classVisitor);
+ this.methodName = methodName;
+ this.methodSignature = methodSignature;
+ this.injected = false;
+ }
+
+ public boolean isInjected() {
+ return injected;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor,
+ String signature, String[] exceptions) {
+ if (!methodName.equals(name) || (methodSignature != null && !methodSignature.equals(descriptor))) {
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ injected = true;
+ MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
+ return new CallbackMethodVisitor(mv);
+ }
+ }
+
+ private static final class CallbackMethodVisitor extends MethodVisitor {
+ public CallbackMethodVisitor(MethodVisitor methodVisitor) {
+ super(ASM7, methodVisitor);
+ }
+
+ /**
+ * @see JBoosterStartupSignal#startupCallback()
+ */
+ @Override
+ public void visitInsn(int opcode) {
+ if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
+ mv.visitMethodInsn(INVOKESTATIC,
+ JBoosterStartupSignal.class.getName().replace('.', '/'),
+ "startupCallback", "()V", false);
+ }
+ mv.visitInsn(opcode);
+ }
+ }
+}
diff --git a/src/jdk.jbooster/share/classes/module-info.java b/src/jdk.jbooster/share/classes/module-info.java
new file mode 100644
index 000000000..5a7c685b5
--- /dev/null
+++ b/src/jdk.jbooster/share/classes/module-info.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+/**
+ * Defines the tools and API for JBooster.
+ *
+ * @moduleGraph
+ * @since 9
+ */
+module jdk.jbooster {
+ requires jdk.internal.vm.ci;
+ requires jdk.unsupported;
+ requires java.logging;
+
+ exports jdk.jbooster.api;
+}
diff --git a/src/jdk.jbooster/share/native/libjbooster/JBooster.c b/src/jdk.jbooster/share/native/libjbooster/JBooster.c
new file mode 100644
index 000000000..385e8780b
--- /dev/null
+++ b/src/jdk.jbooster/share/native/libjbooster/JBooster.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <jni.h>
+#include <jni_util.h>
+#include <jvm.h>
+#include "jdk_jbooster_JBooster.h"
+
+JNIEXPORT void JNICALL
+Java_jdk_jbooster_JBooster_initInVM(JNIEnv * env, jclass unused, jint server_port, jint connection_timeout, jint cleanup_timeout, jstring cache_path)
+{
+ JVM_JBoosterInitVM(env, server_port, connection_timeout, cleanup_timeout, cache_path);
+}
+
+JNIEXPORT void JNICALL
+Java_jdk_jbooster_JBooster_handleConnection(JNIEnv * env, jclass unused, jint connection_fd)
+{
+ JVM_JBoosterHandleConnection(env, connection_fd);
+}
+
+JNIEXPORT void JNICALL
+Java_jdk_jbooster_JBooster_printStoredClientData(JNIEnv * env, jclass unused, jboolean print_all)
+{
+ JVM_JBoosterPrintStoredClientData(env, print_all);
+}
diff --git a/src/jdk.jbooster/share/native/libjbooster/JBoosterStartupSignal.c b/src/jdk.jbooster/share/native/libjbooster/JBoosterStartupSignal.c
new file mode 100644
index 000000000..da3b1fc52
--- /dev/null
+++ b/src/jdk.jbooster/share/native/libjbooster/JBoosterStartupSignal.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 <jni.h>
+#include <jni_util.h>
+#include <jvm.h>
+#include "jdk_jbooster_api_JBoosterStartupSignal.h"
+
+JNIEXPORT void JNICALL
+Java_jdk_jbooster_api_JBoosterStartupSignal_startupNativeCallback(JNIEnv * env, jclass unused)
+{
+ JVM_JBoosterStartupNativeCallback(env);
+}
diff --git a/test/hotspot/gtest/jbooster/test_net.cpp b/test/hotspot/gtest/jbooster/test_net.cpp
new file mode 100644
index 000000000..a2c45be5e
--- /dev/null
+++ b/test/hotspot/gtest/jbooster/test_net.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "precompiled.hpp"
+#include "utilities/macros.hpp"
+
+#if INCLUDE_JBOOSTER
+
+#include "jbooster/net/message.inline.hpp"
+#include "jbooster/net/messageBuffer.inline.hpp"
+#include "jbooster/net/serializationWrappers.inline.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "runtime/os.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/growableArray.hpp"
+#include "unittest.hpp"
+
+static int try_catch_func(int i) {
+ JB_RETURN(i);
+ EXPECT_EQ(i, 0);
+ return 0;
+}
+
+class CustomData1: public StackObj {
+public:
+ enum {
+ V0, V1, V2
+ };
+ enum {
+ M0, M1, M2, M3, M4, M5, M6
+ };
+
+private:
+ int _test_mode;
+ int _v1, _v2;
+
+public:
+ CustomData1(int test_mode, bool is_serialize): _test_mode(test_mode),
+ _v1(is_serialize ? V1 : V0),
+ _v2(is_serialize ? V2 : V0) {}
+
+ int v1() { return _v1; }
+ int v2() { return _v2; }
+
+ int serialize(MessageBuffer& buf) const {
+ buf.serialize_no_meta(_v1);
+ buf.serialize_no_meta(_v2);
+ return 0;
+ }
+
+ int deserialize(MessageBuffer& buf) {
+ buf.deserialize_ref_no_meta(_v1);
+ if (_test_mode == M0) {
+ return 0;
+ } else if (_test_mode == M1) {
+ return JBErr::DESER_TERMINATION;
+ }
+ buf.deserialize_ref_no_meta(_v2);
+ if (_test_mode == M2) {
+ return 0;
+ } else if (_test_mode == M3) {
+ return JBErr::DESER_TERMINATION;
+ } else {
+ int v3;
+ buf.deserialize_ref_no_meta(v3);
+ if (_test_mode == M4) {
+ return 0;
+ } else if (_test_mode == M5) {
+ return JBErr::DESER_TERMINATION;
+ }
+ }
+ return JBErr::BAD_ARG_DATA;
+ }
+};
+
+DECLARE_SERIALIZER_INTRUSIVE(CustomData1);
+
+static void init_recv_message(Message& recv, Message& send) {
+ recv.expand_buf_if_needed(send.cur_buf_offset(), 0);
+ memcpy(recv.buf_beginning(), send.buf_beginning(), send.cur_buf_offset());
+}
+
+static void copy_message_buffer(MessageBuffer& to, MessageBuffer& from) {
+ to.expand_if_needed(from.cur_offset(), 0);
+ memcpy(to.buf(), from.buf(), from.cur_offset());
+}
+
+TEST(JBoosterNet, try_catch) {
+ int i;
+ for (i = 0; i < 9; ++i) {
+ JB_TRY_BREAKABLE {
+ if (i == 5 || i == 6) continue;
+ JB_THROW(try_catch_func(i));
+ EXPECT_EQ(i, 0);
+ } JB_TRY_BREAKABLE_END
+ JB_CATCH(3, 5, 7) {
+ EXPECT_TRUE(JB_ERR == 3 || JB_ERR == 7);
+ }
+ JB_CATCH(2, 4, 6) {
+ EXPECT_TRUE(JB_ERR == 2 || JB_ERR == 4);
+ }
+ JB_CATCH_REST() {
+ EXPECT_TRUE(JB_ERR == 1 || JB_ERR == 8);
+ } JB_CATCH_END;
+ }
+ EXPECT_EQ(i, 9);
+
+ for (i = 0; i < 3; ++i) {
+ JB_TRY {
+ JB_THROW(try_catch_func(i));
+ EXPECT_EQ(i, 0);
+ } JB_TRY_END
+ JB_CATCH_REST() {
+ EXPECT_TRUE(JB_ERR == 1 || JB_ERR == 2);
+ } JB_CATCH_END;
+ }
+ EXPECT_EQ(i, 3);
+}
+
+TEST(JBoosterNet, serializationn_basics) {
+ char cache[1024];
+ uint32_t cache_size;
+ { MessageBuffer buf(SerializationMode::SERIALIZE);
+ char c1 = '6';
+ int i1 = 1234;
+ int64_t l1 = 900000000000000ll;
+ EXPECT_EQ(buf.serialize_no_meta(c1), 0);
+ EXPECT_EQ(buf.cur_offset(), 1u);
+ EXPECT_EQ(buf.serialize_no_meta(i1), 0);
+ EXPECT_EQ(buf.cur_offset(), 5u);
+ EXPECT_EQ(buf.serialize_no_meta(l1), 0);
+ EXPECT_EQ(buf.cur_offset(), 13u);
+
+ uint32_t u1 = 2468;
+ const char* s1 = nullptr;
+ const char* s2 = "hello";
+ const char* s3 = "world!";
+ EXPECT_EQ(buf.serialize_with_meta(&u1), 0);
+ EXPECT_EQ(buf.cur_offset(), 21u);
+ EXPECT_EQ(buf.serialize_with_meta(&s1), 0);
+ EXPECT_EQ(buf.cur_offset(), 29u);
+ EXPECT_EQ(buf.serialize_with_meta(&s2), 0);
+ EXPECT_EQ(buf.cur_offset(), 42u);
+ EXPECT_EQ(buf.serialize_with_meta(&s3), 0);
+ EXPECT_EQ(buf.cur_offset(), 56u);
+
+ cache_size = buf.cur_offset();
+ memcpy(cache, buf.buf(), cache_size);
+ }
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ buf.expand_if_needed(cache_size, 0);
+ memcpy(buf.buf(), cache, cache_size);
+ char c1;
+ int i1;
+ int64_t l1;
+ EXPECT_EQ(buf.deserialize_ref_no_meta(c1), 0);
+ EXPECT_EQ(buf.cur_offset(), 1u);
+ EXPECT_EQ(buf.deserialize_ref_no_meta(i1), 0);
+ EXPECT_EQ(buf.cur_offset(), 5u);
+ EXPECT_EQ(buf.deserialize_ref_no_meta(l1), 0);
+ EXPECT_EQ(buf.cur_offset(), 13u);
+ EXPECT_EQ(c1, '6');
+ EXPECT_EQ(i1, 1234);
+ EXPECT_EQ(l1, 900000000000000ll);
+
+ uint32_t u1;
+ const char* s1 = nullptr;
+ char s2[6];
+ StringWrapper s3;
+ EXPECT_EQ(buf.deserialize_with_meta(&u1), 0);
+ EXPECT_EQ(buf.cur_offset(), 21u);
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ EXPECT_EQ(buf.cur_offset(), 29u);
+ EXPECT_EQ(buf.deserialize_with_meta(&s2), 0);
+ EXPECT_EQ(buf.cur_offset(), 42u);
+ EXPECT_EQ(buf.deserialize_with_meta(&s3), 0);
+ EXPECT_EQ(buf.cur_offset(), 56u);
+ EXPECT_EQ(u1, 2468u);
+ EXPECT_STREQ(s1, nullptr);
+ EXPECT_STREQ(s2, "hello");
+ EXPECT_STREQ(s3.get_string(), "world!");
+ EXPECT_EQ(s3.size(), 6u);
+ }
+}
+
+TEST_VM(JBoosterNet, serializationn_string) {
+ ResourceMark rm;
+ MessageBuffer buf0(SerializationMode::SERIALIZE);
+ const char* ss1 = "123";
+ const char* ss2 = nullptr;
+ char ss3[] = { '4', '5', '6', '\n' };
+ EXPECT_EQ(buf0.serialize_with_meta(&ss1), 0);
+ EXPECT_EQ(buf0.serialize_with_meta(&ss2), 0);
+ EXPECT_EQ(buf0.serialize_with_meta(&ss3), 0);
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ copy_message_buffer(buf, buf0);
+ char* s1 = nullptr;
+ char* s2 = nullptr;
+ const char* s3 = nullptr;
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ EXPECT_EQ(buf.deserialize_with_meta(&s2), 0);
+ EXPECT_EQ(buf.deserialize_with_meta(&s3), 0);
+ EXPECT_STREQ(ss1, s1);
+ EXPECT_EQ(ss2, s2);
+ EXPECT_STREQ(ss3, s3);
+ }
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ copy_message_buffer(buf, buf0);
+ char s1[1];
+ ASSERT_DEATH(buf.deserialize_with_meta(&s1), "");
+ }
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ copy_message_buffer(buf, buf0);
+ char s1[64];
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ EXPECT_STREQ(ss1, s1);
+ ASSERT_DEATH(buf.deserialize_with_meta(&s1), "");
+ }
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ copy_message_buffer(buf, buf0);
+ StringWrapper s1, s2, s3;
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ EXPECT_EQ(buf.deserialize_with_meta(&s2), 0);
+ EXPECT_EQ(buf.deserialize_with_meta(&s3), 0);
+ EXPECT_STREQ(ss1, s1.get_string());
+ EXPECT_EQ(ss2, s2.export_string());
+ EXPECT_STREQ(ss3, s3.export_string());
+ }
+
+ buf0.reset_cur_offset();
+ StringWrapper ss4(nullptr);
+ StringWrapper ss5("");
+ EXPECT_EQ(buf0.serialize_with_meta(&ss4), 0);
+ EXPECT_EQ(buf0.serialize_with_meta(&ss5), 0);
+
+ { MessageBuffer buf(SerializationMode::DESERIALIZE);
+ copy_message_buffer(buf, buf0);
+ const char* s1 = nullptr;
+ char* s2 = nullptr;
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ EXPECT_EQ(buf.deserialize_with_meta(&s2), 0);
+ EXPECT_EQ(nullptr, s1);
+ EXPECT_STREQ("", s2);
+ }
+}
+
+TEST(JBoosterNet, serializationn_crash) {
+ int err;
+ MessageBuffer buf(SerializationMode::BOTH);
+ const char* s = "hello";
+ EXPECT_EQ(buf.serialize_with_meta(&s), 0);
+
+ char s1[6];
+ buf.reset_cur_offset();
+ EXPECT_EQ(buf.deserialize_with_meta(&s1), 0);
+ ASSERT_STREQ(s1, "hello");
+
+ char s2[5];
+ buf.reset_cur_offset();
+ bool old = SuppressFatalErrorMessage;
+ SuppressFatalErrorMessage = true;
+ ASSERT_DEATH(buf.deserialize_with_meta(&s2), "");
+ SuppressFatalErrorMessage = old;
+}
+
+TEST(JBoosterNet, serializationn_wrappers) {
+ MessageBuffer buf(SerializationMode::BOTH);
+ uint32_t mem_size = 16u * 1024;
+ {
+ StringWrapper s1("1");
+ StringWrapper s2("22");
+ StringWrapper s3("333");
+ StringWrapper s4("4444");
+ GrowableArray<StringWrapper*> ga(4, mtJBooster);
+ ga.append(&s1);
+ ga.append(&s2);
+ ga.append(&s3);
+ ga.append(&s4);
+ ArrayWrapper<StringWrapper> aw(&ga);
+ EXPECT_EQ(buf.serialize_with_meta(&aw), 0);
+ EXPECT_EQ(buf.cur_offset(), 0u + (4 + 4) + (1 + 2 + 3 + 4 + 4 * (4 + 4)));
+
+ char* mem = NEW_C_HEAP_ARRAY(char, mem_size, mtJBooster);
+ memset(mem, 0x68, mem_size);
+ MemoryWrapper mw(mem, mem_size);
+ EXPECT_EQ(buf.serialize_with_meta(&mw), 0);
+ FREE_C_HEAP_ARRAY(char, mem);
+ }
+
+ buf.reset_cur_offset();
+
+ {
+ ArrayWrapper<StringWrapper> aw(false);
+ EXPECT_EQ(buf.deserialize_with_meta(&aw), 0);
+ EXPECT_STREQ(aw.get(0)->get_string(), "1");
+ EXPECT_STREQ(aw.get(1)->get_string(), "22");
+ EXPECT_STREQ(aw.get(2)->get_string(), "333");
+ EXPECT_STREQ(aw.get(3)->get_string(), "4444");
+ StringWrapper* sws = aw.get_array<StringWrapper>();
+ EXPECT_STREQ(sws[0].get_string(), "1");
+ EXPECT_STREQ(sws[1].get_string(), "22");
+ EXPECT_STREQ(sws[2].get_string(), "333");
+ EXPECT_STREQ(sws[3].get_string(), "4444");
+
+ MemoryWrapper mw;
+ EXPECT_EQ(buf.deserialize_with_meta(&mw), 0);
+ EXPECT_EQ(mw.size(), mem_size);
+ const int* mem = (const int*) mw.get_memory();
+ int size = (int) mw.size() / 4;
+ for (int i = 0; i < size; ++i) {
+ ASSERT_EQ(mem[i], 0x68686868);
+ }
+ }
+}
+
+static void create_test_file_for_file_wrapper(const char* file_name) {
+ uint32_t mem_size = FileWrapper::MAX_SIZE_PER_TRANS * 2
+ + FileWrapper::MAX_SIZE_PER_TRANS / 2;
+ char* mem = NEW_C_HEAP_ARRAY(char, mem_size, mtJBooster);
+ memset(mem, 0xc4, mem_size);
+
+ errno = 0;
+ int fd = os::open(file_name, O_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0666);
+ ASSERT_TRUE(fd >= 0);
+ ASSERT_EQ(errno, 0);
+ uint32_t left = mem_size;
+ do {
+ uint32_t write_size = (uint32_t) os::write(fd, mem + mem_size - left, left);
+ left -= write_size;
+ } while (left > 0);
+ os::close(fd);
+ FREE_C_HEAP_ARRAY(char, mem);
+}
+
+TEST(JBoosterNet, serializationn_file_wrapper) {
+ MessageBuffer buf(SerializationMode::BOTH);
+ const char* file_name1 = "tmp_jbooster_net_file_wrapper.tmp1";
+ const char* file_name2 = "tmp_jbooster_net_file_wrapper.tmp2";
+ create_test_file_for_file_wrapper(file_name1);
+
+ {
+ FileWrapper fw(file_name1, SerializationMode::SERIALIZE);
+ int times = 0;
+ while (!fw.is_file_all_handled()) {
+ EXPECT_EQ(buf.serialize_with_meta(&fw), 0);
+ ++times;
+ }
+ EXPECT_EQ(times, 3);
+ }
+
+ buf.reset_cur_offset();
+
+ {
+ FileWrapper fw(file_name2, SerializationMode::DESERIALIZE);
+ int times = 0;
+ while (!fw.is_file_all_handled()) {
+ EXPECT_EQ(buf.deserialize_with_meta(&fw), 0);
+ ++times;
+ }
+ EXPECT_EQ(times, 3);
+ }
+ EXPECT_TRUE(FileUtils::is_same(file_name1, file_name2));
+ FileUtils::remove(file_name1);
+ FileUtils::remove(file_name2);
+}
+
+TEST(JBoosterNet, expansion_of_message_buffer) {
+ MessageBuffer buf(SerializationMode::SERIALIZE);
+ ASSERT_EQ(buf.buf_size(), 4096u);
+ uint64_t v1 = 4096;
+ for (int i = 0; i < 4096; ++i) {
+ buf.serialize_no_meta(v1);
+ }
+ EXPECT_EQ(buf.cur_offset(), 32768u);
+ EXPECT_EQ(buf.buf_size(), 32768u);
+ char v2 = 'a';
+ buf.serialize_no_meta(v2);
+ EXPECT_EQ(buf.cur_offset(), 32769u);
+ EXPECT_EQ(buf.buf_size(), 65536u);
+}
+
+TEST(JBoosterNet, message_sending_and_receiving) {
+ Message send(SerializationMode::SERIALIZE);
+ Message recv(SerializationMode::DESERIALIZE);
+
+ const int arr_size = 66u;
+
+ int i1 = 1;
+ char c1 = '2';
+ const char* s1 = "hello";
+ const char* ns1 = nullptr;
+ uintptr_t p1 = 3;
+ GrowableArray<int> ga(arr_size, mtJBooster);
+ for (int i = 0; i < arr_size; ++i) {
+ ga.append(i);
+ }
+ ArrayWrapper<int> aw1(&ga);
+ send.serialize(&i1, &c1, &s1, &ns1, &p1, &aw1);
+ init_recv_message(recv, send);
+
+ int i2 = 0;
+ char c2 = '0';
+ char s2[6];
+ const char* ns2 = "should be null";
+ uintptr_t p2 = 0;
+ ArrayWrapper<int> aw2(false);
+ recv.deserialize(&i2, &c2, &s2, &ns2, &p2, &aw2);
+
+ EXPECT_EQ(i2, i1);
+ EXPECT_EQ(c2, c1);
+ EXPECT_STREQ(s2, s1);
+ EXPECT_EQ(ns2, ns1);
+ EXPECT_EQ(p2, p1);
+ EXPECT_EQ(aw2.size(), arr_size);
+ for (int i = 0; i < arr_size; ++ i) {
+ ASSERT_EQ(*aw2.get(i), i);
+ }
+}
+
+TEST(JBoosterNet, deserialization_exceptions) {
+ Message send(SerializationMode::SERIALIZE);
+
+ {
+ CustomData1 d(-1, true);
+ EXPECT_EQ(send.serialize(&d), 0);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M0, false);
+ EXPECT_EQ(recv.deserialize(&d), JBErr::BAD_ARG_SIZE);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V0);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M1, false);
+ EXPECT_EQ(recv.deserialize(&d), 0);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 2 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V0);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M2, false);
+ EXPECT_EQ(recv.deserialize(&d), 0);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 2 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V2);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M3, false);
+ EXPECT_EQ(recv.deserialize(&d), 0);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 2 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V2);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M4, false);
+ EXPECT_EQ(recv.deserialize(&d), JBErr::BAD_ARG_SIZE);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 3 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V2);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M5, false);
+ EXPECT_EQ(recv.deserialize(&d), JBErr::BAD_ARG_SIZE);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 3 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V2);
+ }
+
+ { Message recv(SerializationMode::DESERIALIZE);
+ init_recv_message(recv, send);
+ CustomData1 d(CustomData1::M6, false);
+ EXPECT_EQ(recv.deserialize(&d), JBErr::BAD_ARG_DATA);
+ EXPECT_EQ(recv.cur_buf_offset(), MessageConst::arg_meta_size + 3 * sizeof(int));
+ EXPECT_EQ(d.v1(), CustomData1::V1);
+ EXPECT_EQ(d.v2(), CustomData1::V2);
+ }
+}
+
+#endif // INCLUDE_JBOOSTER
diff --git a/test/hotspot/gtest/jbooster/test_util.cpp b/test/hotspot/gtest/jbooster/test_util.cpp
new file mode 100644
index 000000000..ab7fd9b39
--- /dev/null
+++ b/test/hotspot/gtest/jbooster/test_util.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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 "precompiled.hpp"
+#include "utilities/macros.hpp"
+
+#if INCLUDE_JBOOSTER
+
+#include "classfile/symbolTable.hpp"
+#include "jbooster/utilities/concurrentHashMap.inline.hpp"
+#include "jbooster/utilities/debugUtils.inline.hpp"
+#include "jbooster/utilities/fileUtils.hpp"
+#include "jbooster/utilities/scalarHashMap.inline.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/thread.hpp"
+#include "unittest.hpp"
+#include "utilities/exceptions.hpp"
+#include "utilities/stringUtils.hpp"
+
+class ATestClass {};
+
+template <typename T>
+static const char* get_type_name(T t) {
+ EXPECT_TRUE(sizeof(t) >= 0);
+ return DebugUtils::type_name<T>();
+}
+
+static void write_file(const char* file_path, const char* content) {
+ int fd = os::open(file_path, O_BINARY | O_WRONLY | O_CREAT, 0666);;
+ os::write(fd, content, strlen(content) + 1);
+ os::close(fd);
+}
+
+TEST_VM(JBoosterUtil, string) {
+ JavaThread* THREAD = JavaThread::current();
+ ResourceMark rm(THREAD);
+ EXPECT_LT(StringUtils::compare(nullptr, "") , 0);
+ EXPECT_GT(StringUtils::compare("", nullptr) , 0);
+ EXPECT_GT(StringUtils::compare("2", "1") , 0);
+ EXPECT_STREQ(StringUtils::copy_to_resource("123") , "123");
+ EXPECT_STREQ(StringUtils::copy_to_resource("1234", 5) , "1234");
+ EXPECT_STREQ(StringUtils::copy_to_heap("123", mtJBooster) , "123");
+ EXPECT_STREQ(StringUtils::copy_to_heap("1234", 5, mtJBooster) , "1234");
+ EXPECT_STREQ(StringUtils::str(SymbolTable::new_symbol("456")), "456");
+ EXPECT_STREQ(StringUtils::str(nullptr), "<null>");
+}
+
+TEST(JBoosterUtil, debug) {
+ int a = 1;
+ char b = 2;
+ ATestClass c;
+ // ignore the memory leak of the returned value
+ EXPECT_STREQ("int", get_type_name(a));
+ EXPECT_STREQ("char", get_type_name(b));
+ EXPECT_STREQ("ATestClass", get_type_name(c));
+}
+
+TEST(JBoosterUtil, file) {
+ const char prefix[] = "gtest-jbooster-tmp";
+ const int prefix_len = sizeof(prefix) - 1;
+
+ FileUtils::mkdir("gtest-jbooster-tmp1");
+ FileUtils::mkdir("gtest-jbooster-tmp2");
+ FileUtils::mkdir("gtest-jbooster-tmp3");
+
+ EXPECT_TRUE(FileUtils::exists("gtest-jbooster-tmp2"));
+ FileUtils::remove("gtest-jbooster-tmp2");
+ EXPECT_FALSE(FileUtils::exists("gtest-jbooster-tmp2"));
+
+ EXPECT_TRUE(FileUtils::exists("gtest-jbooster-tmp3"));
+ EXPECT_FALSE(FileUtils::exists("gtest-jbooster-tmp4"));
+ FileUtils::move("gtest-jbooster-tmp3", "gtest-jbooster-tmp4");
+ EXPECT_TRUE(FileUtils::exists("gtest-jbooster-tmp4"));
+ EXPECT_FALSE(FileUtils::exists("gtest-jbooster-tmp3"));
+
+ write_file("gtest-jbooster-tmp5", "12345");
+ write_file("gtest-jbooster-tmp6", "12345");
+ EXPECT_TRUE(FileUtils::is_same("gtest-jbooster-tmp5", "gtest-jbooster-tmp6"));
+ write_file("gtest-jbooster-tmp6", "123456");
+ EXPECT_FALSE(FileUtils::is_same("gtest-jbooster-tmp5", "gtest-jbooster-tmp6"));
+
+ EXPECT_TRUE(FileUtils::is_same("gtest-jbooster-tmp5", "12345", 6));
+ EXPECT_FALSE(FileUtils::is_same("gtest-jbooster-tmp5", "12346", 6));
+ EXPECT_FALSE(FileUtils::is_same("gtest-jbooster-tmp5", "123456", 7));
+
+ EXPECT_FALSE(FileUtils::is_file("gtest-jbooster-tmp4"));
+ EXPECT_TRUE(FileUtils::is_dir("gtest-jbooster-tmp4"));
+ EXPECT_TRUE(FileUtils::is_file("gtest-jbooster-tmp5"));
+ EXPECT_FALSE(FileUtils::is_dir("gtest-jbooster-tmp5"));
+
+ FileUtils::ListDir ls(".");
+ int file_cnt = 0;
+ int dir_cnt = 0;
+ while (ls.next()) {
+ if (strncmp(ls.name(), prefix, prefix_len) != 0) {
+ continue;
+ }
+ EXPECT_EQ((int) strlen(ls.name()), prefix_len + 1);
+ const char last = ls.name()[prefix_len];
+ if (ls.is_dir()) {
+ EXPECT_TRUE(last == '1' || last == '4');
+ ++dir_cnt;
+ }
+ if (ls.is_file()) {
+ EXPECT_TRUE(last == '5' || last == '6');
+ ++file_cnt;
+ }
+ }
+ EXPECT_EQ(dir_cnt, 2);
+ EXPECT_EQ(file_cnt, 2);
+ EXPECT_TRUE(FileUtils::remove("gtest-jbooster-tmp1"));
+ EXPECT_TRUE(FileUtils::remove("gtest-jbooster-tmp4"));
+ EXPECT_TRUE(FileUtils::remove("gtest-jbooster-tmp5"));
+ EXPECT_TRUE(FileUtils::remove("gtest-jbooster-tmp6"));
+ EXPECT_FALSE(FileUtils::remove("gtest-jbooster-tmp2"));
+ EXPECT_FALSE(FileUtils::remove("gtest-jbooster-tmp7"));
+
+#ifdef LINUX
+ EXPECT_EQ(FileUtils::separator_char(), '/');
+#else
+ ASSERT_TRUE(false);
+#endif // LINUX
+}
+
+TEST_VM(JBoosterUtil, concurrent_map) {
+ JavaThread* THREAD = JavaThread::current();
+ ConcurrentHashMap<int, char, mtJBooster> map;
+ EXPECT_FALSE(map.contains(1, THREAD));
+ EXPECT_EQ(map.get(1, THREAD), nullptr);
+ char v2 = '2';
+ char v3 = '3';
+ EXPECT_EQ(*map.put_if_absent(1, v2, THREAD), '2');
+ EXPECT_EQ(*map.put_if_absent(1, v3, THREAD), '2');
+ EXPECT_TRUE(map.contains(1, THREAD));
+ EXPECT_EQ(*map.get(1, THREAD), '2');
+}
+
+TEST(JBoosterUtil, scalar_map) {
+ ScalarHashSet<int, mtJBooster> set;
+ EXPECT_EQ(set.size(), 0);
+ EXPECT_FALSE(set.has(1));
+ EXPECT_TRUE(set.add(1));
+ EXPECT_FALSE(set.add(1));
+ EXPECT_TRUE(set.has(1));
+ EXPECT_EQ(set.size(), 1);
+ int final_size = 100000;
+ int offset = 1000;
+ for (int i = 1; i < final_size; ++i) {
+ EXPECT_TRUE(set.add(i + offset));
+ }
+ ASSERT_EQ(set.size(), final_size);
+ for (int i = 1; i < final_size; ++i) {
+ EXPECT_TRUE(set.has(i + offset));
+ }
+ EXPECT_FALSE(set.has(final_size + offset));
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+ EXPECT_FALSE(set.has(1));
+ EXPECT_FALSE(set.has(final_size / 2 + offset));
+}
+
+#endif // INCLUDE_JBOOSTER
diff --git a/test/jdk/tools/jbooster/JBoosterCmdTest.java b/test/jdk/tools/jbooster/JBoosterCmdTest.java
new file mode 100644
index 000000000..4a0fe7e7b
--- /dev/null
+++ b/test/jdk/tools/jbooster/JBoosterCmdTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.test.lib.Asserts.*;
+
+/*
+ * jbooster testing.
+ * @test
+ * @summary Test jbooster server
+ * @library ../lib
+ * /test/lib
+ * @modules jdk.jbooster
+ * @build SimpleClient
+ * @run main/othervm/timeout=300 JBoosterCmdTest
+ */
+public class JBoosterCmdTest extends JBoosterTestBase {
+
+ private static void testServerPort1(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of());
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "port not set");
+ }
+
+ private static void testServerPort2(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR));
+ writeToStdin(p, "d\n");
+ writeToStdin(p, "q\n");
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "usable port");
+ }
+
+ private static void testServerPort3(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", "1"));
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "port < 1024");
+ }
+
+ private static void testServerPort4(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", "456716"));
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "port > 65535");
+ }
+
+ private static void testServerArgFormat1(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR, "-t"));
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "no arg for -t");
+ }
+
+ private static void testServerArgFormat2(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR, "-t", "16000"));
+ p.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+ exitNormallyByCommandQ(p);
+ }
+
+ private static void testServerArgFormat3(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR, "-t=16000"));
+ p.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+ exitNormallyByCommandQ(p);
+ }
+
+ private static void testServerArgFormat4(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR, "--connection-timeout", "16000"));
+ p.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+ exitNormallyByCommandQ(p);
+ }
+
+ private static void testServerArgFormat5(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", SERVER_PORT_STR, "--connection-timeout=16000"));
+ p.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+ exitNormallyByCommandQ(p);
+ }
+
+ private static void testServerArgFormat6(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("-p", "-t", "12345"));
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "no arg for -p");
+ }
+
+ private static void testServerArgFormat7(TestContext ctx) throws Exception {
+ Process p = jbooster(ctx, List.of(), List.of("--help=123"));
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "--help do not need arg");
+ }
+
+ private static void testClientArg1(TestContext ctx) throws Exception {
+ Process p = java(ctx, List.of("-XX:+UnlockExperimentalVMOptions", "-XX:+UseJBooster", "SimpleClient"));
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "no port");
+ }
+
+ private static void testClientArg2(TestContext ctx) throws Exception {
+ Process p = java(ctx, CLIENT_OFFLINE_ARGS);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+
+ private static void testClientArg3(TestContext ctx) throws Exception {
+ ArrayList<String> clientArgs = new ArrayList<>(CLIENT_OFFLINE_ARGS);
+ clientArgs.add(clientArgs.size() - 1, "-XX:JBoosterProgramName=custom");
+ Process p = java(ctx, clientArgs);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+
+ public static void main(String[] args) throws Exception {
+ testCases(JBoosterCmdTest.class);
+ }
+}
diff --git a/test/jdk/tools/jbooster/JBoosterNetTest.java b/test/jdk/tools/jbooster/JBoosterNetTest.java
new file mode 100644
index 000000000..a71368127
--- /dev/null
+++ b/test/jdk/tools/jbooster/JBoosterNetTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+import java.net.ServerSocket;
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.test.lib.Asserts.*;
+
+/*
+ * jbooster testing.
+ * @test
+ * @summary Test jbooster server
+ * @library ../lib
+ * /test/lib
+ * @modules jdk.jbooster
+ * @build SimpleClient
+ * @run main/othervm/timeout=300 JBoosterNetTest
+ */
+public class JBoosterNetTest extends JBoosterTestBase {
+
+ private static void testGood(TestContext ctx) throws Exception {
+ Process server = jbooster(ctx, List.of(), SERVER_STANDARD_ARGS);
+ server.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+
+ for (int i = 0; i < 4; ++i) {
+ Process p = java(ctx, CLIENT_STANDARD_ARGS);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+
+ exitNormallyByCommandQ(server);
+ }
+
+ private static void testCachePath(TestContext ctx) throws Exception {
+ Process server = jbooster(ctx, List.of("-Xlog:jbooster*=trace"), SERVER_STANDARD_ARGS);
+ server.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+
+ ArrayList<String> clientArgs = new ArrayList<>(CLIENT_STANDARD_ARGS);
+ clientArgs.add(clientArgs.size() - 1, "-XX:JBoosterProgramName=simple");
+ clientArgs.add(clientArgs.size() - 1, "-XX:+PrintAllClassInfo");
+ clientArgs.add(clientArgs.size() - 1, "-Xlog:jbooster*=trace");
+
+ for (int i = 0; i < 2; ++i) {
+ Process p = java(ctx, clientArgs);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+
+ deletePath(CLIENT_CACHE_PATH);
+
+ for (int i = 0; i < 2; ++i) {
+ Process p = java(ctx, clientArgs);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good after delete client cache");
+ }
+
+ deletePath(SERVER_CACHE_PATH);
+
+ for (int i = 0; i < 2; ++i) {
+ Process p = java(ctx, clientArgs);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good after delete server cache");
+ }
+
+ exitNormallyByCommandQ(server);
+ }
+
+ private static void testUsedPortServer(TestContext ctx) throws Exception {
+ try (ServerSocket ignored = new ServerSocket(SERVER_PORT)) {
+ Process p = jbooster(ctx, List.of(), SERVER_STANDARD_ARGS);
+ p.waitFor(WAIT_SHORT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 1, "port blocked");
+ }
+ }
+
+ private static void testUsedPortClient(TestContext ctx) throws Exception {
+ try (ServerSocket ignored = new ServerSocket(SERVER_PORT)) {
+ Process p = java(ctx, CLIENT_STANDARD_ARGS);
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertNotEquals(p.exitValue(), 0, "port blocked");
+ }
+ }
+
+ private static void testConcurrentConnection1(TestContext ctx) throws Exception {
+ for (int i = 0; i < 6; ++i) {
+ System.out.println("Try " + (i + 1) + ":");
+ Process server = jbooster(ctx, List.of(), SERVER_STANDARD_ARGS);
+ server.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+
+ List<Process> apps = new ArrayList<>();
+ for (int j = 0; j < 4; ++j) {
+ Process p = java(ctx, CLIENT_STANDARD_ARGS);
+ apps.add(p);
+ }
+ for (Process p : apps) {
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+
+ exitNormallyByCommandQ(server);
+ }
+ }
+
+ private static void testConcurrentConnection2(TestContext ctx) throws Exception {
+ Process server = jbooster(ctx, List.of(), SERVER_STANDARD_ARGS);
+ server.waitFor(WAIT_START_TIME, TimeUnit.SECONDS);
+
+ for (int i = 0; i < 12; ++i) {
+ System.out.println("Try " + (i + 1) + ":");
+ ArrayList<String> clientArgs = new ArrayList<>(CLIENT_STANDARD_ARGS);
+ clientArgs.add(clientArgs.size() - 1, "-XX:JBoosterProgramName=simple-" + i);
+
+ List<Process> apps = new ArrayList<>();
+ for (int j = 0; j < 4; ++j) {
+ Process p = java(ctx, clientArgs);
+ apps.add(p);
+ }
+ for (Process p : apps) {
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertEquals(p.exitValue(), 0, "good");
+ }
+ }
+
+ exitNormallyByCommandQ(server);
+ }
+
+ public static void main(String[] args) throws Exception {
+ testCases(JBoosterNetTest.class);
+ }
+}
diff --git a/test/jdk/tools/jbooster/JBoosterTestBase.java b/test/jdk/tools/jbooster/JBoosterTestBase.java
new file mode 100644
index 000000000..8f925d7f3
--- /dev/null
+++ b/test/jdk/tools/jbooster/JBoosterTestBase.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.List;
+
+import static jdk.test.lib.Asserts.*;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.Utils;
+
+/**
+ * A simpile framework that allows you to create a JBooster server or a
+ * app easily.
+ *
+ * @see JcmdBase
+ */
+public class JBoosterTestBase {
+ public static final int WAIT_START_TIME = 2;
+ public static final int WAIT_SHORT_TIME = 8;
+ public static final int WAIT_EXIT_TIME = 64;
+
+ public static final int SERVER_PORT = 41567;
+ public static final String SERVER_PORT_STR = "41567";
+
+ public static final String CLIENT_CACHE_PATH = "jbooster-cache-client";
+ public static final String SERVER_CACHE_PATH = "jbooster-cache-server";
+
+ public static final List<String> CLIENT_STANDARD_ARGS = List.of(
+ "-XX:+UnlockExperimentalVMOptions",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+UseJBooster",
+ "-XX:JBoosterPort=" + SERVER_PORT_STR,
+ "-XX:BoostStopAtLevel=4",
+ "-XX:JBoosterCachePath=" + CLIENT_CACHE_PATH,
+ "-XX:+JBoosterExitIfUnsupported",
+ "-XX:+JBoosterCrashIfNoServer",
+ "-XX:+SkipSharedClassPathCheck",
+ "SimpleClient"
+ );
+
+ public static final List<String> CLIENT_OFFLINE_ARGS = List.of(
+ "-XX:+UnlockExperimentalVMOptions",
+ "-XX:+UseJBooster",
+ "-XX:JBoosterPort=" + SERVER_PORT_STR,
+ "-XX:JBoosterCachePath=" + CLIENT_CACHE_PATH,
+ "SimpleClient"
+ );
+
+ public static final List<String> SERVER_STANDARD_ARGS = List.of(
+ "--server-port=" + SERVER_PORT_STR,
+ "--cache-path=" + SERVER_CACHE_PATH
+ );
+
+ private static final ProcessBuilder processBuilder = new ProcessBuilder();
+
+ public static Process jbooster(TestContext ctx, List<String> vmArgs, List<String> jboosterArgs) throws Exception {
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jbooster");
+ launcher.addVMArgs(Utils.getTestJavaOpts());
+ if (vmArgs != null) {
+ for (String vmArg : vmArgs) {
+ launcher.addVMArg(vmArg);
+ }
+ }
+ if (jboosterArgs != null) {
+ for (String toolArg : jboosterArgs) {
+ launcher.addToolArg(toolArg);
+ }
+ }
+ processBuilder.command(launcher.getCommand());
+ System.out.println(Arrays.toString(processBuilder.command().toArray()).replace(",", ""));
+ return ctx.addProcess(ProcessTools.startProcess("jbooster", processBuilder));
+ }
+
+ public static Process java(TestContext ctx, List<String> args) throws Exception {
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java");
+ if (args != null) {
+ for (String arg : args) {
+ launcher.addToolArg(arg);
+ }
+ }
+ processBuilder.command(launcher.getCommand());
+ System.out.println(Arrays.toString(processBuilder.command().toArray()).replace(",", ""));
+ return ctx.addProcess(ProcessTools.startProcess("java", processBuilder));
+ }
+
+ public static void writeToStdin(Process p, String s) throws IOException {
+ BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
+ stdin.write(s);
+ stdin.flush();
+ }
+
+ public static void exitNormallyByCommandQ(Process p) throws Exception {
+ assertTrue(p.isAlive());
+ writeToStdin(p, "d\n");
+ writeToStdin(p, "q\n");
+ p.waitFor(WAIT_EXIT_TIME, TimeUnit.SECONDS);
+ assertTrue(!p.isAlive());
+ }
+
+ public static boolean deletePath(String path) {
+ return deletePath(new File(path));
+ }
+
+ public static boolean deletePath(File path) {
+ if (path.isDirectory()) {
+ String[] children = path.list();
+ if (children != null) {
+ for (String child : children) {
+ if (!deletePath(new File(path, child))) {
+ return false;
+ }
+ }
+ }
+ }
+ return path.delete();
+ }
+
+ public static void testCase(ProcessHandler c) throws Exception {
+ TestContext ctx = new TestContext();
+ try {
+ c.handle(ctx);
+ } finally {
+ for (Process p : ctx.getProcesses()) {
+ if (p.isAlive()) {
+ p.destroyForcibly();
+ }
+ }
+ }
+ }
+
+ public static void testCases(ProcessHandler... cases) throws Exception {
+ for (ProcessHandler c : cases) {
+ testCase(c);
+ }
+ }
+
+ public static void testCases(Class<? extends JBoosterTestBase> testClazz) throws Exception {
+ for (Method method : testClazz.getDeclaredMethods()) {
+ if (!method.getName().startsWith("test")) {
+ continue;
+ }
+ if (!method.getReturnType().equals(Void.TYPE)) {
+ throw new RuntimeException("The return type of \"" + method.getName() + "\" should be void!");
+ }
+ if (method.getParameterCount() != 1 || method.getParameterTypes()[0] != TestContext.class) {
+ throw new RuntimeException("The parameter type of \"" + method.getName() + "\" should be ArgWrapper!");
+ }
+ method.setAccessible(true);
+ System.out.println("[ \"" + method.getName() + "\" testing ]");
+ testCase(pw -> method.invoke(null, pw));
+ System.out.println("[ \"" + method.getName() + "\" passed ]");
+ }
+ }
+
+ @FunctionalInterface
+ public interface ProcessHandler {
+ void handle(TestContext ctx) throws Exception;
+ }
+
+ public static class TestContext {
+ private final ArrayList<Process> processes = new ArrayList<>();
+
+ public ArrayList<Process> getProcesses() {
+ return processes;
+ }
+
+ public Process addProcess(Process process) {
+ processes.add(process);
+ return process;
+ }
+ }
+}
diff --git a/test/jdk/tools/jbooster/SimpleClient.java b/test/jdk/tools/jbooster/SimpleClient.java
new file mode 100644
index 000000000..65e5d5bf1
--- /dev/null
+++ b/test/jdk/tools/jbooster/SimpleClient.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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.
+ */
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * A simple app for testing that uses the URLClassLoader.
+ */
+public class SimpleClient {
+ public static void main(String[] args) throws Exception {
+ new URLClassLoader(new URL[] {});
+ System.out.println("Hi~");
+ }
+}
diff --git a/test/jdk/tools/launcher/HelpFlagsTest.java b/test/jdk/tools/launcher/HelpFlagsTest.java
index 173074ab9..62405855d 100644
--- a/test/jdk/tools/launcher/HelpFlagsTest.java
+++ b/test/jdk/tools/launcher/HelpFlagsTest.java
@@ -129,6 +129,7 @@ public class HelpFlagsTest extends TestHelper {
new ToolHelpSpec("javadoc", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
new ToolHelpSpec("javap", 1, 1, 1, 0, 1, 1, 2), // -?, -h, --help -help, Documents -help
new ToolHelpSpec("javaw", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help, win only
+ new ToolHelpSpec("jbooster", 0, 0, 0, 0, 0, 0, 1), // none.
new ToolHelpSpec("jcmd", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
new ToolHelpSpec("jdb", 1, 1, 1, 0, 1, 1, 0), // -?, -h, --help -help, Documents -help
new ToolHelpSpec("jdeprscan", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
diff --git a/test/jdk/tools/launcher/VersionCheck.java b/test/jdk/tools/launcher/VersionCheck.java
index 38a61b541..c5b488d76 100644
--- a/test/jdk/tools/launcher/VersionCheck.java
+++ b/test/jdk/tools/launcher/VersionCheck.java
@@ -56,6 +56,7 @@ public class VersionCheck extends TestHelper {
"jaccesswalker-32",
"javaw",
"javaws",
+ "jbooster",
"jcontrol",
"jmc",
"jmc.ini",
@@ -79,6 +80,7 @@ public class VersionCheck extends TestHelper {
"javadoc",
"javacpl",
"javaws",
+ "jbooster",
"jcmd",
"jconsole",
"jcontrol",
--
2.19.1