summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc
blob: 1aaaa1c60d29c1dda726b5890202d8d85bd890a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// 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 "build/build_config.h"

#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"

#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;

// FIXME: On Mac OSX and Linux, this method cannot estimate stack size
// correctly for the main thread.

#elif 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 blink_unittests).
  // 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 Threading::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.
// On Windows ARM64, stack limits could be retrieved by calling
// GetCurrentThreadStackLimits. This API doesn't work on x86 and x86_64 here
// because it requires Windows 8+.
#if defined(ARCH_CPU_X86_64)
  return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase)));
#elif defined(ARCH_CPU_X86)
  return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase)));
#elif defined(ARCH_CPU_ARM64)
  ULONG_PTR lowLimit, highLimit;
  ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
  return reinterpret_cast<void*>(highLimit);
#endif
#else
#error Unsupported getStackStart on this platform.
#endif
}

uintptr_t GetCurrentStackPosition() {
#if defined(COMPILER_MSVC)
  return reinterpret_cast<uintptr_t>(_AddressOfReturnAddress());
#else
  return reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
#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