--- /dev/null
+#include <core/log.h>
+#include <core/injector.h>
+#include <core/internal_deps.h>
+#include <core/internal_libc.h>
+#include <core/internal_libdl.h>
+
+#include "lsan_adapter.h"
+
+
+#define IS_CLEAR_MEMORY 1
+#define CALLER_PC (uptr)__builtin_return_address(0)
+#define CURRENT_FRAME (uptr)__builtin_frame_address(0)
+
+
+namespace __swap {
+namespace lsan_adapter {
+
+// Declare external LSan API
+static void *(*__lsan_allocator_allocate)(uptr size, uptr alignment, int cleared,
+ uptr caller_pc, uptr frame);
+static void *(*__lsan_allocator_reallocate)(void *p, uptr new_size, uptr alignment,
+ uptr caller_pc, uptr frame);
+static void (*__lsan_allocator_deallocate)(void *p);
+static int (*__lsan_allocator_pointer_is_mine)(const void *p);
+
+static const char *(*__lsan_report_file)();
+static void (*__lsan_do_leak_check)();
+
+
+// TODO: Replace with calling a real free()
+extern "C" void __libc_free(void *ptr);
+static void (*real_free)(void *ptr) = __libc_free;
+
+
+// ====================
+// Memory interceptors:
+// ====================
+static void *interceptor_malloc(uptr size)
+{
+ return __lsan_allocator_allocate(size, 1, IS_CLEAR_MEMORY, CALLER_PC, CURRENT_FRAME);
+}
+
+static void interceptor_free(void *ptr)
+{
+ if (__lsan_allocator_pointer_is_mine(ptr)) {
+ __lsan_allocator_deallocate(ptr);
+ } else {
+ real_free(ptr);
+ }
+}
+
+static void *interceptor_calloc(uptr nmemb, uptr size)
+{
+ return __lsan_allocator_allocate(nmemb * size, 1, true, CALLER_PC, CURRENT_FRAME);
+}
+
+static void *interceptor_realloc(void *ptr, uptr size)
+{
+ void *p;
+
+ if (__lsan_allocator_pointer_is_mine(ptr)) {
+ p = __lsan_allocator_reallocate(ptr, size, 1, CALLER_PC, CURRENT_FRAME);
+ } else {
+ if (size == 0) {
+ real_free(ptr);
+ p = 0;
+ } else {
+ p = __lsan_allocator_allocate(size, 1, IS_CLEAR_MEMORY, CALLER_PC, CURRENT_FRAME);
+
+ if (ptr) {
+ uptr real_size = internal::malloc_usable_size(ptr);
+ uptr copy_size = size < real_size ? size : real_size;
+ internal::memcpy(p, ptr, copy_size);
+ }
+ }
+ }
+
+ return p;
+}
+
+
+static struct injector::injection_info info[] = {
+ INJECTION_INFO_INIT("malloc", interceptor_malloc),
+ INJECTION_INFO_INIT("free", interceptor_free),
+ INJECTION_INFO_INIT("cfree", interceptor_free),
+ INJECTION_INFO_INIT("calloc", interceptor_calloc),
+ INJECTION_INFO_INIT("realloc", interceptor_realloc),
+
+/*
+ TODO: Need implement:
+ INJECTION_INFO_INIT("memalign", interceptor_memalign);
+ INJECTION_INFO_INIT("posix_memalign", interceptor_posix_memalign);
+ INJECTION_INFO_INIT("__libc_memalign", interceptor___libc_memalign);
+ INJECTION_INFO_INIT("valloc", interceptor_valloc);
+ INJECTION_INFO_INIT("pvalloc", interceptor_pvalloc);
+ INJECTION_INFO_INIT("malloc_usable_size", interceptor_malloc_usable_size);
+ INJECTION_INFO_INIT("mallinfo", interceptor_mallinfo);
+ INJECTION_INFO_INIT("mallopt", interceptor_mallopt);
+ INJECTION_INFO_INIT("pthread_create", interceptor_pthread_create);
+ INJECTION_INFO_INIT("pthread_join", interceptor_pthread_join);
+ INJECTION_INFO_INIT("mmap", interceptor_mmap);
+ INJECTION_INFO_INIT("mmap64", interceptor_mmap64);
+ INJECTION_INFO_INIT("munmap", interceptor_munmap);
+*/
+};
+
+template<class T, int N>
+constexpr int ARRAY_SIZE(T(&)[N]) { return N; }
+
+
+#define DLSYM_INIT_OR_RETURN(ret_value, handle, sym_name) \
+ do { \
+ sym_name = decltype(sym_name)(internal::dlsym(handle, #sym_name)); \
+ if (!sym_name) { \
+ LOGE("Symbol '" #sym_name "' not found."); \
+ return ret_value; \
+ } \
+ } while (0)
+
+static bool init(const char *lsan_path)
+{
+ static bool is_lsan_init = false;
+
+ if (is_lsan_init) {
+ LOGW("LsanAdapter already initialized");
+ return false;
+ }
+
+ void *handle = internal::dlopen(lsan_path, RTLD_NOW);
+ if (!handle) {
+ LOGE("Cannot load LSan library, path=%s", lsan_path);
+ return false;
+ }
+
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_allocate);
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_deallocate);
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_reallocate);
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_pointer_is_mine);
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_do_leak_check);
+ DLSYM_INIT_OR_RETURN(false, handle, __lsan_report_file);
+
+ injector::inject(info, ARRAY_SIZE(info));
+
+ is_lsan_init = true;
+ LOGI("LSan library has been loaded, path=%s", lsan_path);
+ return true;
+}
+
+static void leak_check()
+{
+ LOGI("Start memory leaks checking");
+ __lsan_do_leak_check();
+ LOGI("Finish memory leaks checking");
+}
+
+static const char *report_file()
+{
+ return __lsan_report_file();
+}
+
+} // namespace lsan_adapter
+} // namespace __swap
+
+
+// =============
+// External API:
+// =============
+
+extern "C" SWAP_INTERFACE_ATTRIBUTE
+int __swap_lsan_adapter_init(const char *lsan_path)
+{
+ if (!__swap::lsan_adapter::init(lsan_path)) {
+ LOGE("Cannot init LSan, path=%s", lsan_path);
+ return -1;
+ }
+ return 0;
+}
+
+extern "C" SWAP_INTERFACE_ATTRIBUTE
+void __swap_lsan_adapter_leak_check()
+{
+ __swap::lsan_adapter::leak_check();
+}
+
+extern "C" SWAP_INTERFACE_ATTRIBUTE
+const char *__swap_lsan_adapter_report_file()
+{
+ return __swap::lsan_adapter::report_file();
+}