您的位置:首页 > 其它

jvm调用JVMTI_EVENT_CLASS_FILE_LOAD_HOOK进行字节码变换的源码分析

2016-04-16 11:02 555 查看
1、首先,如果大家对javaagent不是太了解,可以先阅读寒泉子的博客:
http://www.infoq.com/cn/articles/javaagent-illustrated?utm_source=tuicool&utm_medium=referral
2、调用入口,ClassFileParser::parseClassFile,jvm在将类字节码解析成运行时的类的之前,就会调用agent注册的插桩,进行类字节码变换

if (JvmtiExport::should_post_class_file_load_hook()) { //调用Instrument进行字节码处理
unsigned char* ptr = cfs->buffer(); //获取字节码
unsigned char* end_ptr = cfs->buffer() + cfs->length(); //字节码的结尾

JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain,//类名(jvm内部表示,形如java/lang/Object);类加载器;保护域
&ptr, &end_ptr, //字节码的起始指针和结束指针
&cached_class_file_bytes, //如果字节码被修改了,会将原来的字节码数据保存在这里
&cached_class_file_length);


3、进一步处理,就是做了一步转发,不详解

void JvmtiExport::post_class_file_load_hook(Symbol* h_name,
Handle class_loader,
Handle h_protection_domain,
unsigned char **data_ptr,
unsigned char **end_ptr,
unsigned char **cached_data_ptr,
jint *cached_length_ptr) {
JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
h_protection_domain,
data_ptr, end_ptr,
cached_data_ptr,
cached_length_ptr);
poster.post();
}


4、实际的处理

class JvmtiClassFileLoadHookPoster : public StackObj {
private:
Symbol*            _h_name; //类文件名
Handle               _class_loader; //类加载器
Handle               _h_protection_domain; //保护域,保存类的加载路径等信息
unsigned char **     _data_ptr; //类字节码的起始指针
unsigned char **     _end_ptr; //类字节码的结束指针
JavaThread *         _thread; //当前线程,用于内存分配(TLAB)
jint                 _curr_len;
unsigned char *      _curr_data; //当前正在处理的类字节码,是插桩链处理的中间结果
JvmtiEnv *           _curr_env;
jint *               _cached_length_ptr;
unsigned char **     _cached_data_ptr;//如果类字节码被修改了,保存被修改前的字节码
JvmtiThreadState *   _state;
KlassHandle *        _h_class_being_redefined;
JvmtiClassLoadKind   _load_kind;

public:
inline JvmtiClassFileLoadHookPoster(Symbol* h_name, Handle class_loader,
Handle h_protection_domain, //保护域
unsigned char **data_ptr, unsigned char **end_ptr, //指向实际的数据,初始时,传入原类字节码,转换后,传入处理完的数据
unsigned char **cached_data_ptr, //如果类字节码,被修改了,原类的字节码会保存在这边
jint *cached_length_ptr) {
_h_name = h_name;
_class_loader = class_loader;
_h_protection_domain = h_protection_domain;
_data_ptr = data_ptr;
_end_ptr = end_ptr;
_thread = JavaThread::current();
_curr_len = *end_ptr - *data_ptr; //类字节码的长度
_curr_data = *data_ptr; //类字节码的起始指针
_curr_env = NULL;
_cached_length_ptr = cached_length_ptr; //
_cached_data_ptr = cached_data_ptr;
*_cached_length_ptr = 0;
*_cached_data_ptr = NULL;

_state = _thread->jvmti_thread_state(); //获取当前线程的状态
if (_state != NULL) {
_h_class_being_redefined = _state->get_class_being_redefined();
_load_kind = _state->get_class_load_kind();
// Clear class_being_redefined flag here. The action
// from agent handler could generate a new class file load
// hook event and if it is not cleared the new event generated
// from regular class file load could have this stale redefined
// class handle info.
_state->clear_class_being_redefined();
} else {
// redefine and retransform will always set the thread state
_h_class_being_redefined = (KlassHandle *) NULL;
_load_kind = jvmti_class_load_kind_load;
}
}

void post() {
//    EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
//                   ("JVMTI [%s] class file load hook event triggered",
//                    JvmtiTrace::safe_get_thread_name(_thread)));
post_all_envs(); //调用所有注册的事件的回调函数
copy_modified_data();
}

private:
void post_all_envs() {
if (_load_kind != jvmti_class_load_kind_retransform) {
// for class load and redefine,
// call the non-retransformable agents
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { //遍历所有的JVMTIENV
if (!env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { //如果agent不能够转换类字节码,并且当前的环境运行类加载事件
// non-retransformable agents cannot retransform back,
// so no need to cache the original class file bytes
post_to_env(env, false); //不能转换的agent,不需要缓存原来的字节码
}
}
}
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
// retransformable agents get all events
if (env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
// retransformable agents need to cache the original class file
// bytes if changes are made via the ClassFileLoadHook
post_to_env(env, true);
}
}
}

void post_to_env(JvmtiEnv* env, bool caching_needed) {
unsigned char *new_data = NULL;
jint new_len = 0;
//    EVT_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
//     ("JVMTI [%s] class file load hook event sent %s  data_ptr = %d, data_len = %d",
//               JvmtiTrace::safe_get_thread_name(_thread),
//               _h_name == NULL ? "NULL" : _h_name->as_utf8(),
//               _curr_data, _curr_len ));
JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader,
_h_protection_domain,
_h_class_being_redefined);
JvmtiJavaThreadEventTransition jet(_thread);
JNIEnv* jni_env =  (JvmtiEnv::get_phase() == JVMTI_PHASE_PRIMORDIAL)?
NULL : jem.jni_env();
jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook; //获取当前jvmtiEnv中注册的类文件转换句柄
if (callback != NULL) {
(*callback)(env->jvmti_external(), jni_env, //回调这个函数
jem.class_being_redefined(),
jem.jloader(), jem.class_name(),
jem.protection_domain(),
_curr_len, _curr_data,
&new_len, &new_data);
}
if (new_data != NULL) {
// this agent has modified class data.
if (caching_needed && *_cached_data_ptr == NULL) { //如果类字节码被修改了,需要缓存原来的类字节码
// data has been changed by the new retransformable agent
// and it hasn't already been cached, cache it
*_cached_data_ptr = (unsigned char *)os::malloc(_curr_len);
memcpy(*_cached_data_ptr, _curr_data, _curr_len);
*_cached_length_ptr = _curr_len;
}

if (_curr_data != *_data_ptr) { //应该是_cuur_data != new_data吧?
// curr_data is previous agent modified class data.
// And this has been changed by the new agent so
// we can delete it now.
_curr_env->Deallocate(_curr_data);
}

// Class file data has changed by the current agent.
_curr_data = new_data;
_curr_len = new_len;
// Save the current agent env we need this to deallocate the
// memory allocated by this agent.
_curr_env = env;
}
}

void copy_modified_data() { //将最新的代码保存到数据指针里,返回给调用方
// if one of the agent has modified class file data.
// Copy modified class data to new resources array.
if (_curr_data != *_data_ptr) { //变换后的数据和传入的数据是不同的,才保存的
*_data_ptr = NEW_RESOURCE_ARRAY(u1, _curr_len);
memcpy(*_data_ptr, _curr_data, _curr_len);
*_end_ptr = *_data_ptr + _curr_len;
_curr_env->Deallocate(_curr_data);
}
}
};


5、简化的流程图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: