通过JVMTI识别异常 [英] Identifying exceptions through JVMTI
问题描述
我正在使用JVMTI为Java应用程序编写一种检测工具。我已经看到JVMTI根据 http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#Exception 。
此文档针对事件和ExceptionCatch都声明了
I'm writing an instrumentation tool for Java applications using JVMTI. I've seen that JVMTI detects when an exception has been thrown and when has been caught according to http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#Exception. This document states for both events Exception and ExceptionCatch
异常字段标识抛出的异常对象。
The exception field identifies the thrown exception object.
尽管没有指出如何在运行期间进行比较(即比较Exception中提供的异常与ExceptionCatch中捕获的异常相对应)。换句话说,对于
although it does not indicate on how to compare them during the run (i.e. to compare an exception provided in Exception corresponds to the expcetion caught in ExceptionCatch). In other words, for
# 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时,它并不总是正确的。考虑监视Exception和ExceptionCatch事件的JVMTI代理(下面的源代码),再考虑引发异常的后续Java天真示例(还包括源代码),最后您可以通过 make run与该代理一起运行该示例和给定的Makefile(包含在末尾)。
it does not seem to be always true when comparing the jexception/jobject directly. Consider the JVMTI agent (source code below) that monitors the Exception and ExceptionCatch events, also consider the subsequent Java naïve example (source-code also included) that throws an Exception, and finally you can run the example with the agent through "make run" with the given Makefile (included at the very end).
如果我使用OpenJDK 7运行示例,则会得到以下结果:
If I run the example using OpenJDK 7 it gives me the followign results:
# 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 Java 1.7.0运行示例,则情况有点相似。
If I run the example using IBM's Java 1.7.0 the situation is somewhat similar.
# 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中,请注意,每个Exception都有一个对应的ExceptionCatch,但是我怀疑主要Java代码之外有六个异常,这些异常来自Java VM本身。请注意,exception的值在每对中都不相同。在第5次回调调用之后,exception的值似乎保持不变。在IBM的Java中,情况有些相似,但提出的例外较少。作为Chen Harel在评论中的建议,我存储了引发的异常,并通过IsSameObject JNI的方法与捕获的异常进行了比较,但JNI声称这些异常不相同。
In OpenJDK's, notice that each Exception has a corresponding ExceptionCatch, yet there are six Exceptions outside the main Java code which I suspect that come from the Java VM itself. Notice that the value for exception is not the same in every pair. After th 5th callback call, the value for exception seems to get constant. The situation is somewhat similar in IBM's Java but with less exceptions raised. As a suggestion from Chen Harel from the comments, I've stored the raised exception and compared to the caught exception through the IsSameObject JNI's method but JNI claims that the exceptions are not the same.
那么,可以在应用程序生存期内通过Exception和ExceptionCatch识别异常吗?如果是这样,那是通过JVMTI或JNI比较异常的适当方法?
So, can exceptions be identified during the application life-time by the Exception and ExceptionCatch? If so, which is the appropriate way to compare exceptions through JVMTI or 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)
{
}
example.java
public class example
{
void except () throws Exception
{ throw new Exception ("new-exception"); }
void do_work()
{
System.out.println ("before doing work");
try
{ except(); }
catch (Exception e)
{}
System.out.println ("after doing work");
}
public static void main (String [] args)
{
example e = new example();
e.do_work ();
}
}
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
UPDATE 11月9日
我已经按照Chen Harel的建议创建了对该异常的全局引用。修改后的agent.c版本如下,执行结果如下所示。
I have created a global reference to the exception as Chen Harel suggested. The modified version of the agent.c is as follows and the output of the execution is shown below.
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 = (*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)
{
}
** make run的输出**
** Output of make run **
# make run
...
cb_Exception (exception=0x2b13b0087c18)
cb_ExceptionCatch (exception=0x2b13b0085a08 vs last_exception=0x2b13e4001608)
AreSameObject? = 0
cb_Exception (exception=0x2b13b0085a08)
cb_ExceptionCatch (exception=0x2b13b007a388 vs last_exception=0x2b13e4001610)
AreSameObject? = 0
cb_Exception (exception=0x2b13b007a388)
cb_ExceptionCatch (exception=0x2b13b007a388 vs last_exception=0x2b13e4001618)
AreSameObject? = 1
cb_Exception (exception=0x2b13b007a388)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a00)
AreSameObject? = 0
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a08)
AreSameObject? = 1
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a10)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a18)
AreSameObject? = 1
after doing work
推荐答案
jobject是一个C ++指针,并在其下处理对堆的引用。因此,如果您考虑一下,它更像是一个指针。
jobject is a C++ pointer and the reference to the heap is handled beneath it. So this is more of a pointer to a pointer if you think about it.
测试两个工作项是否相同的方法是使用jni方法 IsSameObject
The way to test if two jobjects are the same is to use the jni method IsSameObject
如果要测试异常的类型,请使用GetObjectClass + IsInstanceOf
If you want to test the type of the exception, use GetObjectClass + IsInstanceOf
EDIT
请注意,为了使Jobject在方法之间保持有效,您将必须使用jni NewGlobalRef方法为其创建引用。
Note that in order to keep jobject(s) valid between methods you will have to create a reference for them with jni NewGlobalRef method.
这篇关于通过JVMTI识别异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!