Coverage Report

Created: 2025-05-09 17:05

/root/doris/be/src/util/jni-util.cpp
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
#include "util/jni-util.h"
19
20
#include <fmt/format.h>
21
#include <glog/logging.h>
22
#include <jni.h>
23
#include <jni_md.h>
24
25
#include <algorithm>
26
#include <cstdlib>
27
#include <filesystem>
28
#include <iterator>
29
#include <memory>
30
#include <mutex>
31
#include <sstream>
32
#include <string>
33
#include <vector>
34
35
#include "common/config.h"
36
#include "gutil/strings/substitute.h"
37
#include "util/doris_metrics.h"
38
#include "util/jni_native_method.h"
39
#include "util/libjvm_loader.h"
40
41
using std::string;
42
43
namespace doris {
44
45
namespace {
46
JavaVM* g_vm;
47
[[maybe_unused]] std::once_flag g_vm_once;
48
[[maybe_unused]] std::once_flag g_jvm_conf_once;
49
50
0
const std::string GetDorisJNIDefaultClasspath() {
51
0
    const auto* doris_home = getenv("DORIS_HOME");
52
0
    DCHECK(doris_home) << "Environment variable DORIS_HOME is not set.";
53
0
54
0
    std::ostringstream out;
55
0
56
0
    auto add_jars_from_path = [&](const std::string& base_path) {
57
0
        if (!std::filesystem::exists(base_path)) {
58
0
            return;
59
0
        }
60
0
        for (const auto& entry : std::filesystem::recursive_directory_iterator(base_path)) {
61
0
            if (entry.path().extension() == ".jar") {
62
0
                if (!out.str().empty()) {
63
0
                    out << ":";
64
0
                }
65
0
                out << entry.path().string();
66
0
            }
67
0
        }
68
0
    };
69
0
70
0
    add_jars_from_path(std::string(doris_home) + "/lib");
71
0
    add_jars_from_path(std::string(doris_home) + "/custom_lib");
72
0
73
0
    // Check and add HADOOP_CONF_DIR if it's set
74
0
    const auto* hadoop_conf_dir = getenv("HADOOP_CONF_DIR");
75
0
    if (hadoop_conf_dir != nullptr && strlen(hadoop_conf_dir) > 0) {
76
0
        if (!out.str().empty()) {
77
0
            out << ":";
78
0
        }
79
0
        out << hadoop_conf_dir;
80
0
    }
81
0
82
0
    DCHECK(!out.str().empty()) << "Empty classpath is invalid.";
83
0
    return out.str();
84
0
}
85
86
0
const std::string GetDorisJNIClasspathOption() {
87
0
    const auto* classpath = getenv("DORIS_CLASSPATH");
88
0
    if (classpath) {
89
0
        return classpath;
90
0
    } else {
91
0
        return "-Djava.class.path=" + GetDorisJNIDefaultClasspath();
92
0
    }
93
0
}
94
95
3
const std::string GetKerb5ConfPath() {
96
3
    return "-Djava.security.krb5.conf=" + config::kerberos_krb5_conf_path;
97
3
}
98
99
3
[[maybe_unused]] void SetEnvIfNecessary() {
100
3
    std::string libhdfs_opts = getenv("LIBHDFS_OPTS") ? getenv("LIBHDFS_OPTS") : "";
101
3
    CHECK(libhdfs_opts != "") << "LIBHDFS_OPTS is not set";
102
3
    libhdfs_opts += fmt::format(" {} ", GetKerb5ConfPath());
103
3
    setenv("LIBHDFS_OPTS", libhdfs_opts.c_str(), 1);
104
3
    LOG(INFO) << "set final LIBHDFS_OPTS: " << libhdfs_opts;
105
3
}
106
107
// Only used on non-x86 platform
108
0
[[maybe_unused]] void FindOrCreateJavaVM() {
109
0
    int num_vms;
110
0
    int rv = JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
111
0
    if (rv == 0) {
112
0
        std::vector<std::string> options;
113
0
114
0
        char* java_opts = getenv("JAVA_OPTS");
115
0
        if (java_opts == nullptr) {
116
0
            options = {
117
0
                    GetDorisJNIClasspathOption(), fmt::format("-Xmx{}", "1g"),
118
0
                    fmt::format("-DlogPath={}/log/jni.log", getenv("DORIS_HOME")),
119
0
                    fmt::format("-Dsun.java.command={}", "DorisBE"), "-XX:-CriticalJNINatives",
120
0
#ifdef __APPLE__
121
0
                    // On macOS, we should disable MaxFDLimit, otherwise the RLIMIT_NOFILE
122
0
                    // will be assigned the minimum of OPEN_MAX (10240) and rlim_cur (See src/hotspot/os/bsd/os_bsd.cpp)
123
0
                    // and it can not pass the check performed by storage engine.
124
0
                    // The newer JDK has fixed this issue.
125
0
                    "-XX:-MaxFDLimit"
126
0
#endif
127
0
            };
128
0
        } else {
129
0
            std::istringstream stream(java_opts);
130
0
            options = std::vector<std::string>(std::istream_iterator<std::string> {stream},
131
0
                                               std::istream_iterator<std::string>());
132
0
            options.push_back(GetDorisJNIClasspathOption());
133
0
        }
134
0
        options.push_back(GetKerb5ConfPath());
135
0
        std::unique_ptr<JavaVMOption[]> jvm_options(new JavaVMOption[options.size()]);
136
0
        for (int i = 0; i < options.size(); ++i) {
137
0
            jvm_options[i] = {const_cast<char*>(options[i].c_str()), nullptr};
138
0
        }
139
0
140
0
        JNIEnv* env = nullptr;
141
0
        JavaVMInitArgs vm_args;
142
0
        vm_args.version = JNI_VERSION_1_8;
143
0
        vm_args.options = jvm_options.get();
144
0
        vm_args.nOptions = options.size();
145
0
        // Set it to JNI_FALSE because JNI_TRUE will let JVM ignore the max size config.
146
0
        vm_args.ignoreUnrecognized = JNI_FALSE;
147
0
148
0
        jint res = JNI_CreateJavaVM(&g_vm, (void**)&env, &vm_args);
149
0
        if (JNI_OK != res) {
150
0
            DCHECK(false) << "Failed to create JVM, code= " << res;
151
0
        }
152
0
    } else {
153
0
        CHECK_EQ(rv, 0) << "Could not find any created Java VM";
154
0
        CHECK_EQ(num_vms, 1) << "No VMs returned";
155
0
    }
156
0
}
157
158
} // anonymous namespace
159
160
bool JniUtil::jvm_inited_ = false;
161
__thread JNIEnv* JniUtil::tls_env_ = nullptr;
162
jclass JniUtil::internal_exc_cl_ = nullptr;
163
jclass JniUtil::jni_util_cl_ = nullptr;
164
jclass JniUtil::jni_native_method_exc_cl_ = nullptr;
165
jmethodID JniUtil::throwable_to_string_id_ = nullptr;
166
jmethodID JniUtil::throwable_to_stack_trace_id_ = nullptr;
167
jmethodID JniUtil::get_jvm_metrics_id_ = nullptr;
168
jmethodID JniUtil::get_jvm_threads_id_ = nullptr;
169
jmethodID JniUtil::get_jmx_json_ = nullptr;
170
jobject JniUtil::jni_scanner_loader_obj_ = nullptr;
171
jmethodID JniUtil::jni_scanner_loader_method_ = nullptr;
172
173
14
Status JniUtfCharGuard::create(JNIEnv* env, jstring jstr, JniUtfCharGuard* out) {
174
14
    DCHECK(jstr != nullptr);
175
14
    DCHECK(!env->ExceptionCheck());
176
14
    jboolean is_copy;
177
14
    const char* utf_chars = env->GetStringUTFChars(jstr, &is_copy);
178
14
    bool exception_check = static_cast<bool>(env->ExceptionCheck());
179
14
    if (utf_chars == nullptr || exception_check) {
180
0
        if (exception_check) env->ExceptionClear();
181
0
        if (utf_chars != nullptr) env->ReleaseStringUTFChars(jstr, utf_chars);
182
0
        auto fail_message = "GetStringUTFChars failed. Probable OOM on JVM side";
183
0
        LOG(WARNING) << fail_message;
184
0
        return Status::InternalError(fail_message);
185
0
    }
186
14
    out->env = env;
187
14
    out->jstr = jstr;
188
14
    out->utf_chars = utf_chars;
189
14
    return Status::OK();
190
14
}
191
192
1.76k
Status JniLocalFrame::push(JNIEnv* env, int max_local_ref) {
193
1.76k
    DCHECK(env_ == nullptr);
194
1.76k
    DCHECK_GT(max_local_ref, 0);
195
1.76k
    if (env->PushLocalFrame(max_local_ref) < 0) {
196
0
        env->ExceptionClear();
197
0
        return Status::InternalError("failed to push frame");
198
0
    }
199
1.76k
    env_ = env;
200
1.76k
    return Status::OK();
201
1.76k
}
202
203
300
Status JniUtil::GetJNIEnvSlowPath(JNIEnv** env) {
204
300
    DCHECK(!tls_env_) << "Call GetJNIEnv() fast path";
205
206
#ifdef USE_LIBHDFS3
207
    std::call_once(g_vm_once, FindOrCreateJavaVM);
208
    int rc = g_vm->GetEnv(reinterpret_cast<void**>(&tls_env_), JNI_VERSION_1_8);
209
    if (rc == JNI_EDETACHED) {
210
        rc = g_vm->AttachCurrentThread((void**)&tls_env_, nullptr);
211
    }
212
    if (rc != 0 || tls_env_ == nullptr) {
213
        return Status::InternalError("Unable to get JVM: {}", rc);
214
    }
215
#else
216
    // the hadoop libhdfs will do all the stuff
217
300
    std::call_once(g_jvm_conf_once, SetEnvIfNecessary);
218
300
    tls_env_ = getJNIEnv();
219
300
#endif
220
300
    *env = tls_env_;
221
300
    return Status::OK();
222
300
}
223
224
10.2k
Status JniUtil::GetJniExceptionMsg(JNIEnv* env, bool log_stack, const string& prefix) {
225
10.2k
    jthrowable exc = env->ExceptionOccurred();
226
10.2k
    if (exc == nullptr) {
227
10.2k
        return Status::OK();
228
10.2k
    }
229
1
    env->ExceptionClear();
230
1
    DCHECK(throwable_to_string_id() != nullptr);
231
1
    const char* oom_msg_template =
232
1
            "$0 threw an unchecked exception. The JVM is likely out "
233
1
            "of memory (OOM).";
234
1
    jstring msg = static_cast<jstring>(
235
1
            env->CallStaticObjectMethod(jni_util_class(), throwable_to_string_id(), exc));
236
1
    if (env->ExceptionOccurred()) {
237
0
        env->ExceptionClear();
238
0
        string oom_msg = strings::Substitute(oom_msg_template, "throwableToString");
239
0
        LOG(WARNING) << oom_msg;
240
0
        return Status::InternalError(oom_msg);
241
0
    }
242
1
    JniUtfCharGuard msg_str_guard;
243
1
    RETURN_IF_ERROR(JniUtfCharGuard::create(env, msg, &msg_str_guard));
244
7
    if (log_stack) {
245
7
        jstring stack = static_cast<jstring>(
246
7
                env->CallStaticObjectMethod(jni_util_class(), throwable_to_stack_trace_id(), exc));
247
7
        if (env->ExceptionOccurred()) {
248
0
            env->ExceptionClear();
249
0
            string oom_msg = strings::Substitute(oom_msg_template, "throwableToStackTrace");
250
0
            LOG(WARNING) << oom_msg;
251
0
            return Status::InternalError(oom_msg);
252
0
        }
253
7
        JniUtfCharGuard c_stack_guard;
254
7
        RETURN_IF_ERROR(JniUtfCharGuard::create(env, stack, &c_stack_guard));
255
7
        LOG(WARNING) << c_stack_guard.get();
256
7
    }
257
258
1
    env->DeleteLocalRef(exc);
259
1
    return Status::InternalError("{}{}", prefix, msg_str_guard.get());
260
1
}
261
262
1.97k
jobject JniUtil::convert_to_java_map(JNIEnv* env, const std::map<std::string, std::string>& map) {
263
    //TODO: ADD EXCEPTION CHECK.
264
1.97k
    jclass hashmap_class = env->FindClass("java/util/HashMap");
265
1.97k
    jmethodID hashmap_constructor = env->GetMethodID(hashmap_class, "<init>", "(I)V");
266
1.97k
    jobject hashmap_object = env->NewObject(hashmap_class, hashmap_constructor, map.size());
267
1.97k
    jmethodID hashmap_put = env->GetMethodID(
268
1.97k
            hashmap_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
269
6.12k
    for (const auto& it : map) {
270
6.12k
        jstring key = env->NewStringUTF(it.first.c_str());
271
6.12k
        jstring value = env->NewStringUTF(it.second.c_str());
272
6.12k
        env->CallObjectMethod(hashmap_object, hashmap_put, key, value);
273
6.12k
        env->DeleteLocalRef(key);
274
6.12k
        env->DeleteLocalRef(value);
275
6.12k
    }
276
1.97k
    env->DeleteLocalRef(hashmap_class);
277
1.97k
    return hashmap_object;
278
1.97k
}
279
280
0
std::map<std::string, std::string> JniUtil::convert_to_cpp_map(JNIEnv* env, jobject map) {
281
0
    std::map<std::string, std::string> resultMap;
282
283
    // Get the class and method ID of the java.util.Map interface
284
0
    jclass mapClass = env->FindClass("java/util/Map");
285
0
    jmethodID entrySetMethod = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
286
287
    // Get the class and method ID of the java.util.Set interface
288
0
    jclass setClass = env->FindClass("java/util/Set");
289
0
    jmethodID iteratorSetMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
290
291
    // Get the class and method ID of the java.util.Iterator interface
292
0
    jclass iteratorClass = env->FindClass("java/util/Iterator");
293
0
    jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z");
294
0
    jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
295
296
    // Get the class and method ID of the java.util.Map.Entry interface
297
0
    jclass entryClass = env->FindClass("java/util/Map$Entry");
298
0
    jmethodID getKeyMethod = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
299
0
    jmethodID getValueMethod = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
300
301
    // Call the entrySet method to get the set of key-value pairs
302
0
    jobject entrySet = env->CallObjectMethod(map, entrySetMethod);
303
304
    // Call the iterator method on the set to iterate over the key-value pairs
305
0
    jobject iteratorSet = env->CallObjectMethod(entrySet, iteratorSetMethod);
306
307
    // Iterate over the key-value pairs
308
0
    while (env->CallBooleanMethod(iteratorSet, hasNextMethod)) {
309
        // Get the current entry
310
0
        jobject entry = env->CallObjectMethod(iteratorSet, nextMethod);
311
312
        // Get the key and value from the entry
313
0
        jobject javaKey = env->CallObjectMethod(entry, getKeyMethod);
314
0
        jobject javaValue = env->CallObjectMethod(entry, getValueMethod);
315
316
        // Convert the key and value to C++ strings
317
0
        const char* key = env->GetStringUTFChars(static_cast<jstring>(javaKey), nullptr);
318
0
        const char* value = env->GetStringUTFChars(static_cast<jstring>(javaValue), nullptr);
319
320
        // Store the key-value pair in the map
321
0
        resultMap[key] = value;
322
323
        // Release the string references
324
0
        env->ReleaseStringUTFChars(static_cast<jstring>(javaKey), key);
325
0
        env->ReleaseStringUTFChars(static_cast<jstring>(javaValue), value);
326
327
        // Delete local references
328
0
        env->DeleteLocalRef(entry);
329
0
        env->DeleteLocalRef(javaKey);
330
0
        env->DeleteLocalRef(javaValue);
331
0
    }
332
333
    // Delete local references
334
0
    env->DeleteLocalRef(iteratorSet);
335
0
    env->DeleteLocalRef(entrySet);
336
0
    env->DeleteLocalRef(mapClass);
337
0
    env->DeleteLocalRef(setClass);
338
0
    env->DeleteLocalRef(iteratorClass);
339
0
    env->DeleteLocalRef(entryClass);
340
341
0
    return resultMap;
342
0
}
343
344
1.52k
Status JniUtil::GetGlobalClassRef(JNIEnv* env, const char* class_str, jclass* class_ref) {
345
1.52k
    *class_ref = NULL;
346
1.52k
    JNI_CALL_METHOD_CHECK_EXCEPTION_DELETE_REF(jclass, local_cl, env, FindClass(class_str));
347
1.52k
    RETURN_IF_ERROR(LocalToGlobalRef(env, local_cl, reinterpret_cast<jobject*>(class_ref)));
348
1.52k
    return Status::OK();
349
1.52k
}
350
351
3.28k
Status JniUtil::LocalToGlobalRef(JNIEnv* env, jobject local_ref, jobject* global_ref) {
352
3.28k
    *global_ref = env->NewGlobalRef(local_ref);
353
    // NewGlobalRef:
354
    // Returns a global reference to the given obj.
355
    //
356
    //May return NULL if:
357
    //  obj refers to null
358
    //  the system has run out of memory
359
    //  obj was a weak global reference and has already been garbage collected
360
3.28k
    if (*global_ref == NULL) {
361
0
        return Status::InternalError(
362
0
                "LocalToGlobalRef fail,global ref is NULL,maybe the system has run out of memory.");
363
0
    }
364
365
    //NewGlobalRef not throw exception,maybe we just need check NULL.
366
3.28k
    RETURN_ERROR_IF_EXC(env);
367
3.28k
    return Status::OK();
368
3.28k
}
369
370
3
Status JniUtil::init_jni_scanner_loader(JNIEnv* env) {
371
    // Get scanner loader;
372
3
    jclass jni_scanner_loader_cls;
373
3
    std::string jni_scanner_loader_str = "org/apache/doris/common/classloader/ScannerLoader";
374
3
    RETURN_IF_ERROR(JniUtil::GetGlobalClassRef(env, jni_scanner_loader_str.c_str(),
375
3
                                               &jni_scanner_loader_cls));
376
3
    jmethodID jni_scanner_loader_constructor =
377
3
            env->GetMethodID(jni_scanner_loader_cls, "<init>", "()V");
378
3
    RETURN_ERROR_IF_EXC(env);
379
3
    jni_scanner_loader_method_ = env->GetMethodID(jni_scanner_loader_cls, "getLoadedClass",
380
3
                                                  "(Ljava/lang/String;)Ljava/lang/Class;");
381
3
    if (jni_scanner_loader_method_ == NULL) {
382
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
383
0
        return Status::InternalError("Failed to find ScannerLoader.getLoadedClass method.");
384
0
    }
385
3
    RETURN_ERROR_IF_EXC(env);
386
3
    jmethodID load_jni_scanner =
387
3
            env->GetMethodID(jni_scanner_loader_cls, "loadAllScannerJars", "()V");
388
3
    RETURN_ERROR_IF_EXC(env);
389
390
3
    jni_scanner_loader_obj_ =
391
3
            env->NewObject(jni_scanner_loader_cls, jni_scanner_loader_constructor);
392
3
    RETURN_ERROR_IF_EXC(env);
393
3
    if (jni_scanner_loader_obj_ == NULL) {
394
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
395
0
        return Status::InternalError("Failed to create ScannerLoader object.");
396
0
    }
397
3
    env->CallVoidMethod(jni_scanner_loader_obj_, load_jni_scanner);
398
3
    RETURN_ERROR_IF_EXC(env);
399
3
    return Status::OK();
400
3
}
401
402
Status JniUtil::get_jni_scanner_class(JNIEnv* env, const char* classname,
403
486
                                      jclass* jni_scanner_class) {
404
    // Get JNI scanner class by class name;
405
486
    jobject loaded_class_obj = env->CallObjectMethod(
406
486
            jni_scanner_loader_obj_, jni_scanner_loader_method_, env->NewStringUTF(classname));
407
486
    RETURN_ERROR_IF_EXC(env);
408
486
    *jni_scanner_class = reinterpret_cast<jclass>(env->NewGlobalRef(loaded_class_obj));
409
486
    RETURN_ERROR_IF_EXC(env);
410
486
    return Status::OK();
411
486
}
412
413
3
Status JniUtil::Init() {
414
3
    RETURN_IF_ERROR(LibJVMLoader::instance().load());
415
416
    // Get the JNIEnv* corresponding to current thread.
417
3
    JNIEnv* env = nullptr;
418
3
    RETURN_IF_ERROR(JniUtil::GetJNIEnv(&env));
419
420
3
    if (env == NULL) return Status::InternalError("Failed to get/create JVM");
421
    // Find JniUtil class and create a global ref.
422
3
    jclass local_jni_util_cl = env->FindClass("org/apache/doris/common/jni/utils/JniUtil");
423
3
    if (local_jni_util_cl == NULL) {
424
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
425
0
        return Status::InternalError("Failed to find JniUtil class.");
426
0
    }
427
3
    jni_util_cl_ = reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_util_cl));
428
3
    if (jni_util_cl_ == NULL) {
429
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
430
0
        return Status::InternalError("Failed to create global reference to JniUtil class.");
431
0
    }
432
3
    env->DeleteLocalRef(local_jni_util_cl);
433
3
    if (env->ExceptionOccurred()) {
434
0
        return Status::InternalError("Failed to delete local reference to JniUtil class.");
435
0
    }
436
437
    // Find InternalException class and create a global ref.
438
3
    jclass local_internal_exc_cl =
439
3
            env->FindClass("org/apache/doris/common/exception/InternalException");
440
3
    if (local_internal_exc_cl == NULL) {
441
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
442
0
        return Status::InternalError("Failed to find JniUtil class.");
443
0
    }
444
3
    internal_exc_cl_ = reinterpret_cast<jclass>(env->NewGlobalRef(local_internal_exc_cl));
445
3
    if (internal_exc_cl_ == NULL) {
446
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
447
0
        return Status::InternalError("Failed to create global reference to JniUtil class.");
448
0
    }
449
3
    env->DeleteLocalRef(local_internal_exc_cl);
450
3
    if (env->ExceptionOccurred()) {
451
0
        return Status::InternalError("Failed to delete local reference to JniUtil class.");
452
0
    }
453
454
    // Find JNINativeMethod class and create a global ref.
455
3
    jclass local_jni_native_exc_cl =
456
3
            env->FindClass("org/apache/doris/common/jni/utils/JNINativeMethod");
457
3
    if (local_jni_native_exc_cl == nullptr) {
458
0
        if (env->ExceptionOccurred()) {
459
0
            env->ExceptionDescribe();
460
0
        }
461
0
        return Status::InternalError("Failed to find JNINativeMethod class.");
462
0
    }
463
3
    jni_native_method_exc_cl_ =
464
3
            reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_native_exc_cl));
465
3
    if (jni_native_method_exc_cl_ == nullptr) {
466
0
        if (env->ExceptionOccurred()) {
467
0
            env->ExceptionDescribe();
468
0
        }
469
0
        return Status::InternalError("Failed to create global reference to JNINativeMethod class.");
470
0
    }
471
3
    env->DeleteLocalRef(local_jni_native_exc_cl);
472
3
    if (env->ExceptionOccurred()) {
473
0
        return Status::InternalError("Failed to delete local reference to JNINativeMethod class.");
474
0
    }
475
3
    std::string resize_column_name = "resizeStringColumn";
476
3
    std::string resize_column_sign = "(JI)J";
477
3
    std::string memory_alloc_name = "memoryTrackerMalloc";
478
3
    std::string memory_alloc_sign = "(J)J";
479
3
    std::string memory_free_name = "memoryTrackerFree";
480
3
    std::string memory_free_sign = "(J)V";
481
3
    static JNINativeMethod java_native_methods[] = {
482
3
            {const_cast<char*>(resize_column_name.c_str()),
483
3
             const_cast<char*>(resize_column_sign.c_str()),
484
3
             (void*)&JavaNativeMethods::resizeStringColumn},
485
3
            {const_cast<char*>(memory_alloc_name.c_str()),
486
3
             const_cast<char*>(memory_alloc_sign.c_str()), (void*)&JavaNativeMethods::memoryMalloc},
487
3
            {const_cast<char*>(memory_free_name.c_str()),
488
3
             const_cast<char*>(memory_free_sign.c_str()), (void*)&JavaNativeMethods::memoryFree},
489
3
    };
490
491
3
    int res = env->RegisterNatives(jni_native_method_exc_cl_, java_native_methods,
492
3
                                   sizeof(java_native_methods) / sizeof(java_native_methods[0]));
493
3
    DCHECK_EQ(res, 0);
494
495
    // Throwable toString()
496
3
    throwable_to_string_id_ = env->GetStaticMethodID(jni_util_cl_, "throwableToString",
497
3
                                                     "(Ljava/lang/Throwable;)Ljava/lang/String;");
498
3
    if (throwable_to_string_id_ == NULL) {
499
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
500
0
        return Status::InternalError("Failed to find JniUtil.throwableToString method.");
501
0
    }
502
503
    // throwableToStackTrace()
504
3
    throwable_to_stack_trace_id_ = env->GetStaticMethodID(
505
3
            jni_util_cl_, "throwableToStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;");
506
3
    if (throwable_to_stack_trace_id_ == NULL) {
507
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
508
0
        return Status::InternalError("Failed to find JniUtil.throwableToFullStackTrace method.");
509
0
    }
510
511
3
    get_jvm_metrics_id_ = env->GetStaticMethodID(jni_util_cl_, "getJvmMemoryMetrics", "()[B");
512
3
    if (get_jvm_metrics_id_ == NULL) {
513
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
514
0
        return Status::InternalError("Failed to find JniUtil.getJvmMemoryMetrics method.");
515
0
    }
516
517
3
    get_jvm_threads_id_ = env->GetStaticMethodID(jni_util_cl_, "getJvmThreadsInfo", "([B)[B");
518
3
    if (get_jvm_threads_id_ == NULL) {
519
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
520
0
        return Status::InternalError("Failed to find JniUtil.getJvmThreadsInfo method.");
521
0
    }
522
523
3
    get_jmx_json_ = env->GetStaticMethodID(jni_util_cl_, "getJMXJson", "()[B");
524
3
    if (get_jmx_json_ == NULL) {
525
0
        if (env->ExceptionOccurred()) env->ExceptionDescribe();
526
0
        return Status::InternalError("Failed to find JniUtil.getJMXJson method.");
527
0
    }
528
3
    RETURN_IF_ERROR(init_jni_scanner_loader(env));
529
3
    jvm_inited_ = true;
530
3
    DorisMetrics::instance()->init_jvm_metrics(env);
531
3
    return Status::OK();
532
3
}
533
534
} // namespace doris