#include "memory_debug.hpp"
#ifdef _WIN32
#include <windows.h>
#endif
#if defined __unix__ || defined __APPLE__ || defined __FreeBSD__ \
|| defined __Fuchsia__
#include <unistd.h>
#include <sys/mman.h>
#endif
#include <assert.h>
#include "dnnl_thread.hpp"
#include "nstl.hpp"
#include "utils.hpp"
namespace dnnl {
namespace impl {
namespace memory_debug {
template <typename T>
static inline T get_page_start(const void *ptr) {
size_t page_mask = ~(getpagesize() - 1);
size_t ptr_cast = reinterpret_cast<size_t>(ptr);
return reinterpret_cast<T>(ptr_cast & page_mask);
}
template <typename T>
static inline T get_page_end(const void *ptr) {
size_t page_mask = ~(getpagesize() - 1);
size_t ptr_cast = reinterpret_cast<size_t>(ptr);
return reinterpret_cast<T>((ptr_cast + getpagesize() - 1) & page_mask);
}
static inline int num_protect_pages() {
if (is_mem_debug())
return DNNL_MEM_DEBUG_PROTECT_SIZE;
else
return 0;
}
size_t protect_size() {
return (size_t)num_protect_pages() * getpagesize();
}
#ifdef _WIN32
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
static inline int mprotect(void *addr, size_t len, int prot) {
return 0;
}
#endif
struct memory_tag_t {
void *memory_start;
size_t buffer_size;
};
static inline memory_tag_t *get_memory_tags(void *ptr) {
return get_page_start<memory_tag_t *>(ptr) - 1;
}
void *malloc(size_t size, int alignment) {
void *ptr;
size_t buffer_size = utils::rnd_up(size, alignment);
int buffer_alignment = alignment;
if (buffer_alignment < getpagesize()) alignment = getpagesize();
size = utils::rnd_up(
size + alignment + 2 * protect_size(), (size_t)alignment);
#ifdef _WIN32
ptr = _aligned_malloc(size, alignment);
int rc = ptr ? 0 : -1;
#else
int rc = ::posix_memalign(&ptr, alignment, size);
#endif
if (rc == 0) {
void *mem_start = ptr;
ptr = utils::align_ptr(
reinterpret_cast<char *>(ptr) + protect_size(), alignment);
if (is_mem_debug_overflow()) {
size_t offset = (alignment - (buffer_size % alignment)) % alignment;
ptr = reinterpret_cast<char *>(ptr) + offset;
}
assert(protect_size() >= 16);
memory_tag_t *tag = get_memory_tags(ptr);
tag->memory_start = mem_start;
tag->buffer_size = buffer_size;
protect_buffer(ptr, buffer_size, engine_kind_t::dnnl_cpu);
}
return (rc == 0) ? ptr : nullptr;
}
void free(void *p) {
if (p != nullptr) {
memory_tag_t *tag = get_memory_tags(p);
int status;
MAYBE_UNUSED(status);
status = mprotect(get_page_start<void *>(tag), getpagesize(),
PROT_WRITE | PROT_READ);
assert(status == 0);
unprotect_buffer(p, tag->buffer_size, engine_kind_t::dnnl_cpu);
p = tag->memory_start;
}
#ifdef _WIN32
_aligned_free(p);
#else
::free(p);
#endif
}
void protect_buffer(void *addr, size_t size, engine_kind_t engine_kind) {
if (engine_kind != engine_kind_t::dnnl_cpu)
return;
char *page_start = get_page_start<char *>(addr);
char *page_end
= get_page_end<char *>(reinterpret_cast<const char *>(addr) + size);
int status;
MAYBE_UNUSED(status);
status = mprotect(page_start - protect_size(), protect_size(), PROT_NONE);
assert(status == 0);
status = mprotect(page_end, protect_size(), PROT_NONE);
assert(status == 0);
uint16_t canary = 0x7ff1;
size_t work_amount = (size_t)((page_end - page_start) / getpagesize());
if (work_amount <= 1) {
uint16_t *ptr_start = reinterpret_cast<uint16_t *>(
reinterpret_cast<size_t>(addr) & ~1);
uint16_t *ptr_end = reinterpret_cast<uint16_t *>(
reinterpret_cast<char *>(addr) + size);
for (uint16_t *curr = ptr_start; curr < ptr_end; curr++) {
*curr = canary;
}
} else {
parallel(0, [&](const int ithr, const int nthr) {
size_t start = 0, end = 0;
balance211(work_amount, nthr, ithr, start, end);
uint16_t *ptr_start = reinterpret_cast<uint16_t *>(
page_start + getpagesize() * start);
uint16_t *ptr_end = reinterpret_cast<uint16_t *>(
page_start + getpagesize() * end);
for (uint16_t *curr = ptr_start; curr < ptr_end; curr++) {
*curr = canary;
}
});
}
}
void unprotect_buffer(
const void *addr, size_t size, engine_kind_t engine_kind) {
if (engine_kind != engine_kind_t::dnnl_cpu)
return;
char *page_start = get_page_start<char *>(addr);
char *page_end
= get_page_end<char *>(reinterpret_cast<const char *>(addr) + size);
int status;
MAYBE_UNUSED(status);
status = mprotect(page_start - protect_size(), protect_size(),
PROT_WRITE | PROT_READ);
assert(status == 0);
status = mprotect(page_end, protect_size(), PROT_WRITE | PROT_READ);
assert(status == 0);
}
} } }