jvmti java_java debug 体系-JVMTI

1 jvmti 构成、介绍

JVMTI(JVM Tool Interface) 位于jpda 最底层, 是Java 虚拟机所提供的native编程接口。 JVMTI可以提供性能分析、debug、内存管理、线程分析等功能。

2 如何使用jvmti

JVMTI是一套本地编程接口,因此使用JVMTI,需要与c/c++ 以及JNI打交道。事实上,开发时一般采用建立一个Agent的方式来使用JVMTI,Agent使用jvmti函数,设置一些回调函数,并从Java虚拟机中得到当前的运行态信息,并作出自己的判断, 最后还可能操作虚拟机的运行态。把Agent编译成一个动态链接库之后,可以再Java程序启动时来加载它(比如IDE调试时使用的libjdwp.so 就是采用这种方式),当然也可以通过Attach方式,中途加入(比如jmap, jps,jstack 等等)。

3 Agent的加载、设置回调、卸载流程

4 JVMTI的环境

使用 JVMTI 的过程,主要是设置 JVMTI 环境,监听虚拟机所产生的事件,以及在某些事件上加上我们所希望的回调函数。

可以通过操作 jvmtiCapabilities 来查询、增加、修改 JVMTI 的环境参数。

另外,虚拟机有自己的一些功能,一开始并未被启动,那么增加或修改 jvmtiCapabilities 也是可能的,但不同的虚拟机对这个功能的处理也不太一样,多数的虚拟机允许增改,但是有一定的限制,比如仅支持在 Agent_OnLoad 时,即虚拟机启动时作出,它某种程度上反映了虚拟机本身的构架。

5 jvmti 基本能力

JVMTI 的功能非常丰富,包含了虚拟机中线程、内存 / 堆 / 栈,类 / 方法 / 变量,事件 / 定时器处理等等 20 多类功能。比如: 事件处理和回调函数、内存控制和对象获取、线程和锁、调试功能。具体的使用方法可以见参考文章 JVMTI 和 Agent 实现

6 event 机制介绍

jvmti 的event管理后续研究一下,再补上。

7 一个agent demo

一个agent 会经历环境初始化、参数解析、注册功能、注册事件响应这几个步骤。运行过程如下:

e59c4eed44a2

agent时序图

代码如下:

7.1 Main.cpp

#include

#include "MethodTraceAgent.h"

#include "jvmti.h"

using namespace std;

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)

{

cout << "Agent_OnLoad(" << vm << ")" << endl;

try{

MethodTraceAgent* agent = new MethodTraceAgent();

agent->Init(vm);

agent->ParseOptions(options);

agent->AddCapability();

agent->RegisterEvent();

} catch (AgentException& e) {

cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]";

return JNI_ERR;

}

return JNI_OK;

}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)

{

cout << "Agent_OnUnload(" << vm << ")" << endl;

}

7.2 MethodTraceAgent.cpp

#include

#include "MethodTraceAgent.h"

#include "jvmti.h"

using namespace std;

jvmtiEnv* MethodTraceAgent::m_jvmti = 0;

char* MethodTraceAgent::m_filter = 0;

MethodTraceAgent::~MethodTraceAgent() throw(AgentException)

{

// 必须释放内存,防止内存泄露

m_jvmti->Deallocate(reinterpret_cast(m_filter));

}

void MethodTraceAgent::Init(JavaVM *vm) const throw(AgentException){

jvmtiEnv *jvmti = 0;

jint ret = (vm)->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION_1_0);

if (ret != JNI_OK || jvmti == 0) {

throw AgentException(JVMTI_ERROR_INTERNAL);

}

m_jvmti = jvmti;

}

void MethodTraceAgent::ParseOptions(const char* str) const throw(AgentException)

{

if (str == 0)

return;

const size_t len = strlen(str);

if (len == 0)

return;

// 必须做好内存复制工作

jvmtiError error;

error = m_jvmti->Allocate(len + 1,reinterpret_cast(&m_filter));

CheckException(error);

strcpy(m_filter, str);

// 可以在这里进行参数解析的工作

// ...

}

void MethodTraceAgent::AddCapability() const throw(AgentException)

{

// 创建一个新的环境

jvmtiCapabilities caps;

memset(&caps, 0, sizeof(caps));

caps.can_generate_method_entry_events = 1;

// 设置当前环境

jvmtiError error = m_jvmti->AddCapabilities(&caps);

CheckException(error);

}

void MethodTraceAgent::RegisterEvent() const throw(AgentException)

{

// 创建一个新的回调函数

jvmtiEventCallbacks callbacks;

memset(&callbacks, 0, sizeof(callbacks));

callbacks.MethodEntry = &MethodTraceAgent::HandleMethodEntry;

// 设置回调函数

jvmtiError error;

error = m_jvmti->SetEventCallbacks(&callbacks, static_cast(sizeof(callbacks)));

CheckException(error);

// 开启事件监听

error = m_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, 0);

CheckException(error);

}

void JNICALL MethodTraceAgent::HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method)

{

try {

jvmtiError error;

jclass clazz;

char* name;

char* signature;

// 获得方法对应的类

error = m_jvmti->GetMethodDeclaringClass(method, &clazz);

CheckException(error);

// 获得类的签名

error = m_jvmti->GetClassSignature(clazz, &signature, 0);

CheckException(error);

// 获得方法名字

error = m_jvmti->GetMethodName(method, &name, NULL, NULL);

CheckException(error);

// 根据参数过滤不必要的方法

if(m_filter != 0){

if (strcmp(m_filter, name) != 0)

return;

}

cout << signature<< " -> " << name << "(..)"<< endl;

// 必须释放内存,避免内存泄露

error = m_jvmti->Deallocate(reinterpret_cast(name));

CheckException(error);

error = m_jvmti->Deallocate(reinterpret_cast(signature));

CheckException(error);

} catch (AgentException& e) {

cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]";

}

}

7.3 MethodTraceAgent.h

#include "jvmti.h"

class AgentException

{

public:

AgentException(jvmtiError err) {

m_error = err;

}

char* what() const throw() {

return "AgentException";

}

jvmtiError ErrCode() const throw() {

return m_error;

}

private:

jvmtiError m_error;

};

class MethodTraceAgent

{

public:

MethodTraceAgent() throw(AgentException){}

~MethodTraceAgent() throw(AgentException);

void Init(JavaVM *vm) const throw(AgentException);

void ParseOptions(const char* str) const throw(AgentException);

void AddCapability() const throw(AgentException);

void RegisterEvent() const throw(AgentException);

static void JNICALL HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method);

private:

static void CheckException(jvmtiError error) throw(AgentException)

{

// 可以根据错误类型扩展对应的异常,这里只做简单处理

if (error != JVMTI_ERROR_NONE) {

throw AgentException(error);

}

}

static jvmtiEnv * m_jvmti;

static char* m_filter;

};

7.4 MethodTraceTest.java

public class MethodTraceTest{

public static void main(String[] args){

MethodTraceTest test = new MethodTraceTest();

test.first();

test.second();

}

public void first(){

System.out.println("=> Call first()");

}

public void second(){

System.out.println("=> Call second()");

}

}

7.6 编译

g++ -w -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/linux

MethodTraceAgent.cpp Main.cpp -fPIC -shared -o libagent.so

7.7 默认运行java

javac MethodTraceTest.java

java MethodTraceTest

结果如下:

e59c4eed44a2

默认运行

7.8 加入我们的agent

java -agentpath:/home/xxx/libagent.so=first MethodTraceTest” 。 //其中的first是个参数,可以透传给jvmti。

运行结果如下:

e59c4eed44a2

加入agent之后的结果

参考文献

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值