diff --git a/.gitignore b/.gitignore index ada4031..325ccd4 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,6 @@ /min_per_thread_cache_size_test /min_per_thread_cache_size_test.exe /missing -/mmap_hook_test /packed_cache_test /packed_cache_test.exe /page_heap_test diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b2fb9a..ef89236 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -474,7 +474,6 @@ if(BUILD_TESTING) add_executable(low_level_alloc_unittest src/base/low_level_alloc.cc src/malloc_hook.cc - src/mmap_hook.cc src/tests/low_level_alloc_unittest.cc) target_compile_definitions(low_level_alloc_unittest PRIVATE NO_TCMALLOC_SAMPLES PERFTOOLS_DLL_DECL= ) target_link_libraries(low_level_alloc_unittest common gtest) @@ -601,17 +600,6 @@ if(BUILD_TESTING) target_link_libraries(malloc_hook_test common gtest) add_test(malloc_hook_test malloc_hook_test) - if(GPERFTOOLS_BUILD_HEAP_PROFILER) - if(NOT MINGW AND NOT MSVC) - add_executable(mmap_hook_test - src/tests/mmap_hook_test.cc - src/mmap_hook.cc - src/malloc_backtrace.cc) - target_link_libraries(mmap_hook_test stacktrace common gtest) - add_test(mmap_hook_test mmap_hook_test) - endif() - endif() - add_executable(malloc_extension_test src/tests/malloc_extension_test.cc) target_link_libraries(malloc_extension_test tcmalloc_minimal gtest) add_test(malloc_extension_test malloc_extension_test) @@ -760,7 +748,6 @@ if(GPERFTOOLS_BUILD_HEAP_PROFILER) src/heap-profiler.cc ${EMERGENCY_MALLOC_CC} src/malloc_backtrace.cc - src/mmap_hook.cc src/heap-checker-stub.cc) add_library(tcmalloc ${TCMALLOC_CC} ${FULL_MALLOC_SRC}) target_compile_definitions(tcmalloc PRIVATE ${EMERGENCY_MALLOC_DEFINE}) diff --git a/Makefile.am b/Makefile.am index 16bc70f..933a56d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -188,7 +188,6 @@ if WITH_HEAP_PROFILER_OR_CHECKER TESTS += low_level_alloc_unittest low_level_alloc_unittest_SOURCES = src/base/low_level_alloc.cc \ src/malloc_hook.cc \ - src/mmap_hook.cc \ src/tests/low_level_alloc_unittest.cc # By default, MallocHook takes stack traces for use by the heap-checker. # We don't need that functionality here, so we turn it off to reduce deps. @@ -359,17 +358,6 @@ malloc_hook_test_CXXFLAGS = -DNO_TCMALLOC_SAMPLES $(AM_CXXFLAGS) malloc_hook_test_CPPFLAGS = $(gtest_CPPFLAGS) malloc_hook_test_LDADD = libcommon.la libgtest.la -if WITH_HEAP_PROFILER_OR_CHECKER -if !MINGW -TESTS += mmap_hook_test -mmap_hook_test_SOURCES = src/tests/mmap_hook_test.cc \ - src/mmap_hook.cc \ - src/malloc_backtrace.cc -mmap_hook_test_CPPFLAGS = $(gtest_CPPFLAGS) -mmap_hook_test_LDADD = libstacktrace.la libcommon.la libgtest.la -endif !MINGW -endif WITH_HEAP_PROFILER_OR_CHECKER - TESTS += sampler_test sampler_test_SOURCES = src/tests/sampler_test.cc \ src/sampler.cc @@ -638,7 +626,6 @@ FULL_MALLOC_SRC = $(MINIMAL_MALLOC_SRC) \ src/heap-profiler.cc \ $(EMERGENCY_MALLOC_CC) \ src/malloc_backtrace.cc \ - src/mmap_hook.cc \ src/heap-checker-stub.cc lib_LTLIBRARIES += libtcmalloc.la diff --git a/src/base/low_level_alloc.cc b/src/base/low_level_alloc.cc index 2ba7a93..90553e9 100644 --- a/src/base/low_level_alloc.cc +++ b/src/base/low_level_alloc.cc @@ -33,6 +33,7 @@ // modules without introducing dependency cycles. // This allocator is slow and wasteful of memory; // it should not be used when performance is key. +#include "config.h" #include "base/low_level_alloc.h" @@ -41,10 +42,11 @@ #include "base/spinlock.h" #include "base/static_storage.h" +// FIXME (need hooks?) #include "malloc_hook-inl.h" #include -#include "mmap_hook.h" +#include "memmap.h" // A first-fit allocator with amortized logarithmic free() time. @@ -450,7 +452,7 @@ LowLevelAlloc::PagesAllocator *LowLevelAlloc::GetDefaultPagesAllocator(void) { } void *DefaultPagesAllocator::MapPages(size_t size) { - auto result = tcmalloc::DirectAnonMMap(true, size); + auto result = tcmalloc::MapAnonymous(size); RAW_CHECK(result.success, "mmap error"); @@ -458,7 +460,7 @@ void *DefaultPagesAllocator::MapPages(size_t size) { } void DefaultPagesAllocator::UnMapPages(void *region, size_t size) { - int munmap_result = tcmalloc::DirectMUnMap(true, region, size); + int munmap_result = munmap(region, size); RAW_CHECK(munmap_result == 0, "LowLevelAlloc::DeleteArena: munmap failed address"); } diff --git a/src/emergency_malloc.cc b/src/emergency_malloc.cc index f157800..6df967e 100644 --- a/src/emergency_malloc.cc +++ b/src/emergency_malloc.cc @@ -44,7 +44,7 @@ #include "base/spinlock.h" #include "base/static_storage.h" #include "internal_logging.h" -#include "mmap_hook.h" +#include "memmap.h" #include "thread_cache_ptr.h" namespace tcmalloc { @@ -73,7 +73,7 @@ class EmergencyArenaPagesAllocator : public LowLevelAlloc::PagesAllocator { }; static void InitEmergencyMalloc(void) { - auto [arena, success] = DirectAnonMMap(false, kEmergencyArenaSize * 2); + auto [arena, success] = MapAnonymous(kEmergencyArenaSize * 2); CHECK_CONDITION(success); uintptr_t arena_ptr = reinterpret_cast(arena); @@ -91,12 +91,15 @@ static void InitEmergencyMalloc(void) { uintptr_t head_unmap_size = ptr - arena_ptr; CHECK_CONDITION(head_unmap_size < kEmergencyArenaSize); if (head_unmap_size != 0) { - DirectMUnMap(false, arena, ptr - arena_ptr); + // Note, yes, we ignore any potential, but ~impossible + // failures. It should be harmless. + (void)munmap(arena, ptr - arena_ptr); } uintptr_t tail_unmap_size = kEmergencyArenaSize - head_unmap_size; void *tail_start = reinterpret_cast(arena_ptr + head_unmap_size + kEmergencyArenaSize); - DirectMUnMap(false, tail_start, tail_unmap_size); + // Failures are ignored. See above. + (void)munmap(tail_start, tail_unmap_size); } ATTRIBUTE_HIDDEN void *EmergencyMalloc(size_t size) { diff --git a/src/heap-profile-table.cc b/src/heap-profile-table.cc index a849636..557d214 100644 --- a/src/heap-profile-table.cc +++ b/src/heap-profile-table.cc @@ -65,7 +65,6 @@ #include "base/sysinfo.h" #include "gperftools/malloc_hook.h" #include "gperftools/stacktrace.h" -#include "memory_region_map.h" #include "symbolize.h" using std::sort; diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc index fb7c536..7151625 100644 --- a/src/heap-profiler.cc +++ b/src/heap-profiler.cc @@ -67,7 +67,6 @@ #include "base/low_level_alloc.h" #include "base/sysinfo.h" // for GetUniquePathFromEnv() #include "heap-profile-table.h" -#include "memory_region_map.h" #ifndef PATH_MAX #ifdef MAXPATHLEN diff --git a/src/malloc_hook.cc b/src/malloc_hook.cc index 8c27003..fdd9014 100644 --- a/src/malloc_hook.cc +++ b/src/malloc_hook.cc @@ -348,9 +348,6 @@ extern "C" int MallocHook_GetCallerStackTrace(void** result, int max_depth, // are no op and we keep them only because we have them exposed in // headers we ship. So keep them for somewhat formal ABI compat. // -// For non-public API for hooking mapping updates see -// mmap_hook.h - extern "C" int MallocHook_AddPreMmapHook(MallocHook_PreMmapHook hook) { return 0; diff --git a/src/memmap.h b/src/memmap.h new file mode 100644 index 0000000..1ef1adf --- /dev/null +++ b/src/memmap.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- + * Copyright (c) 2024, gperftools Contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef MEMMAP_H_ +#define MEMMAP_H_ + +#include "config.h" + +#ifdef WIN32 +// Windows has mmap bits defined in it's port.h header +#else +// Everything we assume is sufficiently POSIX-compatible. Also we +// assume ~everyone has MAP_ANONYMOUS or similar (POSIX, strangely, +// doesn't!) +#include +#include + +// Someone still cares about those near-obsolete OSes that fail to +// supply MAP_ANONYMOUS. +# ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +# endif + +#endif + +#include "base/basictypes.h" + +namespace tcmalloc { + +struct MMapResult { + void* addr; + bool success; +}; + +// MapAnonymous does mmap of r+w anonymous memory, simply saving us +// some hassle of spreading (not 100% portable) flags. +static inline MMapResult MapAnonymous(size_t length) { + MMapResult result; + result.addr = mmap(nullptr, length, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + result.success = (result.addr != MAP_FAILED); + return result; +} + +} // namespace tcmalloc + +#endif // MEMMAP_H_ diff --git a/src/mmap_hook.cc b/src/mmap_hook.cc deleted file mode 100644 index 139b01b..0000000 --- a/src/mmap_hook.cc +++ /dev/null @@ -1,495 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- - * Copyright (c) 2023, gperftools Contributors - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// We require "native" off_t for the system. So that we're able to -// interpose native libc's mmap/mmap64 correctly. Thus we "turn off" -// _FILE_OFFSET_BITS define even if user asked for 64-bit off_t. -#undef _FILE_OFFSET_BITS -// Also, glibc somewhat reasonably insists that _TIME_BITS=64 has to -// imply _FILE_OFFSET_BITS=64. So with us undef-inf _FILE_OFFSET_BITS, -// we sadly need to do the same for _TIME_BITS. Thankfully, none of -// the code in this file depends on time_t. Otherwise, we'd be risking -// trouble. -#undef _TIME_BITS - -#include - -#include "mmap_hook.h" - -#if HAVE_SYS_SYSCALL_H -#include -#endif - -#include -#include - -#include "base/logging.h" -#include "base/spinlock.h" -#include "malloc_backtrace.h" - -// Disable the glibc prototype of mremap(), as older versions of the -// system headers define this function with only four arguments, -// whereas newer versions allow an optional fifth argument: -#ifdef HAVE_MMAP -# define mremap glibc_mremap -# include -# ifndef MAP_ANONYMOUS -# define MAP_ANONYMOUS MAP_ANON -# endif -#include -# undef mremap -#endif - -// __THROW is defined in glibc systems. It means, counter-intuitively, -// "This function will never throw an exception." It's an optional -// optimization tool, but we may need to use it to match glibc prototypes. -#ifndef __THROW // I guess we're not on a glibc system -# define __THROW // __THROW is just an optimization, so ok to make it "" -#endif - -namespace tcmalloc { - -namespace { - -struct MappingHookDescriptor { - MappingHookDescriptor(MMapEventFn fn, - MMapEventNeedBacktraceFn need_backtrace) - : fn(fn), need_backtrace(need_backtrace) {} - - const MMapEventFn fn; - const MMapEventNeedBacktraceFn need_backtrace; - - std::atomic inactive{false}; - std::atomic next; -}; - -static_assert(sizeof(MappingHookDescriptor) == - (sizeof(MappingHookSpace) - offsetof(MappingHookSpace, storage)), ""); -static_assert(alignof(MappingHookDescriptor) == alignof(MappingHookSpace), ""); - -class MappingHooks { -public: - constexpr MappingHooks() {} - - static MappingHookDescriptor* SpaceToDesc(MappingHookSpace* space) { - return reinterpret_cast(space->storage); - } - - void Add(MappingHookSpace *space, MMapEventFn fn, MMapEventNeedBacktraceFn need_backtrace) { - MappingHookDescriptor* desc = SpaceToDesc(space); - if (space->initialized) { - desc->inactive.store(false); - return; - } - - space->initialized = true; - new (desc) MappingHookDescriptor(fn, need_backtrace); - - MappingHookDescriptor* next_candidate = list_head_.load(std::memory_order_relaxed); - do { - desc->next.store(next_candidate, std::memory_order_relaxed); - } while (!list_head_.compare_exchange_strong(next_candidate, desc)); - } - - void Remove(MappingHookSpace* space) { - RAW_CHECK(space->initialized, ""); - SpaceToDesc(space)->inactive.store(true); - } - - int PreInvokeAll(const MappingEvent& evt) { - int stack_depth = 0; - - std::atomic *place = &list_head_; - while (MappingHookDescriptor* desc = place->load(std::memory_order_acquire)) { - place = &desc->next; - if (!desc->inactive && desc->need_backtrace) { - int need = desc->need_backtrace(evt); - stack_depth = std::max(stack_depth, need); - } - } - - return stack_depth; - } - - void InvokeAll(const MappingEvent& evt) { - std::atomic *place = &list_head_; - while (MappingHookDescriptor* desc = place->load(std::memory_order_acquire)) { - place = &desc->next; - if (!desc->inactive) { - desc->fn(evt); - } - } - } - -private: - std::atomic list_head_{}; - -} mapping_hooks; - -} // namespace - -void HookMMapEvents(MappingHookSpace* place, MMapEventFn callback) { - mapping_hooks.Add(place, callback, nullptr); -} - -void HookMMapEventsWithBacktrace(MappingHookSpace* place, MMapEventFn callback, MMapEventNeedBacktraceFn need_backtrace) { - mapping_hooks.Add(place, callback, need_backtrace); -} - -void UnHookMMapEvents(MappingHookSpace* place) { - mapping_hooks.Remove(place); -} - -} // namespace tcmalloc - -#if defined(__linux__) && HAVE_SYS_SYSCALL_H -static void* do_sys_mmap(long sysnr, void* start, size_t length, int prot, int flags, int fd, long offset) { -#if defined(__s390__) - long args[6] = { - (long)start, (long)length, - (long)prot, (long)flags, (long)fd, (long)offset }; - return reinterpret_cast(syscall(sysnr, args)); -#else - return reinterpret_cast( - syscall(sysnr, reinterpret_cast(start), length, prot, flags, fd, offset)); -#endif -} - -static void* do_mmap(void* start, size_t length, int prot, int flags, int fd, int64_t offset) { -#ifdef SYS_mmap2 - static int pagesize = 0; - if (!pagesize) { - pagesize = getpagesize(); - } - if ((offset & (pagesize - 1))) { - errno = EINVAL; - return MAP_FAILED; - } - offset /= pagesize; - -#if !defined(_LP64) && !defined(__x86_64__) - // 32-bit and not x32 (which has "honest" 64-bit syscalls args) - uintptr_t truncated_offset = offset; - // This checks offset being too large for page number still not - // fitting into 32-bit pgoff argument. - if (static_cast(truncated_offset) != offset) { - errno = EINVAL; - return MAP_FAILED; - } -#else - int64_t truncated_offset = offset; -#endif - return do_sys_mmap(SYS_mmap2, start, length, prot, flags, fd, truncated_offset); -#else - - return do_sys_mmap(SYS_mmap, start, length, prot, flags, fd, offset); -#endif -} - -#define DEFINED_DO_MMAP - -#endif // __linux__ - -// Note, we're not risking syscall-ing mmap with 64-bit off_t on -// 32-bit on BSDs. -#if defined(__FreeBSD__) && defined(_LP64) && HAVE_SYS_SYSCALL_H -static void* do_mmap(void* start, size_t length, int prot, int flags, int fd, int64_t offset) { - // BSDs need __syscall to deal with 64-bit args - return reinterpret_cast(__syscall(SYS_mmap, start, length, prot, flags, fd, offset)); -} - -#define DEFINED_DO_MMAP -#endif // 64-bit FreeBSD - -namespace { - -struct BacktraceHelper { - static inline constexpr int kDepth = 32; - void* backtrace[kDepth]; - - int PreInvoke(tcmalloc::MappingEvent* evt) { - int want_stack = tcmalloc::mapping_hooks.PreInvokeAll(*evt); - if (want_stack) { - want_stack = std::min(want_stack, kDepth); - evt->stack = backtrace; - } - return want_stack; - } -}; - -} // namespace - -#ifdef DEFINED_DO_MMAP - -// Note, this code gets build by gcc or gcc-compatible compilers -// (e.g. clang). So we can rely on ALWAYS_INLINE to actually work even -// when built with -O0 -fno-inline. This matters because we're -// carefully controlling backtrace skipping count, so that mmap hook -// sees backtrace just "up-to" mmap call. -static ALWAYS_INLINE -void* do_mmap_with_hooks(void* start, size_t length, int prot, int flags, int fd, int64_t offset) { - void* result = do_mmap(start, length, prot, flags, fd, offset); - if (result == MAP_FAILED) { - return result; - } - - tcmalloc::MappingEvent evt; - evt.before_address = start; - evt.after_address = result; - evt.after_length = length; - evt.after_valid = 1; - evt.file_fd = fd; - evt.file_off = offset; - evt.file_valid = 1; - evt.flags = flags; - evt.prot = prot; - - BacktraceHelper helper; - int want_stack = helper.PreInvoke(&evt); - if (want_stack) { - evt.stack_depth = tcmalloc::GrabBacktrace(evt.stack, want_stack, 1); - } - - tcmalloc::mapping_hooks.InvokeAll(evt); - - return result; -} - -static int do_munmap(void* start, size_t length) { - return syscall(SYS_munmap, start, length); -} -#endif // DEFINED_DO_MMAP - - -// On systems where we know how, we override mmap/munmap/mremap/sbrk -// to provide support for calling the related hooks (in addition, -// of course, to doing what these functions normally do). - -// Some Linux libcs already have "future on" by default and ship with -// native 64-bit off_t-s. One example being musl. We cannot rule out -// glibc changing defaults in future, somehow, or people introducing -// more 32-bit systems with 64-bit off_t (x32 already being one). So -// we check for the case of 32-bit system that has wide off_t. -// -// Note, it would be nice to test some define that is available -// everywhere when off_t is 64-bit, but sadly stuff isn't always -// consistent. So we detect 32-bit system that doesn't have -// _POSIX_V7_ILP32_OFF32 set to 1, which looks less robust than we'd -// like. But from some tests and code inspection this check seems to -// cover glibc, musl, uclibc and bionic. -#if defined(__linux__) && (defined(_LP64) || (!defined(_POSIX_V7_ILP32_OFF32) || _POSIX_V7_ILP32_OFF32 < 0)) -#define GOOD_LINUX_SYSTEM 1 -#else -#define GOOD_LINUX_SYSTEM 0 -#endif - -#if defined(DEFINED_DO_MMAP) && (!defined(__linux__) || GOOD_LINUX_SYSTEM) -// Simple case for 64-bit kernels or 32-bit systems that have native -// 64-bit off_t. On all those systems there are no off_t complications -static_assert(sizeof(int64_t) == sizeof(off_t), ""); - -// We still export mmap64 just in case. Linux libcs tend to have it. But since off_t is 64-bit they're identical -// Also, we can safely assume gcc-like compiler and elf. - -#undef mmap64 -#undef mmap - -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE -void* mmap64(void* start, size_t length, int prot, int flags, int fd, off_t off) - __THROW; - -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE -void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t off) - __THROW; - -void* mmap64(void* start, size_t length, int prot, int flags, int fd, off_t off) __THROW { - return do_mmap_with_hooks(start, length, prot, flags, fd, off); -} -void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t off) __THROW { - return do_mmap_with_hooks(start, length, prot, flags, fd, off); -} - -#define HOOKED_MMAP - -#elif defined(DEFINED_DO_MMAP) && defined(__linux__) && !GOOD_LINUX_SYSTEM -// Linuxes with 32-bit off_t. We're being careful with mmap64 being -// 64-bit and mmap being 32-bit. - -static_assert(sizeof(int32_t) == sizeof(off_t), ""); - -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE -void* mmap64(void* start, size_t length, int prot, int flags, int fd, int64_t off) - __THROW; - -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE -void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t off) - __THROW; - -void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t off) __THROW { - return do_mmap_with_hooks(start, length, prot, flags, fd, off); -} - -void* mmap64(void *start, size_t length, int prot, int flags, int fd, int64_t off) __THROW { - return do_mmap_with_hooks(start, length, prot, flags, fd, off); -} - -#define HOOKED_MMAP - -#endif // Linux/32-bit off_t case - - -#ifdef HOOKED_MMAP - -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE int munmap(void* start, size_t length) __THROW; - -int munmap(void* start, size_t length) __THROW { - int result = tcmalloc::DirectMUnMap(/* invoke_hooks=*/ false, start, length); - if (result < 0) { - return result; - } - - tcmalloc::MappingEvent evt; - evt.before_address = start; - evt.before_length = length; - evt.before_valid = 1; - - BacktraceHelper helper; - int want_stack = helper.PreInvoke(&evt); - if (want_stack) { - evt.stack_depth = tcmalloc::GrabBacktrace(evt.stack, want_stack, 1); - } - - tcmalloc::mapping_hooks.InvokeAll(evt); - - return result; -} -#else // !HOOKED_MMAP -// No mmap/munmap interceptions. But we still provide (internal) DirectXYZ APIs. -#define do_mmap mmap -#define do_munmap munmap -#endif - -tcmalloc::DirectAnonMMapResult tcmalloc::DirectAnonMMap(bool invoke_hooks, size_t length) { - tcmalloc::DirectAnonMMapResult result; - if (invoke_hooks) { - result.addr = mmap(nullptr, length, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - } else { - result.addr = do_mmap(nullptr, length, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - } - result.success = (result.addr != MAP_FAILED); - return result; -} - -int tcmalloc::DirectMUnMap(bool invoke_hooks, void *start, size_t length) { - if (invoke_hooks) { - return munmap(start, length); - } - - return do_munmap(start, length); -} - -#if __linux__ -extern "C" PERFTOOLS_DLL_DECL ATTRIBUTE_NOINLINE -void* mremap(void* old_addr, size_t old_size, size_t new_size, - int flags, ...) __THROW; -// We only handle mremap on Linux so far. -void* mremap(void* old_addr, size_t old_size, size_t new_size, - int flags, ...) __THROW { - va_list ap; - va_start(ap, flags); - void *new_address = va_arg(ap, void *); - va_end(ap); - void* result = (void*)syscall(SYS_mremap, old_addr, old_size, new_size, flags, - new_address); - - if (result != MAP_FAILED) { - tcmalloc::MappingEvent evt; - evt.before_address = old_addr; - evt.before_length = old_size; - evt.before_valid = 1; - evt.after_address = result; - evt.after_length = new_size; - evt.after_valid = 1; - evt.flags = flags; - - BacktraceHelper helper; - int want_stack = helper.PreInvoke(&evt); - if (want_stack) { - evt.stack_depth = tcmalloc::GrabBacktrace(evt.stack, want_stack, 1); - } - - tcmalloc::mapping_hooks.InvokeAll(evt); - } - - return result; -} -#endif // __linux__ - -#if defined(HAVE_SBRK) -extern "C" ATTRIBUTE_VISIBILITY_HIDDEN ATTRIBUTE_NOINLINE -void* tcmalloc_hooked_sbrk(intptr_t increment) { - void *result = sbrk(increment); - if (increment == 0 || result == reinterpret_cast(intptr_t{-1})) { - return result; - } - - tcmalloc::MappingEvent evt; - evt.is_sbrk = 1; - if (increment > 0) { - evt.after_address = result; - evt.after_length = increment; - evt.after_valid = 1; - } else { - intptr_t res_addr = reinterpret_cast(result); - intptr_t new_brk = res_addr + increment; - evt.before_address = reinterpret_cast(new_brk); - evt.before_length = -increment; - evt.before_valid = 1; - } - - BacktraceHelper helper; - int want_stack = helper.PreInvoke(&evt); - if (want_stack) { - evt.stack_depth = tcmalloc::GrabBacktrace(evt.stack, want_stack, 1); - } - tcmalloc::mapping_hooks.InvokeAll(evt); - - return result; -} -#endif - -namespace tcmalloc { -#ifdef HOOKED_MMAP -const bool mmap_hook_works = true; -#else -const bool mmap_hook_works = false; -#endif -} // namespace tcmalloc diff --git a/src/mmap_hook.h b/src/mmap_hook.h deleted file mode 100644 index 546dc5b..0000000 --- a/src/mmap_hook.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- - * Copyright (c) 2023, gperftools Contributors - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// mmap_hook.h holds strictly non-public API for hooking mmap/sbrk -// events as well invoking mmap/munmap with ability to bypass hooks -// (i.e. for low_level_alloc). -#ifndef MMAP_HOOK_H -#define MMAP_HOOK_H - -#include -#include -#include - -#include "base/basictypes.h" - -namespace tcmalloc { - -struct DirectAnonMMapResult { - void* addr; - bool success; -}; - -// DirectAnonMMap does mmap of r+w anonymous memory. Optionally -// bypassing or not mmap hooks. -ATTRIBUTE_VISIBILITY_HIDDEN DirectAnonMMapResult DirectAnonMMap(bool invoke_hooks, size_t length); -// DirectMUnMap does munmap of given region optionally bypassing mmap hooks. -ATTRIBUTE_VISIBILITY_HIDDEN int DirectMUnMap(bool invoke_hooks, void* start, size_t length); - -// We use those by tests to see what parts we think should work. -extern ATTRIBUTE_VISIBILITY_HIDDEN const bool mmap_hook_works; -extern ATTRIBUTE_VISIBILITY_HIDDEN const bool sbrk_hook_works; - -// MMapEventFn gets this struct with all the details of -// mmap/munmap/mremap/sbrk event. -struct MappingEvent { - MappingEvent() { - memset(this, 0, sizeof(*this)); - } - - // before_XXX fields describe address space chunk that was removed - // from address space (say via munmap or mremap) - void* before_address; - size_t before_length; - - // after_XXX fields describe address space chunk that was added to - // address space. - void* after_address; - size_t after_length; - - // This group of fields gets populated from mmap file, flags, prot - // fields. - int prot; - int flags; - int file_fd; - int64_t file_off; - - unsigned after_valid:1; - unsigned before_valid:1; - unsigned file_valid:1; - unsigned is_sbrk:1; - - // NOTE, in order to get mapping event backtrace you need to request - // it via need_backtrace callback. - int stack_depth; - void** stack; -}; - -// Pass this to Hook/Unhook function below. Note, nature of -// implementation requires that this chunk of memory must be valid -// even after unhook. So typical use-case is to use global variable -// storage. -// -// All fields are private. -class MappingHookSpace { -public: - constexpr MappingHookSpace() = default; - - bool initialized = false; - - static constexpr size_t kSize = sizeof(void*) * 4; - alignas(alignof(void*)) char storage[kSize] = {}; -}; - -using MMapEventFn = void (*)(const MappingEvent& evt); -using MMapEventNeedBacktraceFn = int (*)(const MappingEvent& evt); - -// HookMMapEvents address hook for mmap events, using given place to -// store relevant metadata (linked list membership etc). -// -// It does no memory allocation and is safe to be called from hooks of all kinds. -ATTRIBUTE_VISIBILITY_HIDDEN void HookMMapEvents(MappingHookSpace* place, MMapEventFn callback); - -ATTRIBUTE_VISIBILITY_HIDDEN void HookMMapEventsWithBacktrace(MappingHookSpace* place, MMapEventFn callback, - MMapEventNeedBacktraceFn need_backtrace); - -// UnHookMMapEvents undoes effect of HookMMapEvents. This one is also -// entirely safe to be called from out of anywhere. Including from -// inside MMapEventFn invokations. -// -// As noted on MappingHookSpace the place ***must not** be deallocated or -// reused for anything even after unhook. This requirement makes -// implementation simple enough and fits our internal usage use-case -// fine. -ATTRIBUTE_VISIBILITY_HIDDEN void UnHookMMapEvents(MappingHookSpace* place); - -} // namespace tcmalloc - - -#endif // MMAP_HOOK_H diff --git a/src/system-alloc.cc b/src/system-alloc.cc index a4e8864..ee9b4f5 100644 --- a/src/system-alloc.cc +++ b/src/system-alloc.cc @@ -168,18 +168,6 @@ static tcmalloc::StaticStorage default_space; static const char sbrk_name[] = "SbrkSysAllocator"; static const char mmap_name[] = "MmapSysAllocator"; -#ifdef HAVE_SBRK -extern "C" { - // When we're building "full" tcmalloc with mmap_hook.cc linked-in, - // this definition gets overriden by definition in mmap_hook.cc - // which handles hooks which is required by heap checker. - ATTRIBUTE_VISIBILITY_HIDDEN ATTRIBUTE_WEAK - void* tcmalloc_hooked_sbrk(intptr_t increment) { - return sbrk(increment); - } -} -#endif - void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, size_t alignment) { #if !defined(HAVE_SBRK) || defined(__UCLIBC__) @@ -216,11 +204,11 @@ void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/sys/sbrk.c?a=true // http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/libc/misc/sbrk.c?rev=1.1.2.1&content-type=text/plain&cvsroot=glibc // Without this check, sbrk may succeed when it ought to fail.) - if (reinterpret_cast(tcmalloc_hooked_sbrk(0)) + size < size) { + if (reinterpret_cast(sbrk(0)) + size < size) { return nullptr; } - void* result = tcmalloc_hooked_sbrk(size); + void* result = sbrk(size); if (result == reinterpret_cast(-1)) { return nullptr; } @@ -231,7 +219,7 @@ void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, // Try to get more memory for alignment size_t extra = alignment - (ptr & (alignment-1)); - void* r2 = tcmalloc_hooked_sbrk(extra); + void* r2 = sbrk(extra); if (reinterpret_cast(r2) == (ptr + size)) { // Contiguous with previous result return reinterpret_cast(ptr + extra); @@ -239,7 +227,7 @@ void* SbrkSysAllocator::Alloc(size_t size, size_t *actual_size, // Give up and ask for "size + alignment - 1" bytes so // that we can find an aligned region within it. - result = tcmalloc_hooked_sbrk(size + alignment - 1); + result = sbrk(size + alignment - 1); if (result == reinterpret_cast(-1)) { return nullptr; } diff --git a/src/tests/mmap_hook_test.cc b/src/tests/mmap_hook_test.cc deleted file mode 100644 index 6b263d6..0000000 --- a/src/tests/mmap_hook_test.cc +++ /dev/null @@ -1,323 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- - * Copyright (c) 2023, gperftools Contributors - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#undef NDEBUG -#include "config.h" - -// When we end up running this on 32-bit glibc/uclibc/bionic system, -// lets ask for 64-bit off_t (only for stuff like lseek and ftruncate -// below). But ***only*** for this file. -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 -#endif - -#include "config_for_unittests.h" - -#include "mmap_hook.h" - -#include "base/function_ref.h" -#include "gperftools/stacktrace.h" - -#include "tests/testutil.h" - -#include -#include -#include - -#include "gtest/gtest.h" - -#ifdef HAVE_MMAP - -#include -#include -#include -#include -#include - -static_assert(sizeof(off_t) == sizeof(int64_t), ""); - -class MMapHookTest : public ::testing::Test { -public: - static void HandleMappingEvent(const tcmalloc::MappingEvent& evt) { - memcpy(&last_evt_, &evt, sizeof(evt)); - have_last_evt_ = true; - if (evt.stack_depth > 0) { - backtrace_address_ = evt.stack[0]; - } - } - - void SetUp() { - have_last_evt_ = false; - backtrace_address_ = nullptr; - } - - static void SetUpTestSuite() { - tcmalloc::HookMMapEventsWithBacktrace(&hook_space_, &HandleMappingEvent, - [] (const tcmalloc::MappingEvent& evt) { - return 1; - }); - } - static void TearDownTestSuite() { - tcmalloc::UnHookMMapEvents(&hook_space_); - } - -protected: - static inline tcmalloc::MappingEvent last_evt_; - static inline void* backtrace_address_; - static inline bool have_last_evt_; - static inline tcmalloc::MappingHookSpace hook_space_; -}; - -TEST_F(MMapHookTest, MMap) { - if (!tcmalloc::mmap_hook_works) { - puts("mmap test SKIPPED"); - return; - } - - FILE* f = tmpfile(); - ASSERT_NE(f, nullptr) << "errno: " << strerror(errno); - - int fd = fileno(f); - - ASSERT_GE(ftruncate(fd, off_t{1} << 40), 0) << "errno: " << strerror(errno); - - int pagesz = getpagesize(); - - off_t test_off = (off_t{1} << 40) - pagesz * 2; - ASSERT_EQ(lseek(fd, -pagesz * 2, SEEK_END), test_off) << "errno: " << strerror(errno); - - static constexpr char contents[] = "foobarXYZ"; - - ASSERT_EQ(write(fd, contents, sizeof(contents)), sizeof(contents)) << "errno: " << strerror(errno); - - have_last_evt_ = false; - char* mm_addr = static_cast(mmap(nullptr, pagesz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, test_off)); - ASSERT_EQ(memcmp(mm_addr, contents, sizeof(contents)), 0); - - ASSERT_TRUE(have_last_evt_ && !last_evt_.before_valid && last_evt_.after_valid && last_evt_.file_valid); - ASSERT_EQ(last_evt_.after_address, mm_addr); - ASSERT_EQ(last_evt_.after_length, pagesz); - ASSERT_EQ(last_evt_.file_fd, fd); - ASSERT_EQ(last_evt_.file_off, test_off); - ASSERT_EQ(last_evt_.flags, MAP_SHARED); - ASSERT_EQ(last_evt_.prot, (PROT_READ|PROT_WRITE)); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); - -#ifdef __linux__ - void* reserve = mmap(nullptr, pagesz * 2, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - ASSERT_NE(reserve, MAP_FAILED) << "errno: " << strerror(errno); - ASSERT_TRUE(have_last_evt_); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); - - char* new_addr = static_cast(mremap(mm_addr, pagesz, - pagesz * 2, MREMAP_MAYMOVE | MREMAP_FIXED, - reserve)); - ASSERT_NE(new_addr, MAP_FAILED); - ASSERT_EQ(new_addr, reserve); - ASSERT_TRUE(have_last_evt_); - - ASSERT_TRUE(!last_evt_.is_sbrk && last_evt_.after_valid && last_evt_.before_valid && !last_evt_.file_valid); - ASSERT_EQ(last_evt_.after_address, new_addr); - ASSERT_EQ(last_evt_.after_length, pagesz * 2); - ASSERT_EQ(last_evt_.before_address, mm_addr); - ASSERT_EQ(last_evt_.before_length, pagesz); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); - - ASSERT_EQ(pwrite(fd, contents, sizeof(contents), test_off + pagesz + 1), sizeof(contents)) << "errno: " << strerror(errno); - mm_addr = new_addr; - - ASSERT_EQ(memcmp(mm_addr + pagesz + 1, contents, sizeof(contents)), 0); - puts("mremap test PASS"); -#endif - - ASSERT_GE(munmap(mm_addr, pagesz), 0) << "errno: " << strerror(errno); - - ASSERT_TRUE(have_last_evt_ && !last_evt_.is_sbrk && !last_evt_.after_valid && last_evt_.before_valid && !last_evt_.file_valid); - ASSERT_EQ(last_evt_.before_address, mm_addr); - ASSERT_EQ(last_evt_.before_length, pagesz); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); - - size_t sz = 10 * pagesz; - auto result = tcmalloc::DirectAnonMMap(/* invoke_hooks = */false, sz); - ASSERT_TRUE(result.success) << "errno: " << strerror(errno); - ASSERT_NE(result.addr, MAP_FAILED); - ASSERT_FALSE(have_last_evt_); - - ASSERT_EQ(tcmalloc::DirectMUnMap(false, result.addr, sz), 0) << "errno: " << strerror(errno); - - sz = 13 * pagesz; - result = tcmalloc::DirectAnonMMap(/* invoke_hooks = */true, sz); - ASSERT_TRUE(result.success) << "errno: " << strerror(errno); - ASSERT_NE(result.addr, MAP_FAILED); - - ASSERT_TRUE(have_last_evt_ && !last_evt_.is_sbrk && !last_evt_.before_valid && last_evt_.after_valid); - ASSERT_EQ(last_evt_.after_address, result.addr); - ASSERT_EQ(last_evt_.after_length, sz); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); - - sz = sz - pagesz; // lets also check unmapping sub-segment of previously allocated one - ASSERT_EQ(tcmalloc::DirectMUnMap(true, result.addr, sz), 0) << "errno: " << strerror(errno); - ASSERT_TRUE(have_last_evt_ && !last_evt_.is_sbrk && last_evt_.before_valid && !last_evt_.after_valid); - ASSERT_EQ(last_evt_.before_address, result.addr); - ASSERT_EQ(last_evt_.before_length, sz); - - have_last_evt_ = false; - ASSERT_FALSE(HasFatalFailure()); -} - -TEST_F(MMapHookTest, MMapBacktrace) { - if (!tcmalloc::mmap_hook_works) { - puts("mmap backtrace test SKIPPED"); - return; - } - - using mmap_fn = void* (*)(void*, size_t, int, int, int, off_t); - - static void* expected_address; - - struct Helper { - // noinline ensures that all trampoline invocations will call fn - // with same return address (inside trampoline). We use that to - // test backtrace accuracy. - static ATTRIBUTE_NOINLINE - void trampoline(void** res, mmap_fn fn) { - *res = noopt(fn)(nullptr, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - } - static void* prepare(void* hint, size_t sz, int prot, int flags, int fd, off_t off) { - EXPECT_EQ(1, GetStackTrace(&expected_address, 1, 1)); - return nullptr; - } - }; - - void* addr; - Helper::trampoline(&addr, Helper::prepare); - ASSERT_NE(nullptr, expected_address); - ASSERT_EQ(nullptr, addr); - - Helper::trampoline(&addr, mmap); - ASSERT_NE(nullptr, addr); - ASSERT_EQ(backtrace_address_, expected_address); -} - -#ifdef HAVE_SBRK - -extern "C" void* tcmalloc_hooked_sbrk(intptr_t increment); - -static bool sbrk_works = ([] () { - void* result = tcmalloc_hooked_sbrk(8); - return result != reinterpret_cast(intptr_t{-1}); -}()); - -TEST_F(MMapHookTest, Sbrk) { - if (!sbrk_works) { - puts("sbrk test SKIPPED"); - return; - } - - void* addr = tcmalloc_hooked_sbrk(8); - - EXPECT_TRUE(last_evt_.is_sbrk); - EXPECT_TRUE(!last_evt_.before_valid && !last_evt_.file_valid && last_evt_.after_valid); - EXPECT_EQ(last_evt_.after_address, addr); - EXPECT_EQ(last_evt_.after_length, 8); - - ASSERT_FALSE(HasFatalFailure()); - have_last_evt_ = false; - - void* addr2 = tcmalloc_hooked_sbrk(16); - - EXPECT_TRUE(last_evt_.is_sbrk); - EXPECT_TRUE(!last_evt_.before_valid && !last_evt_.file_valid && last_evt_.after_valid); - EXPECT_EQ(last_evt_.after_address, addr2); - EXPECT_EQ(last_evt_.after_length, 16); - - ASSERT_FALSE(HasFatalFailure()); - have_last_evt_ = false; - - char* addr3 = static_cast(tcmalloc_hooked_sbrk(-13)); - - EXPECT_TRUE(last_evt_.is_sbrk); - EXPECT_TRUE(last_evt_.before_valid && !last_evt_.file_valid && !last_evt_.after_valid); - EXPECT_EQ(last_evt_.before_address, addr3-13); - EXPECT_EQ(last_evt_.before_length, 13); - - ASSERT_FALSE(HasFatalFailure()); - have_last_evt_ = false; -} - -TEST_F(MMapHookTest, SbrkBacktrace) { - if (!sbrk_works) { - puts("sbrk backtrace test SKIPPED"); - return; - } - - static void* expected_address; - - struct Helper { - // noinline ensures that all trampoline invocations will call fn - // with same return address (inside trampoline). We use that to - // test backtrace accuracy. - static ATTRIBUTE_NOINLINE - void trampoline(void** res, void* (*fn)(intptr_t increment)) { - *res = noopt(fn)(32); - } - static void* prepare(intptr_t increment) { - EXPECT_EQ(1, GetStackTrace(&expected_address, 1, 1)); - return nullptr; - } - }; - - void* addr; - Helper::trampoline(&addr, Helper::prepare); - ASSERT_NE(nullptr, expected_address); - ASSERT_EQ(nullptr, addr); - - printf("expected_address: %p, &trampoline: %p\n", - expected_address, reinterpret_cast(&Helper::trampoline)); - - // Why cast? Because some OS-es define sbrk as accepting long. - Helper::trampoline(&addr, reinterpret_cast(tcmalloc_hooked_sbrk)); - ASSERT_NE(nullptr, addr); - ASSERT_EQ(backtrace_address_, expected_address); -} - -#endif // HAVE_SBRK - -#endif // HAVE_MMAP diff --git a/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj b/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj index f91eeba..5abfc37 100644 --- a/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj +++ b/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj @@ -209,7 +209,6 @@ - @@ -233,7 +232,6 @@ - @@ -247,4 +245,4 @@ - \ No newline at end of file + diff --git a/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj.filters b/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj.filters index 725d967..5256779 100644 --- a/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj.filters +++ b/vsprojects/low_level_alloc_unittest/low_level_alloc_unittest.vcxproj.filters @@ -44,9 +44,6 @@ Source Files - - Source Files - Source Files @@ -100,9 +97,6 @@ Header Files - - Header Files - Header Files @@ -116,4 +110,4 @@ Header Files - \ No newline at end of file +