Coverage Report

Created: 2024-11-20 15:53

/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
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
0
    do {                                             \
43
0
        if (env->ExceptionCheck()) [[unlikely]]      \
44
0
            return JniUtil::GetJniExceptionMsg(env); \
45
0
    } while (false)
46
47
#define JNI_CALL_METHOD_CHECK_EXCEPTION_DELETE_REF(type, result, env, func) \
48
0
    type result = env->func;                                                \
49
0
    DEFER(env->DeleteLocalRef(result));                                     \
50
0
    RETURN_ERROR_IF_EXC(env)
51
52
#define JNI_CALL_METHOD_CHECK_EXCEPTION(type, result, env, func) \
53
0
    type result = env->func;                                     \
54
0
    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
0
    static jmethodID throwable_to_string_id() { return throwable_to_string_id_; }
65
66
0
    static Status GetJNIEnv(JNIEnv** env) {
67
0
        if (tls_env_) {
68
0
            *env = tls_env_;
69
0
        } else {
70
0
            Status status = GetJNIEnvSlowPath(env);
71
0
            if (!status.ok()) {
72
0
                return status;
73
0
            }
74
0
        }
75
0
        if (*env == nullptr) {
76
0
            return Status::RuntimeError("Failed to get JNIEnv: it is nullptr.");
77
0
        }
78
0
        return Status::OK();
79
0
    }
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
0
    static jclass jni_util_class() { return jni_util_cl_; }
95
0
    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 jobject convert_to_java_map(JNIEnv* env, const std::map<std::string, std::string>& map);
104
    static std::map<std::string, std::string> convert_to_cpp_map(JNIEnv* env, jobject map);
105
106
private:
107
    static Status GetJNIEnvSlowPath(JNIEnv** env);
108
    static Status init_jni_scanner_loader(JNIEnv* env);
109
110
    static bool jvm_inited_;
111
    static jclass internal_exc_cl_;
112
    static jclass jni_native_method_exc_cl_;
113
    static jclass jni_util_cl_;
114
    static jmethodID throwable_to_string_id_;
115
    static jmethodID throwable_to_stack_trace_id_;
116
    static jmethodID get_jvm_metrics_id_;
117
    static jmethodID get_jvm_threads_id_;
118
    static jmethodID get_jmx_json_;
119
    // JNI scanner loader
120
    static jobject jni_scanner_loader_obj_;
121
    static jmethodID jni_scanner_loader_method_;
122
    // Thread-local cache of the JNIEnv for this thread.
123
    static __thread JNIEnv* tls_env_;
124
};
125
126
/// Helper class for lifetime management of chars from JNI, releasing JNI chars when
127
/// destructed
128
class JniUtfCharGuard {
129
public:
130
    /// Construct a JniUtfCharGuards holding nothing
131
0
    JniUtfCharGuard() : utf_chars(nullptr) {}
132
133
    /// Release the held char sequence if there is one.
134
0
    ~JniUtfCharGuard() {
135
0
        if (utf_chars != nullptr) env->ReleaseStringUTFChars(jstr, utf_chars);
136
0
    }
137
138
    /// Try to get chars from jstr. If error is returned, utf_chars and get() remain
139
    /// to be nullptr, otherwise they point to a valid char sequence. The char sequence
140
    /// lives as long as this guard. jstr should not be null.
141
    static Status create(JNIEnv* env, jstring jstr, JniUtfCharGuard* out);
142
143
    /// Get the char sequence. Returns nullptr if the guard does hold a char sequence.
144
0
    const char* get() { return utf_chars; }
145
146
private:
147
    JNIEnv* env = nullptr;
148
    jstring jstr;
149
    const char* utf_chars = nullptr;
150
    DISALLOW_COPY_AND_ASSIGN(JniUtfCharGuard);
151
};
152
153
class JniLocalFrame {
154
public:
155
0
    JniLocalFrame() : env_(nullptr) {}
156
0
    ~JniLocalFrame() {
157
0
        if (env_ != nullptr) env_->PopLocalFrame(nullptr);
158
0
    }
159
160
0
    JniLocalFrame(JniLocalFrame&& other) noexcept : env_(other.env_) { other.env_ = nullptr; }
161
162
    /// Pushes a new JNI local frame. The frame can support max_local_ref local references.
163
    /// The number of local references created inside the frame might exceed max_local_ref,
164
    /// but there is no guarantee that memory will be available.
165
    /// Push should be called at most once.
166
    Status push(JNIEnv* env, int max_local_ref = 10) WARN_UNUSED_RESULT;
167
168
private:
169
    DISALLOW_COPY_AND_ASSIGN(JniLocalFrame);
170
171
    JNIEnv* env_ = nullptr;
172
};
173
174
template <class T>
175
0
Status SerializeThriftMsg(JNIEnv* env, T* msg, jbyteArray* serialized_msg) {
176
0
    int buffer_size = 100 * 1024; // start out with 100KB
177
0
    ThriftSerializer serializer(false, buffer_size);
178
179
0
    uint8_t* buffer = nullptr;
180
0
    uint32_t size = 0;
181
0
    RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer));
182
183
    // Make sure that 'size' is within the limit of INT_MAX as the use of
184
    // 'size' below takes int.
185
0
    if (size > INT_MAX) {
186
0
        return Status::InternalError(
187
0
                "The length of the serialization buffer ({} bytes) exceeds the limit of {} bytes",
188
0
                size, INT_MAX);
189
0
    }
190
191
    /// create jbyteArray given buffer
192
0
    *serialized_msg = env->NewByteArray(size);
193
0
    RETURN_ERROR_IF_EXC(env);
194
0
    if (*serialized_msg == NULL) return Status::InternalError("couldn't construct jbyteArray");
195
0
    env->SetByteArrayRegion(*serialized_msg, 0, size, reinterpret_cast<jbyte*>(buffer));
196
0
    RETURN_ERROR_IF_EXC(env);
197
0
    return Status::OK();
198
0
}
Unexecuted instantiation: _ZN5doris18SerializeThriftMsgINS_23TJdbcExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
Unexecuted instantiation: _ZN5doris18SerializeThriftMsgINS_26TJavaUdfExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
199
200
} // namespace doris