for readability better making I am

This commit is contained in:
Shautvast 2024-06-23 20:39:03 +02:00
parent f275d0de62
commit 862aad4462
3 changed files with 42 additions and 39 deletions

View file

@ -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);
}
})
));
}
} }

View file

@ -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);
}
}

View file

@ -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);
}
}