Implement adapter for LSan from tizen-mainline 94/195594/2
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Thu, 13 Dec 2018 14:17:58 +0000 (17:17 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 28 Dec 2018 08:57:59 +0000 (11:57 +0300)
This adapter implements the following:
  1. Load LSan library
  2. Contains a modified interceptors (because LSan not loaded first)
  3. Inject interceptors to application
  4. Provides an API for using LSan library

Change-Id: Id33efe56b6ae3271858546050f1e2e8cfc8d250f
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
src/CMakeLists.txt
src/core/internal_libc.cpp
src/core/internal_libc.h
src/lsan/lsan_adapter.cpp [new file with mode: 0644]
src/lsan/lsan_adapter.h [new file with mode: 0644]

index b17a80e..0843cc1 100644 (file)
@@ -18,6 +18,7 @@ set(SRC
   core/injector.cpp
   core/internal_libc.cpp
   core/internal_libdl.cpp
+  lsan/lsan_adapter.cpp
 )
 
 add_library(${PROJECT_NAME} SHARED ${SRC})
index 9f744a5..bc6dfe7 100644 (file)
@@ -1,10 +1,20 @@
 #include <sys/mman.h>       // for ::mprotect()
+#include <malloc.h>         // for ::malloc_usable_size()
 #include "internal_libc.h"
 
 
 namespace __swap {
 namespace internal {
 
+void *memcpy(void *dest, const void *src, uptr n) noexcept
+{
+    char *d = (char*)dest;
+    const char *s = (const char *)src;
+    for (uptr i = 0; i < n; ++i)
+        d[i] = s[i];
+    return dest;
+}
+
 int strcmp(const char *s1, const char *s2) noexcept
 {
     for (;; ++s1, ++s2) {
@@ -24,5 +34,11 @@ int mprotect(void *addr, uptr len, int prot) noexcept
     return ::mprotect(addr, len, prot);
 }
 
+uptr malloc_usable_size(void *ptr) noexcept
+{
+    // TODO: Replace it.
+    return ::malloc_usable_size(ptr);
+}
+
 } // namespace internal
 } // namespace __swap
index f842d9b..f89be62 100644 (file)
@@ -6,8 +6,10 @@
 namespace __swap {
 namespace internal {
 
+void *memcpy(void *dest, const void *src, uptr n) noexcept;
 int strcmp(const char *s1, const char *s2) noexcept;
 int mprotect(void *addr, uptr len, int prot) noexcept;
+uptr malloc_usable_size(void *ptr) noexcept;
 
 } // namespace internal
 } // namespace __swap
diff --git a/src/lsan/lsan_adapter.cpp b/src/lsan/lsan_adapter.cpp
new file mode 100644 (file)
index 0000000..b30ac4d
--- /dev/null
@@ -0,0 +1,189 @@
+#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();
+}
diff --git a/src/lsan/lsan_adapter.h b/src/lsan/lsan_adapter.h
new file mode 100644 (file)
index 0000000..9949181
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef LSAN_ADAPTER_H
+#define LSAN_ADAPTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+int __swap_lsan_adapter_init(const char *lsan_path);
+void __swap_lsan_adapter_leak_check();
+const char *__swap_lsan_adapter_report_file();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // LSAN_ADAPTER_H