working now this is
This commit is contained in:
parent
02b15b3e72
commit
8f516d5612
6 changed files with 215 additions and 106 deletions
49
agent/pom.xml
Normal file
49
agent/pom.xml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.github.shautvast.exceptional</groupId>
|
||||||
|
<artifactId>exceptional-parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>exceptional-agent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>22</maven.compiler.source>
|
||||||
|
<maven.compiler.target>22</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>22</source>
|
||||||
|
<target>22</target>
|
||||||
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifestEntries>
|
||||||
|
<Premain-Class>com.github.shautvast.exceptional.Agent</Premain-Class>
|
||||||
|
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
||||||
|
<Can-Redefine-Classes>true</Can-Redefine-Classes>
|
||||||
|
<Boot-Class-Path>/Users/Shautvast/dev/exceptional/lib/target/exceptional-lib-1.0-SNAPSHOT.jar</Boot-Class-Path>
|
||||||
|
</manifestEntries>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
111
agent/src/main/java/com/github/shautvast/exceptional/Agent.java
Normal file
111
agent/src/main/java/com/github/shautvast/exceptional/Agent.java
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.classfile.*;
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import java.lang.constant.ConstantDescs;
|
||||||
|
import java.lang.constant.MethodTypeDesc;
|
||||||
|
import java.lang.instrument.ClassDefinition;
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
public class Agent {
|
||||||
|
|
||||||
|
private static final String MESSAGE = "--- Exceptional agent active";
|
||||||
|
|
||||||
|
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
||||||
|
instrumentation.addTransformer(new ClassFileTransformer() {
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||||
|
return instrumentTheThrowable(className, classfileBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
return instrumentTheThrowable(className, classfileBuffer);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
InputStream stream = Agent.class.getResourceAsStream("/java/lang/Throwable.class");
|
||||||
|
byte[] bytecode = readFully(stream);
|
||||||
|
|
||||||
|
instrumentation.redefineClasses(new ClassDefinition(Throwable.class, bytecode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readFully(InputStream stream) throws IOException {
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
int nRead;
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
while ((nRead = stream.read(data, 0, data.length)) != -1) {
|
||||||
|
buffer.write(data, 0, nRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.flush();
|
||||||
|
return buffer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] instrumentTheThrowable(String className, byte[] classfileBuffer) {
|
||||||
|
if (className.equals("java/lang/Throwable")) {
|
||||||
|
ClassFile classFile = ClassFile.of();
|
||||||
|
ClassModel classModel = classFile.parse(classfileBuffer);
|
||||||
|
|
||||||
|
return classFile.build(classModel.thisClass().asSymbol(), classBuilder -> {
|
||||||
|
for (ClassElement ce : classModel) {
|
||||||
|
if (ce instanceof MethodModel mm) {
|
||||||
|
String signature = mm.methodName() + mm.methodType().stringValue();
|
||||||
|
if (signature.equals("<init>()V")) {
|
||||||
|
classBuilder.withMethod(mm.methodName(), mm.methodType(), Modifier.PUBLIC,
|
||||||
|
methodBuilder -> methodBuilder.withCode(
|
||||||
|
cb -> {
|
||||||
|
// recreate existing code for this method, because... I don't know how to simply add the new code at the end🫣
|
||||||
|
ClassDesc throwable = ClassDesc.of("java.lang.Throwable");
|
||||||
|
cb.aload(0);
|
||||||
|
cb.invokespecial(ConstantDescs.CD_Object, "<init>", MethodTypeDesc.ofDescriptor("()V"));
|
||||||
|
cb.aload(0);
|
||||||
|
cb.aload(0);
|
||||||
|
cb.putfield(throwable, "cause", ClassDesc.ofDescriptor("Ljava/lang/Throwable;"));
|
||||||
|
cb.aload(0);
|
||||||
|
cb.getstatic(throwable, "UNASSIGNED_STACK", ClassDesc.ofDescriptor("[Ljava/lang/StackTraceElement;"));
|
||||||
|
cb.putfield(throwable, "stackTrace", ClassDesc.ofDescriptor("[Ljava/lang/StackTraceElement;"));
|
||||||
|
cb.aload(0);
|
||||||
|
cb.invokevirtual(throwable, "fillInStackTrace", MethodTypeDesc.ofDescriptor("()Ljava/lang/Throwable;"));
|
||||||
|
cb.pop();
|
||||||
|
// cb.getstatic(throwable, "jfrTracing", ConstantDescs.CD_Boolean);
|
||||||
|
// Label end = cb.newLabel();
|
||||||
|
// cb.ifeq(end);
|
||||||
|
// cb.aload(0);
|
||||||
|
// cb.invokevirtual(ConstantDescs.CD_Object, "getClass", MethodTypeDesc.ofDescriptor("()Ljava/lang/Class;"));
|
||||||
|
// cb.aconst_null();
|
||||||
|
// cb.invokestatic(ClassDesc.of("jdk.internal.event.ThrowableTracer"), "traceThrowable", MethodTypeDesc.ofDescriptor("(Ljava/lang/Class;Ljava/lang/String;)V"));
|
||||||
|
// cb.labelBinding(end);
|
||||||
|
|
||||||
|
cb.aload(0);
|
||||||
|
cb.invokestatic(
|
||||||
|
ClassDesc.of("com.github.shautvast.exceptional.ThrowableHandler"), "handle",
|
||||||
|
MethodTypeDesc.ofDescriptor("(Ljava/lang/Throwable;)V"));
|
||||||
|
cb.return_();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classBuilder.with(ce);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return classfileBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
|
||||||
|
//ldc #13 // class com/github/shautvast/exceptional/Foo
|
||||||
|
//invokevirtual #15 // Method java/lang/Class.getModule:()Ljava/lang/Module;
|
||||||
|
//invokevirtual #21 // Method java/lang/Module.getName:()Ljava/lang/String;
|
||||||
|
//invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
|
||||||
37
lib/pom.xml
Normal file
37
lib/pom.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.github.shautvast.exceptional</groupId>
|
||||||
|
<artifactId>exceptional-parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>exceptional-lib</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>22</maven.compiler.source>
|
||||||
|
<maven.compiler.target>22</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>22</source>
|
||||||
|
<target>22</target>
|
||||||
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ThrowableHandler {
|
||||||
|
public static void handle(Throwable throwable) {
|
||||||
|
System.out.print("Handling exception:");
|
||||||
|
Arrays.stream(throwable.getStackTrace()).forEach(System.out::println);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
pom.xml
40
pom.xml
|
|
@ -4,40 +4,14 @@
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.example</groupId>
|
<groupId>org.github.shautvast.exceptional</groupId>
|
||||||
<artifactId>Exceptional</artifactId>
|
<artifactId>exceptional-parent</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<properties>
|
<modules>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<module>agent</module>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<module>lib</module>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
</modules>
|
||||||
</properties>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<source>22</source>
|
|
||||||
<target>22</target>
|
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifestEntries>
|
|
||||||
<Premain-Class>com.github.shautvast.exceptional.Agent</Premain-Class>
|
|
||||||
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
|
||||||
<Can-Redefine-Classes>true</Can-Redefine-Classes>
|
|
||||||
</manifestEntries>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
package com.github.shautvast.exceptional;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.classfile.*;
|
|
||||||
import java.lang.constant.ClassDesc;
|
|
||||||
import java.lang.constant.MethodTypeDesc;
|
|
||||||
import java.lang.instrument.ClassDefinition;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
|
||||||
import java.lang.instrument.IllegalClassFormatException;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
|
||||||
public class Agent {
|
|
||||||
|
|
||||||
private static final String MESSAGE = "--- Exceptional agent active";
|
|
||||||
|
|
||||||
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
|
||||||
instrumentation.addTransformer(new ClassFileTransformer() {
|
|
||||||
@Override
|
|
||||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
|
||||||
return instrumentTheThrowable(className, classfileBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
|
||||||
return instrumentTheThrowable(className, classfileBuffer);
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
InputStream stream = Agent.class.getResourceAsStream("/java/lang/Throwable.class");
|
|
||||||
byte[] b = new byte[stream.available()];
|
|
||||||
stream.read(b);
|
|
||||||
|
|
||||||
instrumentation.redefineClasses(new ClassDefinition(Throwable.class, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] instrumentTheThrowable(String className, byte[] classfileBuffer) {
|
|
||||||
if (className.equals("java/lang/Throwable")) {
|
|
||||||
|
|
||||||
ClassFile classFile = ClassFile.of();
|
|
||||||
ClassModel classModel = classFile.parse(classfileBuffer);
|
|
||||||
|
|
||||||
return classFile.build(classModel.thisClass().asSymbol(), classBuilder -> {
|
|
||||||
for (ClassElement ce : classModel) {
|
|
||||||
if (ce instanceof MethodModel mm) {
|
|
||||||
String signature = mm.methodName() + "." + mm.methodType().stringValue();
|
|
||||||
if (signature.equals("<init>.()V")) {
|
|
||||||
classBuilder.withMethod(mm.methodName(), mm.methodType(), 1,
|
|
||||||
methodBuilder -> methodBuilder.withCode(
|
|
||||||
codebuilder ->
|
|
||||||
// codebuilder.invokeInstruction(Opcode.LDC, );
|
|
||||||
codebuilder.invokeInstruction(Opcode.INVOKESTATIC,
|
|
||||||
ClassDesc.of("com.github.shautvast.exceptional.Agent"), "foo",
|
|
||||||
MethodTypeDesc.ofDescriptor("()V"),
|
|
||||||
false)
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
classBuilder.with(ce);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return classfileBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void foo() {
|
|
||||||
System.out.println("foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue