java异常查看利器之使用 jvmti 的Callback_JVMTI_EVENT_EXCEPTION 事件查看异常
阅读本文前需要了解什么是jvmti,jvmti全称称之为 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方法中吃掉异常之后,不作任何处理。
package com.github.torlight.jvmtit; /**
* Hello world!
*
*/
public class App { public static void main( String[] args ){ System.out.println( "Hello World!" ); try {
throw new NullPointerException("QQQ");
} catch (Exception e) { }
}
}
程序加载相应的扩展,运行之后效果如下所示,可以看到在控制台上面,打印出空指针异常。如果不借助jvmti提供的异常事件进行相应的扩展话,控制台上就不会打印空指针异常信息。其实现原理也很简单,借助jvmti提供的异常事件进行相应的扩展,当jvm捕获到异常时,会回调针对该事件的扩展方法,在该方法体内部调用 printStackTrace 方法,打印异常提示信息。
java.lang.ClassNotFoundException: com.github.torlight.jvmtit.App
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
java.lang.ClassNotFoundException: com.github.torlight.jvmtit.App
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
15 java.lang.NullPointerException: QQQ
16 at com.github.torlight.jvmtit.App.main(App.java:14)
Hello World!
loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
Exception: Ljava/lang/ClassNotFoundException;
loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
Exception: Ljava/lang/ClassNotFoundException;
loaded class name=run in Callback_JVMTI_EVENT_EXCEPTION method
Exception: Ljava/lang/NullPointerException;
agent onload
下面贴出针对jvmti Callback_JVMTI_EVENT_EXCEPTION 事件进行扩展的agent代码。
// 这是主 DLL 文件。 #include "stdafx.h" #include "jvmti_evt_ex.h"
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <jvmti.h> 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 } void JNICALL Callback_JVMTI_EVENT_EXCEPTION (jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception,
jmethodID catch_method,
jlocation catch_location) { printf("loaded class name=%s\n ", "run in Callback_JVMTI_EVENT_EXCEPTION method");
char* class_name; jclass exception_class = jni_env->GetObjectClass(exception);
jvmti_env->GetClassSignature(exception_class, &class_name, NULL);
printf("Exception: %s\n", class_name); printStackTrace(jni_env, exception);
} void JNICALL Callback_JVMTI_EVENT_Exception_Catch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception) { char* class_name;
jclass exception_class = jni_env->GetObjectClass(exception);
jvmti_env->GetClassSignature(exception_class, &class_name, NULL);
printf("Exception: %s\n", class_name); printStackTrace(jni_env, exception);
} JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved){
jvmtiEnv *jvmti = NULL; fprintf(stderr,"agent onload"); //获取JVMTI environment
jint erno = vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
if (erno != JNI_OK) {
fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
return JNI_ERR;
} //注册功能
jvmtiCapabilities capabilities;
(void)memset(&capabilities, , sizeof(jvmtiCapabilities));
capabilities.can_generate_exception_events=1; jvmtiError error = jvmti->AddCapabilities(&capabilities);
if(error != JVMTI_ERROR_NONE) {
fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
return error;
} //设置JVM事件 (JVMTI_EVENT_EXCEPTION) 回调
jvmtiEventCallbacks ex_callbacks;
ex_callbacks.Exception = &Callback_JVMTI_EVENT_EXCEPTION;
error = jvmti->SetEventCallbacks(&ex_callbacks, (jint)sizeof(ex_callbacks));
if(error != JVMTI_ERROR_NONE) {
fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
return error;
} //设置事件通知
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, (jthread)NULL);
if(error != JVMTI_ERROR_NONE) {
fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!,the error code=%d",error);
return error;
} return JNI_OK;
} JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
//do nothing return JNI_OK;
} JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm){
//do nothing }
示例代码和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截图(三)
java异常查看利器之使用 jvmti 的Callback_JVMTI_EVENT_EXCEPTION 事件查看异常的更多相关文章
- Java黑科技之源:JVMTI完全解读
Java生态中有一些非常规的技术,它们能达到一些特别的效果.这些技术的实现原理不去深究的话一般并不是广为人知.这种技术通常被称为黑科技.而这些黑科技中的绝大部分底层都是通过JVMTI实现的. 形象地说 ...
- Effective Java 第三版——73.抛出合乎于抽象的异常
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——70. 对可恢复条件使用检查异常,对编程错误使用运行时异常
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Atitit. Java script 多重多重catch语句的实现and Javascript js 异常机制
Atitit. Java script 多重多重catch语句的实现and Javascript js 异常机制 1. 语法错误(ERROR)和运行期错误(Exception) 1 2. 错误类型判断 ...
- 解决测试redis集群时报"java.lang.NumberFormatException: For input string: "7003@17003..7002@17002"等异常
一.前言 关于redis5.0的集群模式下,通过客户端测试代码调试报"Exception in thread "main" java.lang.NumberFormatE ...
- Java基础 try...catch...catch 使用Exception,去捕获其子类异常
JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code ...
- 老技术新谈,Java应用监控利器JMX(2)
各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期由于架不住来自于程序员内心的灵魂的拷问,于是我们潜心修炼,与 Java 应用监控利器 JMX 正式打了个照面. J ...
- Java虚拟机诊断利器
Java虚拟机诊断利器
- 第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装
微信服务器和第三方服务器之间究竟是通过什么方式进行对话的? 下面,我们先看下图: 其实我们可以简单的理解: (1)首先,用户向微信服务器发送消息: (2)微信服务器接收到用户的消息处理之后,通过开发者 ...
随机推荐
- Tomcat权威指南-读书摘要系列2
2. 配置Tomcat 2.1. 重定向Web应用程序的目录 将工程文件与Tomcat分离 复制conf和webapps文件夹到分离目录: 配置CATALINA_BASE环境变量,值为分离目录: 2. ...
- android listview使用自定义的adapter没有了OnItemClickListener事件解决办法
在使用listview的时用使用自定义的adapter的时候,如果你的item布局中包含有Button,Checkable继承来的所有控件,那么你将无法获取listview的onItemClickLi ...
- 离线下载pip包安装
Host-A 不能上网,但是需要在上面安装python-package 通过另外一台能上网的Host-B主机 1. 下载需要离线安装的Packages 在Host-B上执行如下命令: 安装单个Pack ...
- mysql 主从配置(master/slave)
1. 在每台服务器上创建复制账号(也可以只在master上创建用户,这里配置两个是为了方便以后切换) 备库运行的I/O县城需要建立一个到主库的TCP/IP连接,所以必须在主库创建一个用户,并赋予合适 ...
- Windows系统环境下Solr之Java实战(三)使用solrJ管理索引库
https://www.cnblogs.com/zhuxiaojie/p/5764680.html https://www.cnblogs.com/xieyupeng/p/9317158.html
- 【总结】2017年当下最值得你关注的前端开发框架,不知道你就OUT了!
近几年随着 jQuery.Ext 以及 CSS3 的发展,以 Bootstrap 为代表的前端开发框架如雨后春笋般挤入视野,可谓应接不暇. 在这篇分享中,我将总结2017年当下最值得你关注的前端开发框 ...
- 键盘ASCII码
回车键 -- CR 键0x0d -- 16进制13 -- 10 进制'\r' -- 也可以 换行键 -- LF0x0a -- 16进制10 -- 10 进制'\n' -- 也可以 esc键 ...
- iOS学习笔记(3)— 屏幕旋转
一.屏幕旋转机制: iOS通过加速计判断当前的设备方向和屏幕旋转.当加速计检测到方向变化的时候,屏幕旋转的流程如下: 1.设备旋转时,系统接收到旋转事件. 2.系统将旋转事件通过AppDelegate ...
- 【比赛游记】THUSC2018酱油记
day -1 早上4:30就要起来去飞机场…… 7点的飞机,10:30就到北京了. 北京的街景并没有我想像的漂亮……大概是因为我在四环外〒▽〒 晚上还有CF div3场,果断的去水了,因为太累就没有打 ...
- KDE下安装fcitx后终端不能输入中文
编辑用户的 ~/.profile 文件(或/etc/profile): #fcitx export XIM_PROGRAM=fcitx export XIM=fcitx export GTK_IM_ ...