jvmtiを使用してjava例外を表示する

5667 ワード

日常のモニタリングでは、異常情報は、よく見られる指標と言えるが、不適切な使用(スレッドプールsubmit提出、しかし異常を取っていない、スレッドプールを改造していない)、あるいは論理的な原因で、飲み込む異常が発生した場合があり、これは私たちの異常な分析に一定の影響を与えた.したがって、例外のモニタリングはログから単独で取得できません.
異常throwイベント
jvmtiには、throwとcatch、catchを含む2つの異常なイベントが提供されています.機能の多いものを選ぶのが便利です.
void JNICALL
Exception(jvmtiEnv *jvmti_env,
            JNIEnv* jni_env,
            jthread thread,
            jmethodID method,
            jlocation location,
            jobject exception,
            jmethodID catch_method,
            jlocation catch_location)

関数により,異常なスレッド,異常なメソッド,行番号,異常オブジェクト,catchのメソッド,行番号を取得できることが分かった.関数のパラメータにより,ほぼ取得可能なデータを理解できるようになった.ここではトリガのthrowイベントなので、new Exceptionの操作だけではイベントはトリガされません.一部のコードはExceptionまたはErrorを作成することによって論理を制御し、throw、catchのこのような論理でなければ、ここでは検出できません.異常がthrowだけcatchがない場合、catchのフィールドは空です.
簡易な実装
異常スタックを印刷し、トリガされたメソッドとコード行数、キャプチャされたメソッド、コード行数を出力します.jvmtiの日常的な操作手順は、スイッチをオンにし、コールバック関数を設定し、通知をオンにすることです.
  • スイッチを入れて異常イベントのスイッチを入れます.
  •     caps.can_generate_exception_events = 1;
    
  • コールバック関数を設定コールバック関数を記述する
  • void JNICALL jvmti_EventException
            (jvmtiEnv *jvmti_env,
             JNIEnv
             *jni_env,
             jthread thread,
             jmethodID
             method,
             jlocation location,
             jobject
             exception,
             jmethodID catch_method,
             jlocation
             catch_location) {
        char *classSign = nullptr, *classGenericSign = nullptr,
                *methodName = nullptr, *methodSign = nullptr, *methodGenericSign = nullptr,
                *catchMethodName = nullptr, *catchMethodSign = nullptr, *catchMethodGenericSign = nullptr;
    
        //jni    
        jclass exceptionClass = jni_env->GetObjectClass(exception);
        jmethodID jmethodId = jni_env->GetMethodID(exceptionClass, "printStackTrace", "()V");
        jni_env->CallVoidMethod(exception, jmethodId);
    
        //         
        jvmti_env->GetClassSignature(exceptionClass, &classSign, &classGenericSign);
        //  throw     
        jvmti_env->GetMethodName(method, &methodName, &methodSign, &methodGenericSign);
    
        std::cout << classSign << std::endl;
        std::cout << methodName << " " << methodSign << " " << location << std::endl;
    
        //  catch     
        jvmti_env->GetMethodName(catch_method, &catchMethodName, &catchMethodSign, &catchMethodGenericSign);
        if (catchMethodName != nullptr) {
            std::cout <Deallocate(reinterpret_cast(classSign));
        }
        if (classGenericSign != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(classGenericSign));
        }
        if (methodName != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(methodName));
        }
        if (methodSign != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(methodSign));
        }
        if (methodGenericSign != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(methodGenericSign));
        }
        if (catchMethodName != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(catchMethodName));
        }
        if (catchMethodSign != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(catchMethodSign));
        }
        if (catchMethodGenericSign != nullptr) {
            jvmti_env->Deallocate(reinterpret_cast(catchMethodGenericSign));
        }
    
    }
    

    ここで注意しなければならないのは最後の回収操作です.jvmtiのドキュメントでは、jvmtiインタフェースで生成されたオブジェクトをdeallocate関数で解放する必要があると特に宣言されています.
    JVM TI functions always return an error code via the jvmtiError function return value. Some functions can return additional values through pointers provided by the calling function.In some cases, JVM TI functions allocate memory that your program must explicitly deallocate. This is indicated in the individual JVM TI function descriptions. Empty lists, arrays, sequences, etc are returned as NULL.
    コールバックの設定
        cb.Exception = &jvmti_EventException;
    
  • オープン通知
  •   jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL);
    

    テストケース
        public static void main(String[] args) throws InterruptedException {
            new InterruptedException("test");
            try {
                testException();
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    
        public static void testException() {
            throw new RuntimeException("hello");
        }
    
    

    テスト例は簡単です.主に直接new異常などがトリガーされるかどうか、複数回キャプチャ、throwの結果をテストします.
    
    Ljava/lang/RuntimeException;
    testException ()V 9
    catchmain ([Ljava/lang/String;)V 16
    
    Ljava/lang/Error;
    main ([Ljava/lang/String;)V 25
    
    java.lang.RuntimeException: hello
        at com.company.Main.testException(Main.java:20)
        at com.company.Main.main(Main.java:10)
    java.lang.Error: java.lang.RuntimeException: hello
        at com.company.Main.main(Main.java:12)
    Caused by: java.lang.RuntimeException: hello
        at com.company.Main.testException(Main.java:20)
        at com.company.Main.main(Main.java:10)
    Exception in thread "main" java.lang.Error: java.lang.RuntimeException: hello
        at com.company.Main.main(Main.java:12)
    Caused by: java.lang.RuntimeException: hello
        at com.company.Main.testException(Main.java:20)
        at com.company.Main.main(Main.java:10)
    

    作成したInterruptedExceptionだけが取得されていないことがわかります.Errorはthrowだけcatchがありません.RuntimeExceptionはthrowでcatchが取得しました.