diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/wtf/stack_util.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/wtf/stack_util.cc | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc new file mode 100644 index 00000000000..10f1c0df11f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc @@ -0,0 +1,200 @@ +// Copyright 2017 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 "third_party/blink/renderer/platform/wtf/stack_util.h" + +#include "third_party/blink/renderer/platform/wtf/assertions.h" +#include "third_party/blink/renderer/platform/wtf/threading.h" +#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h" + +#if defined(OS_WIN) +#include <stddef.h> +#include <windows.h> +#include <winnt.h> +#elif defined(__GLIBC__) +extern "C" void* __libc_stack_end; // NOLINT +#endif + +namespace WTF { + +size_t 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__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \ + defined(OS_FUCHSIA) + // 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 defined(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); + CHECK(!error); + pthread_attr_destroy(&attr); + return size; + } +#if defined(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 defined(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 defined(OS_WIN) && defined(COMPILER_MSVC) + return WTFThreadData::ThreadStackSize(); +#else +#error "Stack frame size estimation not supported on this platform." + return 0; +#endif +} + +void* GetStackStart() { +#if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \ + defined(OS_FUCHSIA) + pthread_attr_t attr; + int error; +#if defined(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); + CHECK(!error); + pthread_attr_destroy(&attr); + return reinterpret_cast<uint8_t*>(base) + size; + } +#if defined(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 + NOTREACHED(); + return nullptr; +#endif +#elif defined(OS_MACOSX) + return pthread_get_stackaddr_np(pthread_self()); +#elif defined(OS_WIN) && defined(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 internal { + +uintptr_t g_main_thread_stack_start = 0; +uintptr_t g_main_thread_underestimated_stack_size = 0; + +void InitializeMainThreadStackEstimate() { + // getStackStart is exclusive, not inclusive (i.e. it points past the last + // page of the stack in linear order). So, to ensure an inclusive comparison, + // subtract here and below. + g_main_thread_stack_start = + reinterpret_cast<uintptr_t>(GetStackStart()) - sizeof(void*); + + size_t underestimated_stack_size = GetUnderestimatedStackSize(); + if (underestimated_stack_size > sizeof(void*)) { + underestimated_stack_size = underestimated_stack_size - sizeof(void*); + } + g_main_thread_underestimated_stack_size = underestimated_stack_size; +} + +#if defined(OS_WIN) && defined(COMPILER_MSVC) +size_t ThreadStackSize() { + // Notice that we cannot use the TIB's StackLimit for the stack end, as i + // tracks the end of the committed range. We're after the end of the reserved + // stack area (most of which will be uncommitted, most times.) + MEMORY_BASIC_INFORMATION stack_info; + memset(&stack_info, 0, sizeof(MEMORY_BASIC_INFORMATION)); + size_t result_size = + VirtualQuery(&stack_info, &stack_info, sizeof(MEMORY_BASIC_INFORMATION)); + DCHECK_GE(result_size, sizeof(MEMORY_BASIC_INFORMATION)); + uint8_t* stack_end = reinterpret_cast<uint8_t*>(stack_info.AllocationBase); + + uint8_t* stack_start = reinterpret_cast<uint8_t*>(WTF::GetStackStart()); + CHECK(stack_start); + CHECK_GT(stack_start, stack_end); + size_t thread_stack_size = static_cast<size_t>(stack_start - stack_end); + // When the third last page of the reserved stack is accessed as a + // guard page, the second last page will be committed (along with removing + // the guard bit on the third last) _and_ a stack overflow exception + // is raised. + // + // We have zero interest in running into stack overflow exceptions while + // marking objects, so simply consider the last three pages + one above + // as off-limits and adjust the reported stack size accordingly. + // + // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx + // explains the details. + CHECK_GT(thread_stack_size, 4u * 0x1000); + thread_stack_size -= 4 * 0x1000; + return thread_stack_size; +} +#endif + +} // namespace internal + +} // namespace WTF |