for readability better making I am
This commit is contained in:
parent
f275d0de62
commit
862aad4462
3 changed files with 42 additions and 39 deletions
|
|
@ -1,9 +1,6 @@
|
||||||
package com.github.shautvast.exceptional;
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
import java.lang.classfile.ClassElement;
|
import java.lang.classfile.*;
|
||||||
import java.lang.classfile.ClassFile;
|
|
||||||
import java.lang.classfile.ClassModel;
|
|
||||||
import java.lang.classfile.MethodModel;
|
|
||||||
import java.lang.classfile.instruction.ReturnInstruction;
|
import java.lang.classfile.instruction.ReturnInstruction;
|
||||||
import java.lang.constant.ClassDesc;
|
import java.lang.constant.ClassDesc;
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
|
|
@ -21,6 +18,7 @@ public class Agent {
|
||||||
private static final String MESSAGE = "--- Exceptional agent active";
|
private static final String MESSAGE = "--- Exceptional agent active";
|
||||||
|
|
||||||
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
||||||
|
// add transformer
|
||||||
instrumentation.addTransformer(new ClassFileTransformer() {
|
instrumentation.addTransformer(new ClassFileTransformer() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||||
|
|
@ -33,46 +31,51 @@ public class Agent {
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
// we only want to redefine Throwable
|
||||||
byte[] bytecode = Files.readAllBytes(
|
byte[] bytecode = Files.readAllBytes(
|
||||||
Path.of(Agent.class.getResource("/java/lang/Throwable.class").toURI()));
|
Path.of(Agent.class.getResource("/java/lang/Throwable.class").toURI()));
|
||||||
|
|
||||||
instrumentation.redefineClasses(new ClassDefinition(Throwable.class, bytecode));
|
instrumentation.redefineClasses(new ClassDefinition(Throwable.class, bytecode));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] instrumentThrowable(String className, byte[] classfileBuffer) {
|
private static byte[] instrumentThrowable(String className, byte[] bytecode) {
|
||||||
|
// we only want to instrument Throwable
|
||||||
|
// or rather,,, is this the right way? This way we also intercept new any Exception that is not thrown
|
||||||
|
// But,,, who does that?? (famous last words)
|
||||||
if (className.equals("java/lang/Throwable")) {
|
if (className.equals("java/lang/Throwable")) {
|
||||||
ClassFile classFile = ClassFile.of();
|
ClassFile classFile = ClassFile.of();
|
||||||
ClassModel classModel = classFile.parse(classfileBuffer);
|
ClassModel classModel = classFile.parse(bytecode);
|
||||||
return instrumentByteCode(classFile, classModel);
|
return instrumentConstructors(classFile, classModel);
|
||||||
} else {
|
} else {
|
||||||
return classfileBuffer;
|
return bytecode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] instrumentByteCode(ClassFile classFile, ClassModel classModel) {
|
private static byte[] instrumentConstructors(ClassFile classFile, ClassModel classModel) {
|
||||||
return classFile.build(classModel.thisClass().asSymbol(), classBuilder -> {
|
return classFile.build(classModel.thisClass().asSymbol(), classBuilder -> {
|
||||||
for (ClassElement ce : classModel) {
|
for (ClassElement ce : classModel) {
|
||||||
if (ce instanceof MethodModel mm) {
|
if (ce instanceof MethodModel mm && mm.methodName().toString().equals("<init>")) {
|
||||||
if (mm.methodName().toString().equals("<init>")) {
|
instrumentMethodWithMyExceptionLogger(classBuilder, mm);
|
||||||
classBuilder.withMethod(mm.methodName(), mm.methodType(), mm.flags().flagsMask(),
|
continue;
|
||||||
methodBuilder ->
|
|
||||||
methodBuilder.transformCode(mm.code().get(), ((builder, element) -> {
|
|
||||||
if (element instanceof ReturnInstruction) {
|
|
||||||
builder.aload(0);
|
|
||||||
builder.invokestatic(
|
|
||||||
ClassDesc.of("com.github.shautvast.exceptional.ThrowableHandler"), "handle",
|
|
||||||
MethodTypeDesc.ofDescriptor("(Ljava/lang/Throwable;)V"));
|
|
||||||
builder.return_();
|
|
||||||
} else {
|
|
||||||
builder.with(element);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
classBuilder.with(ce);
|
classBuilder.with(ce);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void instrumentMethodWithMyExceptionLogger(ClassBuilder classBuilder, MethodModel mm) {
|
||||||
|
classBuilder.withMethod(mm.methodName(), mm.methodType(), mm.flags().flagsMask(),
|
||||||
|
methodBuilder ->
|
||||||
|
methodBuilder.transformCode(mm.code().get(), ((builder, element) -> {
|
||||||
|
if (element instanceof ReturnInstruction) {
|
||||||
|
builder.aload(0); // load `this` on the stack, ie the Throwable instance
|
||||||
|
builder.invokestatic( // call my code
|
||||||
|
ClassDesc.of("com.github.shautvast.exceptional.ExceptionLogger"), "log",
|
||||||
|
MethodTypeDesc.ofDescriptor("(Ljava/lang/Throwable;)V"));
|
||||||
|
builder.return_();
|
||||||
|
} else {
|
||||||
|
builder.with(element);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // this code is called from the instrumented code
|
||||||
|
public class ExceptionLogger {
|
||||||
|
public static void log(Throwable throwable) {
|
||||||
|
System.out.print("Logging exception:");
|
||||||
|
Arrays.stream(throwable.getStackTrace()).forEach(System.out::println);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue