This is an automated email from the ASF dual-hosted git repository.

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git

commit d23a5aa8f1ff455b362520e1f0af01ce35062963
Author: daidai <[email protected]>
AuthorDate: Mon May 27 17:20:32 2024 +0800

    [feature](metrics)support be jvm metrics. (#35023)
    
    support be jvm metrics.
    if you `curl http://be_host:webserver_port/metrics` , you will get :
    ```
    doris_be_jvm_heap_size_bytes{type="max"} 8589934592
    doris_be_jvm_heap_size_bytes{type="committed"} 8589934592
    doris_be_jvm_heap_size_bytes{type="used"} 364159504
    
    doris_be_jvm_non_heap_size_bytes{type="committed"} 117899264
    doris_be_jvm_non_heap_size_bytes{type="used"} 115330424
    
    doris_be_jvm_young_size_bytes{type="used"} 255852544
    doris_be_jvm_young_size_bytes{type="peak_used"} 255852544
    doris_be_jvm_young_size_bytes{type="max"} 8589934592
    
    doris_be_jvm_old_size_bytes{type="used"} 94393344
    doris_be_jvm_old_size_bytes{type="peak_used"} 94393344
    doris_be_jvm_old_size_bytes{type="max"} 8589934592
    
    doris_be_jvm_gc{name="G1 Young Generation Count", type="count"} 3
    doris_be_jvm_gc{name="G1 Young Generation Time", type="time"} 33
    doris_be_jvm_gc{name="G1 Old Generation Count", type="count"} 0
    doris_be_jvm_gc{name="G1 Old Generation Time", type="time"} 0
    
    doris_be_jvm_thread{type="count"} 147
    doris_be_jvm_thread{type="peak_count"} 147
    doris_be_jvm_thread{type="new_count"} 0
    doris_be_jvm_thread{type="runnable_count"} 25
    doris_be_jvm_thread{type="blocked_count"} 0
    doris_be_jvm_thread{type="waiting_count"} 48
    doris_be_jvm_thread{type="timed_waiting_count"} 74
    doris_be_jvm_thread{type="terminated_count"} 0
    ```
---
 be/src/util/doris_metrics.cpp |   4 +
 be/src/util/doris_metrics.h   |   6 +
 be/src/util/jni-util.cpp      |   2 +
 be/src/util/jvm_metrics.cpp   | 423 ++++++++++++++++++++++++++++++++++++++++++
 be/src/util/jvm_metrics.h     | 149 +++++++++++++++
 5 files changed, 584 insertions(+)

diff --git a/be/src/util/doris_metrics.cpp b/be/src/util/doris_metrics.cpp
index 0b80b37bc9d..daecaf5e7fd 100644
--- a/be/src/util/doris_metrics.cpp
+++ b/be/src/util/doris_metrics.cpp
@@ -316,6 +316,10 @@ void DorisMetrics::initialize(bool init_system_metrics, 
const std::set<std::stri
     }
 }
 
+void DorisMetrics::init_jvm_metrics(JNIEnv* env) {
+    _jvm_metrics.reset(new JvmMetrics(&_metric_registry, env));
+}
+
 void DorisMetrics::_update() {
     _update_process_thread_num();
     _update_process_fd_num();
diff --git a/be/src/util/doris_metrics.h b/be/src/util/doris_metrics.h
index ea6b68aa24e..c568098195b 100644
--- a/be/src/util/doris_metrics.h
+++ b/be/src/util/doris_metrics.h
@@ -17,11 +17,14 @@
 
 #pragma once
 
+#include <jni.h>
+
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
+#include "util/jvm_metrics.h"
 #include "util/metrics.h"
 #include "util/system_metrics.h"
 
@@ -244,6 +247,8 @@ public:
     MetricRegistry* metric_registry() { return &_metric_registry; }
     SystemMetrics* system_metrics() { return _system_metrics.get(); }
     MetricEntity* server_entity() { return _server_metric_entity.get(); }
+    JvmMetrics* jvm_metrics() { return _jvm_metrics.get(); }
+    void init_jvm_metrics(JNIEnv* env);
 
 private:
     // Don't allow constructor
@@ -260,6 +265,7 @@ private:
     MetricRegistry _metric_registry;
 
     std::unique_ptr<SystemMetrics> _system_metrics;
+    std::unique_ptr<JvmMetrics> _jvm_metrics;
 
     std::shared_ptr<MetricEntity> _server_metric_entity;
 };
diff --git a/be/src/util/jni-util.cpp b/be/src/util/jni-util.cpp
index 3c8f2b0a30f..428c587f667 100644
--- a/be/src/util/jni-util.cpp
+++ b/be/src/util/jni-util.cpp
@@ -35,6 +35,7 @@
 
 #include "common/config.h"
 #include "gutil/strings/substitute.h"
+#include "util/doris_metrics.h"
 #include "util/jni_native_method.h"
 #include "util/libjvm_loader.h"
 
@@ -571,6 +572,7 @@ Status JniUtil::Init() {
     }
     RETURN_IF_ERROR(init_jni_scanner_loader(env));
     jvm_inited_ = true;
+    DorisMetrics::instance()->init_jvm_metrics(env);
     return Status::OK();
 }
 
diff --git a/be/src/util/jvm_metrics.cpp b/be/src/util/jvm_metrics.cpp
new file mode 100644
index 00000000000..e55cf8f3fbe
--- /dev/null
+++ b/be/src/util/jvm_metrics.cpp
@@ -0,0 +1,423 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "jvm_metrics.h"
+
+#include <functional>
+
+#include "util/metrics.h"
+
+namespace doris {
+
+#define DEFINE_JVM_SIZE_BYTES_METRIC(name, type)                               
      \
+    DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(name##_##type, MetricUnit::BYTES, "", 
name, \
+                                         Labels({{"type", #type}}));
+
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_heap_size_bytes, max);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_heap_size_bytes, committed);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_heap_size_bytes, used);
+
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_non_heap_size_bytes, used);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_non_heap_size_bytes, committed);
+
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_young_size_bytes, used);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_young_size_bytes, peak_used);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_young_size_bytes, max);
+
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_old_size_bytes, used);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_old_size_bytes, peak_used);
+DEFINE_JVM_SIZE_BYTES_METRIC(jvm_old_size_bytes, max);
+
+#define DEFINE_JVM_THREAD_METRIC(type)                                         
                 \
+    DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(jvm_thread_##type, 
MetricUnit::NOUNIT, "", jvm_thread, \
+                                         Labels({{"type", #type}}));
+
+DEFINE_JVM_THREAD_METRIC(count);
+DEFINE_JVM_THREAD_METRIC(peak_count);
+DEFINE_JVM_THREAD_METRIC(new_count);
+DEFINE_JVM_THREAD_METRIC(runnable_count);
+DEFINE_JVM_THREAD_METRIC(blocked_count);
+DEFINE_JVM_THREAD_METRIC(waiting_count);
+DEFINE_JVM_THREAD_METRIC(timed_waiting_count);
+DEFINE_JVM_THREAD_METRIC(terminated_count);
+
+DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(jvm_gc_g1_young_generation_count, 
MetricUnit::NOUNIT, "",
+                                     jvm_gc,
+                                     Labels({{"name", "G1 Young generation 
Count"},
+                                             {"type", "count"}}));
+
+DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(jvm_gc_g1_young_generation_time_ms, 
MetricUnit::MILLISECONDS,
+                                     "", jvm_gc,
+                                     Labels({{"name", "G1 Young generation 
Time"},
+                                             {"type", "time"}}));
+
+DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(jvm_gc_g1_old_generation_count, 
MetricUnit::NOUNIT, "", jvm_gc,
+                                     Labels({{"name", "G1 Old generation 
Count"},
+                                             {"type", "count"}}));
+
+DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(jvm_gc_g1_old_generation_time_ms, 
MetricUnit::MILLISECONDS, "",
+                                     jvm_gc,
+                                     Labels({{"name", "G1 Old generation 
Time"},
+                                             {"type", "time"}}));
+
+const char* JvmMetrics::_s_hook_name = "jvm_metrics";
+
+JvmMetrics::JvmMetrics(MetricRegistry* registry, JNIEnv* env) : 
_jvm_stats(env) {
+    DCHECK(registry != nullptr);
+    _registry = registry;
+
+    _server_entity = _registry->register_entity("server");
+    DCHECK(_server_entity != nullptr);
+    if (_jvm_stats.init_complete()) {
+        _server_entity->register_hook(_s_hook_name, 
std::bind(&JvmMetrics::update, this));
+    }
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_heap_size_bytes_max);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_heap_size_bytes_committed);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_heap_size_bytes_used);
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_non_heap_size_bytes_used);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, 
jvm_non_heap_size_bytes_committed);
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_young_size_bytes_used);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_young_size_bytes_peak_used);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_young_size_bytes_max);
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_old_size_bytes_used);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_old_size_bytes_peak_used);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_old_size_bytes_max);
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_peak_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_new_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_runnable_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_blocked_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_waiting_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_timed_waiting_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_thread_terminated_count);
+
+    INT_GAUGE_METRIC_REGISTER(_server_entity, 
jvm_gc_g1_young_generation_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, 
jvm_gc_g1_young_generation_time_ms);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, jvm_gc_g1_old_generation_count);
+    INT_GAUGE_METRIC_REGISTER(_server_entity, 
jvm_gc_g1_old_generation_time_ms);
+}
+
+void JvmMetrics::update() {
+    _jvm_stats.refresh(this);
+}
+#include <util/jni-util.h>
+
+jvmStats::jvmStats(JNIEnv* ENV) : env(ENV) {
+    _managementFactoryClass = 
env->FindClass("java/lang/management/ManagementFactory");
+    if (_managementFactoryClass == nullptr) {
+        LOG(WARNING)
+                << "Class java/lang/management/ManagementFactory Not Find.JVM 
monitoring fails.";
+        return;
+    }
+
+    _getMemoryMXBeanMethod = env->GetStaticMethodID(_managementFactoryClass, 
"getMemoryMXBean",
+                                                    
"()Ljava/lang/management/MemoryMXBean;");
+
+    _memoryUsageClass = env->FindClass("java/lang/management/MemoryUsage");
+    if (_memoryUsageClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/management/MemoryUsage Not Find.JVM 
monitoring fails.";
+        return;
+    }
+    _getMemoryUsageUsedMethod = env->GetMethodID(_memoryUsageClass, "getUsed", 
"()J");
+    _getMemoryUsageCommittedMethod = env->GetMethodID(_memoryUsageClass, 
"getCommitted", "()J");
+    _getMemoryUsageMaxMethod = env->GetMethodID(_memoryUsageClass, "getMax", 
"()J");
+
+    _memoryMXBeanClass = env->FindClass("java/lang/management/MemoryMXBean");
+    if (_memoryMXBeanClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/management/MemoryMXBean Not Find.JVM 
monitoring fails.";
+        return;
+    }
+    _getHeapMemoryUsageMethod = env->GetMethodID(_memoryMXBeanClass, 
"getHeapMemoryUsage",
+                                                 
"()Ljava/lang/management/MemoryUsage;");
+    _getNonHeapMemoryUsageMethod = env->GetMethodID(_memoryMXBeanClass, 
"getNonHeapMemoryUsage",
+                                                    
"()Ljava/lang/management/MemoryUsage;");
+
+    _getMemoryPoolMXBeansMethod = env->GetStaticMethodID(
+            _managementFactoryClass, "getMemoryPoolMXBeans", 
"()Ljava/util/List;");
+
+    _listClass = env->FindClass("java/util/List");
+    if (_listClass == nullptr) {
+        LOG(WARNING) << "Class java/util/List Not Find.JVM monitoring fails.";
+        return;
+    }
+    _getListSizeMethod = env->GetMethodID(_listClass, "size", "()I");
+    _getListUseIndexMethod = env->GetMethodID(_listClass, "get", 
"(I)Ljava/lang/Object;");
+
+    _memoryPoolMXBeanClass = 
env->FindClass("java/lang/management/MemoryPoolMXBean");
+    if (_memoryPoolMXBeanClass == nullptr) {
+        LOG(WARNING)
+                << "Class java/lang/management/MemoryPoolMXBean Not Find.JVM 
monitoring fails.";
+        return;
+    }
+    _getMemoryPoolMXBeanUsageMethod = env->GetMethodID(_memoryPoolMXBeanClass, 
"getUsage",
+                                                       
"()Ljava/lang/management/MemoryUsage;");
+    _getMemoryPollMXBeanPeakMethod = env->GetMethodID(_memoryPoolMXBeanClass, 
"getPeakUsage",
+                                                      
"()Ljava/lang/management/MemoryUsage;");
+    _getMemoryPollMXBeanNameMethod =
+            env->GetMethodID(_memoryPoolMXBeanClass, "getName", 
"()Ljava/lang/String;");
+
+    _getThreadMXBeanMethod = env->GetStaticMethodID(_managementFactoryClass, 
"getThreadMXBean",
+                                                    
"()Ljava/lang/management/ThreadMXBean;");
+
+    _getGarbageCollectorMXBeansMethod = env->GetStaticMethodID(
+            _managementFactoryClass, "getGarbageCollectorMXBeans", 
"()Ljava/util/List;");
+
+    _garbageCollectorMXBeanClass = 
env->FindClass("java/lang/management/GarbageCollectorMXBean");
+    if (_garbageCollectorMXBeanClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/management/GarbageCollectorMXBean Not 
Find.JVM monitoring "
+                        "fails.";
+        return;
+    }
+    _getGCNameMethod =
+            env->GetMethodID(_garbageCollectorMXBeanClass, "getName", 
"()Ljava/lang/String;");
+    _getGCCollectionCountMethod =
+            env->GetMethodID(_garbageCollectorMXBeanClass, 
"getCollectionCount", "()J");
+    _getGCCollectionTimeMethod =
+            env->GetMethodID(_garbageCollectorMXBeanClass, 
"getCollectionTime", "()J");
+
+    _threadMXBeanClass = env->FindClass("java/lang/management/ThreadMXBean");
+    if (_threadMXBeanClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/management/ThreadMXBean Not Find.JVM 
monitoring fails.";
+        return;
+    }
+    _getAllThreadIdsMethod = env->GetMethodID(_threadMXBeanClass, 
"getAllThreadIds", "()[J");
+    _getThreadInfoMethod = env->GetMethodID(_threadMXBeanClass, 
"getThreadInfo",
+                                            
"([JI)[Ljava/lang/management/ThreadInfo;");
+    _getPeakThreadCountMethod = env->GetMethodID(_threadMXBeanClass, 
"getPeakThreadCount", "()I");
+
+    _threadInfoClass = env->FindClass("java/lang/management/ThreadInfo");
+    if (_threadInfoClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/management/ThreadInfo Not Find.JVM 
monitoring fails.";
+        return;
+    }
+
+    _getThreadStateMethod =
+            env->GetMethodID(_threadInfoClass, "getThreadState", 
"()Ljava/lang/Thread$State;");
+
+    _threadStateClass = env->FindClass("java/lang/Thread$State");
+    if (_threadStateClass == nullptr) {
+        LOG(WARNING) << "Class java/lang/Thread$State Not Find.JVM monitoring 
fails.";
+        return;
+    }
+
+    jfieldID newThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "NEW", 
"Ljava/lang/Thread$State;");
+    jfieldID runnableThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "RUNNABLE", 
"Ljava/lang/Thread$State;");
+    jfieldID blockedThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "BLOCKED", 
"Ljava/lang/Thread$State;");
+    jfieldID waitingThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "WAITING", 
"Ljava/lang/Thread$State;");
+    jfieldID timedWaitingThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "TIMED_WAITING", 
"Ljava/lang/Thread$State;");
+    jfieldID terminatedThreadFieldID =
+            env->GetStaticFieldID(_threadStateClass, "TERMINATED", 
"Ljava/lang/Thread$State;");
+
+    _newThreadStateObj = env->GetStaticObjectField(_threadStateClass, 
newThreadFieldID);
+    _runnableThreadStateObj = env->GetStaticObjectField(_threadStateClass, 
runnableThreadFieldID);
+    _blockedThreadStateObj = env->GetStaticObjectField(_threadStateClass, 
blockedThreadFieldID);
+    _waitingThreadStateObj = env->GetStaticObjectField(_threadStateClass, 
waitingThreadFieldID);
+    _timedWaitingThreadStateObj =
+            env->GetStaticObjectField(_threadStateClass, 
timedWaitingThreadFieldID);
+    _terminatedThreadStateObj =
+            env->GetStaticObjectField(_threadStateClass, 
terminatedThreadFieldID);
+
+    LOG(INFO) << "Start JVM monitoring.";
+
+    _init_complete = true;
+}
+
+#include "jni.h"
+
+void jvmStats::refresh(JvmMetrics* jvm_metrics) {
+    if (!_init_complete) {
+        return;
+    }
+    static_cast<void>(JniUtil::GetJNIEnv(&env));
+
+    jobject memoryMXBeanObj =
+            env->CallStaticObjectMethod(_managementFactoryClass, 
_getMemoryMXBeanMethod);
+
+    jobject heapMemoryUsageObj = env->CallObjectMethod(memoryMXBeanObj, 
_getHeapMemoryUsageMethod);
+
+    jlong heapMemoryUsed = env->CallLongMethod(heapMemoryUsageObj, 
_getMemoryUsageUsedMethod);
+    jlong heapMemoryCommitted =
+            env->CallLongMethod(heapMemoryUsageObj, 
_getMemoryUsageCommittedMethod);
+    jlong heapMemoryMax = env->CallLongMethod(heapMemoryUsageObj, 
_getMemoryUsageMaxMethod);
+
+    jvm_metrics->jvm_heap_size_bytes_used->set_value(heapMemoryUsed < 0 ? 0 : 
heapMemoryUsed);
+    jvm_metrics->jvm_heap_size_bytes_committed->set_value(
+            heapMemoryCommitted < 0 ? 0 : heapMemoryCommitted);
+    jvm_metrics->jvm_heap_size_bytes_max->set_value(heapMemoryMax < 0 ? 0 : 
heapMemoryMax);
+
+    jobject nonHeapMemoryUsageObj =
+            env->CallObjectMethod(memoryMXBeanObj, 
_getNonHeapMemoryUsageMethod);
+
+    jlong nonHeapMemoryCommitted =
+            env->CallLongMethod(nonHeapMemoryUsageObj, 
_getMemoryUsageCommittedMethod);
+    jlong nonHeapMemoryUsed = env->CallLongMethod(nonHeapMemoryUsageObj, 
_getMemoryUsageUsedMethod);
+
+    jvm_metrics->jvm_non_heap_size_bytes_committed->set_value(
+            nonHeapMemoryCommitted < 0 ? 0 : nonHeapMemoryCommitted);
+    jvm_metrics->jvm_non_heap_size_bytes_used->set_value(nonHeapMemoryUsed < 0 
? 0
+                                                                               
: nonHeapMemoryUsed);
+
+    jobject memoryPoolMXBeansList =
+            env->CallStaticObjectMethod(_managementFactoryClass, 
_getMemoryPoolMXBeansMethod);
+
+    jint size = env->CallIntMethod(memoryPoolMXBeansList, _getListSizeMethod);
+
+    for (int i = 0; i < size; ++i) {
+        jobject memoryPoolMXBean =
+                env->CallObjectMethod(memoryPoolMXBeansList, 
_getListUseIndexMethod, i);
+        jobject usageObject =
+                env->CallObjectMethod(memoryPoolMXBean, 
_getMemoryPoolMXBeanUsageMethod);
+
+        jlong used = env->CallLongMethod(usageObject, 
_getMemoryUsageUsedMethod);
+        jlong max = env->CallLongMethod(usageObject, _getMemoryUsageMaxMethod);
+
+        jobject peakUsageObject =
+                env->CallObjectMethod(memoryPoolMXBean, 
_getMemoryPollMXBeanPeakMethod);
+
+        jlong peakUsed = env->CallLongMethod(peakUsageObject, 
_getMemoryUsageUsedMethod);
+
+        jstring name =
+                (jstring)env->CallObjectMethod(memoryPoolMXBean, 
_getMemoryPollMXBeanNameMethod);
+        const char* nameStr = env->GetStringUTFChars(name, NULL);
+        if (nameStr != NULL) {
+            auto it = _memoryPoolName.find(nameStr);
+            if (it == _memoryPoolName.end()) {
+                continue;
+            }
+            if (it->second == memoryPoolNameEnum::YOUNG) {
+                jvm_metrics->jvm_young_size_bytes_used->set_value(used < 0 ? 0 
: used);
+                
jvm_metrics->jvm_young_size_bytes_peak_used->set_value(peakUsed < 0 ? 0 : 
peakUsed);
+                jvm_metrics->jvm_young_size_bytes_max->set_value(max < 0 ? 0 : 
max);
+
+            } else if (it->second == memoryPoolNameEnum::OLD) {
+                jvm_metrics->jvm_old_size_bytes_used->set_value(used < 0 ? 0 : 
used);
+                jvm_metrics->jvm_old_size_bytes_peak_used->set_value(peakUsed 
< 0 ? 0 : peakUsed);
+                jvm_metrics->jvm_old_size_bytes_max->set_value(max < 0 ? 0 : 
max);
+            }
+
+            env->ReleaseStringUTFChars(name, nameStr);
+        }
+        env->DeleteLocalRef(memoryPoolMXBean);
+        env->DeleteLocalRef(usageObject);
+        env->DeleteLocalRef(peakUsageObject);
+    }
+
+    jobject threadMXBean =
+            env->CallStaticObjectMethod(_managementFactoryClass, 
_getThreadMXBeanMethod);
+
+    jlongArray threadIds = (jlongArray)env->CallObjectMethod(threadMXBean, 
_getAllThreadIdsMethod);
+    jint threadCount = env->GetArrayLength(threadIds);
+
+    jobjectArray threadInfos =
+            (jobjectArray)env->CallObjectMethod(threadMXBean, 
_getThreadInfoMethod, threadIds, 0);
+
+    int threadsNew = 0, threadsRunnable = 0, threadsBlocked = 0, 
threadsWaiting = 0,
+        threadsTimedWaiting = 0, threadsTerminated = 0;
+    jint peakThreadCount = env->CallIntMethod(threadMXBean, 
_getPeakThreadCountMethod);
+
+    jvm_metrics->jvm_thread_peak_count->set_value(peakThreadCount < 0 ? 0 : 
peakThreadCount);
+    jvm_metrics->jvm_thread_count->set_value(threadCount < 0 ? 0 : 
threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+        jobject threadInfo = env->GetObjectArrayElement(threadInfos, i);
+        if (threadInfo == nullptr) {
+            continue;
+        }
+        jobject threadState = env->CallObjectMethod(threadInfo, 
_getThreadStateMethod);
+
+        if (env->IsSameObject(threadState, _newThreadStateObj)) {
+            threadsNew++;
+        } else if (env->IsSameObject(threadState, _runnableThreadStateObj)) {
+            threadsRunnable++;
+        } else if (env->IsSameObject(threadState, _blockedThreadStateObj)) {
+            threadsBlocked++;
+        } else if (env->IsSameObject(threadState, _waitingThreadStateObj)) {
+            threadsWaiting++;
+        } else if (env->IsSameObject(threadState, 
_timedWaitingThreadStateObj)) {
+            threadsTimedWaiting++;
+        } else if (env->IsSameObject(threadState, _terminatedThreadStateObj)) {
+            threadsTerminated++;
+        }
+        env->DeleteLocalRef(threadInfo);
+        env->DeleteLocalRef(threadState);
+    }
+
+    jvm_metrics->jvm_thread_new_count->set_value(threadsNew < 0 ? 0 : 
threadsNew);
+    jvm_metrics->jvm_thread_runnable_count->set_value(threadsRunnable < 0 ? 0 
: threadsRunnable);
+    jvm_metrics->jvm_thread_blocked_count->set_value(threadsBlocked < 0 ? 0 : 
threadsBlocked);
+    jvm_metrics->jvm_thread_waiting_count->set_value(threadsWaiting < 0 ? 0 : 
threadsWaiting);
+    jvm_metrics->jvm_thread_timed_waiting_count->set_value(
+            threadsTimedWaiting < 0 ? 0 : threadsTimedWaiting);
+    jvm_metrics->jvm_thread_terminated_count->set_value(threadsTerminated < 0 
? 0
+                                                                              
: threadsTerminated);
+
+    jobject gcMXBeansList =
+            env->CallStaticObjectMethod(_managementFactoryClass, 
_getGarbageCollectorMXBeansMethod);
+
+    jint numCollectors = env->CallIntMethod(gcMXBeansList, _getListSizeMethod);
+
+    for (int i = 0; i < numCollectors; i++) {
+        jobject gcMXBean = env->CallObjectMethod(gcMXBeansList, 
_getListUseIndexMethod, i);
+
+        jstring gcName = (jstring)env->CallObjectMethod(gcMXBean, 
_getGCNameMethod);
+        jlong gcCollectionCount = env->CallLongMethod(gcMXBean, 
_getGCCollectionCountMethod);
+        jlong gcCollectionTime = env->CallLongMethod(gcMXBean, 
_getGCCollectionTimeMethod);
+        const char* gcNameStr = env->GetStringUTFChars(gcName, NULL);
+        if (gcNameStr != nullptr) {
+            if (strcmp(gcNameStr, "G1 Young Generation") == 0) {
+                
jvm_metrics->jvm_gc_g1_young_generation_count->set_value(gcCollectionCount);
+                
jvm_metrics->jvm_gc_g1_young_generation_time_ms->set_value(gcCollectionTime);
+
+            } else {
+                
jvm_metrics->jvm_gc_g1_old_generation_count->set_value(gcCollectionCount);
+                
jvm_metrics->jvm_gc_g1_old_generation_time_ms->set_value(gcCollectionTime);
+            }
+
+            env->ReleaseStringUTFChars(gcName, gcNameStr);
+        }
+        env->DeleteLocalRef(gcMXBean);
+    }
+    env->DeleteLocalRef(memoryMXBeanObj);
+    env->DeleteLocalRef(heapMemoryUsageObj);
+    env->DeleteLocalRef(nonHeapMemoryUsageObj);
+    env->DeleteLocalRef(memoryPoolMXBeansList);
+    env->DeleteLocalRef(threadMXBean);
+    env->DeleteLocalRef(gcMXBeansList);
+}
+jvmStats::~jvmStats() {
+    if (!_init_complete) {
+        return;
+    }
+    env->DeleteLocalRef(_newThreadStateObj);
+    env->DeleteLocalRef(_runnableThreadStateObj);
+    env->DeleteLocalRef(_blockedThreadStateObj);
+    env->DeleteLocalRef(_waitingThreadStateObj);
+    env->DeleteLocalRef(_timedWaitingThreadStateObj);
+    env->DeleteLocalRef(_terminatedThreadStateObj);
+}
+
+} // namespace doris
diff --git a/be/src/util/jvm_metrics.h b/be/src/util/jvm_metrics.h
new file mode 100644
index 00000000000..5f9929d8cf0
--- /dev/null
+++ b/be/src/util/jvm_metrics.h
@@ -0,0 +1,149 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <jni.h>
+
+#include "jni.h"
+#include "util/jni-util.h"
+#include "util/metrics.h"
+
+namespace doris {
+
+class JvmMetrics;
+
+class jvmStats {
+private:
+    JNIEnv* env = nullptr;
+    jclass _managementFactoryClass = nullptr;
+    jmethodID _getMemoryMXBeanMethod = nullptr;
+    jclass _memoryUsageClass = nullptr;
+    jclass _memoryMXBeanClass = nullptr;
+    jmethodID _getHeapMemoryUsageMethod = nullptr;
+    jmethodID _getNonHeapMemoryUsageMethod = nullptr;
+    jmethodID _getMemoryUsageUsedMethod = nullptr;
+    jmethodID _getMemoryUsageCommittedMethod = nullptr;
+    jmethodID _getMemoryUsageMaxMethod = nullptr;
+
+    jmethodID _getMemoryPoolMXBeansMethod = nullptr;
+
+    jclass _listClass = nullptr;
+    jmethodID _getListSizeMethod = nullptr;
+    jmethodID _getListUseIndexMethod = nullptr;
+
+    jclass _memoryPoolMXBeanClass = nullptr;
+    jmethodID _getMemoryPoolMXBeanUsageMethod = nullptr;
+
+    jmethodID _getMemoryPollMXBeanPeakMethod = nullptr;
+    jmethodID _getMemoryPollMXBeanNameMethod = nullptr;
+
+    enum memoryPoolNameEnum { YOUNG, SURVIVOR, OLD };
+    const std::map<std::string, memoryPoolNameEnum> _memoryPoolName = {
+            {"Eden Space", YOUNG},
+            {"PS Eden Space", YOUNG},
+            {"Par Eden Space", YOUNG},
+            {"G1 Eden Space", YOUNG},
+
+            {"Survivor Space", SURVIVOR},
+            {"PS Survivor Space", SURVIVOR},
+            {"Par Survivor Space", SURVIVOR},
+            {"G1 Survivor Space", SURVIVOR},
+
+            {"Tenured Gen", OLD},
+            {"PS Old Gen", OLD},
+            {"CMS Old Gen", OLD},
+            {"G1 Old Gen", OLD},
+
+    };
+
+    jmethodID _getThreadMXBeanMethod = nullptr;
+    jclass _threadMXBeanClass = nullptr;
+    jmethodID _getAllThreadIdsMethod = nullptr;
+    jmethodID _getThreadInfoMethod = nullptr;
+    jclass _threadInfoClass = nullptr;
+
+    jmethodID _getPeakThreadCountMethod = nullptr;
+
+    jmethodID _getThreadStateMethod = nullptr;
+    jclass _threadStateClass = nullptr;
+
+    jobject _newThreadStateObj = nullptr;
+    jobject _runnableThreadStateObj = nullptr;
+    jobject _blockedThreadStateObj = nullptr;
+    jobject _waitingThreadStateObj = nullptr;
+    jobject _timedWaitingThreadStateObj = nullptr;
+    jobject _terminatedThreadStateObj = nullptr;
+
+    jclass _garbageCollectorMXBeanClass = nullptr;
+    jmethodID _getGCNameMethod = nullptr;
+    jmethodID _getGarbageCollectorMXBeansMethod = nullptr;
+    jmethodID _getGCCollectionCountMethod = nullptr;
+    jmethodID _getGCCollectionTimeMethod = nullptr;
+
+    bool _init_complete = false;
+
+public:
+    jvmStats(JNIEnv* ENV);
+    bool init_complete() { return _init_complete; }
+    void refresh(JvmMetrics* jvm_metrics);
+    ~jvmStats();
+};
+
+class JvmMetrics {
+public:
+    JvmMetrics(MetricRegistry* registry, JNIEnv* env);
+    ~JvmMetrics() {}
+    void update();
+
+    IntGauge* jvm_heap_size_bytes_max = nullptr;
+    IntGauge* jvm_heap_size_bytes_committed = nullptr;
+    IntGauge* jvm_heap_size_bytes_used = nullptr;
+
+    IntGauge* jvm_non_heap_size_bytes_used = nullptr;
+    IntGauge* jvm_non_heap_size_bytes_committed = nullptr;
+
+    IntGauge* jvm_young_size_bytes_used = nullptr;
+    IntGauge* jvm_young_size_bytes_peak_used = nullptr;
+    IntGauge* jvm_young_size_bytes_max = nullptr;
+
+    IntGauge* jvm_old_size_bytes_used = nullptr;
+    IntGauge* jvm_old_size_bytes_peak_used = nullptr;
+    IntGauge* jvm_old_size_bytes_max = nullptr;
+
+    IntGauge* jvm_thread_count = nullptr;
+    IntGauge* jvm_thread_peak_count = nullptr;
+    IntGauge* jvm_thread_new_count = nullptr;
+    IntGauge* jvm_thread_runnable_count = nullptr;
+    IntGauge* jvm_thread_blocked_count = nullptr;
+    IntGauge* jvm_thread_waiting_count = nullptr;
+    IntGauge* jvm_thread_timed_waiting_count = nullptr;
+    IntGauge* jvm_thread_terminated_count = nullptr;
+
+    IntGauge* jvm_gc_g1_young_generation_count = nullptr;
+    IntGauge* jvm_gc_g1_young_generation_time_ms = nullptr;
+    IntGauge* jvm_gc_g1_old_generation_count = nullptr;
+    IntGauge* jvm_gc_g1_old_generation_time_ms = nullptr;
+
+private:
+    jvmStats _jvm_stats;
+    std::shared_ptr<MetricEntity> _server_entity;
+    static const char* _s_hook_name;
+    MetricRegistry* _registry = nullptr;
+};
+
+} // namespace doris


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to