mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 18:12:02 +00:00
Try to fix GWPAsan
This commit is contained in:
parent
ee3e7f2fd0
commit
5c6c378fae
@ -291,7 +291,7 @@ if (TARGET ch_contrib::llvm)
|
||||
endif ()
|
||||
|
||||
if (TARGET ch_contrib::gwp_asan)
|
||||
target_link_libraries (clickhouse_common_io PRIVATE ch_contrib::gwp_asan)
|
||||
target_link_libraries (clickhouse_common_io PUBLIC ch_contrib::gwp_asan)
|
||||
target_link_libraries (clickhouse_new_delete PRIVATE ch_contrib::gwp_asan)
|
||||
endif()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <Common/Allocator.h>
|
||||
#include <Common/memory.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/logger_useful.h>
|
||||
#include <Common/formatReadable.h>
|
||||
@ -10,6 +11,12 @@
|
||||
#include <Poco/Logger.h>
|
||||
#include <sys/mman.h> /// MADV_POPULATE_WRITE
|
||||
|
||||
namespace ProfileEvents
|
||||
{
|
||||
extern const Event GWPAsanAllocateSuccess;
|
||||
extern const Event GWPAsanAllocateFailed;
|
||||
extern const Event GWPAsanFree;
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -59,13 +66,37 @@ void prefaultPages([[maybe_unused]] void * buf_, [[maybe_unused]] size_t len_)
|
||||
template <bool clear_memory, bool populate>
|
||||
void * allocNoTrack(size_t size, size_t alignment)
|
||||
{
|
||||
|
||||
void * buf;
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(Memory::GuardedAlloc.shouldSample()))
|
||||
{
|
||||
if (void * ptr = Memory::GuardedAlloc.allocate(size, alignment))
|
||||
{
|
||||
if constexpr (clear_memory)
|
||||
memset(ptr, 0, size);
|
||||
|
||||
if constexpr (populate)
|
||||
prefaultPages(ptr, size);
|
||||
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (alignment <= MALLOC_MIN_ALIGNMENT)
|
||||
{
|
||||
if constexpr (clear_memory)
|
||||
buf = ::calloc(size, 1);
|
||||
else
|
||||
{
|
||||
buf = ::malloc(size);
|
||||
}
|
||||
|
||||
if (nullptr == buf)
|
||||
throw DB::ErrnoException(DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, "Allocator: Cannot malloc {}.", ReadableSize(size));
|
||||
@ -91,6 +122,15 @@ void * allocNoTrack(size_t size, size_t alignment)
|
||||
|
||||
void freeNoTrack(void * buf)
|
||||
{
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(Memory::GuardedAlloc.pointerIsMine(buf)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanFree);
|
||||
Memory::GuardedAlloc.deallocate(buf);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
::free(buf);
|
||||
}
|
||||
|
||||
@ -144,8 +184,54 @@ void * Allocator<clear_memory_, populate>::realloc(void * buf, size_t old_size,
|
||||
{
|
||||
/// nothing to do.
|
||||
/// BTW, it's not possible to change alignment while doing realloc.
|
||||
return buf;
|
||||
}
|
||||
else if (alignment <= MALLOC_MIN_ALIGNMENT)
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(Memory::GuardedAlloc.shouldSample()))
|
||||
{
|
||||
if (void * ptr = Memory::GuardedAlloc.allocate(new_size, alignment))
|
||||
{
|
||||
auto trace_free = CurrentMemoryTracker::free(old_size);
|
||||
auto trace_alloc = CurrentMemoryTracker::alloc(new_size);
|
||||
trace_free.onFree(buf, old_size);
|
||||
|
||||
memcpy(ptr, buf, std::min(old_size, new_size));
|
||||
free(buf, old_size);
|
||||
trace_alloc.onAlloc(buf, new_size);
|
||||
|
||||
if constexpr (clear_memory)
|
||||
if (new_size > old_size)
|
||||
memset(reinterpret_cast<char *>(ptr) + old_size, 0, new_size - old_size);
|
||||
|
||||
if constexpr (populate)
|
||||
prefaultPages(ptr, new_size);
|
||||
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(Memory::GuardedAlloc.pointerIsMine(buf)))
|
||||
{
|
||||
/// Big allocs that requires a copy. MemoryTracker is called inside 'alloc', 'free' methods.
|
||||
void * new_buf = alloc(new_size, alignment);
|
||||
memcpy(new_buf, buf, std::min(old_size, new_size));
|
||||
free(buf, old_size);
|
||||
buf = new_buf;
|
||||
|
||||
if constexpr (populate)
|
||||
prefaultPages(buf, new_size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (alignment <= MALLOC_MIN_ALIGNMENT)
|
||||
{
|
||||
/// Resize malloc'd memory region with no special alignment requirement.
|
||||
auto trace_free = CurrentMemoryTracker::free(old_size);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <Common/CurrentMetrics.h>
|
||||
#include <Common/filesystemHelpers.h>
|
||||
#include <Common/logger_useful.h>
|
||||
#include <Common/memory.h>
|
||||
#include <IO/UncompressedCache.h>
|
||||
#include <IO/MMappedFileCache.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <Common/Allocator.h>
|
||||
#include <Common/BitHelpers.h>
|
||||
#include <Common/memcpySmall.h>
|
||||
@ -11,12 +13,16 @@
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
# include <gwp_asan/platform_specific/guarded_pool_allocator_tls.h>
|
||||
|
||||
#endif
|
||||
|
||||
/** Whether we can use memcpy instead of a loop with assignment to T from U.
|
||||
* It is Ok if types are the same. And if types are integral and of the same size,
|
||||
* example: char, signed char, unsigned char.
|
||||
@ -112,6 +118,10 @@ protected:
|
||||
template <typename ... TAllocatorParams>
|
||||
void alloc(size_t bytes, TAllocatorParams &&... allocator_params)
|
||||
{
|
||||
#if USE_GWP_ASAN
|
||||
gwp_asan::getThreadLocals()->NextSampleCounter = 1;
|
||||
#endif
|
||||
|
||||
char * allocated = reinterpret_cast<char *>(TAllocator::alloc(bytes, std::forward<TAllocatorParams>(allocator_params)...));
|
||||
|
||||
c_start = allocated + pad_left;
|
||||
@ -141,6 +151,10 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
gwp_asan::getThreadLocals()->NextSampleCounter = 1;
|
||||
#endif
|
||||
|
||||
unprotect();
|
||||
|
||||
ptrdiff_t end_diff = c_end - c_start;
|
||||
|
@ -744,6 +744,10 @@ The server successfully detected this situation and will download merged part fr
|
||||
\
|
||||
M(ReadWriteBufferFromHTTPRequestsSent, "Number of HTTP requests sent by ReadWriteBufferFromHTTP") \
|
||||
M(ReadWriteBufferFromHTTPBytes, "Total size of payload bytes received and sent by ReadWriteBufferFromHTTP. Doesn't include HTTP headers.") \
|
||||
\
|
||||
M(GWPAsanAllocateSuccess, "Number of successful allocations done by GWPAsan") \
|
||||
M(GWPAsanAllocateFailed, "Number of failed allocations done by GWPAsan (i.e. filled pool)") \
|
||||
M(GWPAsanFree, "Number of free operations done by GWPAsan") \
|
||||
|
||||
|
||||
#ifdef APPLY_FOR_EXTERNAL_EVENTS
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include <Common/memory.h>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
/** These functions can be substituted instead of regular ones when memory tracking is needed.
|
||||
*/
|
||||
|
||||
extern "C" void * clickhouse_malloc(size_t size)
|
||||
{
|
||||
void * res = malloc(size);
|
||||
void * res = nullptr;
|
||||
res = malloc(size);
|
||||
|
||||
if (res)
|
||||
{
|
||||
AllocationTrace trace;
|
||||
|
22
src/Common/memory.cpp
Normal file
22
src/Common/memory.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include <gwp_asan/guarded_pool_allocator.h>
|
||||
#include <Common/memory.h>
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
namespace Memory
|
||||
{
|
||||
gwp_asan::GuardedPoolAllocator GuardedAlloc;
|
||||
static bool guarded_alloc_initialized = []
|
||||
{
|
||||
gwp_asan::options::initOptions();
|
||||
gwp_asan::options::Options &opts = gwp_asan::options::getOptions();
|
||||
opts.MaxSimultaneousAllocations = 256;
|
||||
GuardedAlloc.init(opts);
|
||||
|
||||
///std::cerr << "GwpAsan is initialized, the options are { Enabled: " << opts.Enabled
|
||||
/// << ", MaxSimultaneousAllocations: " << opts.MaxSimultaneousAllocations
|
||||
/// << ", SampleRate: " << opts.SampleRate << " }\n";
|
||||
|
||||
return true;
|
||||
}();
|
||||
}
|
||||
#endif
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <Common/Concepts.h>
|
||||
#include <Common/CurrentMemoryTracker.h>
|
||||
#include <Common/ProfileEvents.h>
|
||||
#include "config.h"
|
||||
|
||||
#if USE_JEMALLOC
|
||||
@ -17,13 +18,24 @@
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
# include <gwp_asan/guarded_pool_allocator.h>
|
||||
# include <gwp_asan/optional/options_parser.h>
|
||||
|
||||
static gwp_asan::GuardedPoolAllocator GuardedAlloc;
|
||||
#endif
|
||||
|
||||
namespace ProfileEvents
|
||||
{
|
||||
extern const Event GWPAsanAllocateSuccess;
|
||||
extern const Event GWPAsanAllocateFailed;
|
||||
extern const Event GWPAsanFree;
|
||||
}
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
extern gwp_asan::GuardedPoolAllocator GuardedAlloc;
|
||||
#endif
|
||||
|
||||
inline ALWAYS_INLINE size_t alignToSizeT(std::align_val_t align) noexcept
|
||||
{
|
||||
return static_cast<size_t>(align);
|
||||
@ -39,12 +51,26 @@ inline ALWAYS_INLINE void * newImpl(std::size_t size, TAlign... align)
|
||||
if constexpr (sizeof...(TAlign) == 1)
|
||||
{
|
||||
if (void * ptr = GuardedAlloc.allocate(size, alignToSizeT(align...)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (void * ptr = GuardedAlloc.allocate(size))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -69,7 +95,14 @@ inline ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept
|
||||
if (unlikely(GuardedAlloc.shouldSample()))
|
||||
{
|
||||
if (void * ptr = GuardedAlloc.allocate(size))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return malloc(size);
|
||||
@ -81,7 +114,14 @@ inline ALWAYS_INLINE void * newNoExept(std::size_t size, std::align_val_t align)
|
||||
if (unlikely(GuardedAlloc.shouldSample()))
|
||||
{
|
||||
if (void * ptr = GuardedAlloc.allocate(size, alignToSizeT(align)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateSuccess);
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanAllocateFailed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return aligned_alloc(static_cast<size_t>(align), size);
|
||||
@ -92,6 +132,7 @@ inline ALWAYS_INLINE void deleteImpl(void * ptr) noexcept
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanFree);
|
||||
GuardedAlloc.deallocate(ptr);
|
||||
return;
|
||||
}
|
||||
@ -111,6 +152,7 @@ inline ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size, TAlign... al
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanFree);
|
||||
GuardedAlloc.deallocate(ptr);
|
||||
return;
|
||||
}
|
||||
@ -131,6 +173,7 @@ inline ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size [[maybe_unuse
|
||||
#if USE_GWP_ASAN
|
||||
if (unlikely(GuardedAlloc.pointerIsMine(ptr)))
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::GWPAsanFree);
|
||||
GuardedAlloc.deallocate(ptr);
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
#include "config.h"
|
||||
#include <Common/memory.h>
|
||||
@ -42,27 +41,6 @@ static struct InitializeJemallocZoneAllocatorForOSX
|
||||
} initializeJemallocZoneAllocatorForOSX;
|
||||
#endif
|
||||
|
||||
#if USE_GWP_ASAN
|
||||
|
||||
#include <gwp_asan/optional/options_parser.h>
|
||||
|
||||
/// Both clickhouse_new_delete and clickhouse_common_io links gwp_asan, but It should only init once, otherwise it
|
||||
/// will cause unexpected deadlock.
|
||||
static struct InitGwpAsan
|
||||
{
|
||||
InitGwpAsan()
|
||||
{
|
||||
gwp_asan::options::initOptions();
|
||||
gwp_asan::options::Options &opts = gwp_asan::options::getOptions();
|
||||
GuardedAlloc.init(opts);
|
||||
|
||||
///std::cerr << "GwpAsan is initialized, the options are { Enabled: " << opts.Enabled
|
||||
/// << ", MaxSimultaneousAllocations: " << opts.MaxSimultaneousAllocations
|
||||
/// << ", SampleRate: " << opts.SampleRate << " }\n";
|
||||
}
|
||||
} init_gwp_asan;
|
||||
#endif
|
||||
|
||||
/// Replace default new/delete with memory tracking versions.
|
||||
/// @sa https://en.cppreference.com/w/cpp/memory/new/operator_new
|
||||
/// https://en.cppreference.com/w/cpp/memory/new/operator_delete
|
||||
|
Loading…
Reference in New Issue
Block a user