[enhancement](java-udf) Support loading libjvm at runtime (#13660)

This commit is contained in:
Adonis Ling
2022-10-28 08:45:12 +08:00
committed by GitHub
parent 20363edc73
commit 2ef8f3f6f4
9 changed files with 167 additions and 172 deletions

View File

@ -603,26 +603,13 @@ include_directories(
)
if (BUILD_JAVA_UDF)
execute_process(COMMAND chmod 755 ${BASE_DIR}/../tools/find_libjvm.sh)
execute_process(COMMAND ${BASE_DIR}/../tools/find_libjvm.sh OUTPUT_VARIABLE LIBJVM_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
FILE(GLOB_RECURSE LIB_JVM ${LIBJVM_PATH})
if("${LIB_JVM}" STREQUAL "")
message(STATUS "Disable JAVA UDF because there is no libjvm found!")
include_directories($ENV{JAVA_HOME}/include)
if (NOT OS_MACOSX)
include_directories($ENV{JAVA_HOME}/include/linux)
else()
set(DORIS_DEPENDENCIES
${DORIS_DEPENDENCIES}
jvm
)
add_library(jvm SHARED IMPORTED)
set_target_properties(jvm PROPERTIES IMPORTED_LOCATION ${LIB_JVM})
include_directories($ENV{JAVA_HOME}/include)
if (NOT OS_MACOSX)
include_directories($ENV{JAVA_HOME}/include/linux)
else()
include_directories($ENV{JAVA_HOME}/include/darwin)
endif()
add_definitions("-DLIBJVM")
include_directories($ENV{JAVA_HOME}/include/darwin)
endif()
add_definitions("-DLIBJVM")
endif()
if (NOT OS_MACOSX)

View File

@ -127,6 +127,10 @@ if (OS_MACOSX)
list(APPEND UTIL_FILES perf_counters_mac.cpp disk_info_mac.cpp)
endif()
if (BUILD_JAVA_UDF)
list(APPEND UTIL_FILES libjvm_loader.cpp)
endif()
add_library(Util STATIC
${UTIL_FILES}
)

View File

@ -29,6 +29,7 @@
#include "common/config.h"
#include "gutil/once.h"
#include "gutil/strings/substitute.h"
#include "libjvm_loader.h"
using std::string;
@ -67,7 +68,7 @@ const std::string GetDorisJNIClasspath() {
void FindOrCreateJavaVM() {
int num_vms;
int rv = JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
int rv = LibJVMLoader::JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
if (rv == 0) {
auto classpath = GetDorisJNIClasspath();
std::string heap_size = fmt::format("-Xmx{}", config::jvm_max_heap_size);
@ -91,7 +92,7 @@ void FindOrCreateJavaVM() {
// Set it to JNI_FALSE because JNI_TRUE will let JVM ignore the max size config.
vm_args.ignoreUnrecognized = JNI_FALSE;
jint res = JNI_CreateJavaVM(&g_vm, (void**)&env, &vm_args);
jint res = LibJVMLoader::JNI_CreateJavaVM(&g_vm, (void**)&env, &vm_args);
if (JNI_OK != res) {
DCHECK(false) << "Failed to create JVM, code= " << res;
}
@ -213,6 +214,8 @@ Status JniUtil::LocalToGlobalRef(JNIEnv* env, jobject local_ref, jobject* global
}
Status JniUtil::Init() {
RETURN_IF_ERROR(LibJVMLoader::instance().load());
// Get the JNIEnv* corresponding to current thread.
JNIEnv* env;
RETURN_IF_ERROR(JniUtil::GetJNIEnv(&env));

View File

@ -0,0 +1,99 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "util/libjvm_loader.h"
#include <dlfcn.h>
#include <cstdlib>
#include <filesystem>
#include <mutex>
#include "common/status.h"
namespace {
#ifndef __APPLE__
#define LIBJVM_SO "libjvm.so"
#else
#define LIBJVM_SO "libjvm.dylib"
#endif
template <typename T>
doris::Status resolve_symbol(T& pointer, void* handle, const char* symbol) {
pointer = reinterpret_cast<T>(dlsym(handle, symbol));
return (pointer != nullptr)
? doris::Status::OK()
: doris::Status::RuntimeError("Failed to resolve the symbol %s", symbol);
}
} // namespace
namespace doris {
LibJVMLoader::JNI_GetCreatedJavaVMsPointer LibJVMLoader::JNI_GetCreatedJavaVMs = nullptr;
LibJVMLoader::JNI_CreateJavaVMPointer LibJVMLoader::JNI_CreateJavaVM = nullptr;
LibJVMLoader& LibJVMLoader::instance() {
static std::once_flag find_library;
static std::string library;
std::call_once(find_library, []() {
const auto* java_home = getenv("JAVA_HOME");
if (!java_home) {
return;
}
std::string path(java_home);
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
if (entry.path().filename() == LIBJVM_SO) {
library = entry.path().string();
break;
}
}
});
static LibJVMLoader loader(library);
return loader;
}
Status LibJVMLoader::load() {
if (_library.empty()) {
return Status::RuntimeError("Failed to find the library %s.", LIBJVM_SO);
}
static std::once_flag resolve_symbols;
static Status status;
std::call_once(resolve_symbols, [this]() {
_handle = std::unique_ptr<void, void (*)(void*)>(dlopen(_library.c_str(), RTLD_LAZY),
[](void* handle) { dlclose(handle); });
if (!_handle) {
status = Status::RuntimeError(dlerror());
return;
}
if (status = resolve_symbol(JNI_GetCreatedJavaVMs, _handle.get(), "JNI_GetCreatedJavaVMs");
!status.ok()) {
return;
}
if (status = resolve_symbol(JNI_CreateJavaVM, _handle.get(), "JNI_CreateJavaVM");
!status.ok()) {
return;
}
});
return status;
}
} // namespace doris

View File

@ -0,0 +1,54 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <dlfcn.h>
#include <jni.h>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
namespace doris {
class Status;
class LibJVMLoader {
public:
LibJVMLoader(const LibJVMLoader&) = delete;
LibJVMLoader& operator=(const LibJVMLoader&) = delete;
static LibJVMLoader& instance();
Status load();
using JNI_GetCreatedJavaVMsPointer = std::add_pointer_t<decltype(::JNI_GetCreatedJavaVMs)>;
static JNI_GetCreatedJavaVMsPointer JNI_GetCreatedJavaVMs;
using JNI_CreateJavaVMPointer = std::add_pointer_t<decltype(::JNI_CreateJavaVM)>;
static JNI_CreateJavaVMPointer JNI_CreateJavaVM;
private:
explicit LibJVMLoader(std::string_view library)
: _library(library), _handle(nullptr, nullptr) {}
const std::string _library;
std::unique_ptr<void, void (*)(void*)> _handle;
};
} // namespace doris

View File

@ -1,93 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
set -eo pipefail
curdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
if [[ "$(uname -s)" == 'Darwin' ]] && command -v brew &>/dev/null; then
PATH="$(brew --prefix)/opt/gnu-getopt/bin:${PATH}"
export PATH
fi
DORIS_HOME="$(
cd "${curdir}/.."
pwd
)"
export DORIS_HOME
jdk_version() {
local java_cmd="${1}"
local result
local IFS=$'\n'
if [[ -z "${java_cmd}" ]]; then
result=no_java
return 1
else
local version
# remove \r for Cygwin
version="$("${java_cmd}" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n' | grep version | awk '{print $3}')"
version="${version//\"/}"
if [[ "${version}" =~ ^1\. ]]; then
result="$(echo "${version}" | awk -F '.' '{print $2}')"
else
result="$(echo "${version}" | awk -F '.' '{print $1}')"
fi
fi
echo "${result}"
return 0
}
setup_java_env() {
local java_version
if [[ -z "${JAVA_HOME}" ]]; then
return 1
fi
local jvm_arch='amd64'
if [[ "$(uname -m)" == 'aarch64' ]]; then
jvm_arch='aarch64'
fi
java_version="$(
set -e
jdk_version "${JAVA_HOME}/bin/java"
)"
if [[ "${java_version}" -gt 8 ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
# JAVA_HOME is jdk
elif [[ -d "${JAVA_HOME}/jre" ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
# JAVA_HOME is jre
else
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
fi
}
# prepare jvm if needed
setup_java_env || true
if [[ -e "${DORIS_HOME}/bin/palo_env.sh" ]]; then
# shellcheck disable=1091
source "${DORIS_HOME}/bin/palo_env.sh"
fi
chmod 755 "${DORIS_HOME}/lib/doris_be"
"${DORIS_HOME}"/lib/doris_be --version

View File

@ -104,35 +104,6 @@ jdk_version() {
return 0
}
setup_java_env() {
local java_version
if [[ -z "${JAVA_HOME}" ]]; then
return 1
fi
local jvm_arch='amd64'
if [[ "$(uname -m)" == 'aarch64' ]]; then
jvm_arch='aarch64'
fi
java_version="$(
set -e
jdk_version "${JAVA_HOME}/bin/java"
)"
if [[ "${java_version}" -gt 8 ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
# JAVA_HOME is jdk
elif [[ -d "${JAVA_HOME}/jre" ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
# JAVA_HOME is jre
else
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
fi
}
# prepare jvm if needed
setup_java_env || true
# export env variables from be.conf
#
# UDF_RUNTIME_DIR

View File

@ -508,7 +508,6 @@ if [[ "${BUILD_BE}" -eq 1 ]]; then
"${DORIS_OUTPUT}/udf/include"
cp -r -p "${DORIS_HOME}/be/output/bin"/* "${DORIS_OUTPUT}/be/bin"/
cp -r -p "${DORIS_HOME}/bin/check_be_version.sh" "${DORIS_OUTPUT}/be/bin"/
cp -r -p "${DORIS_HOME}/be/output/conf"/* "${DORIS_OUTPUT}/be/conf"/
# Fix Killed: 9 error on MacOS (arm64).

View File

@ -230,35 +230,6 @@ jdk_version() {
return 0
}
setup_java_env() {
local java_version
if [[ -z "${JAVA_HOME}" ]]; then
return 1
fi
local jvm_arch='amd64'
if [[ "$(uname -m)" == 'aarch64' ]]; then
jvm_arch='aarch64'
fi
java_version="$(
set -e
jdk_version "${JAVA_HOME}/bin/java"
)"
if [[ "${java_version}" -gt 8 ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/server:${JAVA_HOME}/lib:${LD_LIBRARY_PATH}"
# JAVA_HOME is jdk
elif [[ -d "${JAVA_HOME}/jre" ]]; then
export LD_LIBRARY_PATH="${JAVA_HOME}/jre/lib/${jvm_arch}/server:${JAVA_HOME}/jre/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
# JAVA_HOME is jre
else
export LD_LIBRARY_PATH="${JAVA_HOME}/lib/${jvm_arch}/server:${JAVA_HOME}/lib/${jvm_arch}:${LD_LIBRARY_PATH}"
fi
}
# prepare jvm if needed
setup_java_env || true
# prepare gtest output dir
GTEST_OUTPUT_DIR="${CMAKE_BUILD_DIR}/gtest_output"
rm -rf "${GTEST_OUTPUT_DIR}"