Skip to content

Commit 8d75d0d

Browse files
Merge pull request #1 from GuanceCloud/attachv1
Attachv1
2 parents 23ea412 + d282d64 commit 8d75d0d

File tree

10 files changed

+396
-1
lines changed

10 files changed

+396
-1
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
target
2+
dependency-reduced-pom.xml
3+
/.metadata/
4+
/.recommenders/
5+
.classpath
6+
.project
7+
.settings
8+
.idea/
9+
*.iml
10+
.DS_Store
11+
html_docs
12+
docs/html/
13+
**/__pycache__/
14+
.m2/
15+
fetch.log
16+
.ci/.aws

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,24 @@
1-
# agent-attach-java
1+
# agent-attach-java
2+
3+
## 原理
4+
Attach 根本原理是使用文件 Socket 通讯, 就是往一个文件中写入 `load instrument xxx.jar=params`, 再给 jvm 一个 SIGQUIT 信号, 之后 jvm 读取 socket 文件, 加载对应的 agent.
5+
6+
## 如何在 jvm 运行时动态添加 agent
7+
1. 首先下载***指定***的 ddtrace , 并放到 /usr/local/ddtrace/
8+
```shell
9+
mkdir -p /usr/local/ddtrace
10+
cd /usr/local/ddtrace
11+
wget https://github.com/GuanceCloud/dd-trace-java/releases/download/v0.113.0-attach/dd-java-agent.jar
12+
13+
```
14+
15+
2. 启动 java 程序
16+
3. 启动 agent-attach-java.jar
17+
```shell
18+
# 命令的参数有三个 options download agent-jar 没有的话 都是默认值
19+
# options=""
20+
# download ="" 下个版本(指定版本下载)
21+
# agent-jar="/usr/local/ddtrace/dd-java-agent.jar"
22+
java -jar agent-attach-java.jar -options "dd.agent.port=9529"
23+
24+
```
5.4 MB
Binary file not shown.

pom.xml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>agent-attach-java</groupId>
7+
<artifactId>agent-attach-java</artifactId>
8+
<name>agent-attach-java</name>
9+
<version>1.0</version>
10+
<description>agent-attach-java</description>
11+
<packaging>jar</packaging>
12+
13+
<build>
14+
<finalName>agent-attach-java</finalName>
15+
<plugins>
16+
<plugin>
17+
<groupId>org.apache.maven.plugins</groupId>
18+
<artifactId>maven-compiler-plugin</artifactId>
19+
<configuration>
20+
<source>1.8</source>
21+
<target>1.8</target>
22+
</configuration>
23+
</plugin>
24+
<plugin>
25+
<groupId>org.apache.maven.plugins</groupId>
26+
<artifactId>maven-assembly-plugin</artifactId>
27+
<executions>
28+
<execution>
29+
<goals>
30+
<goal>attached</goal>
31+
</goals>
32+
<phase>package</phase>
33+
<configuration>
34+
<descriptorRefs>
35+
<descriptorRef>jar-with-dependencies</descriptorRef>
36+
</descriptorRefs>
37+
<archive>
38+
<manifest>
39+
<mainClass>name.dhruba.javaagent.MyMainClass</mainClass>
40+
</manifest>
41+
<!-- <manifestEntries>
42+
<Premain-Class>name.dhruba.javaagent.MyJavaAgent</Premain-Class>
43+
<Agent-Class>name.dhruba.javaagent.MyJavaAgent</Agent-Class>
44+
<Can-Redefine-Classes>true</Can-Redefine-Classes>
45+
<Can-Retransform-Classes>true</Can-Retransform-Classes>
46+
</manifestEntries>-->
47+
</archive>
48+
</configuration>
49+
</execution>
50+
</executions>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
55+
<dependencies>
56+
57+
<dependency>
58+
<groupId>junit</groupId>
59+
<artifactId>junit</artifactId>
60+
<version>4.7</version>
61+
<scope>test</scope>
62+
</dependency>
63+
64+
<dependency>
65+
<groupId>asm</groupId>
66+
<artifactId>asm</artifactId>
67+
<version>3.2</version>
68+
</dependency>
69+
70+
<dependency>
71+
<groupId>io.earcam.wrapped</groupId>
72+
<artifactId>com.sun.tools.attach</artifactId>
73+
<version>1.8.0_jdk8u131-b11</version>
74+
<scope>compile</scope>
75+
<type>jar</type>
76+
</dependency>
77+
78+
<dependency>
79+
<groupId>org.slf4j</groupId>
80+
<artifactId>jcl-over-slf4j</artifactId>
81+
<version>1.5.8</version>
82+
<scope>runtime</scope>
83+
</dependency>
84+
85+
<dependency>
86+
<groupId>ch.qos.logback</groupId>
87+
<artifactId>logback-classic</artifactId>
88+
<version>0.9.9</version>
89+
</dependency>
90+
91+
</dependencies>
92+
93+
</project>

src/main/java/META-INF/MANIFEST.MF

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Manifest-Version: 1.0
2+
Main-Class: name.dhruba.javaagent.MyMainClass
3+
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package name.dhruba.javaagent;
2+
3+
import java.lang.instrument.IllegalClassFormatException;
4+
import java.security.ProtectionDomain;
5+
6+
import org.objectweb.asm.Opcodes;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
public class ClassFileTransformer implements java.lang.instrument.ClassFileTransformer, Opcodes {
11+
12+
static final Logger logger = LoggerFactory.getLogger(ClassFileTransformer.class);
13+
14+
@Override
15+
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
16+
ProtectionDomain protectionDomain, byte[] classfileBuffer)
17+
throws IllegalClassFormatException {
18+
logger.info("class file transformer invoked for className: {}", className);
19+
/*
20+
21+
if (className.equals("name/dhruba/user/MyUser")) {
22+
23+
ClassWriter cw = new ClassWriter(0);
24+
MethodVisitor mv;
25+
26+
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "name/dhruba/user/MyUser", null,
27+
"java/lang/Object", null);
28+
29+
cw.visitSource(null, null);
30+
31+
{
32+
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
33+
mv.visitCode();
34+
Label l0 = new Label();
35+
mv.visitLabel(l0);
36+
mv.visitLineNumber(3, l0);
37+
mv.visitVarInsn(ALOAD, 0);
38+
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
39+
mv.visitInsn(RETURN);
40+
Label l1 = new Label();
41+
mv.visitLabel(l1);
42+
mv.visitLocalVariable("this", "Lname/dhruba/user/MyUser;", null, l0, l1, 0);
43+
mv.visitMaxs(1, 1);
44+
mv.visitEnd();
45+
}
46+
{
47+
mv = cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
48+
mv.visitCode();
49+
Label l0 = new Label();
50+
mv.visitLabel(l0);
51+
mv.visitLineNumber(6, l0);
52+
mv.visitLdcInsn("bar");
53+
mv.visitInsn(ARETURN);
54+
Label l1 = new Label();
55+
mv.visitLabel(l1);
56+
mv.visitLocalVariable("this", "Lname/dhruba/user/MyUser;", null, l0, l1, 0);
57+
mv.visitMaxs(1, 1);
58+
mv.visitEnd();
59+
}
60+
cw.visitEnd();
61+
62+
return cw.toByteArray();
63+
}
64+
*/
65+
66+
return classfileBuffer;
67+
}
68+
69+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package name.dhruba.javaagent;
2+
3+
public class Config {
4+
private String options;
5+
6+
private String downloadAddress;
7+
8+
private String agentJar;
9+
10+
public String getOptions() {
11+
return options;
12+
}
13+
14+
public String getDownloadAddress() {
15+
return downloadAddress;
16+
}
17+
18+
public String getAgentJar() {
19+
return agentJar;
20+
}
21+
22+
private Config(String options,String downloadAddress,String agentJar){
23+
this.options = options;
24+
this.downloadAddress = downloadAddress;
25+
this.agentJar = agentJar;
26+
}
27+
28+
static Config parse(String... args){
29+
String option = "";
30+
String downloadAddr= "";
31+
String currentArg = "";
32+
String agentJar= "";
33+
for(String arg : args){
34+
if (arg.startsWith("-")){
35+
currentArg =arg;
36+
}else {
37+
switch (currentArg){
38+
case "options":
39+
option = arg;
40+
case "download":
41+
downloadAddr = arg;
42+
case "agent-jar":
43+
agentJar = arg;
44+
}
45+
}
46+
}
47+
48+
return new Config(option,downloadAddr,agentJar);
49+
}
50+
51+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package name.dhruba.javaagent;
2+
3+
import java.lang.management.ManagementFactory;
4+
import java.util.List;
5+
import java.util.Properties;
6+
7+
import com.sun.tools.attach.VirtualMachineDescriptor;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import com.sun.tools.attach.VirtualMachine;
12+
13+
public class JavaAgentLoader {
14+
static final Logger logger = LoggerFactory.getLogger(JavaAgentLoader.class);
15+
16+
private static final String jarFilePath = "/usr/local/ddtrace/dd-java-agent.jar";
17+
18+
public static void loadAgent(String agentJar ,String options) {
19+
logger.info("dynamically loading javaagent");
20+
String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
21+
int p = nameOfRunningVM.indexOf('@');
22+
String pid = nameOfRunningVM.substring(0, p);
23+
System.out.println("------------------into-------------");
24+
System.out.println("------------------p-------------"+p);
25+
System.out.println("------------------pid-------------"+pid);
26+
System.out.println("------------------args-------------"+options);
27+
28+
try {
29+
List<VirtualMachineDescriptor> list = VirtualMachine.list();
30+
for (int i = 0; i < list.size(); i++) {
31+
VirtualMachineDescriptor virtualMachineDescriptor = list.get(i);
32+
String version = virtualMachineDescriptor.id();
33+
System.out.println(version);
34+
System.out.println(virtualMachineDescriptor.id());
35+
System.out.println(virtualMachineDescriptor.displayName());
36+
System.out.println(virtualMachineDescriptor.toString());
37+
38+
VirtualMachine attach = VirtualMachine.attach(version);
39+
if (agentJar != null && !agentJar.equals("")){
40+
attach.loadAgent(jarFilePath, options);
41+
}else {
42+
attach.loadAgent(agentJar, options);
43+
}
44+
45+
46+
Properties properties = attach.getAgentProperties();
47+
Properties sysproperties = attach.getSystemProperties();
48+
System.out.println( properties.toString());
49+
System.out.println( sysproperties.toString());
50+
System.out.println("DdTrace attach agent to displayName is:"+virtualMachineDescriptor.displayName());
51+
52+
attach.detach();
53+
}
54+
55+
// -------------
56+
// VirtualMachine vm = VirtualMachine.attach(pid);
57+
// vm.loadAgent(jarFilePath, "Ddd.env:test,Ddd.service.name:demo001,Ddd.agent.port:9529");// 参数
58+
// vm.detach();
59+
} catch (Exception e) {
60+
throw new RuntimeException(e);
61+
}
62+
}
63+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package name.dhruba.javaagent;
2+
3+
import java.lang.instrument.Instrumentation;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
public class MyJavaAgent {
9+
10+
static final Logger logger = LoggerFactory.getLogger(MyJavaAgent.class);
11+
12+
private static Instrumentation instrumentation;
13+
14+
/**
15+
* JVM hook to statically load the javaagent at startup.
16+
*
17+
* After the Java Virtual Machine (JVM) has initialized, the premain method
18+
* will be called. Then the real application main method will be called.
19+
*
20+
* @param args
21+
* @param inst
22+
* @throws Exception
23+
*/
24+
// public static void premain(String args, Instrumentation inst) throws Exception {
25+
// logger.info("premain method invoked with args: {} and inst: {}", args, inst);
26+
// instrumentation = inst;
27+
// instrumentation.addTransformer(new MyClassFileTransformer());
28+
// }
29+
30+
/**
31+
* JVM hook to dynamically load javaagent at runtime.
32+
*
33+
* The agent class may have an agentmain method for use when the agent is
34+
* started after VM startup.
35+
*
36+
* @param args
37+
* @param inst
38+
* @throws Exception
39+
*/
40+
// public static void agentmain(String args, Instrumentation inst) throws Exception {
41+
// logger.info("agentmain method invoked with args: {} and inst: {}", args, inst);
42+
// instrumentation = inst;
43+
// instrumentation.addTransformer(new MyClassFileTransformer());
44+
// }
45+
46+
/**
47+
* Programmatic hook to dynamically load javaagent at runtime.
48+
*/
49+
50+
}

0 commit comments

Comments
 (0)