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
}
在启动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();
}
在函数里,通过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);
……
}
…..
}