利用jvmti查看java异常

日常监控中,异常信息,可以说是一个常见的指标,但是有些时候,因为使用不当(线程池submit提交,但是没有取异常,也没有改造线程池),或者是逻辑原因,出现了吞异常的情况,这个对我们异常的分析带来了一定的影响。所以异常的监控不能单独从日志中检索。

异常throw事件

jvmti中提供了两个异常的事件,一个是包含throw和catch,一个是catch。选择功能多的那个方便一点。

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的日常操作步骤就是,打开开关,设置回调函数,开启通知。

  1. 打开开关
    把异常事件的开关打开。
    caps.can_generate_exception_events = 1;
  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 <<"catch"<< catchMethodName << " " << catchMethodSign << " " << catch_location << std::endl;
    }


    if (classSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(classSign));
    }
    if (classGenericSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(classGenericSign));
    }
    if (methodName != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(methodName));
    }
    if (methodSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(methodSign));
    }
    if (methodGenericSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(methodGenericSign));
    }
    if (catchMethodName != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(catchMethodName));
    }
    if (catchMethodSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(catchMethodSign));
    }
    if (catchMethodGenericSign != nullptr) {
        jvmti_env->Deallocate(reinterpret_cast<unsigned char *>(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;
  1. 开启通知
  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都获取到了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容