/src/serenity/AK/StackInfo.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Assertions.h> |
8 | | #include <AK/Platform.h> |
9 | | #include <AK/StackInfo.h> |
10 | | #include <stdio.h> |
11 | | #include <string.h> |
12 | | |
13 | | #ifdef AK_OS_SERENITY |
14 | | # include <serenity.h> |
15 | | #elif defined(AK_OS_LINUX) || defined(AK_LIBC_GLIBC) || defined(AK_OS_MACOS) || defined(AK_OS_NETBSD) || defined(AK_OS_SOLARIS) || defined(AK_OS_HAIKU) |
16 | | # include <pthread.h> |
17 | | # include <sys/resource.h> |
18 | | #elif defined(AK_OS_FREEBSD) || defined(AK_OS_OPENBSD) |
19 | | # include <pthread.h> |
20 | | # include <pthread_np.h> |
21 | | #elif defined(AK_OS_WINDOWS) |
22 | | # include <Windows.h> |
23 | | // NOTE: Prevent clang-format from re-ordering this header order |
24 | | # include <Processthreadsapi.h> |
25 | | #endif |
26 | | |
27 | | namespace AK { |
28 | | |
29 | | StackInfo::StackInfo() |
30 | 54 | { |
31 | | #ifdef AK_OS_SERENITY |
32 | | if (get_stack_bounds(&m_base, &m_size) < 0) { |
33 | | perror("get_stack_bounds"); |
34 | | VERIFY_NOT_REACHED(); |
35 | | } |
36 | | #elif defined(AK_OS_LINUX) or defined(AK_LIBC_GLIBC) or defined(AK_OS_FREEBSD) or defined(AK_OS_NETBSD) or defined(AK_OS_SOLARIS) or defined(AK_OS_HAIKU) |
37 | | int rc; |
38 | 54 | pthread_attr_t attr; |
39 | 54 | pthread_attr_init(&attr); |
40 | | |
41 | 54 | # if defined(AK_OS_LINUX) or defined(AK_LIBC_GLIBC) or defined(AK_OS_HAIKU) |
42 | 54 | if ((rc = pthread_getattr_np(pthread_self(), &attr)) != 0) { |
43 | 0 | fprintf(stderr, "pthread_getattr_np: %s\n", strerror(rc)); |
44 | 0 | VERIFY_NOT_REACHED(); |
45 | 0 | } |
46 | | # else |
47 | | if ((rc = pthread_attr_get_np(pthread_self(), &attr)) != 0) { |
48 | | fprintf(stderr, "pthread_attr_get_np: %s\n", strerror(rc)); |
49 | | VERIFY_NOT_REACHED(); |
50 | | } |
51 | | # endif |
52 | | |
53 | 54 | if ((rc = pthread_attr_getstack(&attr, (void**)&m_base, &m_size)) != 0) { |
54 | 0 | fprintf(stderr, "pthread_attr_getstack: %s\n", strerror(rc)); |
55 | 0 | VERIFY_NOT_REACHED(); |
56 | 0 | } |
57 | 54 | pthread_attr_destroy(&attr); |
58 | | #elif defined(AK_OS_MACOS) |
59 | | // NOTE: !! On MacOS, pthread_get_stackaddr_np gives the TOP of the stack, not the bottom! |
60 | | FlatPtr top_of_stack = (FlatPtr)pthread_get_stackaddr_np(pthread_self()); |
61 | | m_size = (size_t)pthread_get_stacksize_np(pthread_self()); |
62 | | // https://github.com/rust-lang/rust/issues/43347#issuecomment-316783599 |
63 | | // https://developer.apple.com/library/archive/qa/qa1419/_index.html |
64 | | // |
65 | | // MacOS seems inconsistent on what stack size is given for the main thread. |
66 | | // According to the Apple docs, default for main thread is 8MB, and default for |
67 | | // other threads is 512KB |
68 | | if (pthread_main_np() == 1) { |
69 | | // Apparently the main thread's stack size is not reported correctly on macOS |
70 | | // but we can use getrlimit to get the correct value. |
71 | | rlimit limit {}; |
72 | | getrlimit(RLIMIT_STACK, &limit); |
73 | | if (limit.rlim_cur == RLIM_INFINITY) { |
74 | | m_size = 8 * MiB; |
75 | | } else { |
76 | | m_size = limit.rlim_cur; |
77 | | } |
78 | | } |
79 | | m_base = top_of_stack - m_size; |
80 | | #elif defined(AK_OS_OPENBSD) |
81 | | int rc; |
82 | | stack_t thread_stack; |
83 | | if ((rc = pthread_stackseg_np(pthread_self(), &thread_stack)) != 0) { |
84 | | fprintf(stderr, "pthread_stackseg_np: %s\n", strerror(rc)); |
85 | | VERIFY_NOT_REACHED(); |
86 | | } |
87 | | FlatPtr top_of_stack = (FlatPtr)thread_stack.ss_sp; |
88 | | m_size = (size_t)thread_stack.ss_size; |
89 | | m_base = top_of_stack - m_size; |
90 | | #elif defined(AK_OS_WINDOWS) |
91 | | ULONG_PTR low_limit = 0; |
92 | | ULONG_PTR high_limit = 0; |
93 | | GetCurrentThreadStackLimits(&low_limit, &high_limit); |
94 | | |
95 | | m_base = static_cast<FlatPtr>(low_limit); |
96 | | m_size = static_cast<size_t>(high_limit - low_limit); |
97 | | #else |
98 | | # pragma message "StackInfo not supported on this platform! Recursion checks and stack scans may not work properly" |
99 | | m_size = (size_t)~0; |
100 | | m_base = 0; |
101 | | #endif |
102 | | |
103 | 54 | m_top = m_base + m_size; |
104 | | |
105 | | #if defined(AK_OS_LINUX) && !defined(AK_LIBC_GLIBC) |
106 | | // Note: musl libc always gives the initial size of the main thread's stack |
107 | | if (getpid() == static_cast<pid_t>(gettid())) { |
108 | | rlimit limit; |
109 | | getrlimit(RLIMIT_STACK, &limit); |
110 | | rlim_t size = limit.rlim_cur; |
111 | | if (size == RLIM_INFINITY) |
112 | | size = 8 * 0x10000; |
113 | | // account for a guard page |
114 | | size -= static_cast<rlim_t>(sysconf(_SC_PAGESIZE)); |
115 | | m_size = static_cast<size_t>(size); |
116 | | m_base = m_top - m_size; |
117 | | } |
118 | | #endif |
119 | 54 | } |
120 | | |
121 | | } |