OLD | NEW |
| (Empty) |
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 "wtf/StackUtil.h" | |
6 | |
7 #include "wtf/Assertions.h" | |
8 #include "wtf/Threading.h" | |
9 #include "wtf/WTFThreadData.h" | |
10 | |
11 #if OS(WIN) | |
12 #include <stddef.h> | |
13 #include <windows.h> | |
14 #include <winnt.h> | |
15 #elif defined(__GLIBC__) | |
16 extern "C" void* __libc_stack_end; // NOLINT | |
17 #endif | |
18 | |
19 namespace WTF { | |
20 | |
21 size_t getUnderestimatedStackSize() { | |
22 // FIXME: ASAN bot uses a fake stack as a thread stack frame, | |
23 // and its size is different from the value which APIs tells us. | |
24 #if defined(ADDRESS_SANITIZER) | |
25 return 0; | |
26 #endif | |
27 | |
28 // FIXME: On Mac OSX and Linux, this method cannot estimate stack size | |
29 // correctly for the main thread. | |
30 | |
31 #if defined(__GLIBC__) || OS(ANDROID) || OS(FREEBSD) | |
32 // pthread_getattr_np() can fail if the thread is not invoked by | |
33 // pthread_create() (e.g., the main thread of webkit_unit_tests). | |
34 // If so, a conservative size estimate is returned. | |
35 | |
36 pthread_attr_t attr; | |
37 int error; | |
38 #if OS(FREEBSD) | |
39 pthread_attr_init(&attr); | |
40 error = pthread_attr_get_np(pthread_self(), &attr); | |
41 #else | |
42 error = pthread_getattr_np(pthread_self(), &attr); | |
43 #endif | |
44 if (!error) { | |
45 void* base; | |
46 size_t size; | |
47 error = pthread_attr_getstack(&attr, &base, &size); | |
48 RELEASE_ASSERT(!error); | |
49 pthread_attr_destroy(&attr); | |
50 return size; | |
51 } | |
52 #if OS(FREEBSD) | |
53 pthread_attr_destroy(&attr); | |
54 #endif | |
55 | |
56 // Return a 512k stack size, (conservatively) assuming the following: | |
57 // - that size is much lower than the pthreads default (x86 pthreads has a 2M | |
58 // default.) | |
59 // - no one is running Blink with an RLIMIT_STACK override, let alone as | |
60 // low as 512k. | |
61 // | |
62 return 512 * 1024; | |
63 #elif OS(MACOSX) | |
64 // pthread_get_stacksize_np() returns too low a value for the main thread on | |
65 // OSX 10.9, | |
66 // http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html | |
67 // | |
68 // Multiple workarounds possible, adopt the one made by | |
69 // https://github.com/robovm/robovm/issues/274 | |
70 // (cf. | |
71 // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Mult
ithreading/CreatingThreads/CreatingThreads.html | |
72 // on why hardcoding sizes is reasonable.) | |
73 if (pthread_main_np()) { | |
74 #if defined(IOS) | |
75 pthread_attr_t attr; | |
76 pthread_attr_init(&attr); | |
77 size_t guardSize = 0; | |
78 pthread_attr_getguardsize(&attr, &guardSize); | |
79 // Stack size for the main thread is 1MB on iOS including the guard page | |
80 // size. | |
81 return (1 * 1024 * 1024 - guardSize); | |
82 #else | |
83 // Stack size for the main thread is 8MB on OSX excluding the guard page | |
84 // size. | |
85 return (8 * 1024 * 1024); | |
86 #endif | |
87 } | |
88 return pthread_get_stacksize_np(pthread_self()); | |
89 #elif OS(WIN) && COMPILER(MSVC) | |
90 return WTFThreadData::threadStackSize(); | |
91 #else | |
92 #error "Stack frame size estimation not supported on this platform." | |
93 return 0; | |
94 #endif | |
95 } | |
96 | |
97 void* getStackStart() { | |
98 #if defined(__GLIBC__) || OS(ANDROID) || OS(FREEBSD) | |
99 pthread_attr_t attr; | |
100 int error; | |
101 #if OS(FREEBSD) | |
102 pthread_attr_init(&attr); | |
103 error = pthread_attr_get_np(pthread_self(), &attr); | |
104 #else | |
105 error = pthread_getattr_np(pthread_self(), &attr); | |
106 #endif | |
107 if (!error) { | |
108 void* base; | |
109 size_t size; | |
110 error = pthread_attr_getstack(&attr, &base, &size); | |
111 RELEASE_ASSERT(!error); | |
112 pthread_attr_destroy(&attr); | |
113 return reinterpret_cast<uint8_t*>(base) + size; | |
114 } | |
115 #if OS(FREEBSD) | |
116 pthread_attr_destroy(&attr); | |
117 #endif | |
118 #if defined(__GLIBC__) | |
119 // pthread_getattr_np can fail for the main thread. In this case | |
120 // just like NaCl we rely on the __libc_stack_end to give us | |
121 // the start of the stack. | |
122 // See https://code.google.com/p/nativeclient/issues/detail?id=3431. | |
123 return __libc_stack_end; | |
124 #else | |
125 NOTREACHED(); | |
126 return nullptr; | |
127 #endif | |
128 #elif OS(MACOSX) | |
129 return pthread_get_stackaddr_np(pthread_self()); | |
130 #elif OS(WIN) && COMPILER(MSVC) | |
131 // On Windows stack limits for the current thread are available in | |
132 // the thread information block (TIB). Its fields can be accessed through | |
133 // FS segment register on x86 and GS segment register on x86_64. | |
134 #ifdef _WIN64 | |
135 return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase))); | |
136 #else | |
137 return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase))); | |
138 #endif | |
139 #else | |
140 #error Unsupported getStackStart on this platform. | |
141 #endif | |
142 } | |
143 | |
144 namespace internal { | |
145 | |
146 uintptr_t s_mainThreadStackStart = 0; | |
147 uintptr_t s_mainThreadUnderestimatedStackSize = 0; | |
148 | |
149 void initializeMainThreadStackEstimate() { | |
150 // getStackStart is exclusive, not inclusive (i.e. it points past the last | |
151 // page of the stack in linear order). So, to ensure an inclusive comparison, | |
152 // subtract here and below. | |
153 s_mainThreadStackStart = | |
154 reinterpret_cast<uintptr_t>(getStackStart()) - sizeof(void*); | |
155 | |
156 size_t underestimatedStackSize = getUnderestimatedStackSize(); | |
157 if (underestimatedStackSize > sizeof(void*)) { | |
158 underestimatedStackSize = underestimatedStackSize - sizeof(void*); | |
159 } | |
160 s_mainThreadUnderestimatedStackSize = underestimatedStackSize; | |
161 } | |
162 | |
163 #if OS(WIN) && COMPILER(MSVC) | |
164 size_t threadStackSize() { | |
165 // Notice that we cannot use the TIB's StackLimit for the stack end, as i | |
166 // tracks the end of the committed range. We're after the end of the reserved | |
167 // stack area (most of which will be uncommitted, most times.) | |
168 MEMORY_BASIC_INFORMATION stackInfo; | |
169 memset(&stackInfo, 0, sizeof(MEMORY_BASIC_INFORMATION)); | |
170 size_t resultSize = | |
171 VirtualQuery(&stackInfo, &stackInfo, sizeof(MEMORY_BASIC_INFORMATION)); | |
172 DCHECK_GE(resultSize, sizeof(MEMORY_BASIC_INFORMATION)); | |
173 uint8_t* stackEnd = reinterpret_cast<uint8_t*>(stackInfo.AllocationBase); | |
174 | |
175 uint8_t* stackStart = reinterpret_cast<uint8_t*>(WTF::getStackStart()); | |
176 RELEASE_ASSERT(stackStart && stackStart > stackEnd); | |
177 size_t s_threadStackSize = static_cast<size_t>(stackStart - stackEnd); | |
178 // When the third last page of the reserved stack is accessed as a | |
179 // guard page, the second last page will be committed (along with removing | |
180 // the guard bit on the third last) _and_ a stack overflow exception | |
181 // is raised. | |
182 // | |
183 // We have zero interest in running into stack overflow exceptions while | |
184 // marking objects, so simply consider the last three pages + one above | |
185 // as off-limits and adjust the reported stack size accordingly. | |
186 // | |
187 // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-man
agement.aspx | |
188 // explains the details. | |
189 RELEASE_ASSERT(s_threadStackSize > 4 * 0x1000); | |
190 s_threadStackSize -= 4 * 0x1000; | |
191 return s_threadStackSize; | |
192 } | |
193 #endif | |
194 | |
195 } // namespace internal | |
196 | |
197 } // namespace WTF | |
OLD | NEW |