Expose bindings versions and print on startup in debug mode
[ldk-java] / java_strings.py
index 96d6909678ce7e58997f7866a4af65b0db8b40ef..1987c10e58db6d3915cdf7e2ba7f8fe1aafb1a2f 100644 (file)
@@ -37,9 +37,21 @@ public class bindings {
                System.loadLibrary(\"lightningjni\");
                init(java.lang.Enum.class, VecOrSliceDef.class);
                init_class_cache();
+               if (!get_lib_version_string().equals(get_ldk_java_bindings_version()))
+                       throw new IllegalArgumentException("Compiled LDK library and LDK class failes do not match");
+               // Fetching the LDK versions from C also checks that the header and binaries match
+               get_ldk_c_bindings_version();
+               get_ldk_version();
        }
        static native void init(java.lang.Class c, java.lang.Class slicedef);
        static native void init_class_cache();
+       static native String get_lib_version_string();
+
+       public static String get_ldk_java_bindings_version() {
+               return "<git_version_ldk_garbagecollected>";
+       }
+       public static native String get_ldk_c_bindings_version();
+       public static native String get_ldk_version();
 
        public static native boolean deref_bool(long ptr);
        public static native long deref_long(long ptr);
@@ -60,6 +72,7 @@ public class bindings {
         self.util_fn_pfx = """package org.ldk.structs;
 import org.ldk.impl.bindings;
 import java.util.Arrays;
+import org.ldk.enums.*;
 
 public class UtilMethods {
 """
@@ -75,7 +88,6 @@ class CommonBase {
 """
 
         self.c_file_pfx = """#include \"org_ldk_impl_bindings.h\"
-#include <rust_types.h>
 #include <lightning.h>
 #include <string.h>
 #include <stdatomic.h>
@@ -83,6 +95,35 @@ class CommonBase {
 
 """
 
+        if self.target == Target.ANDROID:
+            self.c_file_pfx = self.c_file_pfx + """
+#include <android/log.h>
+#define DEBUG_PRINT(...) __android_log_print(ANDROID_LOG_ERROR, "LDK", __VA_ARGS__)
+
+#include <unistd.h>
+#include <pthread.h>
+static void* redirect_stderr(void* ignored) {
+       int pipes[2];
+       pipe(pipes);
+       dup2(pipes[1], STDERR_FILENO);
+       char buf[8192];
+       memset(buf, 0, 8192);
+       ssize_t len;
+       while ((len = read(pipes[0], buf, 8191)) > 0) {
+               DEBUG_PRINT("%s\\n", buf);
+               memset(buf, 0, 8192);
+       }
+       DEBUG_PRINT("End of stderr???\\n");
+       return 0;
+}
+void __attribute__((constructor)) spawn_stderr_redirection() {
+       pthread_t thread;
+       pthread_create(&thread, NULL, &redirect_stderr, NULL);
+}
+"""
+        else:
+            self.c_file_pfx = self.c_file_pfx + "#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)\n"
+
         if not DEBUG:
             self.c_file_pfx = self.c_file_pfx + """#define MALLOC(a, _) malloc(a)
 #define FREE(p) if ((long)(p) > 1024) { free(p); }
@@ -96,12 +137,66 @@ class CommonBase {
 // Assert a is true or do nothing
 #define CHECK(a) DO_ASSERT(a)
 
+void __attribute__((constructor)) debug_log_version() {
+       if (check_get_ldk_version() == NULL)
+               DEBUG_PRINT("LDK version did not match the header we built against\\n");
+       if (check_get_ldk_bindings_version() == NULL)
+               DEBUG_PRINT("LDK C Bindings version did not match the header we built against\\n");
+       DEBUG_PRINT("Loaded LDK-Java Bindings with LDK %s and LDK-C-Bindings %s\\n", check_get_ldk_version(), check_get_ldk_bindings_version());
+}
+
 // Running a leak check across all the allocations and frees of the JDK is a mess,
 // so instead we implement our own naive leak checker here, relying on the -wrap
 // linker option to wrap malloc/calloc/realloc/free, tracking everyhing allocated
 // and free'd in Rust or C across the generated bindings shared library.
 #include <threads.h>
-#include <execinfo.h>
+"""
+
+            if self.target == Target.ANDROID:
+                self.c_file_pfx = self.c_file_pfx + """
+#include <unwind.h>
+#include <dlfcn.h>
+
+// Implement the execinfo functions using _Unwind_backtrace. This seems to miss many Rust
+// symbols, so we only use it on Android, where execinfo.h is unavailable.
+
+struct BacktraceState {
+       void** current;
+       void** end;
+};
+static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void* arg) {
+       struct BacktraceState* state = (struct BacktraceState*)arg;
+       uintptr_t pc = _Unwind_GetIP(context);
+       if (pc) {
+               if (state->current == state->end) {
+                       return _URC_END_OF_STACK;
+               } else {
+                       *state->current++ = (void*)(pc);
+               }
+       }
+       return _URC_NO_REASON;
+}
+
+int backtrace(void** buffer, int max) {
+       struct BacktraceState state = { buffer, buffer + max };
+       _Unwind_Backtrace(unwind_callback, &state);
+       return state.current - buffer;
+}
+
+void backtrace_symbols_fd(void ** buffer, int count, int _fd) {
+       for (int idx = 0; idx < count; ++idx) {
+               Dl_info info;
+               if (dladdr(buffer[idx], &info) && info.dli_sname) {
+                       DEBUG_PRINT("%p: %s", buffer[idx], info.dli_sname);
+               } else {
+                       DEBUG_PRINT("%p: ???", buffer[idx]);
+               }
+       }
+}
+"""
+            else:
+                self.c_file_pfx = self.c_file_pfx + "#include <execinfo.h>\n"
+            self.c_file_pfx = self.c_file_pfx + """
 #include <unistd.h>
 static mtx_t allocation_mtx;
 
@@ -116,7 +211,7 @@ typedef struct allocation {
        const char* struct_name;
        void* bt[BT_MAX];
        int bt_len;
-       size_t alloc_len;
+       unsigned long alloc_len;
 } allocation;
 static allocation* allocation_ll = NULL;
 
@@ -146,11 +241,11 @@ static void alloc_freed(void* ptr) {
        while (it->ptr != ptr) {
                p = it; it = it->next;
                if (it == NULL) {
-                       fprintf(stderr, "Tried to free unknown pointer %p at:\\n", ptr);
+                       DEBUG_PRINT("Tried to free unknown pointer %p at:\\n", ptr);
                        void* bt[BT_MAX];
                        int bt_len = backtrace(bt, BT_MAX);
                        backtrace_symbols_fd(bt, bt_len, STDERR_FILENO);
-                       fprintf(stderr, "\\n\\n");
+                       DEBUG_PRINT("\\n\\n");
                        DO_ASSERT(mtx_unlock(&allocation_mtx) == thrd_success);
                        return; // addrsan should catch malloc-unknown and print more info than we have
                }
@@ -195,21 +290,21 @@ void __wrap_reallocarray(void* ptr, size_t new_sz) {
 }
 
 void __attribute__((destructor)) check_leaks() {
-       size_t alloc_count = 0;
-       size_t alloc_size = 0;
-       fprintf(stderr, "The following LDK-allocated blocks still remain.\\n");
-       fprintf(stderr, "Note that this is only accurate if System.gc(); System.runFinalization()\\n");
-       fprintf(stderr, "was called prior to exit after all LDK objects were out of scope.\\n");
+       unsigned long alloc_count = 0;
+       unsigned long alloc_size = 0;
+       DEBUG_PRINT("The following LDK-allocated blocks still remain.\\n");
+       DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n");
+       DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n");
        for (allocation* a = allocation_ll; a != NULL; a = a->next) {
-               fprintf(stderr, "%s %p (%lu bytes) remains:\\n", a->struct_name, a->ptr, a->alloc_len);
+               DEBUG_PRINT("%s %p (%lu bytes) remains:\\n", a->struct_name, a->ptr, a->alloc_len);
                backtrace_symbols_fd(a->bt, a->bt_len, STDERR_FILENO);
-               fprintf(stderr, "\\n\\n");
+               DEBUG_PRINT("\\n\\n");
                alloc_count++;
                alloc_size += a->alloc_len;
        }
-       fprintf(stderr, "%lu allocations remained for %lu bytes.\\n", alloc_count, alloc_size);
-       fprintf(stderr, "Note that this is only accurate if System.gc(); System.runFinalization()\\n");
-       fprintf(stderr, "was called prior to exit after all LDK objects were out of scope.\\n");
+       DEBUG_PRINT("%lu allocations remained for %lu bytes.\\n", alloc_count, alloc_size);
+       DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n");
+       DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n");
 }
 """
         self.c_file_pfx = self.c_file_pfx + """
@@ -308,12 +403,12 @@ typedef jbyteArray int8_tArray;
 
 static inline jstring str_ref_to_java(JNIEnv *env, const char* chars, size_t len) {
        // Sadly we need to create a temporary because Java can't accept a char* without a 0-terminator
-       char* err_buf = MALLOC(len + 1, "str conv buf");
-       memcpy(err_buf, chars, len);
-       err_buf[len] = 0;
-       jstring err_conv = (*env)->NewStringUTF(env, chars);
-       FREE(err_buf);
-       return err_conv;
+       char* conv_buf = MALLOC(len + 1, "str conv buf");
+       memcpy(conv_buf, chars, len);
+       conv_buf[len] = 0;
+       jstring ret = (*env)->NewStringUTF(env, conv_buf);
+       FREE(conv_buf);
+       return ret;
 }
 static inline LDKStr java_to_owned_str(JNIEnv *env, jstring str) {
        uint64_t str_len = (*env)->GetStringUTFLength(env, str);
@@ -329,6 +424,16 @@ static inline LDKStr java_to_owned_str(JNIEnv *env, jstring str) {
        };
        return res;
 }
+
+JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1lib_1version_1string(JNIEnv *env, jclass _c) {
+       return str_ref_to_java(env, "<git_version_ldk_garbagecollected>", strlen("<git_version_ldk_garbagecollected>"));
+}
+JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1ldk_1c_1bindings_1version(JNIEnv *env, jclass _c) {
+       return str_ref_to_java(env, check_get_ldk_bindings_version(), strlen(check_get_ldk_bindings_version()));
+}
+JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1ldk_1version(JNIEnv *env, jclass _c) {
+       return str_ref_to_java(env, check_get_ldk_version(), strlen(check_get_ldk_version()));
+}
 """
 
         self.hu_struct_file_prefix = """package org.ldk.structs;