blob: 1d164f510a36de4ecb3328e6c80065f4913eca5d [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/heap/StackFrameDepth.h"
#include "public/platform/Platform.h"
#if OS(WIN)
#include <stddef.h>
#include <windows.h>
#include <winnt.h>
#elif defined(__GLIBC__)
extern "C" void* __libc_stack_end; // NOLINT
#endif
namespace blink {
static const char* s_avoidOptimization = nullptr;
// NEVER_INLINE ensures that |dummy| array on configureLimit() is not optimized
// away, and the stack frame base register is adjusted |kSafeStackFrameSize|.
NEVER_INLINE static uintptr_t currentStackFrameBaseOnCallee(const char* dummy) {
s_avoidOptimization = dummy;
return StackFrameDepth::currentStackFrame();
}
uintptr_t StackFrameDepth::getFallbackStackLimit() {
// Allocate an |kSafeStackFrameSize|-sized object on stack and query
// stack frame base after it.
char dummy[kSafeStackFrameSize];
// Check that the stack frame can be used.
dummy[sizeof(dummy) - 1] = 0;
return currentStackFrameBaseOnCallee(dummy);
}
void StackFrameDepth::enableStackLimit() {
// All supported platforms will currently return a non-zero estimate,
// except if ASan is enabled.
size_t stackSize = getUnderestimatedStackSize();
if (!stackSize) {
m_stackFrameLimit = getFallbackStackLimit();
return;
}
static const int kStackRoomSize = 1024;
Address stackBase = reinterpret_cast<Address>(getStackStart());
RELEASE_ASSERT(stackSize > static_cast<const size_t>(kStackRoomSize));
size_t stackRoom = stackSize - kStackRoomSize;
RELEASE_ASSERT(stackBase > reinterpret_cast<Address>(stackRoom));
m_stackFrameLimit = reinterpret_cast<uintptr_t>(stackBase - stackRoom);
// If current stack use is already exceeding estimated limit, mark as
// disabled.
if (!isSafeToRecurse())
disableStackLimit();
}
size_t StackFrameDepth::getUnderestimatedStackSize() {
// FIXME: ASAN bot uses a fake stack as a thread stack frame,
// and its size is different from the value which APIs tells us.
#if defined(ADDRESS_SANITIZER)
return 0;
#endif
// FIXME: On Mac OSX and Linux, this method cannot estimate stack size
// correctly for the main thread.
#if defined(__GLIBC__) || OS(ANDROID) || OS(FREEBSD)
// pthread_getattr_np() can fail if the thread is not invoked by
// pthread_create() (e.g., the main thread of webkit_unit_tests).
// If so, a conservative size estimate is returned.
pthread_attr_t attr;
int error;
#if OS(FREEBSD)
pthread_attr_init(&attr);
error = pthread_attr_get_np(pthread_self(), &attr);
#else
error = pthread_getattr_np(pthread_self(), &attr);
#endif
if (!error) {
void* base;
size_t size;
error = pthread_attr_getstack(&attr, &base, &size);
RELEASE_ASSERT(!error);
pthread_attr_destroy(&attr);
return size;
}
#if OS(FREEBSD)
pthread_attr_destroy(&attr);
#endif
// Return a 512k stack size, (conservatively) assuming the following:
// - that size is much lower than the pthreads default (x86 pthreads has a 2M
// default.)
// - no one is running Blink with an RLIMIT_STACK override, let alone as
// low as 512k.
//
return 512 * 1024;
#elif OS(MACOSX)
// pthread_get_stacksize_np() returns too low a value for the main thread on
// OSX 10.9,
// http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html
//
// Multiple workarounds possible, adopt the one made by
// https://github.com/robovm/robovm/issues/274
// (cf.
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
// on why hardcoding sizes is reasonable.)
if (pthread_main_np()) {
#if defined(IOS)
pthread_attr_t attr;
pthread_attr_init(&attr);
size_t guardSize = 0;
pthread_attr_getguardsize(&attr, &guardSize);
// Stack size for the main thread is 1MB on iOS including the guard page
// size.
return (1 * 1024 * 1024 - guardSize);
#else
// Stack size for the main thread is 8MB on OSX excluding the guard page
// size.
return (8 * 1024 * 1024);
#endif
}
return pthread_get_stacksize_np(pthread_self());
#elif OS(WIN) && COMPILER(MSVC)
return ThreadState::current()->threadStackSize();
#else
#error "Stack frame size estimation not supported on this platform."
return 0;
#endif
}
void* StackFrameDepth::getStackStart() {
#if defined(__GLIBC__) || OS(ANDROID) || OS(FREEBSD)
pthread_attr_t attr;
int error;
#if OS(FREEBSD)
pthread_attr_init(&attr);
error = pthread_attr_get_np(pthread_self(), &attr);
#else
error = pthread_getattr_np(pthread_self(), &attr);
#endif
if (!error) {
void* base;
size_t size;
error = pthread_attr_getstack(&attr, &base, &size);
RELEASE_ASSERT(!error);
pthread_attr_destroy(&attr);
return reinterpret_cast<uint8_t*>(base) + size;
}
#if OS(FREEBSD)
pthread_attr_destroy(&attr);
#endif
#if defined(__GLIBC__)
// pthread_getattr_np can fail for the main thread. In this case
// just like NaCl we rely on the __libc_stack_end to give us
// the start of the stack.
// See https://code.google.com/p/nativeclient/issues/detail?id=3431.
return __libc_stack_end;
#else
ASSERT_NOT_REACHED();
return nullptr;
#endif
#elif OS(MACOSX)
return pthread_get_stackaddr_np(pthread_self());
#elif OS(WIN) && COMPILER(MSVC)
// On Windows stack limits for the current thread are available in
// the thread information block (TIB). Its fields can be accessed through
// FS segment register on x86 and GS segment register on x86_64.
#ifdef _WIN64
return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase)));
#else
return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase)));
#endif
#else
#error Unsupported getStackStart on this platform.
#endif
}
} // namespace blink