| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// Integration tests for pthread_getstack_np. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "hdr/pthread_macros.h" |
| #include "hdr/stdint_proxy.h" |
| #include "hdr/sys_mman_macros.h" |
| #include "src/pthread/pthread_attr_destroy.h" |
| #include "src/pthread/pthread_attr_init.h" |
| #include "src/pthread/pthread_attr_setstack.h" |
| #include "src/pthread/pthread_create.h" |
| #include "src/pthread/pthread_getstack_np.h" |
| #include "src/pthread/pthread_join.h" |
| #include "src/pthread/pthread_self.h" |
| #include "src/sys/mman/mmap.h" |
| #include "src/sys/mman/munmap.h" |
| #include "src/unistd/sysconf.h" |
| #include "test/IntegrationTest/test.h" |
| |
| static void check_readable(const void *start, size_t size) { |
| size_t pagesize = LIBC_NAMESPACE::sysconf(_SC_PAGESIZE); |
| auto *bytes = static_cast<const volatile char *>(start); |
| for (size_t offset = 0; offset < size; offset += pagesize) |
| (void)bytes[offset]; |
| if (size > 0) |
| (void)bytes[size - 1]; |
| } |
| |
| static void *child_func(void *) { |
| void *stackaddr = nullptr; |
| size_t stacksize = 0; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_getstack_np(LIBC_NAMESPACE::pthread_self(), |
| &stackaddr, &stacksize), |
| 0); |
| ASSERT_NE(stackaddr, static_cast<void *>(nullptr)); |
| ASSERT_NE(stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP)); |
| ASSERT_TRUE(stacksize > 0); |
| |
| uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&stackaddr); |
| uintptr_t stack_low = reinterpret_cast<uintptr_t>(stackaddr); |
| uintptr_t stack_high = stack_low + stacksize; |
| ASSERT_TRUE(local_var_addr >= stack_low); |
| ASSERT_TRUE(local_var_addr < stack_high); |
| |
| check_readable(stackaddr, stacksize); |
| |
| return nullptr; |
| } |
| |
| static void test_main_thread() { |
| void *stackaddr = nullptr; |
| size_t stacksize = 1234; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_getstack_np(LIBC_NAMESPACE::pthread_self(), |
| &stackaddr, &stacksize), |
| 0); |
| ASSERT_NE(stackaddr, static_cast<void *>(nullptr)); |
| ASSERT_EQ(stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP)); |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(stackaddr) % |
| LIBC_NAMESPACE::sysconf(_SC_PAGESIZE), |
| static_cast<uintptr_t>(0)); |
| |
| // Stack grows downwards on linux, so local variables on the main thread stack |
| // should be at addresses lower than the initial stack top address. |
| uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&stackaddr); |
| ASSERT_TRUE(local_var_addr < reinterpret_cast<uintptr_t>(stackaddr)); |
| |
| // The stack region between the current stack frame and the top of the stack |
| // should be readable. |
| check_readable(&stackaddr, |
| reinterpret_cast<uintptr_t>(stackaddr) - local_var_addr); |
| } |
| |
| static void test_child_thread_default() { |
| pthread_t th; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr, child_func, nullptr), |
| 0); |
| void *retval = nullptr; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0); |
| } |
| |
| static void test_child_thread_custom_stack() { |
| size_t custom_stacksize = PTHREAD_STACK_MIN * 2; |
| void *custom_stack = |
| LIBC_NAMESPACE::mmap(nullptr, custom_stacksize, PROT_READ | PROT_WRITE, |
| MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| ASSERT_NE(custom_stack, MAP_FAILED); |
| ASSERT_NE(custom_stack, static_cast<void *>(nullptr)); |
| |
| pthread_attr_t attr; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setstack(&attr, custom_stack, |
| custom_stacksize), |
| 0); |
| |
| pthread_t th; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, &attr, child_func, nullptr), 0); |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0); |
| |
| void *stackaddr = nullptr; |
| size_t stacksize = 0; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_getstack_np(th, &stackaddr, &stacksize), 0); |
| ASSERT_EQ(stackaddr, custom_stack); |
| ASSERT_EQ(stacksize, custom_stacksize); |
| check_readable(stackaddr, stacksize); |
| |
| void *retval = nullptr; |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0); |
| ASSERT_EQ(LIBC_NAMESPACE::munmap(custom_stack, custom_stacksize), 0); |
| } |
| |
| TEST_MAIN() { |
| test_main_thread(); |
| test_child_thread_default(); |
| test_child_thread_custom_stack(); |
| return 0; |
| } |