blob: a7bf655070446b194b148906af204a02f6132cff [file]
//===----------------------------------------------------------------------===//
//
// 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;
}