赞
踩
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);
- 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、简化的流程图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。