通过JVMTI识别异常 [英] Identifying exceptions through JVMTI

查看:72
本文介绍了通过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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆