Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4stacklimits.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/qv4stacklimits_p.h>
5#include <private/qobject_p.h>
6#include <private/qthread_p.h>
7
8#include <QtCore/qfile.h>
9
10#if defined(Q_OS_UNIX)
11# include <pthread.h>
12#endif
13
14#ifdef Q_OS_WIN
15# include <QtCore/qt_windows.h>
16#elif defined(Q_OS_FREEBSD_KERNEL) || defined(Q_OS_OPENBSD)
17# include <pthread_np.h>
18#elif defined(Q_OS_LINUX)
19# include <unistd.h>
20# include <sys/resource.h> // for getrlimit()
21# include <sys/syscall.h> // for SYS_gettid
22# if defined(__GLIBC__) && QT_CONFIG(dlopen)
23# include <dlfcn.h>
24# endif
25#elif defined(Q_OS_DARWIN)
26# include <sys/resource.h> // for getrlimit()
27#elif defined(Q_OS_QNX)
28# include <devctl.h>
29# include <sys/procfs.h>
30# include <sys/types.h>
31# include <unistd.h>
32#elif defined(Q_OS_INTEGRITY)
33# include <INTEGRITY.h>
34#elif defined(Q_OS_VXWORKS)
35# include <taskLib.h>
36#elif defined(Q_OS_WASM)
37# include <emscripten/stack.h>
38#endif
39
41
42namespace QV4 {
43
45 // Default safety margin at the end of the usable stack.
46 // Since we don't check the stack on every instruction, we might overrun our soft limit.
47 DefaultSafetyMargin = 128 * 1024,
48#if defined(Q_OS_IOS)
49 PlatformStackSize = 1024 * 1024,
51#elif defined(Q_OS_MACOS)
52 PlatformStackSize = 8 * 1024 * 1024,
54#elif defined(Q_OS_ANDROID)
55 // Android appears to have 1MB stacks.
56 PlatformStackSize = 1024 * 1024,
58#elif defined(Q_OS_LINUX)
59 // On linux, we assume 8MB stacks if rlimit doesn't work.
60 PlatformStackSize = 8 * 1024 * 1024,
62#elif defined(Q_OS_QNX)
63 // QNX's stack is only 512k by default
64 PlatformStackSize = 512 * 1024,
66#else
67 // We try to claim 512k if we don't know anything else.
68 PlatformStackSize = 512 * 1024,
70#endif
71};
72
73// We may not be able to take the negative of the type
74// used to represent stack size, but we can always add
75// or subtract it to/from a quint8 pointer.
76
77template<typename Size>
78static void *incrementStackPointer(void *base, Size amount)
79{
80#if Q_STACK_GROWTH_DIRECTION > 0
81 return static_cast<quint8 *>(base) + amount;
82#else
83 return static_cast<quint8 *>(base) - amount;
84#endif
85}
86
87template<typename Size>
88static void *decrementStackPointer(void *base, Size amount)
89{
90#if Q_STACK_GROWTH_DIRECTION > 0
91 return static_cast<quint8 *>(base) - amount;
92#else
93 return static_cast<quint8 *>(base) + amount;
94#endif
95}
96
105
106#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX)
107
108// On linux and darwin, on the main thread, the pthread functions
109// may not return the true stack size since the main thread stack
110// may grow. Use rlimit instead. rlimit does not work for secondary
111// threads, though. If getrlimit fails, we assume the platform
112// stack size.
113static qsizetype getMainStackSizeFromRlimit()
114{
115 rlimit limit;
116 return (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY)
117 ? qsizetype(limit.rlim_cur)
119}
120#endif
121
122#if defined(Q_OS_INTEGRITY)
123
124StackProperties stackProperties()
125{
126 Address stackLow, stackHigh;
127 CheckSuccess(GetTaskStackLimits(CurrentTask(), &stackLow, &stackHigh));
128# if Q_STACK_GROWTH_DIRECTION < 0
129 return createStackProperties(reinterpret_cast<void *>(stackHigh), stackHigh - stackLow);
130# else
131 return createStackProperties(reinterpret_cast<void *>(stackLow), stackHigh - stackLow);
132# endif
133}
134
135#elif defined(Q_OS_DARWIN)
136
137StackProperties stackProperties()
138{
139 pthread_t thread = pthread_self();
141 pthread_get_stackaddr_np(thread),
142 pthread_main_np()
143 ? getMainStackSizeFromRlimit()
144 : qsizetype(pthread_get_stacksize_np(thread)));
145}
146
147#elif defined(Q_OS_WIN)
148
149static_assert(Q_STACK_GROWTH_DIRECTION < 0);
150StackProperties stackProperties()
151{
152 // MinGW complains about out of bounds array access in compiler headers
154 QT_WARNING_DISABLE_GCC("-Warray-bounds")
155
156 // Get the stack base.
157# ifdef _WIN64
158 PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
159# else
160 PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
161# endif
162
164
165 quint8 *stackBase = reinterpret_cast<quint8 *>(pTib->StackBase);
166
167 // Get the stack limit. tib->StackLimit is the size of the
168 // currently mapped stack. The address space is larger.
169 MEMORY_BASIC_INFORMATION mbi = {};
170 if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
171 qFatal("Could not retrieve memory information for stack.");
172
173 quint8 *stackLimit = reinterpret_cast<quint8 *>(mbi.AllocationBase);
174 return createStackProperties(stackBase, qsizetype(stackBase - stackLimit));
175}
176
177#elif defined(Q_OS_OPENBSD)
178
179StackProperties stackProperties()
180{
181 // From the OpenBSD docs:
182 //
183 // The pthread_stackseg_np() function returns information about the given thread's stack.
184 // A stack_t is the same as a struct sigaltstack (see sigaltstack(2)) except the ss_sp
185 // variable points to the top of the stack instead of the base.
186 //
187 // Since the example in the sigaltstack(2) documentation shows ss_sp being assigned the result
188 // of a malloc() call, we can assume that "top of the stack" means "the highest address", not
189 // the logical top of the stack.
190
191 stack_t ss;
192 rc = pthread_stackseg_np(pthread_self, &ss);
193#if Q_STACK_GROWTH_DIRECTION < 0
194 return createStackProperties(ss.ss_sp);
195#else
196 return createStackProperties(decrementStackPointer(ss.ss_sp, ss.ss_size));
197#endif
198}
199
200#elif defined(Q_OS_QNX)
201
202StackProperties stackProperties()
203{
204 const auto tid = pthread_self();
205 procfs_status status;
206 status.tid = tid;
207
208 const int fd = open("/proc/self/ctl", O_RDONLY);
209 if (fd == -1)
210 qFatal("Could not open /proc/self/ctl");
211 const auto guard = qScopeGuard([fd]() { close(fd); });
212
213 if (devctl(fd, DCMD_PROC_TIDSTATUS, &status, sizeof(status), 0) != EOK)
214 qFatal("Could not query thread status for current thread");
215
216 if (status.tid != tid)
217 qFatal("Thread status query returned garbage");
218
219#if Q_STACK_GROWTH_DIRECTION < 0
221 decrementStackPointer(reinterpret_cast<void *>(status.stkbase), status.stksize),
222 status.stksize);
223#else
224 return createStackProperties(reinterpret_cast<void *>(status.stkbase), status.stksize);
225#endif
226}
227
228#elif defined(Q_OS_WASM)
229
230StackProperties stackProperties()
231{
232 const uintptr_t base = emscripten_stack_get_base();
233 const uintptr_t end = emscripten_stack_get_end();
234 const size_t size = base - end;
235 return createStackProperties(reinterpret_cast<void *>(base), size);
236}
237
238#elif defined(Q_OS_VXWORKS)
239
240StackProperties stackProperties()
241{
242 TASK_DESC taskDescription;
243 taskInfoGet(taskIdSelf(), &taskDescription);
244 return createStackProperties(taskDescription.td_pStackBase, taskDescription.td_stackSize);
245}
246
247#else
248
250{
251 // If stackSize is given, do not trust the stack size returned by pthread_attr_getstack
252
253 pthread_t thread = pthread_self();
254 pthread_attr_t sattr;
255# if defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(Q_OS_NETBSD)
256 pthread_attr_init(&sattr);
257 pthread_attr_get_np(thread, &sattr);
258# else
259 pthread_getattr_np(thread, &sattr);
260# endif
261
262 // pthread_attr_getstack returns the address of the memory region, which is the physical
263 // base of the stack, not the logical one.
264 void *stackBase;
265 size_t regionSize;
266 int rc = pthread_attr_getstack(&sattr, &stackBase, &regionSize);
267 pthread_attr_destroy(&sattr);
268
269 if (rc)
270 qFatal("Cannot find stack base");
271
272# if Q_STACK_GROWTH_DIRECTION < 0
273 stackBase = decrementStackPointer(stackBase, regionSize);
274# endif
275
276 return createStackProperties(stackBase, stackSize ? stackSize : regionSize);
277}
278
279#if defined(Q_OS_LINUX)
280
281static void *stackBaseFromLibc()
282{
283#if defined(__GLIBC__) && QT_CONFIG(dlopen)
284 void **libcStackEnd = static_cast<void **>(dlsym(RTLD_DEFAULT, "__libc_stack_end"));
285 if (!libcStackEnd)
286 return nullptr;
287 if (void *stackBase = *libcStackEnd)
288 return stackBase;
289#endif
290 return nullptr;
291}
292
293struct StackSegment {
296};
297
298static StackSegment stackSegmentFromProc()
299{
300 QFile maps(QStringLiteral("/proc/self/maps"));
301 if (!maps.open(QIODevice::ReadOnly))
302 return {0, 0};
303
304 const quintptr stackAddr = reinterpret_cast<quintptr>(&maps);
305
306 char buffer[1024];
307 while (true) {
308 const qint64 length = maps.readLine(buffer, 1024);
309 if (length <= 0)
310 break;
311
313 bool ok = false;
314
315 const qsizetype boundary = line.indexOf('-');
316 if (boundary < 0)
317 continue;
318
319 const quintptr base = line.sliced(0, boundary).toULongLong(&ok, 16);
320 if (!ok || base > stackAddr)
321 continue;
322
323 const qsizetype end = line.indexOf(' ', boundary);
324 if (end < 0)
325 continue;
326
327 const quintptr limit = line.sliced(boundary + 1, end - boundary - 1).toULongLong(&ok, 16);
328 if (!ok || limit <= stackAddr)
329 continue;
330
331 return {base, limit};
332 }
333
334 return {0, 0};
335}
336
337StackProperties stackProperties()
338{
339 if (getpid() != static_cast<pid_t>(syscall(SYS_gettid)))
340 return stackPropertiesGeneric();
341
342 // On linux (including android), the pthread functions are expensive
343 // and unreliable on the main thread.
344
345 // First get the stack size from rlimit
346 const qsizetype stackSize = getMainStackSizeFromRlimit();
347
348 // If we have glibc and libdl, we can query a special symbol in glibc to find the base.
349 // That is extremely cheap, compared to all other options.
350 if (stackSize) {
351 if (void *base = stackBaseFromLibc())
352 return createStackProperties(base, stackSize);
353 }
354
355 // Try to read the stack segment from /proc/self/maps if possible.
356 const StackSegment segment = stackSegmentFromProc();
357 if (segment.base) {
358# if Q_STACK_GROWTH_DIRECTION > 0
359 void *stackBase = reinterpret_cast<void *>(segment.base);
360# else
361 void *stackBase = reinterpret_cast<void *>(segment.limit);
362# endif
364 stackBase, stackSize ? stackSize : segment.limit - segment.base);
365 }
366
367 // If we can't read /proc/self/maps, use the pthread functions after all, but
368 // override the stackSize. The main thread can grow its stack, and the pthread
369 // functions typically return the currently allocated stack size.
370 return stackPropertiesGeneric(stackSize);
371}
372
373#else // Q_OS_LINUX
374
376
377#endif // Q_OS_LINUX
378#endif
379
380} // namespace QV4
381
\inmodule QtCore
Definition qfile.h:93
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
QString sliced(qsizetype pos) const &
Definition qstring.h:394
qulonglong toULongLong(bool *ok=nullptr, int base=10) const
Returns the string converted to an {unsigned long long} using base base, which is 10 by default and m...
Combined button and popup list for selecting options.
static void * incrementStackPointer(void *base, Size amount)
static void * decrementStackPointer(void *base, Size amount)
@ PlatformStackSize
@ DefaultSafetyMargin
@ PlatformSafetyMargin
static StackProperties createStackProperties(void *base, qsizetype size=PlatformStackSize)
StackProperties stackProperties()
StackProperties stackPropertiesGeneric(qsizetype stackSize=0)
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_PUSH
#define qFatal
Definition qlogging.h:168
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLuint buffer
GLuint64 GLenum GLint fd
GLuint segment
GLint limit
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define QStringLiteral(str)
size_t quintptr
Definition qtypes.h:167
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
unsigned char quint8
Definition qtypes.h:46
static const uint base
Definition qurlidna.cpp:20
#define Q_STACK_GROWTH_DIRECTION
file open(QIODevice::ReadOnly)