blob: 62265aaaba05ef75fca0c33937156ca556ece610 [file] [log] [blame]
/** @file
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <unistd.h>
#include <sys/types.h>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include "tscore/ink_memory.h"
#include "tscore/ink_error.h"
#include "tscore/ink_assert.h"
#include "tscore/ink_align.h"
#include "tscore/JeMiAllocator.h"
#include "tscore/Diags.h"
namespace je_mi_malloc
{
#if JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED
thread_local extent_alloc_t *JeMiNodumpAllocator::original_alloc = nullptr;
thread_local extent_hooks_t JeMiNodumpAllocator::extent_hooks;
thread_local int JeMiNodumpAllocator::arena_flags = 0;
void *
JeMiNodumpAllocator::alloc(extent_hooks_t *extent, void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit,
unsigned int arena_id)
{
void *result = original_alloc(extent, new_addr, size, alignment, zero, commit, arena_id);
if (result != nullptr) {
// Seems like we don't really care if the advice went through
// in the original code, so just keeping it the same here.
ats_madvise((caddr_t)result, size, MADV_DONTDUMP);
}
return result;
}
int
JeMiNodumpAllocator::extend_and_setup_arena()
{
unsigned int arena_id;
size_t arena_id_len = sizeof(arena_id);
if (auto ret = mallctl("arenas.create", &arena_id, &arena_id_len, nullptr, 0)) {
ink_abort("Unable to extend arena: %s", std::strerror(ret));
}
int flags = MALLOCX_ARENA(arena_id) | MALLOCX_TCACHE_NONE;
// Read the existing hooks
const auto key = "arena." + std::to_string(arena_id) + ".extent_hooks";
extent_hooks_t *hooks;
size_t hooks_len = sizeof(hooks);
if (auto ret = mallctl(key.c_str(), &hooks, &hooks_len, nullptr, 0)) {
ink_abort("Unable to get the hooks: %s", std::strerror(ret));
}
// Set the custom hook
original_alloc = hooks->alloc;
extent_hooks = *hooks;
extent_hooks.alloc = &JeMiNodumpAllocator::alloc;
extent_hooks_t *new_hooks = &extent_hooks;
if (auto ret = mallctl(key.c_str(), nullptr, nullptr, &new_hooks, sizeof(new_hooks))) {
ink_abort("Unable to set the hooks: %s", std::strerror(ret));
}
Debug("JeAllocator", "arena \"%ud\" created with flags \"%d\"", arena_id, flags);
arena_flags = flags;
return flags;
}
#elif MIMALLOC_NODUMP_ALLOCATOR_SUPPORTED
thread_local mi_heap_t *JeMiNodumpAllocator::nodump_heap = nullptr;
#endif /* JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED */
/**
* This will retain the original functionality if
* !defined(JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED)
*/
void *
JeMiNodumpAllocator::allocate(InkFreeList *f)
{
#if JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED
int flags = arena_flags;
if (unlikely(flags == 0)) {
flags = extend_and_setup_arena();
}
#elif MIMALLOC_NODUMP_ALLOCATOR_SUPPORTED
if (unlikely(nodump_heap == nullptr)) {
nodump_heap = mi_heap_new();
if (unlikely(nodump_heap == nullptr)) {
ink_abort("couldn't create new mimalloc heap");
}
}
#endif
void *newp = nullptr;
if (f->advice) {
#if JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED
if (likely(f->type_size > 0)) {
flags |= MALLOCX_ALIGN(f->alignment);
if (unlikely((newp = mallocx(f->type_size, flags)) == nullptr)) {
ink_abort("couldn't allocate %u bytes", f->type_size);
}
}
#elif MIMALLOC_NODUMP_ALLOCATOR_SUPPORTED
if (likely(f->type_size > 0)) {
if (unlikely((newp = mi_heap_malloc_aligned(nodump_heap, f->type_size, f->alignment)) == nullptr)) {
ink_abort("couldn't allocate %u bytes", f->type_size);
}
ats_madvise(static_cast<caddr_t>(newp), f->type_size, f->advice);
}
#else
newp = ats_memalign(f->alignment, f->type_size);
if (INK_ALIGN((uint64_t)newp, ats_pagesize()) == (uint64_t)newp) {
ats_madvise(static_cast<caddr_t>(newp), INK_ALIGN(f->type_size, f->alignment), f->advice);
}
#endif
} else {
newp = ats_memalign(f->alignment, f->type_size);
}
return newp;
}
void
JeMiNodumpAllocator::deallocate(InkFreeList *f, void *ptr)
{
if (f->advice) {
#if JEMALLOC_NODUMP_ALLOCATOR_SUPPORTED
if (likely(ptr)) {
dallocx(ptr, MALLOCX_TCACHE_NONE);
}
#elif MIMALLOC_NODUMP_ALLOCATOR_SUPPORTED
if (likely(ptr)) {
mi_free(ptr);
}
#else
ats_free(ptr);
#endif
} else {
ats_free(ptr);
}
}
JeMiNodumpAllocator &
globalJeMiNodumpAllocator()
{
static auto instance = new JeMiNodumpAllocator();
return *instance;
}
} // namespace je_mi_malloc