Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}