/root/doris/be/src/util/jni-util.h
| Line | Count | Source | 
| 1 |  | // Licensed to the Apache Software Foundation (ASF) under one | 
| 2 |  | // or more contributor license agreements.  See the NOTICE file | 
| 3 |  | // distributed with this work for additional information | 
| 4 |  | // regarding copyright ownership.  The ASF licenses this file | 
| 5 |  | // to you under the Apache License, Version 2.0 (the | 
| 6 |  | // "License"); you may not use this file except in compliance | 
| 7 |  | // with the License.  You may obtain a copy of the License at | 
| 8 |  | // | 
| 9 |  | //   http://www.apache.org/licenses/LICENSE-2.0 | 
| 10 |  | // | 
| 11 |  | // Unless required by applicable law or agreed to in writing, | 
| 12 |  | // software distributed under the License is distributed on an | 
| 13 |  | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
| 14 |  | // KIND, either express or implied.  See the License for the | 
| 15 |  | // specific language governing permissions and limitations | 
| 16 |  | // under the License. | 
| 17 |  |  | 
| 18 |  | #pragma once | 
| 19 |  |  | 
| 20 |  | #include <butil/macros.h> | 
| 21 |  | #include <jni.h> | 
| 22 |  | #include <limits.h> | 
| 23 |  | #include <stddef.h> | 
| 24 |  | #include <stdint.h> | 
| 25 |  |  | 
| 26 |  | #include <string> | 
| 27 |  |  | 
| 28 |  | #include "common/status.h" | 
| 29 |  | #include "jni_md.h" | 
| 30 |  | #include "util/defer_op.h" | 
| 31 |  | #include "util/thrift_util.h" | 
| 32 |  |  | 
| 33 |  | #ifdef USE_HADOOP_HDFS | 
| 34 |  | // defined in hadoop/hadoop-hdfs-project/hadoop-hdfs/src/main/native/libhdfs/jni_helper.c | 
| 35 |  | extern "C" JNIEnv* getJNIEnv(void); | 
| 36 |  | #endif | 
| 37 |  |  | 
| 38 |  | namespace doris { | 
| 39 |  | class JniUtil; | 
| 40 |  |  | 
| 41 |  | #define RETURN_ERROR_IF_EXC(env)                     \ | 
| 42 | 94.9k |     do {                                             \ | 
| 43 | 94.9k |         if (env->ExceptionCheck()) [[unlikely]]      \ | 
| 44 | 4 |             return JniUtil::GetJniExceptionMsg(env); \ | 
| 45 | 94.9k |     } while (false) | 
| 46 |  |  | 
| 47 |  | #define JNI_CALL_METHOD_CHECK_EXCEPTION_DELETE_REF(type, result, env, func) \ | 
| 48 | 12.4k |     type result = env->func;                                                \ | 
| 49 | 12.4k |     DEFER(env->DeleteLocalRef(result));                                     \ | 
| 50 | 12.4k |     RETURN_ERROR_IF_EXC(env) | 
| 51 |  |  | 
| 52 |  | #define JNI_CALL_METHOD_CHECK_EXCEPTION(type, result, env, func) \ | 
| 53 | 4.85k |     type result = env->func;                                     \ | 
| 54 | 4.85k |     RETURN_ERROR_IF_EXC(env) | 
| 55 |  |  | 
| 56 |  | //In order to reduce the potential risks caused by not handling exceptions, | 
| 57 |  | // you need to refer to  https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html | 
| 58 |  | // to confirm whether the jni method will throw an exception. | 
| 59 |  |  | 
| 60 |  | class JniUtil { | 
| 61 |  | public: | 
| 62 |  |     static Status Init() WARN_UNUSED_RESULT; | 
| 63 |  |  | 
| 64 | 8 |     static jmethodID throwable_to_string_id() { return throwable_to_string_id_; } | 
| 65 |  |  | 
| 66 | 15.6k |     static Status GetJNIEnv(JNIEnv** env) { | 
| 67 | 15.6k |         if (tls_env_) { | 
| 68 | 15.4k |             *env = tls_env_; | 
| 69 | 15.4k |         } else { | 
| 70 | 212 |             Status status = GetJNIEnvSlowPath(env); | 
| 71 | 212 |             if (!status.ok()) { | 
| 72 | 0 |                 return status; | 
| 73 | 0 |             } | 
| 74 | 212 |         } | 
| 75 | 15.6k |         if (*env == nullptr) { | 
| 76 | 0 |             return Status::JniError("Failed to get JNIEnv: it is nullptr."); | 
| 77 | 0 |         } | 
| 78 | 15.6k |         return Status::OK(); | 
| 79 | 15.6k |     } | 
| 80 |  |  | 
| 81 |  |     //jclass is generally a local reference. | 
| 82 |  |     //Method ID and field ID values are forever. | 
| 83 |  |     //If you want to use the jclass across multiple threads or multiple calls into the JNI code you need | 
| 84 |  |     // to create a global reference to it with GetGlobalClassRef(). | 
| 85 |  |     static Status GetGlobalClassRef(JNIEnv* env, const char* class_str, | 
| 86 |  |                                     jclass* class_ref) WARN_UNUSED_RESULT; | 
| 87 |  |  | 
| 88 |  |     static Status LocalToGlobalRef(JNIEnv* env, jobject local_ref, | 
| 89 |  |                                    jobject* global_ref) WARN_UNUSED_RESULT; | 
| 90 |  |  | 
| 91 |  |     static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true, | 
| 92 |  |                                      const std::string& prefix = "") WARN_UNUSED_RESULT; | 
| 93 |  |  | 
| 94 | 8 |     static jclass jni_util_class() { return jni_util_cl_; } | 
| 95 | 4 |     static jmethodID throwable_to_stack_trace_id() { return throwable_to_stack_trace_id_; } | 
| 96 |  |  | 
| 97 |  |     static const int64_t INITIAL_RESERVED_BUFFER_SIZE = 1024; | 
| 98 |  |     // TODO: we need a heuristic strategy to increase buffer size for variable-size output. | 
| 99 | 0 |     static inline int64_t IncreaseReservedBufferSize(int n) { | 
| 100 | 0 |         return INITIAL_RESERVED_BUFFER_SIZE << n; | 
| 101 | 0 |     } | 
| 102 |  |     static Status get_jni_scanner_class(JNIEnv* env, const char* classname, jclass* loaded_class); | 
| 103 |  |     static Status convert_to_java_map(JNIEnv* env, const std::map<std::string, std::string>& map, | 
| 104 |  |                                       jobject* hashmap_object); | 
| 105 |  |     static Status convert_to_cpp_map(JNIEnv* env, jobject map, | 
| 106 |  |                                      std::map<std::string, std::string>* resultMap); | 
| 107 |  |     static size_t get_max_jni_heap_memory_size(); | 
| 108 |  |     static Status clean_udf_class_load_cache(const std::string& function_signature); | 
| 109 |  |  | 
| 110 |  | private: | 
| 111 |  |     static void parse_max_heap_memory_size_from_jvm(JNIEnv* env); | 
| 112 |  |     static Status GetJNIEnvSlowPath(JNIEnv** env); | 
| 113 |  |     static Status init_jni_scanner_loader(JNIEnv* env); | 
| 114 |  |  | 
| 115 |  |     static bool jvm_inited_; | 
| 116 |  |     static jclass internal_exc_cl_; | 
| 117 |  |     static jclass jni_native_method_exc_cl_; | 
| 118 |  |     static jclass jni_util_cl_; | 
| 119 |  |     static jmethodID throwable_to_string_id_; | 
| 120 |  |     static jmethodID throwable_to_stack_trace_id_; | 
| 121 |  |     static jmethodID get_jvm_metrics_id_; | 
| 122 |  |     static jmethodID get_jvm_threads_id_; | 
| 123 |  |     static jmethodID get_jmx_json_; | 
| 124 |  |     // JNI scanner loader | 
| 125 |  |     static jobject jni_scanner_loader_obj_; | 
| 126 |  |     static jmethodID jni_scanner_loader_method_; | 
| 127 |  |     // Thread-local cache of the JNIEnv for this thread. | 
| 128 |  |     static __thread JNIEnv* tls_env_; | 
| 129 |  |     static jlong max_jvm_heap_memory_size_; | 
| 130 |  |     static jmethodID _clean_udf_cache_method_id; | 
| 131 |  | }; | 
| 132 |  |  | 
| 133 |  | /// Helper class for lifetime management of chars from JNI, releasing JNI chars when | 
| 134 |  | /// destructed | 
| 135 |  | class JniUtfCharGuard { | 
| 136 |  | public: | 
| 137 |  |     /// Construct a JniUtfCharGuards holding nothing | 
| 138 | 8 |     JniUtfCharGuard() : utf_chars(nullptr) {} | 
| 139 |  |  | 
| 140 |  |     /// Release the held char sequence if there is one. | 
| 141 | 8 |     ~JniUtfCharGuard() { | 
| 142 | 8 |         if (utf_chars != nullptr) env->ReleaseStringUTFChars(jstr, utf_chars); | 
| 143 | 8 |     } | 
| 144 |  |  | 
| 145 |  |     /// Try to get chars from jstr. If error is returned, utf_chars and get() remain | 
| 146 |  |     /// to be nullptr, otherwise they point to a valid char sequence. The char sequence | 
| 147 |  |     /// lives as long as this guard. jstr should not be null. | 
| 148 |  |     static Status create(JNIEnv* env, jstring jstr, JniUtfCharGuard* out); | 
| 149 |  |  | 
| 150 |  |     /// Get the char sequence. Returns nullptr if the guard does hold a char sequence. | 
| 151 | 8 |     const char* get() { return utf_chars; } | 
| 152 |  |  | 
| 153 |  | private: | 
| 154 |  |     JNIEnv* env = nullptr; | 
| 155 |  |     jstring jstr; | 
| 156 |  |     const char* utf_chars = nullptr; | 
| 157 |  |     DISALLOW_COPY_AND_ASSIGN(JniUtfCharGuard); | 
| 158 |  | }; | 
| 159 |  |  | 
| 160 |  | class JniLocalFrame { | 
| 161 |  | public: | 
| 162 | 5.75k |     JniLocalFrame() : env_(nullptr) {} | 
| 163 | 5.86k |     ~JniLocalFrame() { | 
| 164 | 5.86k |         if (env_ != nullptr) env_->PopLocalFrame(nullptr); | 
| 165 | 5.86k |     } | 
| 166 |  |  | 
| 167 | 0 |     JniLocalFrame(JniLocalFrame&& other) noexcept : env_(other.env_) { other.env_ = nullptr; } | 
| 168 |  |  | 
| 169 |  |     /// Pushes a new JNI local frame. The frame can support max_local_ref local references. | 
| 170 |  |     /// The number of local references created inside the frame might exceed max_local_ref, | 
| 171 |  |     /// but there is no guarantee that memory will be available. | 
| 172 |  |     /// Push should be called at most once. | 
| 173 |  |     Status push(JNIEnv* env, int max_local_ref = 10) WARN_UNUSED_RESULT; | 
| 174 |  |  | 
| 175 |  | private: | 
| 176 |  |     DISALLOW_COPY_AND_ASSIGN(JniLocalFrame); | 
| 177 |  |  | 
| 178 |  |     JNIEnv* env_ = nullptr; | 
| 179 |  | }; | 
| 180 |  |  | 
| 181 |  | template <class T> | 
| 182 | 5.81k | Status SerializeThriftMsg(JNIEnv* env, T* msg, jbyteArray* serialized_msg) { | 
| 183 | 5.81k |     int buffer_size = 100 * 1024; // start out with 100KB | 
| 184 | 5.81k |     ThriftSerializer serializer(false, buffer_size); | 
| 185 |  |  | 
| 186 | 5.81k |     uint8_t* buffer = nullptr; | 
| 187 | 5.81k |     uint32_t size = 0; | 
| 188 | 5.81k |     RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer)); | 
| 189 |  |  | 
| 190 |  |     // Make sure that 'size' is within the limit of INT_MAX as the use of | 
| 191 |  |     // 'size' below takes int. | 
| 192 | 5.81k |     if (size > INT_MAX) { | 
| 193 | 0 |         return Status::JniError( | 
| 194 | 0 |                 "The length of the serialization buffer ({} bytes) exceeds the limit of {} bytes", | 
| 195 | 0 |                 size, INT_MAX); | 
| 196 | 0 |     } | 
| 197 |  |  | 
| 198 |  |     /// create jbyteArray given buffer | 
| 199 | 5.81k |     *serialized_msg = env->NewByteArray(size); | 
| 200 | 5.81k |     RETURN_ERROR_IF_EXC(env); | 
| 201 | 5.81k |     if (*serialized_msg == nullptr) { | 
| 202 | 0 |         return Status::JniError("couldn't construct jbyteArray"); | 
| 203 | 0 |     } | 
| 204 | 5.81k |     env->SetByteArrayRegion(*serialized_msg, 0, size, reinterpret_cast<jbyte*>(buffer)); | 
| 205 | 5.81k |     RETURN_ERROR_IF_EXC(env); | 
| 206 | 5.81k |     return Status::OK(); | 
| 207 | 5.81k | } _ZN5doris18SerializeThriftMsgINS_23TJdbcExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray| Line | Count | Source |  | 182 | 7 | Status SerializeThriftMsg(JNIEnv* env, T* msg, jbyteArray* serialized_msg) { |  | 183 | 7 |     int buffer_size = 100 * 1024; // start out with 100KB |  | 184 | 7 |     ThriftSerializer serializer(false, buffer_size); |  | 185 |  |  |  | 186 | 7 |     uint8_t* buffer = nullptr; |  | 187 | 7 |     uint32_t size = 0; |  | 188 | 7 |     RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer)); |  | 189 |  |  |  | 190 |  |     // Make sure that 'size' is within the limit of INT_MAX as the use of |  | 191 |  |     // 'size' below takes int. |  | 192 | 7 |     if (size > INT_MAX) { |  | 193 | 0 |         return Status::JniError( |  | 194 | 0 |                 "The length of the serialization buffer ({} bytes) exceeds the limit of {} bytes", |  | 195 | 0 |                 size, INT_MAX); |  | 196 | 0 |     } |  | 197 |  |  |  | 198 |  |     /// create jbyteArray given buffer |  | 199 | 7 |     *serialized_msg = env->NewByteArray(size); |  | 200 | 7 |     RETURN_ERROR_IF_EXC(env); |  | 201 | 7 |     if (*serialized_msg == nullptr) { |  | 202 | 0 |         return Status::JniError("couldn't construct jbyteArray"); |  | 203 | 0 |     } |  | 204 | 7 |     env->SetByteArrayRegion(*serialized_msg, 0, size, reinterpret_cast<jbyte*>(buffer)); |  | 205 | 7 |     RETURN_ERROR_IF_EXC(env); |  | 206 | 7 |     return Status::OK(); |  | 207 | 7 | } | 
_ZN5doris18SerializeThriftMsgINS_26TJavaUdfExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray| Line | Count | Source |  | 182 | 5.81k | Status SerializeThriftMsg(JNIEnv* env, T* msg, jbyteArray* serialized_msg) { |  | 183 | 5.81k |     int buffer_size = 100 * 1024; // start out with 100KB |  | 184 | 5.81k |     ThriftSerializer serializer(false, buffer_size); |  | 185 |  |  |  | 186 | 5.81k |     uint8_t* buffer = nullptr; |  | 187 | 5.81k |     uint32_t size = 0; |  | 188 | 5.81k |     RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer)); |  | 189 |  |  |  | 190 |  |     // Make sure that 'size' is within the limit of INT_MAX as the use of |  | 191 |  |     // 'size' below takes int. |  | 192 | 5.81k |     if (size > INT_MAX) { |  | 193 | 0 |         return Status::JniError( |  | 194 | 0 |                 "The length of the serialization buffer ({} bytes) exceeds the limit of {} bytes", |  | 195 | 0 |                 size, INT_MAX); |  | 196 | 0 |     } |  | 197 |  |  |  | 198 |  |     /// create jbyteArray given buffer |  | 199 | 5.81k |     *serialized_msg = env->NewByteArray(size); |  | 200 | 5.81k |     RETURN_ERROR_IF_EXC(env); |  | 201 | 5.81k |     if (*serialized_msg == nullptr) { |  | 202 | 0 |         return Status::JniError("couldn't construct jbyteArray"); |  | 203 | 0 |     } |  | 204 | 5.81k |     env->SetByteArrayRegion(*serialized_msg, 0, size, reinterpret_cast<jbyte*>(buffer)); |  | 205 | 5.81k |     RETURN_ERROR_IF_EXC(env); |  | 206 | 5.81k |     return Status::OK(); |  | 207 | 5.81k | } | 
 | 
| 208 |  |  | 
| 209 |  | } // namespace doris |