1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "build/build_config.h"
6
7 #include "third_party/blink/renderer/platform/wtf/stack_util.h"
8
9 #include "third_party/blink/renderer/platform/wtf/assertions.h"
10 #include "third_party/blink/renderer/platform/wtf/threading.h"
11
12 #if defined(OS_WIN)
13 #include <stddef.h>
14 #include <windows.h>
15 #include <winnt.h>
16 #elif defined(__GLIBC__)
17 extern "C" void* __libc_stack_end; // NOLINT
18 #endif
19
20 #if defined(OS_FREEBSD)
21 #include <sys/signal.h>
22 #include <pthread_np.h>
23 #endif
24
25 namespace WTF {
26
GetUnderestimatedStackSize()27 size_t GetUnderestimatedStackSize() {
28 // FIXME: ASAN bot uses a fake stack as a thread stack frame,
29 // and its size is different from the value which APIs tells us.
30 #if defined(ADDRESS_SANITIZER)
31 return 0;
32
33 // FIXME: On Mac OSX and Linux, this method cannot estimate stack size
34 // correctly for the main thread.
35
36 #elif defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
37 defined(OS_FUCHSIA)
38 // pthread_getattr_np() can fail if the thread is not invoked by
39 // pthread_create() (e.g., the main thread of blink_unittests).
40 // If so, a conservative size estimate is returned.
41
42 pthread_attr_t attr;
43 int error;
44 #if defined(OS_FREEBSD)
45 pthread_attr_init(&attr);
46 error = pthread_attr_get_np(pthread_self(), &attr);
47 #else
48 error = pthread_getattr_np(pthread_self(), &attr);
49 #endif
50 if (!error) {
51 void* base;
52 size_t size;
53 error = pthread_attr_getstack(&attr, &base, &size);
54 CHECK(!error);
55 pthread_attr_destroy(&attr);
56 return size;
57 }
58 #if defined(OS_FREEBSD)
59 pthread_attr_destroy(&attr);
60 #endif
61
62 // Return a 512k stack size, (conservatively) assuming the following:
63 // - that size is much lower than the pthreads default (x86 pthreads has a 2M
64 // default.)
65 // - no one is running Blink with an RLIMIT_STACK override, let alone as
66 // low as 512k.
67 //
68 return 512 * 1024;
69 #elif defined(OS_MACOSX)
70 // pthread_get_stacksize_np() returns too low a value for the main thread on
71 // OSX 10.9,
72 // http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html
73 //
74 // Multiple workarounds possible, adopt the one made by
75 // https://github.com/robovm/robovm/issues/274
76 // (cf.
77 // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
78 // on why hardcoding sizes is reasonable.)
79 if (pthread_main_np()) {
80 #if defined(IOS)
81 pthread_attr_t attr;
82 pthread_attr_init(&attr);
83 size_t guardSize = 0;
84 pthread_attr_getguardsize(&attr, &guardSize);
85 // Stack size for the main thread is 1MB on iOS including the guard page
86 // size.
87 return (1 * 1024 * 1024 - guardSize);
88 #else
89 // Stack size for the main thread is 8MB on OSX excluding the guard page
90 // size.
91 return (8 * 1024 * 1024);
92 #endif
93 }
94 return pthread_get_stacksize_np(pthread_self());
95 #elif defined(OS_WIN) && defined(COMPILER_MSVC)
96 return Threading::ThreadStackSize();
97 #else
98 #error "Stack frame size estimation not supported on this platform."
99 return 0;
100 #endif
101 }
102
GetStackStart()103 void* GetStackStart() {
104 #if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
105 defined(OS_FUCHSIA)
106 pthread_attr_t attr;
107 int error;
108 #if defined(OS_FREEBSD)
109 pthread_attr_init(&attr);
110 error = pthread_attr_get_np(pthread_self(), &attr);
111 #else
112 error = pthread_getattr_np(pthread_self(), &attr);
113 #endif
114 if (!error) {
115 void* base;
116 size_t size;
117 error = pthread_attr_getstack(&attr, &base, &size);
118 CHECK(!error);
119 pthread_attr_destroy(&attr);
120 return reinterpret_cast<uint8_t*>(base) + size;
121 }
122 #if defined(OS_FREEBSD)
123 pthread_attr_destroy(&attr);
124 #endif
125 #if defined(__GLIBC__)
126 // pthread_getattr_np can fail for the main thread. In this case
127 // just like NaCl we rely on the __libc_stack_end to give us
128 // the start of the stack.
129 // See https://code.google.com/p/nativeclient/issues/detail?id=3431.
130 return __libc_stack_end;
131 #else
132 NOTREACHED();
133 return nullptr;
134 #endif
135 #elif defined(OS_MACOSX)
136 return pthread_get_stackaddr_np(pthread_self());
137 #elif defined(OS_WIN) && defined(COMPILER_MSVC)
138 // On Windows stack limits for the current thread are available in
139 // the thread information block (TIB). Its fields can be accessed through
140 // FS segment register on x86 and GS segment register on x86_64.
141 // On Windows ARM64, stack limits could be retrieved by calling
142 // GetCurrentThreadStackLimits. This API doesn't work on x86 and x86_64 here
143 // because it requires Windows 8+.
144 #if defined(ARCH_CPU_X86_64)
145 return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase)));
146 #elif defined(ARCH_CPU_X86)
147 return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase)));
148 #elif defined(ARCH_CPU_ARM64)
149 ULONG_PTR lowLimit, highLimit;
150 ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
151 return reinterpret_cast<void*>(highLimit);
152 #endif
153 #else
154 #error Unsupported getStackStart on this platform.
155 #endif
156 }
157
GetCurrentStackPosition()158 uintptr_t GetCurrentStackPosition() {
159 #if defined(COMPILER_MSVC)
160 return reinterpret_cast<uintptr_t>(_AddressOfReturnAddress());
161 #else
162 return reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
163 #endif
164 }
165
166 namespace internal {
167
168 uintptr_t g_main_thread_stack_start = 0;
169 uintptr_t g_main_thread_underestimated_stack_size = 0;
170
InitializeMainThreadStackEstimate()171 void InitializeMainThreadStackEstimate() {
172 // getStackStart is exclusive, not inclusive (i.e. it points past the last
173 // page of the stack in linear order). So, to ensure an inclusive comparison,
174 // subtract here and below.
175 g_main_thread_stack_start =
176 reinterpret_cast<uintptr_t>(GetStackStart()) - sizeof(void*);
177
178 size_t underestimated_stack_size = GetUnderestimatedStackSize();
179 if (underestimated_stack_size > sizeof(void*)) {
180 underestimated_stack_size = underestimated_stack_size - sizeof(void*);
181 }
182 g_main_thread_underestimated_stack_size = underestimated_stack_size;
183 }
184
185 #if defined(OS_WIN) && defined(COMPILER_MSVC)
ThreadStackSize()186 size_t ThreadStackSize() {
187 // Notice that we cannot use the TIB's StackLimit for the stack end, as i
188 // tracks the end of the committed range. We're after the end of the reserved
189 // stack area (most of which will be uncommitted, most times.)
190 MEMORY_BASIC_INFORMATION stack_info;
191 memset(&stack_info, 0, sizeof(MEMORY_BASIC_INFORMATION));
192 size_t result_size =
193 VirtualQuery(&stack_info, &stack_info, sizeof(MEMORY_BASIC_INFORMATION));
194 DCHECK_GE(result_size, sizeof(MEMORY_BASIC_INFORMATION));
195 uint8_t* stack_end = reinterpret_cast<uint8_t*>(stack_info.AllocationBase);
196
197 uint8_t* stack_start = reinterpret_cast<uint8_t*>(WTF::GetStackStart());
198 CHECK(stack_start);
199 CHECK_GT(stack_start, stack_end);
200 size_t thread_stack_size = static_cast<size_t>(stack_start - stack_end);
201 // When the third last page of the reserved stack is accessed as a
202 // guard page, the second last page will be committed (along with removing
203 // the guard bit on the third last) _and_ a stack overflow exception
204 // is raised.
205 //
206 // We have zero interest in running into stack overflow exceptions while
207 // marking objects, so simply consider the last three pages + one above
208 // as off-limits and adjust the reported stack size accordingly.
209 //
210 // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
211 // explains the details.
212 CHECK_GT(thread_stack_size, 4u * 0x1000);
213 thread_stack_size -= 4 * 0x1000;
214 return thread_stack_size;
215 }
216 #endif
217
218 } // namespace internal
219
220 } // namespace WTF
221