java异常查看利器之使用 jvmti 的Callback_JVMTI_EVENT_EXCEPTION 事件查看异常

  阅读本文前需要了解什么是jvmtijvmti全称称之为 JVM Tool Interface,有关jvmti更详细的知识,本文不再详细列出。大家可以借助百度来了解有关它更为详尽的内容。

  在开源文件大行其道的今天,基于java种种解决方案和框架纷绘踏至而来,浩瀚如海看不完也学不尽。在采用这些解决方案和框架进行项目开发时,往往会出现当程序卡壳时,既无异常提示信息亦没有与之对应的日志输出的局面。每每出现这样的困境时,往往只能通过打断点来一步步调试跟踪来解决。更有甚者,基于某一底层的框架进行相应的开发时,受限于框架开发者的精力和时间等因素的影响,如果框架针对某异常处理设计的不合理,处理异常时没有向外抛出异常,同时又没有输出日志信息。当出现问题时,雪上加霜的是框架又没有提供源码用于打断点调试,此时只能借助通过反编译工具,阅读框架源码来尝试解决问题。每每出现这些困境,真希望有一种工具能够洞悉那些被框架“吃掉”没有向往抛出的异常,以便加快问题的解决步伐。

  为了方便开发,一直都想做一个有关java异常查看的小工具。想了很长时间,想到了如下几种实现方式:

  • 借助字节码工具,在每一个方法开头和结尾处插入java异常捕获代码。这种方式实现起来效率太低了,况且如果在方法体内,捕获异常并没有向外抛出的话,就算采用这种方式也看不到异常。
  • SpringMVC框架针对异常进行了统一的封装和处理,只要进行相应的扩展就能捕获到程序抛出的异常。这种实现方式较前一种比较看来,效率大大提高了,但是仍然没有解决前者提到的,如果应用程序内部自己“吃掉异常”,不向外抛出异常的话,依然无法捕捉到异常,而且这种实现实现方式仅仅局限于使用了SpringMVC框架的WEB应用程序,如果使用了其它的WEB架构或者非WEB的应用程序就会无能为力,局限性太强。

  思来索去,想到java应用程序的运行肯定是离不开jvm的,不妨看一下jvm中有没有提供这样的扩展。在网上搜索了一番,发现jvm还真提供了这样的扩展。

JVM Tool Interface 链接地址:https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#Exception



示例代码,在main方法中吃掉异常之后,不作任何处理。

 1 package com.github.torlight.jvmtit;
 2
 3 /**
 4  * Hello world!
 5  *
 6  */
 7 public class App {
 8
 9     public static void main( String[] args ){
10
11         System.out.println( "Hello World!" );
12
13         try {
14             throw new NullPointerException("QQQ");
15         } catch (Exception e) {
16
17         }
18     }
19 }

  程序加载相应的扩展,运行之后效果如下所示,可以看到在控制台上面,打印出空指针异常。如果不借助jvmti提供的异常事件进行相应的扩展话,控制台上就不会打印空指针异常信息。其实现原理也很简单,借助jvmti提供的异常事件进行相应的扩展,当jvm捕获到异常时,会回调针对该事件的扩展方法,在该方法体内部调用 printStackTrace 方法,打印异常提示信息。

 1 java.lang.ClassNotFoundException: com.github.torlight.jvmtit.App
 2     at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
 3     at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
 4     at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
 5     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
 6     at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 7     at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
 8 java.lang.ClassNotFoundException: com.github.torlight.jvmtit.App
 9     at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
10     at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
11     at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
12     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
13     at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
14     at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
15 java.lang.NullPointerException: QQQ
16     at com.github.torlight.jvmtit.App.main(App.java:14)
17 Hello World!
18 loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
19  Exception: Ljava/lang/ClassNotFoundException;
20 loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
21  Exception: Ljava/lang/ClassNotFoundException;
22 loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
23  Exception: Ljava/lang/NullPointerException;
24 agent onload


下面贴出针对jvmti Callback_JVMTI_EVENT_EXCEPTION 事件进行扩展的agent代码。

  1 // 这是主 DLL 文件。
  2
  3 #include "stdafx.h"
  4
  5 #include "jvmti_evt_ex.h"
  6 #include <stdio.h>
  7 #include <memory.h>
  8 #include <string.h>
  9 #include <jvmti.h>
 10
 11 void printStackTrace(JNIEnv* env, jobject exception) {
 12     jclass throwable_class = (*env).FindClass("java/lang/Throwable");
 13     jmethodID print_method = (*env).GetMethodID(throwable_class, "printStackTrace", "()V");
 14     (*env).CallVoidMethod(exception, print_method);
 15 }
 16
 17 void JNICALL Callback_JVMTI_EVENT_EXCEPTION (jvmtiEnv *jvmti_env,
 18     JNIEnv* jni_env,
 19     jthread thread,
 20     jmethodID method,
 21     jlocation location,
 22     jobject exception,
 23     jmethodID catch_method,
 24     jlocation catch_location) {
 25
 26     printf("loaded class name=%s\n ", "run in Callback_JVMTI_EVENT_EXCEPTION method");
 27     char* class_name;
 28
 29     jclass exception_class = jni_env->GetObjectClass(exception);
 30     jvmti_env->GetClassSignature(exception_class, &class_name, NULL);
 31     printf("Exception: %s\n", class_name);
 32
 33     printStackTrace(jni_env, exception);
 34 }
 35
 36
 37 void JNICALL Callback_JVMTI_EVENT_Exception_Catch (jvmtiEnv *jvmti_env,
 38     JNIEnv* jni_env,
 39     jthread thread,
 40     jmethodID method,
 41     jlocation location,
 42     jobject exception)    {
 43
 44     char* class_name;
 45     jclass exception_class = jni_env->GetObjectClass(exception);
 46     jvmti_env->GetClassSignature(exception_class, &class_name, NULL);
 47     printf("Exception: %s\n", class_name);
 48
 49     printStackTrace(jni_env, exception);
 50 }
 51
 52
 53 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved){
 54     jvmtiEnv *jvmti = NULL;
 55
 56     fprintf(stderr,"agent onload");
 57
 58     //获取JVMTI environment
 59     jint erno = vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
 60     if (erno != JNI_OK) {
 61         fprintf(stderr, "ERROR: Couldn‘t get JVMTI environment");
 62         return JNI_ERR;
 63     }
 64
 65     //注册功能
 66     jvmtiCapabilities capabilities;
 67     (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 68     capabilities.can_generate_exception_events=1;
 69
 70     jvmtiError error = jvmti->AddCapabilities(&capabilities);
 71     if(error != JVMTI_ERROR_NONE) {
 72         fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
 73         return  error;
 74     }
 75
 76     //设置JVM事件 (JVMTI_EVENT_EXCEPTION) 回调
 77     jvmtiEventCallbacks ex_callbacks;
 78     ex_callbacks.Exception = &Callback_JVMTI_EVENT_EXCEPTION;
 79     error = jvmti->SetEventCallbacks(&ex_callbacks, (jint)sizeof(ex_callbacks));
 80     if(error != JVMTI_ERROR_NONE) {
 81         fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
 82         return error;
 83     }
 84
 85     //设置事件通知
 86     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, (jthread)NULL);
 87     if(error != JVMTI_ERROR_NONE) {
 88         fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!,the error code=%d",error);
 89         return  error;
 90     }
 91
 92     return JNI_OK;
 93 }
 94
 95 JNIEXPORT jint JNICALL
 96     Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
 97         //do nothing
 98
 99     return JNI_OK;
100 }
101
102 JNIEXPORT void JNICALL
103     Agent_OnUnload(JavaVM *vm){
104         //do nothing
105
106 }

  示例代码和agent代码均已经上传至github上面(链接地址:https://github.com/gittorlight/java-other/tree/master/jvmti_evt_ex),我是用 visual studio 2010 来编译agent的,编译的时候需要根据所下载的jdk是32位还是64位来选择相对应的头文件。我使用的是64位的jdk 1.8,所以使用的是64位的头文件。截图如下所示:

  编译agent截图(一)

编译agent截图(二)

编译agent截图(三)

编译完成agent之后,在应用程序的启动参数上面使用-agentpath 参数来加载该agent。以eclipse为例,截图如下所示:

加载agent截图(一)

加载agent截图(二)

加载agent截图(三)

原文地址:https://www.cnblogs.com/yql1986/p/9695319.html

时间: 2024-08-10 02:39:14

java异常查看利器之使用 jvmti 的Callback_JVMTI_EVENT_EXCEPTION 事件查看异常的相关文章

JAVA程序 写供别人调用的接口方法的时候 异常应该怎么处理?

要看出现的是哪种异常了.如果是使用某些定义好的函数,并且函数本身会产生异常处理方法一般两种:1.自己用try{}catch(){}语句捕获异常并处理.2.在定义接口的后面写上throw Exception.把异常抛出让使用接口的人处理异常.两种方法都可以.但如果是RuntimeException异常,那就是自己程序某些地方写错了,那你就必须找到并修改程序.JAVA程序 写供别人调用的接口方法的时候 异常应该怎么处理?,布布扣,bubuko.com

Atitit. Java script 多重多重catch语句的实现and Javascript js 异常机制

Atitit. Java script 多重多重catch语句的实现and Javascript js 异常机制 1. 语法错误(ERROR)和运行期错误(Exception) 1 2. 错误类型判断 二种方法: 1 3. 我们常接触到的异常包括: 2 4. ------代码 2 5. 参考 4 1. 语法错误(ERROR)和运行期错误(Exception) Javascript提供了两种特殊的错误处理方式 BOM包含一个onerror事件处理函数,这个window对象与图像对象上都有 同时EC

Java反编译利器-Jad, Jode, Java Decompiler等及其IDE插件

转自:http://blog.csdn.net/superbeck/article/details/5189231 对于长年使用Java的程序员,大部分应该都会或多或少的使用到反编译软件.毕竟,不可能你所使用到的每一个包都会提供完善的javadoc,而且,有时候代码比javadoc更容易理解.这里将讲述笔者所了解的一些反编译软件以及它们各自的IDE插件版. 简介 现在业内的反编译软件据说有几十种.其中有jad,以及基于jad而开发的其他反编译软件(Front End Plus.mDeJava.D

Java基础 try...catch...catch 使用众多异常的父类 Exception,去捕 获 其所有子类异常

????JDK :OpenJDK-11 ?????OS :CentOS 7.6.1810 ?????IDE :Eclipse 2019?03 typesetting :Markdown ? code package per.jizuiku.base; /** * @author 给最苦 * @date 2019/06/29 * @blog www.cnblogs.com/jizuiku */ class Demo { /** * @param args */ public static void

使用Windows事件查看器调试崩溃

本文讨论如何使用Windows事件查看器获取实际崩溃的模块以及代码中崩溃的位置.示例代码是用C++编写的,以生成不同类型的崩溃,例如访问冲突和堆栈溢出. 简介 我经常听同事和QA那里听说,一个特定的崩溃很容易在客户机上重现,而不是在他们的机器上重现.这是一个棘手的问题,因为开发人员无法在客户机上调试崩溃.最终的结果是支持团队和客户之间无休止的沟通,甚至是现场会议.很少有聪明的程序员自己开发一个崩溃日志系统来确定导致崩溃的代码.很少有人会在代码中全面地实现try-catch块,以缩小问题的范围.

查看linux系统常用的命令,Linux查看系统配置常用命令

一.linux CPU大小  cat /proc/cpuinfo |grep "model name" && cat /proc/cpuinfo |grep "physical id" 说明:Linux下可以在/proc/cpuinfo中看到每个cpu的详细信息.但是对于双核的cpu,在cpuinfo中会看到两个cpu.常常会让人误以为是两个单核的cpu.其实应该通过Physical Processor ID来区分单核和双核.而Physical Pr

“DBUtility.DbHelperSQL”的类型初始值设定项引发异常 “DBUtility.DbHelperSQL”的类型初始值设定项引发异常

今天遇到了一个这样的问题"DBUtility.DbHelperSQL"的类型初始值设定项引发异常"DBUtility.DbHelperSQL"的类型初始值设定项引发异常 也许有和我遇到这问题的人也在这个问题怎么解决,在没有解决这个问题的时候我也和你们现在的心情一样,也百度过了,goolge过但是还是没有解决,在这个时候我们需要冷静下来想想问什么会出现这个问题 分析一下问题出现的原因,现在我来解答为什么会出现这个错误,其实很简单就是你没有配置好web连接数据库的语句,

IIS事件查看器_WebServer事件查看器_帮助查看IIS-Web服务器事件执行日志

IIS服务器是我们常用的Web站点部署工具,而我们有时可能遇到IIS服务器的应用程序池莫名其妙的关闭了,或者是其他未知原因等等,我们这是可以通过微软提供的WebServer(Web服务事件查看器),来帮助我们查找原因. 打开方法 编辑 方法一:可以通过单击鼠标右键至"我的电脑"(windows7的"计算机"),在弹出的快捷菜单下选择"管理",会弹出计算机管理菜单,选择菜单下的"事件查看器"即可,单击会出现三个选项,其中的系统可

Win10电脑老是自动弹出“事件查看器”?教你一招关掉它!

有网友表示,电脑升级win10系统后,“事件查看器”老是自动弹出,关闭后过段时间又弹出,玩游戏时也会自己弹出,严重影响心情.今天我就教大家如何解决这个问题,快来看看吧! 1.首先,按WIN+R键打开运行窗口,输入services.msc 回车打开: 2.找到[Windows Event Log],双击它,将“启动类型”修改为[足球比分],然后点[停止],“确定”即可. 设置好后,“事件管理器”就不会再自动弹出啦~ 如果你也升级了win10系统,快去设置下吧!