I am trying to communicate from a java application with a different java virtual machine via an JVMTI attached agent (C++). The basic idea is to attach the agent from VM A to VM B via the Attach API (https://docs.oracle.com/javase/8/docs/technotes/guides/attach/index.html) and later on communicate with the agent so that it can delegate my commands from VM A to the VM B. I have a JavaFX application which lists all java VMs (VirtualMachine.list()) and attaches an agent to them. For every java VM there is a button which should send a String to the agent which is attached to the corresponding VM.
Images:
What I have reached so far:
I am able to attach a native agent via the Attach API to a running virtual machine. The Agent_OnAttach method is executed and so I am able to do some basic checks on the VM B if it is suitable for my needs.
The problem:
After the agent has been attached I am not able to communicate with the agent any more. It would be possible to attach a new agent every time I want to send a command to VM B but since I have no possibility to remove attached agents this would result in a huge number of agents attached to the VM. I would also prefer not to use files or sockets for the communication.
I tried to use JNI for the communication but I am not able to setup a communication between the shared library and the native agent. I didn’t find a way to save the jvmtiEnv from the Agent_OnAttach method to use it from the native method.
The only ways I can think of solving this problem is either to attach a new agent every time I want to send a command (which would require finding a way to detach the agent after executing its Agent_OnAttach method) or to figure out how to use JNI in order to communicate with the agent after attaching.
Native agent:
jvmtiExtensionFunction getExtensionFunction(jvmtiEnv* jvmti, const std::string& functionName) {
....
return function;
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
jvmtiEnv* jvmti = NULL;
// Initialize jvmti
vm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_2);
std::string isVMSuitableFunctionName("vmCheckFunction");
jvmtiExtensionFunction isVMSuitableFunction = getExtensionFunction(jvmti, isVMSuitableFunctionName);
int result = 1;
if(isVMSuitableFunction != NULL) {
jboolean jIsVMSuitable;
isVMSuitableFunction(jvmti, &jIsVMSuitable);
if(jIsVMSuitable) {
result = 0;
} else {
result = 2;
}
}
return result;
}
JNIEXPORT void JNICALL Java_jni_method_tag(JNIEnv *env, jobject obj, jstring tag) {
// Communicate with the VM the agent has been attached to
}
Java class:
public class TaggingAgent {
public void attachToVM(String vmID, String agentPath) throws AgentAttachException {
try {
VirtualMachine machine = VirtualMachine.attach(vmID);
machine.loadAgentPath(agentPath);
machine.detach();
} catch (AgentInitializationException e) {
switch (e.returnValue()) {
case 1:
throw new AgentAttachException("JVM is not suitable.", e);
case 2:
throw new AgentAttachException("JVM doesn't support this action.", e);
default:
throw new AgentAttachException("Couldn't attach agent to JVM.", e);
}
} catch (AttachNotSupportedException | IOException | AgentLoadException e) {
throw new AgentAttachException("Couldn't attach agent to JVM.", e);
}
}
public native void tag(String tag);
}
I already checked the following questions:
How to communicate with jvmti agent attached on a running JVM
Communication between JVMTI agent and separate JVM
Unloading a JVMTI agent at runtime?
but it seems like there has no good solution been found.