Java 通过JVMTI识别异常
我正在使用JVMTI为Java应用程序编写一个检测工具。我已经看到,JVMTI可以检测何时抛出了异常,何时捕获了异常。 本文档说明了事件异常和异常批次 异常字段标识抛出的异常对象 尽管它没有说明如何在运行期间比较它们(即,比较exception中提供的异常与ExceptionCatch中捕获的表达式相对应)。换句话说,对于Java 通过JVMTI识别异常,java,exception,jvmti,Java,Exception,Jvmti,我正在使用JVMTI为Java应用程序编写一个检测工具。我已经看到,JVMTI可以检测何时抛出了异常,何时捕获了异常。 本文档说明了事件异常和异常批次 异常字段标识抛出的异常对象 尽管它没有说明如何在运行期间比较它们(即,比较exception中提供的异常与ExceptionCatch中捕获的表达式相对应)。换句话说,对于 # java -version java version "1.7.0_85" OpenJDK Runtime Environment (IcedTea 2.6.1) (7u
# java -version
java version "1.7.0_85"
OpenJDK Runtime Environment (IcedTea 2.6.1) (7u85-2.6.1-5ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.85-b03, mixed mode)
当直接比较jexception/jobject时,它似乎并不总是正确的。考虑监视异常和异常捕获事件的JVMTI代理(下面的源代码),还考虑引发异常的后续java天真示例(源代码也包括),最后您可以通过代理与“给定的MaFIX文件”(包括在最末尾)一起使用“运行运行”来运行示例。p>
如果我使用OpenJDK 7运行该示例,它会给出以下结果:
# make run
...
cb_Exception (exception=0x2ae6b8087be8)
cb_ExceptionCatch (exception=0x2ae6b80859f8 vs last_exception=0x2ae6b8087be8)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b80859f8)
cb_ExceptionCatch (exception=0x2ae6b807a388 vs last_exception=0x2ae6b80859f8)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b807a388)
cb_ExceptionCatch (exception=0x2ae6b807a388 vs last_exception=0x2ae6b807a388)
AreSameObject? = 1
cb_Exception (exception=0x2ae6b807a388)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b807a388)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
after doing work
如果我使用IBM的Java1.7.0运行这个示例,情况就有点类似
# make run
...
cb_Exception (exception=0x7d78a0)
cb_ExceptionCatch (exception=0x7d7950 vs last_exception=0x7d78a0)
AreSameObject? = 1
cb_Exception (exception=0x7d7938)
cb_ExceptionCatch (exception=0x7d7950 vs last_exception=0x7d7938)
AreSameObject? = 0
cb_Exception (exception=0x7d7938)
cb_ExceptionCatch (exception=0x7d79a8 vs last_exception=0x7d7938)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x7d7a60)
cb_ExceptionCatch (exception=0x7d7a98 vs last_exception=0x7d7a60)
AreSameObject? = 0
after doing work
在OpenJDK中,请注意每个异常都有一个对应的ExceptionCatch,但是在主Java代码之外有六个异常,我怀疑它们来自Java VM本身。请注意,exception的值在每一对中都不相同。在第5次回调调用之后,exception的值似乎变为常量。IBM的Java中的情况有些类似,但引发的异常较少。根据Chen Harel在评论中的建议,我存储了引发的异常,并通过IsSameObject JNI的方法将其与捕获的异常进行了比较,但JNI声称这些异常并不相同
那么,在应用程序生命周期中,可以通过Exception和ExceptionCatch识别异常吗?如果是这样,通过JVMTI或JNI比较异常的合适方法是什么
agent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = exception;
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = (*jni_env)->NewGlobalRef (jni_env, exception);
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
(*jni_env)->DeleteGlobalRef(jni_env, last_exception);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
Makefile
JAVA_JDK=/usr/lib/jvm/java-7-openjdk-amd64
all: libagent.so example.class
libagent.so: agent.c
gcc -shared -fPIC -DPIC agent.c -o libagent.so -I$(JAVA_JDK)/include
example.class: example.java
javac example.java
run: libagent.so example.class
java -agentpath:$(PWD)/libagent.so example
11月9日更新
正如Chen Harel所建议的,我已经创建了一个对异常的全局引用。agent.c的修改版本如下所示,执行的输出如下所示
agent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = exception;
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = (*jni_env)->NewGlobalRef (jni_env, exception);
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
(*jni_env)->DeleteGlobalRef(jni_env, last_exception);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
JOBECT是C++指针,对堆的引用在它下面处理。如果你仔细想想,这更像是指向一个指针的指针
测试两个作业对象是否相同的方法是使用jni方法 如果要测试异常的类型,请使用GetObjectClass+IsInstanceOf 编辑请注意,为了使jobject在方法之间保持有效,您必须使用jni NewGlobalRef方法为它们创建一个引用。Chen,我更新了我的示例,将抛出的异常存储到一个全局变量中,然后将捕获的异常与此全局变量进行比较。然而,结果似乎并不令人满意,因为例外情况并不匹配。此外,我在IBM的Java 1.7.0上对此进行了测试,结果有些相似。在jni NewGlobalRef上简洁地编辑答案以创建可重用的jobject有趣的是,NewGlobalRef的使用在IBM的Java 1.7.0中起作用。但是,OpenJDK 7中的情况并没有改善,有几个例外不匹配。我已经用全局引用的创建(和删除)更新了agent.c。我还发布了执行结果。嗨,Harald,我正试图编写一个与您类似的代理,但不幸的是,我在findClass和loadClass中遇到异常,我知道它们可能是由于NoClassDefFoundException引起的,但我在包含类文件的同一目录下运行java程序,有什么想法吗?