Agent在java中本质是一个动态库,利用JVMTI暴露出来的一些接口实现逻辑的入侵,需要实现如下的一个或者多个函数:
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved);
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm);
Agent_OnLoad 函数 加载agent后调用的函数
Agent_OnAttach 函数 当agent在启动时加载,所调用的Agent函数
Agent_OnUnload 函数 当agent卸载时调用
目前常见的几个Agent
1. 以-agentlib: 或者-agentpath:为开头的格式,例如:常见的eclipse中的debug代码 -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:xxxx,这种情况下的agent 是jdwp
2. 以-javaagent:为开头的默认为instrument的agent
JVM在启动的时候,读取参数 -agentlib -agentpath -javaagent 构建了AgentLibrary的链表
代码如下:
if (match_option(option, "-agentlib:", &tail) || (is_absolute_path = match_option(option, "-agentpath:", &tail))) { if(tail != NULL) { const char* pos = strchr(tail, '='); size_t len = (pos == NULL) ? strlen(tail) : pos - tail; char* name = strncpy(NEW_C_HEAP_ARRAY(char, len + 1), tail, len); name[len] = '\0'; char *options = NULL; if(pos != NULL) { options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(pos + 1) + 1), pos + 1); } #ifdef JVMTI_KERNEL if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) { warning("profiling and debugging agents are not supported with Kernel VM"); } else #endif // JVMTI_KERNEL add_init_agent(name, options, is_absolute_path); } // -javaagent } else if (match_option(option, "-javaagent:", &tail)) { if(tail != NULL) { char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1), tail); add_init_agent("instrument", options, false); } // -Xnoclassgc }</span>
在启动JVM create_vm的时候会初始化agent的链表中的每个agent库,加载所指定的动态库, 并调用里面的Agent_OnLoad方法,对instrument的就是加载libinstrument的动态库instrument.so
// Create agents for -agentlib: -agentpath: and converted -Xrun void Threads::create_vm_init_agents() { extern struct JavaVM_ main_vm; AgentLibrary* agent; JvmtiExport::enter_onload_phase(); for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) { OnLoadEntry_t on_load_entry = lookup_agent_on_load(agent); if (on_load_entry != NULL) { // Invoke the Agent_OnLoad function jint err = (*on_load_entry)(&main_vm, agent->options(), NULL); if (err != JNI_OK) { vm_exit_during_initialization("agent library failed to init", agent->name()); } } else { vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name()); } } JvmtiExport::enter_primordial_phase(); }</span>
在函数里,通过OnloadEntry方法来调用instruments动态库里的Onload方法
// Create agents for -agentlib: -agentpath: and converted -Xrun void Threads::create_vm_init_agents() { extern struct JavaVM_ main_vm; AgentLibrary* agent; JvmtiExport::enter_onload_phase(); for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) { OnLoadEntry_t on_load_entry = lookup_agent_on_load(agent); if (on_load_entry != NULL) { // Invoke the Agent_OnLoad function jint err = (*on_load_entry)(&main_vm, agent->options(), NULL); if (err != JNI_OK) { vm_exit_during_initialization("agent library failed to init", agent->name()); } } else { vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name()); } } JvmtiExport::enter_primordial_phase(); }
在方法Agent_OnLoad中创建一个新的JPLISAgent(Java Programming Language Instrumentation Services Agent), 初始化了类和包里的配置文件,并且同时从Vm环境中获取了jvmtiEnv 的环境。
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) { JPLISInitializationError initerror = JPLIS_INIT_ERROR_NONE; jint result = JNI_OK; JPLISAgent * agent = NULL; initerror = createNewJPLISAgent(vm, &agent); if ( initerror == JPLIS_INIT_ERROR_NONE ) { if (parseArgumentTail(tail, &jarfile, &options) != 0) { fprintf(stderr, "-javaagent: memory allocation failure.\n"); return JNI_ERR; } attributes = readAttributes(jarfile); if (attributes == NULL) { fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile); free(jarfile); if (options != NULL) free(options); return JNI_ERR; } premainClass = getAttribute(attributes, "Premain-Class"); if (premainClass == NULL) { fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n", jarfile); free(jarfile); if (options != NULL) free(options); freeAttributes(attributes); return JNI_ERR; } /* * Add to the jarfile */ appendClassPath(agent, jarfile); …… } ….. }