Coverage Report

Created: 2024-11-18 10:37

/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/thrift_util.h"
31
32
#ifdef USE_HADOOP_HDFS
33
// defined in hadoop/hadoop-hdfs-project/hadoop-hdfs/src/main/native/libhdfs/jni_helper.c
34
extern "C" JNIEnv* getJNIEnv(void);
35
#endif
36
37
namespace doris {
38
class JniUtil;
39
40
#define RETURN_ERROR_IF_EXC(env)                                     \
41
0
    do {                                                             \
42
0
        jthrowable exc = (env)->ExceptionOccurred();                 \
43
0
        if (exc != nullptr) return JniUtil::GetJniExceptionMsg(env); \
44
0
    } while (false)
45
46
class JniUtil {
47
public:
48
    static Status Init() WARN_UNUSED_RESULT;
49
50
0
    static jmethodID throwable_to_string_id() { return throwable_to_string_id_; }
51
52
0
    static Status GetJNIEnv(JNIEnv** env) {
53
0
        if (tls_env_) {
54
0
            *env = tls_env_;
55
0
        } else {
56
0
            Status status = GetJNIEnvSlowPath(env);
57
0
            if (!status.ok()) {
58
0
                return status;
59
0
            }
60
0
        }
61
0
        if (*env == nullptr) {
62
0
            return Status::RuntimeError("Failed to get JNIEnv: it is nullptr.");
63
0
        }
64
0
        return Status::OK();
65
0
    }
66
67
    static Status GetGlobalClassRef(JNIEnv* env, const char* class_str,
68
                                    jclass* class_ref) WARN_UNUSED_RESULT;
69
70
    static Status LocalToGlobalRef(JNIEnv* env, jobject local_ref,
71
                                   jobject* global_ref) WARN_UNUSED_RESULT;
72
73
    static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true,
74
                                     const std::string& prefix = "") WARN_UNUSED_RESULT;
75
76
0
    static jclass jni_util_class() { return jni_util_cl_; }
77
0
    static jmethodID throwable_to_stack_trace_id() { return throwable_to_stack_trace_id_; }
78
79
    static const int64_t INITIAL_RESERVED_BUFFER_SIZE = 1024;
80
    // TODO: we need a heuristic strategy to increase buffer size for variable-size output.
81
0
    static inline int64_t IncreaseReservedBufferSize(int n) {
82
0
        return INITIAL_RESERVED_BUFFER_SIZE << n;
83
0
    }
84
    static Status get_jni_scanner_class(JNIEnv* env, const char* classname, jclass* loaded_class);
85
    static jobject convert_to_java_map(JNIEnv* env, const std::map<std::string, std::string>& map);
86
    static std::map<std::string, std::string> convert_to_cpp_map(JNIEnv* env, jobject map);
87
88
private:
89
    static Status GetJNIEnvSlowPath(JNIEnv** env);
90
    static Status init_jni_scanner_loader(JNIEnv* env);
91
92
    static bool jvm_inited_;
93
    static jclass internal_exc_cl_;
94
    static jclass jni_native_method_exc_cl_;
95
    static jclass jni_util_cl_;
96
    static jmethodID throwable_to_string_id_;
97
    static jmethodID throwable_to_stack_trace_id_;
98
    static jmethodID get_jvm_metrics_id_;
99
    static jmethodID get_jvm_threads_id_;
100
    static jmethodID get_jmx_json_;
101
    // JNI scanner loader
102
    static jobject jni_scanner_loader_obj_;
103
    static jmethodID jni_scanner_loader_method_;
104
    // Thread-local cache of the JNIEnv for this thread.
105
    static __thread JNIEnv* tls_env_;
106
};
107
108
/// Helper class for lifetime management of chars from JNI, releasing JNI chars when
109
/// destructed
110
class JniUtfCharGuard {
111
public:
112
    /// Construct a JniUtfCharGuards holding nothing
113
0
    JniUtfCharGuard() : utf_chars(nullptr) {}
114
115
    /// Release the held char sequence if there is one.
116
0
    ~JniUtfCharGuard() {
117
0
        if (utf_chars != nullptr) env->ReleaseStringUTFChars(jstr, utf_chars);
118
0
    }
119
120
    /// Try to get chars from jstr. If error is returned, utf_chars and get() remain
121
    /// to be nullptr, otherwise they point to a valid char sequence. The char sequence
122
    /// lives as long as this guard. jstr should not be null.
123
    static Status create(JNIEnv* env, jstring jstr, JniUtfCharGuard* out);
124
125
    /// Get the char sequence. Returns nullptr if the guard does hold a char sequence.
126
0
    const char* get() { return utf_chars; }
127
128
private:
129
    JNIEnv* env;
130
    jstring jstr;
131
    const char* utf_chars;
132
    DISALLOW_COPY_AND_ASSIGN(JniUtfCharGuard);
133
};
134
135
class JniLocalFrame {
136
public:
137
0
    JniLocalFrame() : env_(nullptr) {}
138
0
    ~JniLocalFrame() {
139
0
        if (env_ != nullptr) env_->PopLocalFrame(nullptr);
140
0
    }
141
142
0
    JniLocalFrame(JniLocalFrame&& other) noexcept : env_(other.env_) { other.env_ = nullptr; }
143
144
    /// Pushes a new JNI local frame. The frame can support max_local_ref local references.
145
    /// The number of local references created inside the frame might exceed max_local_ref,
146
    /// but there is no guarantee that memory will be available.
147
    /// Push should be called at most once.
148
    Status push(JNIEnv* env, int max_local_ref = 10) WARN_UNUSED_RESULT;
149
150
private:
151
    DISALLOW_COPY_AND_ASSIGN(JniLocalFrame);
152
153
    JNIEnv* env_;
154
};
155
156
template <class T>
157
0
Status SerializeThriftMsg(JNIEnv* env, T* msg, jbyteArray* serialized_msg) {
158
0
    int buffer_size = 100 * 1024; // start out with 100KB
159
0
    ThriftSerializer serializer(false, buffer_size);
160
161
0
    uint8_t* buffer = NULL;
162
0
    uint32_t size = 0;
163
0
    RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer));
164
165
    // Make sure that 'size' is within the limit of INT_MAX as the use of
166
    // 'size' below takes int.
167
0
    if (size > INT_MAX) {
168
0
        return Status::InternalError(
169
0
                "The length of the serialization buffer ({} bytes) exceeds the limit of {} bytes",
170
0
                size, INT_MAX);
171
0
    }
172
173
    /// create jbyteArray given buffer
174
0
    *serialized_msg = env->NewByteArray(size);
175
0
    RETURN_ERROR_IF_EXC(env);
176
0
    if (*serialized_msg == NULL) return Status::InternalError("couldn't construct jbyteArray");
177
0
    env->SetByteArrayRegion(*serialized_msg, 0, size, reinterpret_cast<jbyte*>(buffer));
178
0
    RETURN_ERROR_IF_EXC(env);
179
0
    return Status::OK();
180
0
}
Unexecuted instantiation: _ZN5doris18SerializeThriftMsgINS_23TJdbcExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
Unexecuted instantiation: _ZN5doris18SerializeThriftMsgINS_26TJavaUdfExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
181
182
} // namespace doris