[libc] Add GNU extension functions pthread_setname_np and pthread_getname_np.

Reviewed By: michaelrj, lntue

Differential Revision: https://reviews.llvm.org/D130872
This commit is contained in:
Siva Chandra Reddy 2022-07-29 19:50:35 +00:00
parent 1c52b4f798
commit 658c84e415
16 changed files with 368 additions and 1 deletions

View File

@ -251,8 +251,10 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_create
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_join
libc.src.pthread.pthread_self
libc.src.pthread.pthread_setname_np
libc.src.pthread.pthread_mutex_destroy
libc.src.pthread.pthread_mutex_init
libc.src.pthread.pthread_mutex_lock

View File

@ -111,10 +111,30 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> {
]
>;
HeaderSpec PThread = HeaderSpec<
"pthread.h",
[], // Macros
[], // Types
[], // Enumerations
[
FunctionSpec<
"pthread_setname_np",
RetValSpec<IntType>,
[ArgSpec<PThreadTType>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"pthread_getname_np",
RetValSpec<IntType>,
[ArgSpec<PThreadTType>, ArgSpec<CharPtr>, ArgSpec<SizeTType>]
>,
]
>;
let Headers = [
CType,
FEnv,
Math,
PThread,
StdIO,
String,
];

View File

@ -50,7 +50,6 @@ def POSIX : StandardSpec<"POSIX"> {
ConstType ConstPThreadMutexTPtr = ConstType<PThreadMutexTPtr>;
ConstType ConstRestrictedPThreadMutexTPtr = ConstType<RestrictedPThreadMutexTPtr>;
NamedType PThreadTType = NamedType<"pthread_t">;
PtrType PThreadTPtr = PtrType<PThreadTType>;
RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;

View File

@ -107,6 +107,8 @@ def FILE : NamedType<"FILE">;
def FILEPtr : PtrType<FILE>;
def FILERestrictedPtr : RestrictedPtrType<FILE>;
def PThreadTType : NamedType<"pthread_t">;
//added because __assert_fail needs it.
def UnsignedType : NamedType<"unsigned">;

View File

@ -85,6 +85,8 @@ public:
// Return true if any write operation(s) failed due to insufficient size.
bool overflow() const { return err; }
size_t bufsize() const { return data.size(); }
};
} // namespace cpp

View File

@ -25,6 +25,8 @@ add_header_library(
DEPENDS
libc.src.__support.common
libc.src.__support.CPP.atomic
libc.src.__support.CPP.string_view
libc.src.__support.CPP.stringstream
)
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)

View File

@ -30,6 +30,8 @@ add_object_library(
libc.include.sys_syscall
libc.src.__support.CPP.atomic
libc.src.__support.CPP.error
libc.src.__support.CPP.stringstream
libc.src.__support.CPP.string_view
libc.src.__support.threads.thread_common
COMPILE_OPTIONS
-O3

View File

@ -8,8 +8,10 @@
#include "src/__support/threads/thread.h"
#include "config/linux/app.h"
#include "src/__support/CPP/StringView.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/error.h"
#include "src/__support/CPP/stringstream.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
#include "src/__support/threads/linux/futex_word.h" // For FutexWordType
@ -17,7 +19,10 @@
#include <arm_acle.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/futex.h>
#include <linux/prctl.h> // For PR_SET_NAME
#include <linux/sched.h> // For CLONE_* flags.
#include <stdint.h>
#include <sys/mman.h> // For PROT_* and MAP_* definitions.
@ -33,6 +38,7 @@ static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
#error "SYS_mmap or SYS_mmap2 not available on the target platform"
#endif
static constexpr size_t NAME_SIZE_MAX = 16; // Includes the null terminator
static constexpr size_t DEFAULT_STACK_SIZE = (1 << 16); // 64KB
static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
static constexpr unsigned CLONE_SYSCALL_FLAGS =
@ -278,4 +284,95 @@ bool Thread::operator==(const Thread &thread) const {
return attrib->tid == thread.attrib->tid;
}
static constexpr cpp::StringView THREAD_NAME_PATH_PREFIX("/proc/self/task/");
static constexpr size_t THREAD_NAME_PATH_SIZE =
THREAD_NAME_PATH_PREFIX.size() +
IntegerToString<int>::BUFSIZE + // Size of tid
1 + // For '/' character
5; // For the file name "comm" and the nullterminator.
static void construct_thread_name_file_path(cpp::StringStream &stream,
int tid) {
stream << THREAD_NAME_PATH_PREFIX << tid << '/' << cpp::StringView("comm")
<< cpp::StringStream::ENDS;
}
int Thread::set_name(const cpp::StringView &name) {
if (name.size() >= NAME_SIZE_MAX)
return ERANGE;
if (*this == self) {
// If we are setting the name of the current thread, then we can
// use the syscall to set the name.
int retval = __llvm_libc::syscall(SYS_prctl, PR_SET_NAME, name.data());
if (retval < 0)
return -retval;
else
return 0;
}
char path_name_buffer[THREAD_NAME_PATH_SIZE];
cpp::StringStream path_stream(path_name_buffer);
construct_thread_name_file_path(path_stream, attrib->tid);
#ifdef SYS_open
int fd = __llvm_libc::syscall(SYS_open, path_name_buffer, O_RDWR);
#else
int fd = __llvm_libc::syscall(SYS_openat, AT_FDCWD, path_name_buffer, O_RDWR);
#endif
if (fd < 0)
return -fd;
int retval = __llvm_libc::syscall(SYS_write, fd, name.data(), name.size());
__llvm_libc::syscall(SYS_close, fd);
if (retval < 0)
return -retval;
else if (retval != int(name.size()))
return EIO;
else
return 0;
}
int Thread::get_name(cpp::StringStream &name) const {
if (name.bufsize() < NAME_SIZE_MAX)
return ERANGE;
char name_buffer[NAME_SIZE_MAX];
if (*this == self) {
// If we are getting the name of the current thread, then we can
// use the syscall to get the name.
int retval = __llvm_libc::syscall(SYS_prctl, PR_GET_NAME, name_buffer);
if (retval < 0)
return -retval;
name << name_buffer;
return 0;
}
char path_name_buffer[THREAD_NAME_PATH_SIZE];
cpp::StringStream path_stream(path_name_buffer);
construct_thread_name_file_path(path_stream, attrib->tid);
#ifdef SYS_open
int fd = __llvm_libc::syscall(SYS_open, path_name_buffer, O_RDONLY);
#else
int fd =
__llvm_libc::syscall(SYS_openat, AT_FDCWD, path_name_buffer, O_RDONLY);
#endif
if (fd < 0)
return -fd;
int retval = __llvm_libc::syscall(SYS_read, fd, name_buffer, NAME_SIZE_MAX);
__llvm_libc::syscall(SYS_close, fd);
if (retval < 0)
return -retval;
if (retval == NAME_SIZE_MAX)
return ERANGE;
if (name_buffer[retval - 1] == '\n')
name_buffer[retval - 1] = '\0';
else
name_buffer[retval] = '\0';
name << name_buffer;
return 0;
}
} // namespace __llvm_libc

View File

@ -9,7 +9,9 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
#include "src/__support/CPP/StringView.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/stringstream.h"
#include "src/__support/architectures.h"
#include <stddef.h> // For size_t
@ -165,6 +167,12 @@ struct Thread {
// Return true if this thread is equal to the other thread.
bool operator==(const Thread &other) const;
// Set the name of the thread. Return the error number on error.
int set_name(const cpp::StringView &name);
// Return the name of the thread in |name|. Return the error number of error.
int get_name(cpp::StringStream &name) const;
};
extern thread_local Thread self;

View File

@ -301,3 +301,29 @@ add_entrypoint_object(
libc.include.pthread
libc.src.__support.threads.thread
)
add_entrypoint_object(
pthread_setname_np
SRCS
pthread_setname_np.cpp
HDRS
pthread_setname_np.h
DEPENDS
libc.include.pthread
libc.src.__support.CPP.array_ref
libc.src.__support.CPP.string_view
libc.src.__support.threads.thread
)
add_entrypoint_object(
pthread_getname_np
SRCS
pthread_getname_np.cpp
HDRS
pthread_getname_np.h
DEPENDS
libc.include.pthread
libc.src.__support.CPP.array_ref
libc.src.__support.CPP.stringstream
libc.src.__support.threads.thread
)

View File

@ -0,0 +1,32 @@
//===-- Linux implementation of the pthread_setname_np function -----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "pthread_getname_np.h"
#include "src/__support/CPP/ArrayRef.h"
#include "src/__support/CPP/StringView.h"
#include "src/__support/common.h"
#include "src/__support/threads/thread.h"
#include <pthread.h>
#include <stddef.h>
namespace __llvm_libc {
static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread),
"Mismatch between pthread_t and internal Thread.");
LLVM_LIBC_FUNCTION(int, pthread_getname_np,
(pthread_t th, char *buf, size_t len)) {
auto *thread = reinterpret_cast<__llvm_libc::Thread *>(&th);
cpp::MutableArrayRef<char> name_buf(buf, len);
cpp::StringStream name_stream(name_buf);
return thread->get_name(name_stream);
}
} // namespace __llvm_libc

View File

@ -0,0 +1,21 @@
//===-- Implementation header for pthread_getname_np function ---*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H
#include <pthread.h>
#include <stddef.h>
namespace __llvm_libc {
int pthread_getname_np(pthread_t, char *, size_t);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H

View File

@ -0,0 +1,28 @@
//===-- Linux implementation of the pthread_setname_np function -----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "pthread_setname_np.h"
#include "src/__support/CPP/StringView.h"
#include "src/__support/CPP/error.h"
#include "src/__support/common.h"
#include "src/__support/threads/thread.h"
#include <pthread.h>
namespace __llvm_libc {
static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread),
"Mismatch between pthread_t and internal Thread.");
LLVM_LIBC_FUNCTION(int, pthread_setname_np, (pthread_t th, const char *name)) {
auto *thread = reinterpret_cast<__llvm_libc::Thread *>(&th);
return thread->set_name(cpp::StringView(name));
}
} // namespace __llvm_libc

View File

@ -0,0 +1,20 @@
//===-- Implementation header for pthread_setname_np function ---*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H
#include <pthread.h>
namespace __llvm_libc {
int pthread_setname_np(pthread_t, const char *name);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H

View File

@ -53,3 +53,26 @@ add_integration_test(
libc.src.pthread.pthread_join
libc.src.pthread.pthread_self
)
add_integration_test(
pthread_name_test
SUITE
libc-pthread-integration-tests
SRCS
pthread_name_test.cpp
LOADER
libc.loader.linux.crt1
DEPENDS
libc.include.errno
libc.include.pthread
libc.src.errno.errno
libc.src.pthread.pthread_create
libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_join
libc.src.pthread.pthread_mutex_destroy
libc.src.pthread.pthread_mutex_init
libc.src.pthread.pthread_mutex_lock
libc.src.pthread.pthread_mutex_unlock
libc.src.pthread.pthread_self
libc.src.pthread.pthread_setname_np
)

View File

@ -0,0 +1,83 @@
//===-- Tests for pthread_equal -------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "src/__support/CPP/StringView.h"
#include "src/pthread/pthread_create.h"
#include "src/pthread/pthread_getname_np.h"
#include "src/pthread/pthread_join.h"
#include "src/pthread/pthread_mutex_destroy.h"
#include "src/pthread/pthread_mutex_init.h"
#include "src/pthread/pthread_mutex_lock.h"
#include "src/pthread/pthread_mutex_unlock.h"
#include "src/pthread/pthread_self.h"
#include "src/pthread/pthread_setname_np.h"
#include "utils/IntegrationTest/test.h"
#include <errno.h>
#include <pthread.h>
using StringView = __llvm_libc::cpp::StringView;
char child_thread_name_buffer[16];
pthread_mutex_t mutex;
static void *child_func(void *) {
__llvm_libc::pthread_mutex_lock(&mutex);
auto self = __llvm_libc::pthread_self();
__llvm_libc::pthread_getname_np(self, child_thread_name_buffer, 16);
__llvm_libc::pthread_mutex_unlock(&mutex);
return nullptr;
}
TEST_MAIN() {
// We init and lock the mutex so that we guarantee that the child thread is
// waiting after startup.
ASSERT_EQ(__llvm_libc::pthread_mutex_init(&mutex, nullptr), 0);
ASSERT_EQ(__llvm_libc::pthread_mutex_lock(&mutex), 0);
auto main_thread = __llvm_libc::pthread_self();
const char MAIN_THREAD_NAME[] = "main_thread";
char thread_name_buffer[16];
ASSERT_EQ(__llvm_libc::pthread_setname_np(main_thread, MAIN_THREAD_NAME), 0);
ASSERT_EQ(
__llvm_libc::pthread_getname_np(main_thread, thread_name_buffer, 16), 0);
ASSERT_TRUE(StringView(MAIN_THREAD_NAME)
.equals(StringView(
reinterpret_cast<const char *>(thread_name_buffer))));
pthread_t th;
ASSERT_EQ(__llvm_libc::pthread_create(&th, nullptr, child_func, nullptr), 0);
// This new thread should of course not be equal to the main thread.
const char CHILD_THREAD_NAME[] = "child_thread";
ASSERT_EQ(__llvm_libc::pthread_setname_np(th, CHILD_THREAD_NAME), 0);
ASSERT_EQ(__llvm_libc::pthread_getname_np(th, thread_name_buffer, 16), 0);
ASSERT_TRUE(StringView(CHILD_THREAD_NAME)
.equals(StringView(
reinterpret_cast<const char *>(thread_name_buffer))));
ASSERT_EQ(__llvm_libc::pthread_mutex_unlock(&mutex), 0);
void *retval;
ASSERT_EQ(__llvm_libc::pthread_join(th, &retval), 0);
ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
// Make sure that the child thread saw it name correctly.
ASSERT_TRUE(StringView(CHILD_THREAD_NAME)
.equals(StringView(reinterpret_cast<const char *>(
child_thread_name_buffer))));
__llvm_libc::pthread_mutex_destroy(&mutex);
ASSERT_EQ(__llvm_libc::pthread_setname_np(main_thread,
"a really long name for a thread"),
ERANGE);
char smallbuf[1];
ASSERT_EQ(__llvm_libc::pthread_getname_np(main_thread, smallbuf, 1), ERANGE);
return 0;
}