Coverage Report

Created: 2025-07-23 16:44

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