Coverage Report

Created: 2024-11-18 11:49

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