/* 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 #include #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 // NOLINT #else #include // NOLINT #include // NOLINT #endif #if defined(LEAN_EMSCRIPTEN) #include #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(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(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(get_stack_pointer()); if (curr_stack < g_stack_threshold) throw_stack_space_exception(component_name); } } #endif