当前位置:   article > 正文

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

classfileloadhook

1、首先,如果大家对javaagent不是太了解,可以先阅读寒泉子的博客:

http://www.infoq.com/cn/articles/javaagent-illustrated?utm_source=tuicool&utm_medium=referral

2、调用入口,ClassFileParser::parseClassFile,jvm在将类字节码解析成运行时的类的之前,就会调用agent注册的插桩,进行类字节码变换

  1. if (JvmtiExport::should_post_class_file_load_hook()) { //调用Instrument进行字节码处理
  2. unsigned char* ptr = cfs->buffer(); //获取字节码
  3. unsigned char* end_ptr = cfs->buffer() + cfs->length(); //字节码的结尾
  4. JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain,//类名(jvm内部表示,形如java/lang/Object);类加载器;保护域
  5. &ptr, &end_ptr, //字节码的起始指针和结束指针
  6. &cached_class_file_bytes, //如果字节码被修改了,会将原来的字节码数据保存在这里
  7. &cached_class_file_length);

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

  1. void JvmtiExport::post_class_file_load_hook(Symbol* h_name,
  2. Handle class_loader,
  3. Handle h_protection_domain,
  4. unsigned char **data_ptr,
  5. unsigned char **end_ptr,
  6. unsigned char **cached_data_ptr,
  7. jint *cached_length_ptr) {
  8. JvmtiClassFileLoadHookPoster poster(h_name, class_loader,
  9. h_protection_domain,
  10. data_ptr, end_ptr,
  11. cached_data_ptr,
  12. cached_length_ptr);
  13. poster.post();
  14. }


4、实际的处理

  1. class JvmtiClassFileLoadHookPoster : public StackObj {
  2. private:
  3. Symbol* _h_name; //类文件名
  4. Handle _class_loader; //类加载器
  5. Handle _h_protection_domain; //保护域,保存类的加载路径等信息
  6. unsigned char ** _data_ptr; //类字节码的起始指针
  7. unsigned char ** _end_ptr; //类字节码的结束指针
  8. JavaThread * _thread; //当前线程,用于内存分配(TLAB)
  9. jint _curr_len;
  10. unsigned char * _curr_data; //当前正在处理的类字节码,是插桩链处理的中间结果
  11. JvmtiEnv * _curr_env;
  12. jint * _cached_length_ptr;
  13. unsigned char ** _cached_data_ptr;//如果类字节码被修改了,保存被修改前的字节码
  14. JvmtiThreadState * _state;
  15. KlassHandle * _h_class_being_redefined;
  16. JvmtiClassLoadKind _load_kind;
  17. public:
  18. inline JvmtiClassFileLoadHookPoster(Symbol* h_name, Handle class_loader,
  19. Handle h_protection_domain, //保护域
  20. unsigned char **data_ptr, unsigned char **end_ptr, //指向实际的数据,初始时,传入原类字节码,转换后,传入处理完的数据
  21. unsigned char **cached_data_ptr, //如果类字节码,被修改了,原类的字节码会保存在这边
  22. jint *cached_length_ptr) {
  23. _h_name = h_name;
  24. _class_loader = class_loader;
  25. _h_protection_domain = h_protection_domain;
  26. _data_ptr = data_ptr;
  27. _end_ptr = end_ptr;
  28. _thread = JavaThread::current();
  29. _curr_len = *end_ptr - *data_ptr; //类字节码的长度
  30. _curr_data = *data_ptr; //类字节码的起始指针
  31. _curr_env = NULL;
  32. _cached_length_ptr = cached_length_ptr; //
  33. _cached_data_ptr = cached_data_ptr;
  34. *_cached_length_ptr = 0;
  35. *_cached_data_ptr = NULL;
  36. _state = _thread->jvmti_thread_state(); //获取当前线程的状态
  37. if (_state != NULL) {
  38. _h_class_being_redefined = _state->get_class_being_redefined();
  39. _load_kind = _state->get_class_load_kind();
  40. // Clear class_being_redefined flag here. The action
  41. // from agent handler could generate a new class file load
  42. // hook event and if it is not cleared the new event generated
  43. // from regular class file load could have this stale redefined
  44. // class handle info.
  45. _state->clear_class_being_redefined();
  46. } else {
  47. // redefine and retransform will always set the thread state
  48. _h_class_being_redefined = (KlassHandle *) NULL;
  49. _load_kind = jvmti_class_load_kind_load;
  50. }
  51. }
  52. void post() {
  53. // EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
  54. // ("JVMTI [%s] class file load hook event triggered",
  55. // JvmtiTrace::safe_get_thread_name(_thread)));
  56. post_all_envs(); //调用所有注册的事件的回调函数
  57. copy_modified_data();
  58. }
  59. private:
  60. void post_all_envs() {
  61. if (_load_kind != jvmti_class_load_kind_retransform) {
  62. // for class load and redefine,
  63. // call the non-retransformable agents
  64. JvmtiEnvIterator it;
  65. for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { //遍历所有的JVMTIENV
  66. if (!env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { //如果agent不能够转换类字节码,并且当前的环境运行类加载事件
  67. // non-retransformable agents cannot retransform back,
  68. // so no need to cache the original class file bytes
  69. post_to_env(env, false); //不能转换的agent,不需要缓存原来的字节码
  70. }
  71. }
  72. }
  73. JvmtiEnvIterator it;
  74. for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
  75. // retransformable agents get all events
  76. if (env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
  77. // retransformable agents need to cache the original class file
  78. // bytes if changes are made via the ClassFileLoadHook
  79. post_to_env(env, true);
  80. }
  81. }
  82. }
  83. void post_to_env(JvmtiEnv* env, bool caching_needed) {
  84. unsigned char *new_data = NULL;
  85. jint new_len = 0;
  86. // EVT_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
  87. // ("JVMTI [%s] class file load hook event sent %s data_ptr = %d, data_len = %d",
  88. // JvmtiTrace::safe_get_thread_name(_thread),
  89. // _h_name == NULL ? "NULL" : _h_name->as_utf8(),
  90. // _curr_data, _curr_len ));
  91. JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader,
  92. _h_protection_domain,
  93. _h_class_being_redefined);
  94. JvmtiJavaThreadEventTransition jet(_thread);
  95. JNIEnv* jni_env = (JvmtiEnv::get_phase() == JVMTI_PHASE_PRIMORDIAL)?
  96. NULL : jem.jni_env();
  97. jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook; //获取当前jvmtiEnv中注册的类文件转换句柄
  98. if (callback != NULL) {
  99. (*callback)(env->jvmti_external(), jni_env, //回调这个函数
  100. jem.class_being_redefined(),
  101. jem.jloader(), jem.class_name(),
  102. jem.protection_domain(),
  103. _curr_len, _curr_data,
  104. &new_len, &new_data);
  105. }
  106. if (new_data != NULL) {
  107. // this agent has modified class data.
  108. if (caching_needed && *_cached_data_ptr == NULL) { //如果类字节码被修改了,需要缓存原来的类字节码
  109. // data has been changed by the new retransformable agent
  110. // and it hasn't already been cached, cache it
  111. *_cached_data_ptr = (unsigned char *)os::malloc(_curr_len);
  112. memcpy(*_cached_data_ptr, _curr_data, _curr_len);
  113. *_cached_length_ptr = _curr_len;
  114. }
  115. if (_curr_data != *_data_ptr) { //应该是_cuur_data != new_data吧?
  116. // curr_data is previous agent modified class data.
  117. // And this has been changed by the new agent so
  118. // we can delete it now.
  119. _curr_env->Deallocate(_curr_data);
  120. }
  121. // Class file data has changed by the current agent.
  122. _curr_data = new_data;
  123. _curr_len = new_len;
  124. // Save the current agent env we need this to deallocate the
  125. // memory allocated by this agent.
  126. _curr_env = env;
  127. }
  128. }
  129. void copy_modified_data() { //将最新的代码保存到数据指针里,返回给调用方
  130. // if one of the agent has modified class file data.
  131. // Copy modified class data to new resources array.
  132. if (_curr_data != *_data_ptr) { //变换后的数据和传入的数据是不同的,才保存的
  133. *_data_ptr = NEW_RESOURCE_ARRAY(u1, _curr_len);
  134. memcpy(*_data_ptr, _curr_data, _curr_len);
  135. *_end_ptr = *_data_ptr + _curr_len;
  136. _curr_env->Deallocate(_curr_data);
  137. }
  138. }
  139. };


5、简化的流程图



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/450777
推荐阅读
相关标签
  

闽ICP备14008679号