Finally working it is!
This commit is contained in:
parent
84374f5d0d
commit
5d368dd063
11 changed files with 371 additions and 53 deletions
17
README.md
17
README.md
|
|
@ -1 +1,18 @@
|
||||||
Experiment with java22 and Rust to monitor exceptions in a JVM
|
Experiment with java22 and Rust to monitor exceptions in a JVM
|
||||||
|
|
||||||
|
Running:
|
||||||
|
* Update the path to the rust lib (temp fix) in ExceptionLogger for your setup
|
||||||
|
* mvn clean install
|
||||||
|
* cd rustlib; cargo build
|
||||||
|
* create a minimal class in a separate project
|
||||||
|
```java
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
throw new Throwable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* run it with (adjust paths):
|
||||||
|
``` bash
|
||||||
|
java22 -javaagent:$EXCEPTIONAL_PROJECT/exceptional/agent/target/exceptional-agent-1.0-SNAPSHOT.jar --enable-preview -classpath $YOUR_CLASSPATH Main
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,15 @@
|
||||||
<maven.compiler.target>22</maven.compiler.target>
|
<maven.compiler.target>22</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.github.shautvast.exceptional</groupId>
|
||||||
|
<artifactId>exceptional-lib</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -38,7 +47,9 @@
|
||||||
<Premain-Class>com.github.shautvast.exceptional.Agent</Premain-Class>
|
<Premain-Class>com.github.shautvast.exceptional.Agent</Premain-Class>
|
||||||
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
||||||
<Can-Redefine-Classes>true</Can-Redefine-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>
|
<Boot-Class-Path>
|
||||||
|
/Users/Shautvast/dev/exceptional/lib/target/exceptional-lib-1.0-SNAPSHOT.jar
|
||||||
|
</Boot-Class-Path>
|
||||||
</manifestEntries>
|
</manifestEntries>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,80 @@
|
||||||
package com.github.shautvast.exceptional;
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
import java.lang.classfile.*;
|
import java.lang.classfile.ClassBuilder;
|
||||||
import java.lang.classfile.instruction.ReturnInstruction;
|
import java.lang.classfile.ClassElement;
|
||||||
|
import java.lang.classfile.ClassFile;
|
||||||
|
import java.lang.classfile.MethodModel;
|
||||||
|
import java.lang.classfile.instruction.ThrowInstruction;
|
||||||
import java.lang.constant.ClassDesc;
|
import java.lang.constant.ClassDesc;
|
||||||
import java.lang.constant.MethodTypeDesc;
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import java.lang.instrument.ClassDefinition;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.nio.file.Files;
|
import java.lang.reflect.AccessFlag;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
|
|
||||||
public class Agent {
|
public class Agent {
|
||||||
|
|
||||||
@SuppressWarnings("DataFlowIssue")
|
private static final String EXCEPTIONLOGGER = ExceptionLogger.class.getName();
|
||||||
|
|
||||||
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
|
||||||
System.err.println("--->Exceptional agent active");
|
System.err.println("--->Exceptional agent active");
|
||||||
// add transformer
|
// 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) {
|
||||||
return instrumentThrowable(className, classfileBuffer);
|
return instrumentExceptionLoggerAtThrow(className, classfileBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||||
return instrumentThrowable(className, classfileBuffer);
|
return instrumentExceptionLoggerAtThrow(className, classfileBuffer);
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
// we only want to redefine Throwable
|
|
||||||
byte[] bytecode = Files.readAllBytes(
|
|
||||||
Path.of(Agent.class.getResource("/java/lang/Throwable.class").toURI()));
|
|
||||||
instrumentation.redefineClasses(new ClassDefinition(Throwable.class, bytecode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] instrumentThrowable(String className, byte[] bytecode) {
|
/*
|
||||||
// we only want to instrument Throwable
|
* Every throw opcode will be preceded by a call to our ExceptionLogger
|
||||||
// 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)
|
private static byte[] instrumentExceptionLoggerAtThrow(String className, byte[] classfileBuffer) {
|
||||||
if (className.equals("java/lang/Throwable")) {
|
var classFile = ClassFile.of();
|
||||||
ClassFile classFile = ClassFile.of();
|
var classModel = classFile.parse(classfileBuffer);
|
||||||
ClassModel classModel = classFile.parse(bytecode);
|
|
||||||
return instrumentConstructors(classFile, classModel);
|
|
||||||
} else {
|
|
||||||
return bytecode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 && mm.methodName().toString().equals("<init>")) {
|
// not all methods, TODO include synthetic?
|
||||||
instrumentMethodWithMyExceptionLogger(classBuilder, mm);
|
if (ce instanceof MethodModel methodModel && !methodModel.flags().has(AccessFlag.ABSTRACT)
|
||||||
continue;
|
&& !methodModel.flags().has(AccessFlag.NATIVE)
|
||||||
}
|
&& !methodModel.flags().has(AccessFlag.BRIDGE)) {
|
||||||
|
transform(classBuilder, methodModel);
|
||||||
|
} else {
|
||||||
|
// keep all other class elements
|
||||||
classBuilder.with(ce);
|
classBuilder.with(ce);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void instrumentMethodWithMyExceptionLogger(ClassBuilder classBuilder, MethodModel methodModel) {
|
private static void transform(ClassBuilder classBuilder, MethodModel methodModel) {
|
||||||
methodModel.code().ifPresent(code ->
|
methodModel.code().ifPresent(code -> classBuilder.withMethod(
|
||||||
classBuilder.withMethod(methodModel.methodName(), methodModel.methodType(), methodModel.flags().flagsMask(),
|
methodModel.methodName(), // copy name, type and modifiers from the original
|
||||||
methodBuilder ->
|
methodModel.methodType(),
|
||||||
methodBuilder.transformCode(code, ((builder, element) -> {
|
methodModel.flags().flagsMask(),
|
||||||
if (element instanceof ReturnInstruction) {
|
methodBuilder -> {
|
||||||
builder.aload(0); // load `this` on the stack, ie the Throwable instance
|
// keep the existing annotations (and other method elements) in place
|
||||||
builder.invokestatic( // call my code
|
methodModel.forEachElement(methodBuilder::with);
|
||||||
ClassDesc.of("com.github.shautvast.exceptional.ExceptionLogger"), "log",
|
// change the code
|
||||||
|
methodBuilder.transformCode(code, (builder, element) -> {
|
||||||
|
// this way of instrumenting may miss the already loaded classes, java.lang.String for example.
|
||||||
|
// May need to circumvent that
|
||||||
|
if (element instanceof ThrowInstruction) {
|
||||||
|
builder.dup(); // on top of the stack is the current exception instance
|
||||||
|
// duplicate it to make sure the `athrow` op has something to throw
|
||||||
|
// after the invoke to ExceptionLogger has popped one off
|
||||||
|
builder.invokestatic( // call my code with the exception as argument
|
||||||
|
ClassDesc.of(EXCEPTIONLOGGER), "log",
|
||||||
MethodTypeDesc.ofDescriptor("(Ljava/lang/Throwable;)V"));
|
MethodTypeDesc.ofDescriptor("(Ljava/lang/Throwable;)V"));
|
||||||
builder.return_();
|
|
||||||
} else {
|
|
||||||
builder.with(element); // leave any other bytecode in place
|
|
||||||
}
|
}
|
||||||
})
|
builder.with(element); // leave any other element in place
|
||||||
)));
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
63
lib/dependency-reduced-pom.xml
Normal file
63
lib/dependency-reduced-pom.xml
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?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/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>exceptional-parent</artifactId>
|
||||||
|
<groupId>org.github.shautvast.exceptional</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>exceptional-lib</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>22</source>
|
||||||
|
<target>22</target>
|
||||||
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration />
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.target>22</maven.compiler.target>
|
||||||
|
<maven.compiler.source>22</maven.compiler.source>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
</project>
|
||||||
30
lib/pom.xml
30
lib/pom.xml
|
|
@ -18,6 +18,19 @@
|
||||||
<maven.compiler.target>22</maven.compiler.target>
|
<maven.compiler.target>22</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
@ -30,8 +43,23 @@
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -1,11 +1,38 @@
|
||||||
package com.github.shautvast.exceptional;
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.lang.foreign.*;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
|
||||||
@SuppressWarnings("unused") // this code is called from the instrumented code
|
@SuppressWarnings("unused") // this code is called from the instrumented code
|
||||||
public class ExceptionLogger {
|
public class ExceptionLogger {
|
||||||
|
private static final Arena arena = Arena.ofConfined();
|
||||||
|
private static final Linker linker = Linker.nativeLinker();
|
||||||
|
// //TODO relative path, or configurable
|
||||||
|
private static final SymbolLookup rustlib = SymbolLookup.libraryLookup("/Users/Shautvast/dev/exceptional/rustlib/target/debug/librustlib.dylib", arena);
|
||||||
|
private final static MethodHandle logNative;
|
||||||
|
private final static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
static {
|
||||||
|
MemorySegment logFunction = rustlib.find("log_java_exception").orElseThrow();
|
||||||
|
logNative = linker.downcallHandle(logFunction, FunctionDescriptor.ofVoid(
|
||||||
|
ValueLayout.ADDRESS
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// how does this behave in a multithreaded context??
|
||||||
|
// probably need a ringbuffer with fixed memory to make this work efficiently
|
||||||
public static void log(Throwable throwable) {
|
public static void log(Throwable throwable) {
|
||||||
System.out.print("Logging exception:");
|
try {
|
||||||
Arrays.stream(throwable.getStackTrace()).forEach(System.out::println);
|
// use json for now because of ease of integration
|
||||||
|
if (throwable != null) {
|
||||||
|
String json = objectMapper.writeValueAsString(throwable);
|
||||||
|
var data = arena.allocateFrom(json); // reuse instead of reallocating?
|
||||||
|
logNative.invoke(data); // invoke the rust function
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.github.shautvast.exceptional;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class ExceptionLoggerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test(){
|
||||||
|
ExceptionLogger.log(new Throwable());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
96
rustlib/Cargo.lock
generated
Normal file
96
rustlib/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustlib"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
13
rustlib/Cargo.toml
Normal file
13
rustlib/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "rustlib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = { version = "1.0", features = [] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
anyhow = "1.0"
|
||||||
32
rustlib/src/Throwable.rs
Normal file
32
rustlib/src/Throwable.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Throwable {
|
||||||
|
cause: Option<Box<Throwable>>,
|
||||||
|
#[serde(rename (deserialize = "stackTrace"))]
|
||||||
|
stack_trace: Vec<Stacktrace>,
|
||||||
|
message: Option<String>,
|
||||||
|
suppressed: Vec<String>,
|
||||||
|
#[serde(rename (deserialize = "localizedMessage"))]
|
||||||
|
localized_message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Stacktrace{
|
||||||
|
#[serde(rename (deserialize = "classLoaderName"))]
|
||||||
|
class_loader_name: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "moduleName"))]
|
||||||
|
module_name: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "moduleVersion"))]
|
||||||
|
module_version: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "methodName"))]
|
||||||
|
method_name: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "fileName"))]
|
||||||
|
file_name: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "lineNumber"))]
|
||||||
|
line_number: Option<u32>,
|
||||||
|
#[serde(rename (deserialize = "className"))]
|
||||||
|
class_name: Option<String>,
|
||||||
|
#[serde(rename (deserialize = "nativeMethod"))]
|
||||||
|
native_method: Option<bool>,
|
||||||
|
}
|
||||||
17
rustlib/src/lib.rs
Normal file
17
rustlib/src/lib.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
mod throwable;
|
||||||
|
|
||||||
|
use std::ffi::{c_char, CStr};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn log_java_exception(raw_string: *const c_char) {
|
||||||
|
let c_str = unsafe { CStr::from_ptr(raw_string) };
|
||||||
|
let string = c_str.to_str();
|
||||||
|
if let Ok(json) = string {
|
||||||
|
println!("receiving {}", json);
|
||||||
|
let error: throwable::Throwable = serde_json::from_str(json).unwrap();
|
||||||
|
println!("{:?}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {}
|
||||||
Loading…
Add table
Reference in a new issue