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、简化的流程图