-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d822e0d
commit 3b4a97d
Showing
6 changed files
with
437 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 100 additions & 47 deletions
147
...trap/joylive-bootstrap-premain/src/main/java/com/jd/live/agent/bootstrap/AgentLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,119 @@ | ||
package com.jd.live.agent.bootstrap; | ||
|
||
import com.sun.tools.attach.AgentInitializationException; | ||
import com.sun.tools.attach.AgentLoadException; | ||
import com.sun.tools.attach.AttachNotSupportedException; | ||
import com.sun.tools.attach.VirtualMachine; | ||
import com.sun.tools.attach.VirtualMachineDescriptor; | ||
import com.jd.live.agent.bootstrap.option.AgentOption; | ||
import com.jd.live.agent.bootstrap.option.OptionParser; | ||
import com.sun.tools.attach.*; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
import static com.jd.live.agent.bootstrap.LivePath.LIVE_JAR; | ||
|
||
/** | ||
* The {@code AgentLoader} class provides functionality to attach a Java agent to a running JVM. | ||
* It parses command-line options, identifies the target JVM, and loads the agent into it. | ||
*/ | ||
public class AgentLoader { | ||
|
||
private AgentLoader() { | ||
} | ||
|
||
public static void main(String[] args) | ||
throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { | ||
List<VirtualMachineDescriptor> vmDescriptors = VirtualMachine.list(); | ||
|
||
if (vmDescriptors.isEmpty()) { | ||
System.out.println("No Java process found!"); | ||
return; | ||
/** | ||
* The main method to start the agent loader. It parses the command-line arguments, | ||
* finds the target JVM, and loads the specified agent into it. | ||
* | ||
* @param args the command-line arguments | ||
* @throws IOException if an I/O error occurs | ||
* @throws AttachNotSupportedException if the target JVM does not support attaching | ||
* @throws AgentLoadException if the agent cannot be loaded | ||
* @throws AgentInitializationException if the agent initialization fails | ||
*/ | ||
public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { | ||
AgentOption option = OptionParser.parse(args); | ||
if (option != null) { | ||
VirtualMachineDescriptor descriptor = getVmDescriptor(option); | ||
if (descriptor != null) { | ||
File path = getPath(option); | ||
if (path != null) { | ||
option.setAgentPath(path.getAbsolutePath()); | ||
VirtualMachine vm = VirtualMachine.attach(descriptor); | ||
// Launch Agent | ||
vm.loadAgent(new File(path, LIVE_JAR).getPath(), option.getAgentArgs()); | ||
vm.detach(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
System.out.println("Select the Java process that you want to use the agent."); | ||
for (int i = 0; i < vmDescriptors.size(); i++) { | ||
VirtualMachineDescriptor descriptor = vmDescriptors.get(i); | ||
System.out.println(i + ": " + descriptor.id() + " " + descriptor.displayName()); | ||
/** | ||
* Retrieves the path to the agent directory. If the path is not provided or is invalid, | ||
* it prompts the user to enter a valid path interactively. | ||
* | ||
* @param option the agent options | ||
* @return the valid agent directory path | ||
* @throws IOException if an I/O error occurs while reading input | ||
*/ | ||
private static File getPath(AgentOption option) throws IOException { | ||
String path = option.getAgentPath(); | ||
File file = path == null || path.isEmpty() ? LivePath.getRootPath(System.getenv(), null) : new File(path); | ||
long counter = 0; | ||
while (file == null || !option.isValidPath(file)) { | ||
counter++; | ||
if (option.isInteractive()) { | ||
System.out.print(counter == 1 | ||
? "Enter agent directory (the live.jar in this directory is used as the entry by default):" | ||
: "Enter agent directory:"); | ||
path = new BufferedReader(new InputStreamReader(System.in)).readLine(); | ||
file = path == null || path.isEmpty() ? null : new File(path.trim()); | ||
} else { | ||
System.out.println("The agent directory is invalid. path=" + file); | ||
return null; | ||
} | ||
} | ||
return file; | ||
} | ||
|
||
// Read the sequence number entered by the user | ||
BufferedReader userInputReader = new BufferedReader(new InputStreamReader(System.in)); | ||
System.out.print("Please enter the Java program number to be used by the agent:"); | ||
int selectedProcessIndex = Integer.parseInt(userInputReader.readLine()); | ||
|
||
if (selectedProcessIndex < 0 || selectedProcessIndex >= vmDescriptors.size()) { | ||
System.out.println("Invalid program number!"); | ||
return; | ||
/** | ||
* Retrieves the descriptor of the target JVM. If the process ID is not provided or is invalid, | ||
* it prompts the user to select a valid JVM process interactively. | ||
* | ||
* @param option the agent options | ||
* @return the descriptor of the target JVM | ||
* @throws IOException if an I/O error occurs while reading input | ||
*/ | ||
private static VirtualMachineDescriptor getVmDescriptor(AgentOption option) throws IOException { | ||
String pid = AgentOption.getPid(); | ||
Map<String, VirtualMachineDescriptor> descriptors = VirtualMachine.list().stream() | ||
.filter(v -> !v.id().equals(pid)) | ||
.collect(Collectors.toMap(VirtualMachineDescriptor::id, v -> v)); | ||
String jvmId = option.getProcessId(); | ||
if (!descriptors.isEmpty() && option.isInteractive()) { | ||
long counter = 0; | ||
while (jvmId == null || jvmId.isEmpty() || !descriptors.containsKey(jvmId)) { | ||
counter++; | ||
if (counter == 1) { | ||
System.out.println("Select the java process id to be attached."); | ||
for (VirtualMachineDescriptor vm : descriptors.values()) { | ||
System.out.println(vm.id() + " " + vm.displayName()); | ||
} | ||
} | ||
System.out.print("Please enter the pid:"); | ||
// Read the jvm id entered by the user | ||
jvmId = new BufferedReader(new InputStreamReader(System.in)).readLine(); | ||
jvmId = jvmId == null || jvmId.isEmpty() ? null : jvmId.trim(); | ||
} | ||
} | ||
|
||
// Connect to the selected virtual machine | ||
VirtualMachineDescriptor selectedDescriptor = vmDescriptors.get(selectedProcessIndex); | ||
System.out.println("The process ID you selected is:" + selectedDescriptor.id()); | ||
|
||
VirtualMachine vm = VirtualMachine.attach(selectedDescriptor); | ||
|
||
// Obtain the agent directory | ||
System.out.print("Enter the directory where the agent is located (the live.jar in this directory is used as the entry by default):"); | ||
String agentPath = userInputReader.readLine(); | ||
|
||
// Obtain the parameters of the incoming agent | ||
System.out.print("Please enter the parameters passed to the agent (can be empty, the default parameter is agentPath):"); | ||
String agentArgs = "agentPath=" + agentPath + "," + userInputReader.readLine(); | ||
userInputReader.close(); | ||
|
||
try { | ||
// Launch Agent | ||
vm.loadAgent(agentPath + "/live.jar", agentArgs); | ||
vm.detach(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
VirtualMachineDescriptor descriptor = jvmId == null || jvmId.isEmpty() | ||
? null : descriptors.get(jvmId); | ||
if (descriptor == null) { | ||
System.out.println("The java process is not found. pid=" + jvmId); | ||
return null; | ||
} | ||
|
||
return descriptor; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.