blob: 0a8bb73d43f12bd517de1178ddfbb6f5e6945ed9 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <pthread.h>
#include <gtest/gtest.h>
#include <bionic/libc/private/ScopedPthreadMutexLocker.h>
#include <private/pthread_context.h>
#include <thread_context.h>
struct Args {
Args()
: has_started(false),
should_exit(false) {
pthread_mutex_init(&mu, NULL);
pthread_cond_init(&cond, NULL);
}
~Args() {
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mu);
}
bool has_started;
bool should_exit;
pthread_mutex_t mu;
pthread_cond_t cond;
};
static void* WaitFn(void* arg) {
Args* args = static_cast<Args*>(arg);
{
ScopedPthreadMutexLocker lock(&args->mu);
args->has_started = true;
pthread_cond_signal(&args->cond);
while (!args->should_exit)
pthread_cond_wait(&args->cond, &args->mu);
}
return NULL;
}
TEST(pthread_thread_context, get_thread_infos) {
ASSERT_EQ(1, __pthread_get_thread_count(true));
Args args;
// Create a new thread and wait until it finishes its
// initialization.
// TODO(crbug.com/372248): Remove the wait. This synchronization is
// only for Bare Metal mode. On Bare Metal mode, pthread_context
// uses |stack_end_from_irt| in pthread_internal_t to find the
// location of a stack. Unlike other pthread attributes, this is
// filled by the created thread, not by the creator. So, there is a
// race. pthread_context may read an uninitialized value in
// |stack_end_from_irt|.
pthread_t thread;
{
ScopedPthreadMutexLocker lock(&args.mu);
ASSERT_EQ(0, pthread_create(&thread, NULL, WaitFn, &args));
while (!args.has_started)
pthread_cond_wait(&args.cond, &args.mu);
}
// Verify data in the thread list.
__pthread_context_info_t infos[100];
int thread_count = __pthread_get_thread_infos(true, true, 100, infos);
ASSERT_EQ(2, thread_count);
for (int i = 0; i < thread_count; ++i) {
ASSERT_TRUE(infos[i].stack_base != NULL);
ASSERT_GT(infos[i].stack_size, 0);
#if defined(BARE_METAL_BIONIC)
ASSERT_EQ(infos[i].stack_size, 1024 * 1024);
#endif
}
// Let the thread finish.
{
ScopedPthreadMutexLocker lock(&args.mu);
args.should_exit = true;
pthread_cond_signal(&args.cond);
}
pthread_join(thread, NULL);
}
TEST(pthread_thread_context, get_cur_thread_context) {
__pthread_context_info_t info;
info.has_context_regs = true;
__pthread_get_current_thread_info(&info);
ASSERT_FALSE(info.has_context_regs);
SAVE_CONTEXT_REGS();
memset(&info, 0, sizeof(info));
__pthread_get_current_thread_info(&info);
#if defined(__x86_64__)
ASSERT_TRUE(info.has_context_regs);
ASSERT_NE(0, info.context_regs[REG_RIP]);
#elif defined(__arm__)
ASSERT_TRUE(info.has_context_regs);
ASSERT_NE(0, info.context_regs[15]);
#else
ASSERT_FALSE(info.has_context_regs);
#endif
CLEAR_CONTEXT_REGS();
info.has_context_regs = true;
__pthread_get_current_thread_info(&info);
ASSERT_FALSE(info.has_context_regs);
}