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注册的插桩,进行类字节码变换
3、进一步处理,就是做了一步转发,不详解
4、实际的处理
5、简化的流程图
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、简化的流程图
相关文章推荐
- ZOJ 3715 Kindergarten Election
- 有关Angular 2.0的一切
- 在测试新功能的时候提示缺少表或者序列-----运维日志33
- ZOJ 3715 Kindergarten Election
- 算法Sedgewick第四版-第1章基础-002一些工具类算法(Euclid’s algorithm)
- c语言:猴子吃桃问题
- Android Binder机制(1):Binder架构分析
- 深入分析 Java 中的中文编码问题
- SonarLint插件的安装与使用
- 【LeetCode】109. Convert Sorted List to Binary Search Tree 解法及注释,分治,递归
- 2--第四层
- vc++/C++操作mysql数据库一些注意事项
- 【LeetCode】299 Bulls and Cows (java实现)
- NavigationController 详解
- 广义表的长度和深度、表头表尾运算
- 解决Mybatis 分页插件pagehelper不支持逆向工程Example条件查询
- 安卓开发 第二篇 我的安卓应用架构设计
- c++ 初学者,求助,课后习题。
- [置顶] 嵌入式开发之 STM32自行车码表(图文)
- 低功耗蓝牙BLE之修改广播间隔等参数