From 467919e9506ca0c1acb5a2f1e476a55f429217b9 Mon Sep 17 00:00:00 2001 From: gaopy3 Date: Thu, 21 Nov 2024 11:48:07 +0000 Subject: [PATCH] re-implement glibc-malloc hooking --- deps/oblib/src/lib/CMakeLists.txt | 3 +- deps/oblib/src/lib/alloc/malloc_hook.cpp | 149 ++++----- .../src/lib/alloc/malloc_hook_extended.cpp | 280 ++++++++++++++++ deps/oblib/unittest/CMakeLists.txt | 4 +- deps/oblib/unittest/lib/CMakeLists.txt | 2 +- .../unittest/lib/alloc/test_malloc_hook.cpp | 299 +++++++++++------- 6 files changed, 540 insertions(+), 197 deletions(-) create mode 100644 deps/oblib/src/lib/alloc/malloc_hook_extended.cpp diff --git a/deps/oblib/src/lib/CMakeLists.txt b/deps/oblib/src/lib/CMakeLists.txt index 0cec78316..8504701cd 100644 --- a/deps/oblib/src/lib/CMakeLists.txt +++ b/deps/oblib/src/lib/CMakeLists.txt @@ -411,7 +411,8 @@ target_link_libraries(ob_malloc ob_malloc_object) add_library(malloc_hook STATIC alloc/malloc_hook.cpp - alloc/malloc_hook.h) + alloc/malloc_hook.h + alloc/malloc_hook_extended.cpp) target_link_libraries(malloc_hook oblib_base) if(OB_BUILD_CLOSE_MODULES) diff --git a/deps/oblib/src/lib/alloc/malloc_hook.cpp b/deps/oblib/src/lib/alloc/malloc_hook.cpp index c096cc6a7..aa2c0e7db 100644 --- a/deps/oblib/src/lib/alloc/malloc_hook.cpp +++ b/deps/oblib/src/lib/alloc/malloc_hook.cpp @@ -11,12 +11,16 @@ */ #include "malloc_hook.h" -#include -#include "lib/utility/utility.h" #include "lib/utility/ob_defer.h" #include "lib/allocator/ob_mem_leak_checker.h" #include "lib/allocator/ob_malloc.h" +#define OBMALLOC_ATTR(s) __attribute__((s)) +#define OBMALLOC_EXPORT __attribute__((visibility("default"))) +#define OBMALLOC_ALLOC_SIZE(s) __attribute__((alloc_size(s))) +#define OBMALLOC_NOTHROW __attribute__((nothrow)) +#define LIBC_ALIAS(fn) __attribute__((alias (#fn), used)) + using namespace oceanbase; using namespace oceanbase::common; using namespace oceanbase::lib; @@ -37,7 +41,7 @@ struct Header { static const uint32_t MAGIC_CODE = 0XA1B2C3D1; static const uint32_t SIZE; - Header(int32_t size, bool from_mmap) + Header(uint32_t size, bool from_mmap) : magic_code_(MAGIC_CODE), data_size_(size), offset_(0), @@ -47,7 +51,7 @@ struct Header void mark_unused() { magic_code_ &= ~0x1; } static Header *ptr2header(void *ptr) { return reinterpret_cast((char*)ptr - SIZE); } uint32_t magic_code_; - int32_t data_size_; + uint32_t data_size_; uint32_t offset_; uint8_t from_mmap_; char padding_[3]; @@ -55,7 +59,6 @@ struct Header } __attribute__((aligned (16))); const uint32_t Header::SIZE = offsetof(Header, data_); -const size_t max_retry_size = 2 << 30; // 2G void *ob_malloc_retry(size_t size) { @@ -72,7 +75,7 @@ void *ob_malloc_retry(size_t size) if (OB_ISNULL(ptr)) { ::usleep(10000); // 10ms } - } while (OB_ISNULL(ptr) && !(size > max_retry_size || 0 == size)); + } while (OB_ISNULL(ptr) && 0 != size); return ptr; } @@ -95,9 +98,15 @@ static inline int ob_munmap(void *addr, size_t length) return syscall(SYS_munmap, addr, length); } -void *ob_malloc_hook(size_t size, const void *) +EXTERN_C_BEGIN + +OBMALLOC_EXPORT +void OBMALLOC_NOTHROW * +OBMALLOC_ATTR(malloc) OBMALLOC_ALLOC_SIZE(1) +malloc(size_t size) { void *ptr = nullptr; + abort_unless(size <= UINT32_MAX - Header::SIZE); size_t real_size = size + Header::SIZE; void *tmp_ptr = nullptr; bool from_mmap = false; @@ -113,17 +122,17 @@ void *ob_malloc_hook(size_t size, const void *) tmp_ptr = ob_malloc_retry(real_size); } if (OB_LIKELY(tmp_ptr != nullptr)) { - abort_unless(size <= INT32_MAX); - auto *header = new (tmp_ptr) Header((int32_t)size, from_mmap); + Header *header = new (tmp_ptr) Header((uint32_t)size, from_mmap); ptr = header->data_; } return ptr; } -void ob_free_hook(void *ptr, const void *) +OBMALLOC_EXPORT void OBMALLOC_NOTHROW +free(void *ptr) { if (OB_LIKELY(ptr != nullptr)) { - auto *header = Header::ptr2header(ptr); + Header *header = Header::ptr2header(ptr); abort_unless(header->check_magic_code()); header->mark_unused(); void *orig_ptr = (char*)header - header->offset_; @@ -138,9 +147,17 @@ void ob_free_hook(void *ptr, const void *) } } -void *ob_realloc_hook(void *ptr, size_t size, const void *caller) +OBMALLOC_EXPORT +void OBMALLOC_NOTHROW * +OBMALLOC_ALLOC_SIZE(2) +realloc(void *ptr, size_t size) { + if (0 == size && nullptr != ptr) { + free(ptr); + return nullptr; + } void *nptr = nullptr; + abort_unless(size <= UINT32_MAX - Header::SIZE); size_t real_size = size + Header::SIZE; void *tmp_ptr = nullptr; bool from_mmap = false; @@ -156,60 +173,60 @@ void *ob_realloc_hook(void *ptr, size_t size, const void *caller) tmp_ptr = ob_malloc_retry(real_size); } if (OB_LIKELY(tmp_ptr != nullptr)) { - abort_unless(size <= INT32_MAX); - auto *header = new (tmp_ptr) Header((int32_t)size, from_mmap); + Header *header = new (tmp_ptr) Header((uint32_t)size, from_mmap); nptr = header->data_; if (ptr != nullptr) { - auto *old_header = Header::ptr2header(ptr); + Header *old_header = Header::ptr2header(ptr); abort_unless(old_header->check_magic_code()); memmove(nptr, ptr, MIN(old_header->data_size_, size)); - ob_free_hook(old_header->data_, caller); + free(old_header->data_); } } return nptr; } -void *ob_memalign_hook(size_t alignment, size_t size, const void *) +OBMALLOC_EXPORT +void OBMALLOC_NOTHROW * +OBMALLOC_ATTR(malloc) +memalign(size_t alignment, size_t size) { void *ptr = nullptr; - if (OB_LIKELY(size > 0)) { - // Make sure alignment is power of 2 - { - size_t a = 8; - while (a < alignment) - a <<= 1; - alignment = a; - } - size_t real_size = 2 * MAX(alignment, Header::SIZE) + size; - void *tmp_ptr = nullptr; - bool from_mmap = false; - if (OB_UNLIKELY(!g_malloc_hook_inited || in_hook())) { - if (MAP_FAILED == (tmp_ptr = ob_mmap(nullptr, real_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) { - tmp_ptr = nullptr; - } - from_mmap = true; - } else { - bool in_hook_bak = in_hook(); - in_hook()= true; - DEFER(in_hook()= in_hook_bak); - tmp_ptr = ob_malloc_retry(real_size); - } - if (OB_LIKELY(tmp_ptr != nullptr)) { - char *start = (char *)tmp_ptr + Header::SIZE; - char *align_ptr = (char *)up_align(reinterpret_cast(start), alignment); - char *pheader = align_ptr - Header::SIZE; - size_t offset = pheader - (char*)tmp_ptr; - abort_unless(size <= INT32_MAX); - auto *header = new (pheader) Header((int32_t)size, from_mmap); - header->offset_ = (uint32_t)offset; - ptr = header->data_; + // avoid alignment overflow + abort_unless(alignment <= UINT32_MAX / 2); + // Make sure alignment is power of 2 + { + size_t a = 8; + while (a < alignment) + a <<= 1; + alignment = a; + } + abort_unless(size <= UINT32_MAX - 2 * MAX(alignment, Header::SIZE)); + size_t real_size = 2 * MAX(alignment, Header::SIZE) + size; + void *tmp_ptr = nullptr; + bool from_mmap = false; + if (OB_UNLIKELY(!g_malloc_hook_inited || in_hook())) { + if (MAP_FAILED == (tmp_ptr = ob_mmap(nullptr, real_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) { + tmp_ptr = nullptr; } + from_mmap = true; + } else { + bool in_hook_bak = in_hook(); + in_hook()= true; + DEFER(in_hook()= in_hook_bak); + tmp_ptr = ob_malloc_retry(real_size); + } + if (OB_LIKELY(tmp_ptr != nullptr)) { + char *start = (char *)tmp_ptr + Header::SIZE; + char *align_ptr = (char *)up_align(reinterpret_cast(start), alignment); + char *pheader = align_ptr - Header::SIZE; + size_t offset = pheader - (char*)tmp_ptr; + Header *header = new (pheader) Header((uint32_t)size, from_mmap); + header->offset_ = (uint32_t)offset; + ptr = header->data_; } return ptr; } -EXTERN_C_BEGIN - void *ob_mmap_hook(void *addr, size_t length, int prot, int flags, int fd, loff_t offset) { return ob_mmap(addr, length, prot, flags, fd, offset); @@ -220,39 +237,25 @@ int ob_munmap_hook(void *addr, size_t length) return ob_munmap(addr, length); } -EXTERN_C_END - -#if !defined(__MALLOC_HOOK_VOLATILE) -#define MALLOC_HOOK_MAYBE_VOLATILE /**/ -#else -#define MALLOC_HOOK_MAYBE_VOLATILE __MALLOC_HOOK_VOLATILE -#endif - -EXTERN_C_BEGIN - -__attribute__((visibility("default"))) void *(*MALLOC_HOOK_MAYBE_VOLATILE __malloc_hook)(size_t, const void *) = ob_malloc_hook; -__attribute__((visibility("default"))) void (*MALLOC_HOOK_MAYBE_VOLATILE __free_hook)(void *, const void *) = ob_free_hook; -__attribute__((visibility("default"))) void *(*MALLOC_HOOK_MAYBE_VOLATILE __realloc_hook)(void *, size_t, const void *) = ob_realloc_hook; -__attribute__((visibility("default"))) void *(*MALLOC_HOOK_MAYBE_VOLATILE __memalign_hook)(size_t, size_t, const void *) = ob_memalign_hook; - __attribute__((visibility("default"))) void *mmap(void *addr, size_t, int, int, int, loff_t) __attribute__((weak,alias("ob_mmap_hook"))); __attribute__((visibility("default"))) void *mmap64(void *addr, size_t, int, int, int, loff_t) __attribute__((weak,alias("ob_mmap_hook"))); __attribute__((visibility("default"))) int munmap(void *addr, size_t length) __attribute__((weak,alias("ob_munmap_hook"))); -size_t malloc_usable_size(void *ptr) +OBMALLOC_EXPORT size_t OBMALLOC_NOTHROW +malloc_usable_size(void *ptr) { size_t ret = 0; - static const bool malloc_hook_disabled = glibc_prereq(2, 34); - if (malloc_hook_disabled) { - static int (*real_malloc_usable_size)(void *ptr) = - (typeof(real_malloc_usable_size))dlsym(RTLD_NEXT, "malloc_usable_size"); - ret = real_malloc_usable_size(ptr); - } else if (OB_LIKELY(ptr != nullptr)) { - auto *header = Header::ptr2header(ptr); + if (OB_LIKELY(nullptr != ptr)) { + Header *header = Header::ptr2header(ptr); abort_unless(header->check_magic_code()); ret = header->data_size_; } return ret; } +void *__libc_malloc(size_t size) LIBC_ALIAS(malloc); +void *__libc_realloc(void* ptr, size_t size) LIBC_ALIAS(realloc); +void __libc_free(void* ptr) LIBC_ALIAS(free); +void *__libc_memalign(size_t align, size_t s) LIBC_ALIAS(memalign); + EXTERN_C_END diff --git a/deps/oblib/src/lib/alloc/malloc_hook_extended.cpp b/deps/oblib/src/lib/alloc/malloc_hook_extended.cpp new file mode 100644 index 000000000..57d8258a4 --- /dev/null +++ b/deps/oblib/src/lib/alloc/malloc_hook_extended.cpp @@ -0,0 +1,280 @@ +/** + * Copyright (c) 2024 OceanBase + * OceanBase CE is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan PubL v2. + * You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PubL v2 for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LIKELY(x) __builtin_expect(!!(x),!!1) +#define UNLIKELY(x) __builtin_expect(!!(x),!!0) +#define MALLOC_ATTR(s) __attribute__((s)) +#define MALLOC_EXPORT __attribute__((visibility("default"))) +#define MALLOC_ALLOC_SIZE(s) __attribute__((alloc_size(s))) +#define MALLOC_ALLOC_SIZE2(s1, s2) __attribute__((alloc_size(s1, s2))) +#define MALLOC_NOTHROW __attribute__((nothrow)) +#define powerof2(x) ((((x) - 1) & (x)) == 0) +#define LIBC_ALIAS(fn) __attribute__((alias (#fn), used)) + +static size_t get_page_size() +{ + static size_t ps = getpagesize(); + return ps; +} + +void get_glibc_version(int &major, int &minor) +{ + major = 0; + minor = 0; + const char *glibc_version = gnu_get_libc_version(); + if (NULL != glibc_version) { + sscanf(glibc_version, "%d.%d", &major, &minor); + } +} + +bool glibc_prereq(int major, int minor) +{ + int cur_major = 0; + int cur_minor = 0; + get_glibc_version(cur_major, cur_minor); + return (cur_major > major) || (cur_major == major && cur_minor >= minor); +} + +extern "C" { + +extern void *malloc(size_t size); +extern void free(void *ptr); +extern void *realloc(void *ptr, size_t size); +extern void *memalign(size_t alignment, size_t size); + +MALLOC_EXPORT +void MALLOC_NOTHROW * +MALLOC_ATTR(malloc) MALLOC_ALLOC_SIZE2(1, 2) +calloc(size_t nmemb, size_t size) +{ + size_t real_size; + if (UNLIKELY(__builtin_mul_overflow(nmemb, size, &real_size))) { + abort(); + } + void *ptr = malloc(real_size); + if (LIKELY(nullptr != ptr && real_size > 0)) { + memset(ptr, 0, real_size); + } + return ptr; +} + +MALLOC_EXPORT void MALLOC_NOTHROW +cfree(void* ptr) +{ + free(ptr); +} + +MALLOC_EXPORT void MALLOC_NOTHROW +free_sized(void* ptr, size_t size) +{ + free(ptr); +} + +MALLOC_EXPORT void MALLOC_NOTHROW +free_aligned_sized(void* ptr, size_t alignment, size_t size) +{ + free(ptr); +} + +MALLOC_EXPORT +void MALLOC_NOTHROW * +MALLOC_ATTR(malloc) MALLOC_ALLOC_SIZE(2) +aligned_alloc(size_t alignment, size_t size) +{ + // glibc-2.38 and above adopt ISO 17 standard aligned_alloc + static const bool newstd = glibc_prereq(2, 38); + if (newstd) { + if (!powerof2 (alignment) || alignment == 0) { + errno = EINVAL; + return nullptr; + } + } + return memalign(alignment, size); +} + +MALLOC_EXPORT int MALLOC_NOTHROW +MALLOC_ATTR(nonnull(1)) +posix_memalign(void** memptr, size_t alignment, size_t size) +{ + int err = 0; + if (0 != alignment % sizeof (void *) + || 0 != !powerof2 (alignment / sizeof (void *)) + || 0 == alignment + || nullptr == memptr) { + return EINVAL; + } + + *memptr = nullptr; + void *ptr = memalign(alignment, size); + if (nullptr == ptr) { + err = ENOMEM; + } else { + *memptr = ptr; + } + return err; +} + +MALLOC_EXPORT +void MALLOC_NOTHROW * +MALLOC_ATTR(malloc) +valloc(size_t size) +{ + return memalign(get_page_size(), size); +} + +MALLOC_EXPORT +void MALLOC_NOTHROW * +MALLOC_ATTR(malloc) +pvalloc(size_t size) +{ + const size_t pagesize = get_page_size(); + size_t page_mask = pagesize - 1; + size_t rounded_bytes; + if (UNLIKELY(__builtin_add_overflow(size, page_mask, &rounded_bytes))) { + abort(); + } + rounded_bytes = rounded_bytes & ~(page_mask); + return memalign(pagesize, rounded_bytes); +} + +void *__libc_calloc(size_t n, size_t size) LIBC_ALIAS(calloc); +void __libc_free_sized(void* ptr, size_t size) LIBC_ALIAS(free_sized); +void __libc_free_aligned_sized(void* ptr, size_t alignment, size_t size) LIBC_ALIAS(free_aligned_sized); +void *__libc_valloc(size_t size) LIBC_ALIAS(valloc); +void *__libc_pvalloc(size_t size) LIBC_ALIAS(pvalloc); +int __posix_memalign(void** r, size_t a, size_t s) LIBC_ALIAS(posix_memalign); + +} // extern "C" end + +void *operator new(std::size_t size) +{ + void *ptr = malloc(size); + if (UNLIKELY(nullptr == ptr)) { + throw std::bad_alloc(); + } + return ptr; +} + +void *operator new[](std::size_t size) +{ + void *ptr = malloc(size); + if (UNLIKELY(nullptr == ptr)) { + throw std::bad_alloc(); + } + return ptr; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept { + return malloc(size); +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept { + return malloc(size); +} + +void operator delete(void *ptr) noexcept +{ + free(ptr); +} + +void operator delete[](void *ptr) noexcept +{ + free(ptr); +} + +void operator delete(void *ptr, const std::nothrow_t &) noexcept +{ + free(ptr); +} + +void operator delete[](void *ptr, const std::nothrow_t &) noexcept +{ + free(ptr); +} + +#if __cpp_sized_deallocation >= 201309 +// C++14 sized-delete operators +void operator delete(void *ptr, std::size_t size) noexcept +{ + free(ptr); +} + +void operator delete[](void *ptr, std::size_t size) noexcept +{ + free(ptr); +} +#endif + +#if __cpp_aligned_new >= 201606 +// C++17 aligned operators +void *operator new(std::size_t size, std::align_val_t alignment) { + void *ptr = memalign(static_cast(alignment), size); + if (UNLIKELY(nullptr == ptr)) { + throw std::bad_alloc(); + } + return ptr; +} + +void *operator new[](std::size_t size, std::align_val_t alignment) { + void *ptr = memalign(static_cast(alignment), size); + if (UNLIKELY(nullptr == ptr)) { + throw std::bad_alloc(); + } + return ptr; +} + +void *operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept { + return memalign(static_cast(alignment), size); +} + +void *operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept { + return memalign(static_cast(alignment), size); +} + +void operator delete(void* ptr, std::align_val_t) noexcept +{ + free(ptr); +} + +void operator delete(void* ptr, std::align_val_t, const std::nothrow_t &) noexcept +{ + free(ptr); +} + +void operator delete(void* ptr, std::size_t size, std::align_val_t al) noexcept +{ + free(ptr); +} + +void operator delete[](void* ptr, std::align_val_t) noexcept +{ + free(ptr); +} + +void operator delete[](void* ptr, std::align_val_t, const std::nothrow_t &) noexcept +{ + free(ptr); +} + +void operator delete[](void* ptr, std::size_t size, std::align_val_t al) noexcept +{ + free(ptr); +} +#endif diff --git a/deps/oblib/unittest/CMakeLists.txt b/deps/oblib/unittest/CMakeLists.txt index 25e018604..fb43b5f0e 100644 --- a/deps/oblib/unittest/CMakeLists.txt +++ b/deps/oblib/unittest/CMakeLists.txt @@ -6,9 +6,9 @@ enable_testing() function(oblib_addtest mainfile) get_filename_component(testname ${mainfile} NAME_WE) - add_executable(${testname} ${ARGV}) + add_executable(${testname} ${mainfile}) add_test(${testname} ${testname}) - target_link_libraries(${testname} PRIVATE oblib oblib_testbase -static-libgcc -static-libstdc++ + target_link_libraries(${testname} PRIVATE oblib oblib_testbase -static-libgcc -static-libstdc++ ${ARGN} ${OB_RELRO_FLAG} -Wl,-T,${CMAKE_SOURCE_DIR}/rpm/ld.lds) endfunction() diff --git a/deps/oblib/unittest/lib/CMakeLists.txt b/deps/oblib/unittest/lib/CMakeLists.txt index 90b2be584..eb577e0b0 100644 --- a/deps/oblib/unittest/lib/CMakeLists.txt +++ b/deps/oblib/unittest/lib/CMakeLists.txt @@ -10,7 +10,7 @@ oblib_addtest(parquet/test_parquet.cpp) oblib_addtest(alloc/test_alloc_struct.cpp) oblib_addtest(alloc/test_block_set.cpp) oblib_addtest(alloc/test_chunk_mgr.cpp) -oblib_addtest(alloc/test_malloc_hook.cpp) +oblib_addtest(alloc/test_malloc_hook.cpp malloc_hook) oblib_addtest(alloc/test_malloc_allocator.cpp) oblib_addtest(alloc/test_malloc_allocator_new.cpp) oblib_addtest(alloc/test_object_mgr.cpp) diff --git a/deps/oblib/unittest/lib/alloc/test_malloc_hook.cpp b/deps/oblib/unittest/lib/alloc/test_malloc_hook.cpp index 4b9fbe08c..988c80619 100644 --- a/deps/oblib/unittest/lib/alloc/test_malloc_hook.cpp +++ b/deps/oblib/unittest/lib/alloc/test_malloc_hook.cpp @@ -21,6 +21,7 @@ using namespace oceanbase::common; int main(int argc, char *argv[]) { + init_malloc_hook(); oceanbase::common::ObLogger::get_logger().set_log_level("INFO"); OB_LOGGER.set_log_level("INFO"); ::testing::InitGoogleTest(&argc, argv); @@ -40,141 +41,199 @@ public: } }; +struct Header +{ + static const uint32_t MAGIC_CODE = 0XA1B2C3D1; + static const uint32_t SIZE; + Header(int32_t size, bool from_mmap) + : magic_code_(MAGIC_CODE), + data_size_(size), + offset_(0), + from_mmap_(from_mmap) + {} + bool check_magic_code() const { return MAGIC_CODE == magic_code_; } + void mark_unused() { magic_code_ &= ~0x1; } + static Header *ptr2header(void *ptr) { return reinterpret_cast((char*)ptr - SIZE); } + uint32_t magic_code_; + int32_t data_size_; + uint32_t offset_; + uint8_t from_mmap_; + char padding_[3]; + char data_[0]; +} __attribute__((aligned (16))); -#if 0 TEST_F(TestMallocHook, Basic) { const static size_t size = 100; - for (int i = 0; i < 3; i++) { - if (0 == i) { - lib::glibc_hook_opt = GHO_NOHOOK; - } else if (1 == i) { - lib::glibc_hook_opt = GHO_HOOK; - } else { - lib::glibc_hook_opt = GHO_NONULL; - } - bool from_glibc = 0 == i; - void *ptr = nullptr; + void *ptr = nullptr; + int ret = 0; - // calloc - char zero_buf[100]; - memset(zero_buf, 0 , 100); - ptr = calloc(10, 10); - ASSERT_NE(ptr, nullptr); - memset(ptr, 0, size); - HookHeader *header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, 10 * 10); - // check 0 - ASSERT_EQ(0, memcmp(ptr, zero_buf, 100)); - free(ptr); + // calloc + char zero_buf[100]; + memset(zero_buf, 0 , 100); + ptr = calloc(10, 10); + ASSERT_NE(ptr, nullptr); + memset(ptr, 0, size); + Header *header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, 10 * 10); + // check 0 + ASSERT_EQ(0, memcmp(ptr, zero_buf, 100)); + free(ptr); - // malloc - ptr = malloc(size); - ASSERT_NE(ptr, nullptr); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); - char buf[size]; - memset(buf, 'a', size); - memcpy(ptr, buf, size); + // malloc + ptr = malloc(size); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); + char buf[size]; + memset(buf, 'a', size); + memcpy(ptr, buf, size); - // realloc - void *new_ptr = realloc(ptr, size * 2); - ASSERT_NE(new_ptr, nullptr); - header = reinterpret_cast((char*)new_ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size * 2); - ASSERT_EQ(0, memcmp(new_ptr, buf, size)); - free(new_ptr); + // realloc + void *new_ptr = realloc(ptr, size * 2); + ASSERT_NE(new_ptr, nullptr); + header = Header::ptr2header(new_ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size * 2); + ASSERT_EQ(0, memcmp(new_ptr, buf, size)); + free(new_ptr); - // realloc(nullptr, size > 0); - ptr = realloc(nullptr, size); - ASSERT_NE(ptr, nullptr); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); + // realloc(nullptr, size > 0); + ptr = realloc(nullptr, size); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); - // realloc(size zoom in && out); - ptr = malloc(size); - ASSERT_NE(ptr, nullptr); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); - // zoom in - ptr = realloc(ptr, size * 2); - ASSERT_NE(ptr, nullptr); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size * 2); - // zoom out - ptr = realloc(ptr, size); - ASSERT_NE(ptr, nullptr); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); + // realloc(size zoom in && out); + ptr = malloc(size); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); + // zoom in + ptr = realloc(ptr, size * 2); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size * 2); + // zoom out + ptr = realloc(ptr, size); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); - // memalign - size_t alignment = 4L << 10; - ptr = memalign(alignment, size); - ASSERT_NE(ptr, nullptr); - ASSERT_EQ(0, (size_t)ptr & (alignment - 1)); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); - free(ptr); + // memalign + size_t alignment = 4L << 10; + ptr = memalign(alignment, size); + ASSERT_NE(ptr, nullptr); + ASSERT_EQ(0, (size_t)ptr & (alignment - 1)); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); + free(ptr); - // valloc && realloc - ptr = valloc(size); - ASSERT_NE(ptr, nullptr); - ASSERT_EQ(0, (size_t)ptr & (sysconf(_SC_PAGESIZE) - 1)); - header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size); - new_ptr = realloc(ptr, size * 2); - header = reinterpret_cast((char*)new_ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - ASSERT_EQ(header->data_size_, size * 2); + // valloc && realloc + ptr = valloc(size); + ASSERT_NE(ptr, nullptr); + ASSERT_EQ(0, (size_t)ptr & (sysconf(_SC_PAGESIZE) - 1)); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size); + new_ptr = realloc(ptr, size * 2); + header = Header::ptr2header(new_ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size * 2); + free(new_ptr); - // posix_memalign - posix_memalign(&ptr, 32, size); - ASSERT_NE(ptr, nullptr); - free(ptr); - } + // posix_memalign + ret = posix_memalign(&ptr, alignment, size); + ASSERT_EQ(0, ret); + ASSERT_NE(ptr, nullptr); + ASSERT_EQ(0, (size_t)ptr & (alignment - 1)); + free(ptr); + + // free_aligned_sized + ptr = aligned_alloc(alignment, size); + ASSERT_NE(ptr, nullptr); + ASSERT_EQ(0, (size_t)ptr & (alignment - 1)); + free(ptr); + + // pvalloc + ptr = pvalloc(size); + ASSERT_NE(ptr, nullptr); + ASSERT_EQ(0, (size_t)ptr & (sysconf(_SC_PAGESIZE) - 1)); + header = Header::ptr2header(ptr); + ASSERT_EQ(header->data_size_, sysconf(_SC_PAGESIZE)); + free(ptr); // realloc && cross - for (int i = 0; i < 2; i++) { - lib::glibc_hook_opt = 0 == i ? GHO_NOHOOK : GHO_HOOK; - bool from_glibc = 0 == i; - void *ptr = malloc(size); - ASSERT_NE(ptr, nullptr); - HookHeader *header = reinterpret_cast((char*)ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == from_glibc); - char buf[size]; - memset(buf, 'a', size); - memcpy(ptr, buf, size); - lib::glibc_hook_opt = 0 == i ? GHO_HOOK : GHO_NOHOOK; - void *new_ptr = realloc(ptr, size * 2); - ASSERT_NE(new_ptr, nullptr); - header = reinterpret_cast((char*)new_ptr - HOOK_HEADER_SIZE); - ASSERT_TRUE(header->from_glibc_ == !from_glibc); - ASSERT_EQ(header->data_size_, size * 2); - ASSERT_EQ(0, memcmp(new_ptr, buf, size)); - lib::glibc_hook_opt = 0 == i ? GHO_NOHOOK : GHO_HOOK; - void *ret = realloc(new_ptr, 0); - ASSERT_EQ(ret, nullptr); - } + ptr = malloc(size); + ASSERT_NE(ptr, nullptr); + header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + memset(buf, 'a', size); + memcpy(ptr, buf, size); + new_ptr = realloc(ptr, size * 2); + ASSERT_NE(new_ptr, nullptr); + header = Header::ptr2header(new_ptr); + ASSERT_TRUE(header->check_magic_code()); + ASSERT_EQ(header->data_size_, size * 2); + ASSERT_EQ(0, memcmp(new_ptr, buf, size)); + free(new_ptr); +} +TEST_F(TestMallocHook, test_strdup) +{ // strdup - char *str = strdup("test"); - HookHeader *header = reinterpret_cast((char*)str - HOOK_HEADER_SIZE); - ASSERT_EQ(0, strcmp(str, "test")); - ASSERT_EQ(header->MAGIC_CODE_, HOOK_MAGIC_CODE); -#ifdef NDEBUG - memset(header, 0 , sizeof(HookHeader)); - // Avoid order rearrangement - std::cout << str << std::endl; -#endif + const char *original = "test"; + char *str = strdup(original); + ASSERT_NE(str, nullptr); + Header *header = Header::ptr2header(str); + ASSERT_EQ(0, strcmp(str, original)); + ASSERT_TRUE(header->check_magic_code()); + free(str); + + // strndup + size_t n = 3; + str = strndup(original, n); + ASSERT_NE(str, nullptr); + header = Header::ptr2header(str); + ASSERT_EQ(0, strncmp(str, original, n)); + ASSERT_TRUE(header->check_magic_code()); free(str); } -#endif + +TEST_F(TestMallocHook, test_malloc_usable_size) +{ + const static size_t size = 100; + void *ptr = malloc(size); + ASSERT_NE(ptr, nullptr); + Header *header = Header::ptr2header(ptr); + ASSERT_TRUE(header->check_magic_code()); + size_t ptr_size = malloc_usable_size(ptr); + ASSERT_EQ(size, ptr_size); + free(ptr); +} + +TEST_F(TestMallocHook, test_cpp_operator) +{ + // new && delete + int *ptr = new int; + ASSERT_NE(ptr, nullptr); + Header *header = Header::ptr2header((void *)ptr); + ASSERT_TRUE(header->check_magic_code()); + delete ptr; + + // new[] && delete[] + size_t n = 10; + int *ptr_array = new int[n]; + ASSERT_NE(ptr_array, nullptr); + header = Header::ptr2header((void *)ptr_array); + ASSERT_TRUE(header->check_magic_code()); + ptr_array[n - 1] = 10; + delete[] ptr_array; +}