This PR should make lean better-behaved around sanitizers, per https://github.com/google/sanitizers/issues/1688. As far as I can tell, https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn#algorithm replaces local variables with heap allocations, and so taking the address of a local is not effective at producing a monotonic measure of stack usage. The approach used here is the same as the one used by clang.
136 lines
3.6 KiB
C++
136 lines
3.6 KiB
C++
/*
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include <memory.h>
|
|
#include <iostream>
|
|
#include "runtime/thread.h"
|
|
#include "runtime/exception.h"
|
|
#include "runtime/stackinfo.h"
|
|
|
|
#if !defined(LEAN_USE_SPLIT_STACK)
|
|
#if defined(LEAN_WINDOWS)
|
|
// no extra included needed so far
|
|
#elif defined(__APPLE__)
|
|
#include <sys/resource.h> // NOLINT
|
|
#else
|
|
#include <sys/time.h> // NOLINT
|
|
#include <sys/resource.h> // NOLINT
|
|
#endif
|
|
|
|
#if defined(LEAN_EMSCRIPTEN)
|
|
#include <emscripten/stack.h>
|
|
#endif
|
|
|
|
namespace lean {
|
|
void throw_get_stack_size_failed() {
|
|
throw exception("failed to retrieve thread stack size");
|
|
}
|
|
|
|
#if defined(LEAN_WINDOWS)
|
|
size_t get_stack_size(bool main) {
|
|
if (main) {
|
|
return LEAN_WIN_STACK_SIZE;
|
|
} else {
|
|
return lthread::get_thread_stack_size();
|
|
}
|
|
}
|
|
#elif defined (__APPLE__)
|
|
size_t get_stack_size(bool main) {
|
|
if (main) {
|
|
// Retrieve stack size of the main thread.
|
|
struct rlimit curr;
|
|
if (getrlimit(RLIMIT_STACK, &curr) != 0) {
|
|
throw_get_stack_size_failed();
|
|
}
|
|
return curr.rlim_cur;
|
|
} else {
|
|
return lthread::get_thread_stack_size();
|
|
}
|
|
}
|
|
#elif defined(LEAN_EMSCRIPTEN)
|
|
size_t get_stack_size(bool main) {
|
|
if (main) {
|
|
return emscripten_stack_get_end() - emscripten_stack_get_base();
|
|
} else {
|
|
return lthread::get_thread_stack_size();
|
|
}
|
|
}
|
|
#else
|
|
size_t get_stack_size(bool main) {
|
|
if (main) {
|
|
// Retrieve stack size of the main thread.
|
|
struct rlimit curr;
|
|
if (getrlimit(RLIMIT_STACK, &curr) != 0) {
|
|
throw_get_stack_size_failed();
|
|
}
|
|
return curr.rlim_cur;
|
|
} else {
|
|
return lthread::get_thread_stack_size();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef __has_builtin
|
|
#define __has_builtin(x) 0 /* for non-clang compilers */
|
|
#endif
|
|
|
|
// taken from https://github.com/llvm/llvm-project/blob/llvmorg-10.0.0-rc1/clang/lib/Basic/Stack.cpp#L24
|
|
static void *get_stack_pointer() {
|
|
#if __GNUC__ || __has_builtin(__builtin_frame_address)
|
|
return __builtin_frame_address(0);
|
|
#elif defined(_MSC_VER)
|
|
return _AddressOfReturnAddress();
|
|
#else
|
|
char x = 0;
|
|
char *volatile ptr = &x;
|
|
return ptr;
|
|
#endif
|
|
}
|
|
|
|
LEAN_THREAD_VALUE(bool, g_stack_info_init, false);
|
|
LEAN_THREAD_VALUE(size_t, g_stack_size, 0);
|
|
LEAN_THREAD_VALUE(size_t, g_stack_base, 0);
|
|
LEAN_THREAD_VALUE(size_t, g_stack_threshold, 0);
|
|
|
|
void save_stack_info(bool main) {
|
|
g_stack_info_init = true;
|
|
g_stack_size = get_stack_size(main);
|
|
g_stack_base = reinterpret_cast<size_t>(get_stack_pointer());
|
|
/* g_stack_threshold is a redundant value used to optimize check_stack */
|
|
g_stack_threshold = g_stack_base + LEAN_STACK_BUFFER_SPACE - g_stack_size;
|
|
if (g_stack_threshold > g_stack_base + LEAN_STACK_BUFFER_SPACE) {
|
|
// negative overflow
|
|
g_stack_threshold = 0;
|
|
}
|
|
}
|
|
|
|
size_t get_used_stack_size() {
|
|
size_t curr_stack = reinterpret_cast<size_t>(get_stack_pointer());
|
|
return g_stack_base - curr_stack;
|
|
}
|
|
|
|
size_t get_available_stack_size() {
|
|
size_t sz = get_used_stack_size();
|
|
if (sz > g_stack_size)
|
|
return 0;
|
|
else
|
|
return g_stack_size - sz;
|
|
}
|
|
|
|
// separate definition to allow breakpoint in debugger
|
|
void throw_stack_space_exception(char const * component_name) {
|
|
throw stack_space_exception(component_name);
|
|
}
|
|
|
|
void check_stack(char const * component_name) {
|
|
if (!g_stack_info_init)
|
|
save_stack_info(false);
|
|
size_t curr_stack = reinterpret_cast<size_t>(get_stack_pointer());
|
|
if (curr_stack < g_stack_threshold)
|
|
throw_stack_space_exception(component_name);
|
|
}
|
|
}
|
|
#endif
|