drop mmap_hook
This commit is contained in:
parent
00d235325f
commit
6a7419e39f
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
13
Makefile.am
13
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
|
||||
|
|
|
@ -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 <gperftools/malloc_hook.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
|
|
|
@ -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<uintptr_t>(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<void *>(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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// 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_
|
495
src/mmap_hook.cc
495
src/mmap_hook.cc
|
@ -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 <config.h>
|
||||
|
||||
#include "mmap_hook.h"
|
||||
|
||||
#if HAVE_SYS_SYSCALL_H
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#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 <sys/mman.h>
|
||||
# ifndef MAP_ANONYMOUS
|
||||
# define MAP_ANONYMOUS MAP_ANON
|
||||
# endif
|
||||
#include <sys/types.h>
|
||||
# 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<bool> inactive{false};
|
||||
std::atomic<MappingHookDescriptor*> 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<MappingHookDescriptor*>(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<MappingHookDescriptor*> *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<MappingHookDescriptor*> *place = &list_head_;
|
||||
while (MappingHookDescriptor* desc = place->load(std::memory_order_acquire)) {
|
||||
place = &desc->next;
|
||||
if (!desc->inactive) {
|
||||
desc->fn(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<MappingHookDescriptor*> 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<void*>(syscall(sysnr, args));
|
||||
#else
|
||||
return reinterpret_cast<void*>(
|
||||
syscall(sysnr, reinterpret_cast<uintptr_t>(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<int64_t>(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<void*>(__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<void*>(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<uintptr_t>(result);
|
||||
intptr_t new_brk = res_addr + increment;
|
||||
evt.before_address = reinterpret_cast<void*>(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
|
137
src/mmap_hook.h
137
src/mmap_hook.h
|
@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
|
@ -168,18 +168,6 @@ static tcmalloc::StaticStorage<DefaultSysAllocator> 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<intptr_t>(tcmalloc_hooked_sbrk(0)) + size < size) {
|
||||
if (reinterpret_cast<intptr_t>(sbrk(0)) + size < size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* result = tcmalloc_hooked_sbrk(size);
|
||||
void* result = sbrk(size);
|
||||
if (result == reinterpret_cast<void*>(-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<uintptr_t>(r2) == (ptr + size)) {
|
||||
// Contiguous with previous result
|
||||
return reinterpret_cast<void*>(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<void*>(-1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<char*>(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<char*>(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<void*>(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<char*>(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<void*>(&Helper::trampoline));
|
||||
|
||||
// Why cast? Because some OS-es define sbrk as accepting long.
|
||||
Helper::trampoline(&addr, reinterpret_cast<void*(*)(intptr_t)>(tcmalloc_hooked_sbrk));
|
||||
ASSERT_NE(nullptr, addr);
|
||||
ASSERT_EQ(backtrace_address_, expected_address);
|
||||
}
|
||||
|
||||
#endif // HAVE_SBRK
|
||||
|
||||
#endif // HAVE_MMAP
|
|
@ -209,7 +209,6 @@
|
|||
<ClCompile Include="..\..\src\base\generic_writer.cc" />
|
||||
<ClCompile Include="..\..\src\base\sysinfo.cc" />
|
||||
<ClCompile Include="..\..\src\base\proc_maps_iterator.cc" />
|
||||
<ClCompile Include="..\..\src\mmap_hook.cc" />
|
||||
<ClCompile Include="..\..\src\malloc_backtrace.cc" />
|
||||
<ClCompile Include="..\..\src\malloc_hook.cc" />
|
||||
<ClCompile Include="..\..\src\stacktrace.cc" />
|
||||
|
@ -233,7 +232,6 @@
|
|||
<ClInclude Include="..\..\src\gperftools\stacktrace_config.h" />
|
||||
<ClInclude Include="..\..\src\malloc_backtrace.h" />
|
||||
<ClInclude Include="..\..\src\malloc_hook-inl.h" />
|
||||
<ClInclude Include="..\..\src\mmap_hook.h" />
|
||||
<ClInclude Include="..\..\src\stacktrace_win32-inl.h.h" />
|
||||
<ClInclude Include="..\config.h" />
|
||||
<ClInclude Include="..\..\src\windows\port.h" />
|
||||
|
@ -247,4 +245,4 @@
|
|||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<Import Project="$(MSBuildProjectDirectory)\..\common.vcxproj.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -44,9 +44,6 @@
|
|||
<ClCompile Include="..\..\src\malloc_hook.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\mmap_hook.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\windows\port.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -100,9 +97,6 @@
|
|||
<ClInclude Include="..\..\src\malloc_hook-inl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\mmap_hook.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\windows\port.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -116,4 +110,4 @@
|
|||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue