diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -55,6 +55,7 @@ libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr + libc.src.string.strsignal libc.src.string.strspn libc.src.string.strstr libc.src.string.strtok diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -579,6 +579,11 @@ ArgSpec, ArgSpec] >, + FunctionSpec< + "strsignal", + RetValSpec, + [ArgSpec] + >, ] >; diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -72,19 +72,6 @@ libc.src.errno.errno ) - -add_object_library( - error_to_string - HDRS - error_to_string.h - SRCS - error_to_string.cpp - DEPENDS - libc.include.errno - libc.src.__support.CPP.string_view - libc.src.__support.CPP.stringstream -) - add_header_library( integer_operations HDRS @@ -123,6 +110,7 @@ add_subdirectory(FPUtil) add_subdirectory(OSUtil) +add_subdirectory(StringUtil) # Thread support is used by other "File". So, we add the "threads" # before "File". diff --git a/libc/src/__support/StringUtil/CMakeLists.txt b/libc/src/__support/StringUtil/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/__support/StringUtil/CMakeLists.txt @@ -0,0 +1,39 @@ +add_header_library( + message_mapper + HDRS + message_mapper.h + DEPENDS + libc.src.__support.CPP.string_view + libc.src.__support.CPP.optional +) + +add_object_library( + error_to_string + HDRS + error_to_string.h + SRCS + error_to_string.cpp + DEPENDS + .message_mapper + libc.include.errno + libc.src.__support.CPP.span + libc.src.__support.CPP.string_view + libc.src.__support.CPP.stringstream + libc.src.__support.integer_to_string +) + + +add_object_library( + signal_to_string + HDRS + signal_to_string.h + SRCS + signal_to_string.cpp + DEPENDS + .message_mapper + libc.include.signal + libc.src.__support.CPP.span + libc.src.__support.CPP.string_view + libc.src.__support.CPP.stringstream + libc.src.__support.integer_to_string +) diff --git a/libc/src/__support/error_to_string.h b/libc/src/__support/StringUtil/error_to_string.h rename from libc/src/__support/error_to_string.h rename to libc/src/__support/StringUtil/error_to_string.h --- a/libc/src/__support/error_to_string.h +++ b/libc/src/__support/StringUtil/error_to_string.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/span.h" #include "src/__support/CPP/string_view.h" -#include "src/__support/integer_to_string.h" #ifndef LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING #define LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING @@ -16,7 +16,7 @@ cpp::string_view get_error_string(int err_num); -cpp::string_view get_error_string(int err_num, cpp::string_view buffer); +cpp::string_view get_error_string(int err_num, cpp::span buffer); } // namespace __llvm_libc diff --git a/libc/src/__support/StringUtil/error_to_string.cpp b/libc/src/__support/StringUtil/error_to_string.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/StringUtil/error_to_string.cpp @@ -0,0 +1,216 @@ +//===-- Implementation of a class for mapping errors to strings -----------===// +// +// 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/StringUtil/error_to_string.h" + +#include "src/__support/CPP/span.h" +#include "src/__support/CPP/string_view.h" +#include "src/__support/CPP/stringstream.h" +#include "src/__support/StringUtil/message_mapper.h" +#include "src/__support/integer_to_string.h" + +#include +#include + +namespace __llvm_libc { +namespace internal { + +constexpr size_t max_buff_size() { + constexpr size_t unknown_str_len = sizeof("Unknown error"); + constexpr size_t max_num_len = + __llvm_libc::IntegerToString::dec_bufsize(); + // the buffer should be able to hold "Unknown error" + ' ' + num_str + return (unknown_str_len + 1 + max_num_len) * sizeof(char); +} + +// This is to hold error strings that have to be custom built. It may be +// rewritten on every call to strerror (or other error to string function). +constexpr size_t ERR_BUFFER_SIZE = max_buff_size(); +thread_local char error_buffer[ERR_BUFFER_SIZE]; + +// Since the StringMappings array is a map from error numbers to their +// corresponding strings, we have to have an array large enough we can use the +// error numbers as indexes. Thankfully there are 132 errors in the above list +// (41 and 58 are skipped) and the highest number is 133. If other platforms use +// different error numbers, then this number may need to be adjusted. +// Also if negative numbers or particularly large numbers are used, then the +// array should be turned into a proper hashmap. +constexpr size_t ERR_ARRAY_SIZE = 134; + +constexpr MsgMapping raw_err_array[] = { + MsgMapping(0, "Success"), + MsgMapping(EPERM, "Operation not permitted"), + MsgMapping(ENOENT, "No such file or directory"), + MsgMapping(ESRCH, "No such process"), + MsgMapping(EINTR, "Interrupted system call"), + MsgMapping(EIO, "Input/output error"), + MsgMapping(ENXIO, "No such device or address"), + MsgMapping(E2BIG, "Argument list too long"), + MsgMapping(ENOEXEC, "Exec format error"), + MsgMapping(EBADF, "Bad file descriptor"), + MsgMapping(ECHILD, "No child processes"), + MsgMapping(EAGAIN, "Resource temporarily unavailable"), + MsgMapping(ENOMEM, "Cannot allocate memory"), + MsgMapping(EACCES, "Permission denied"), + MsgMapping(EFAULT, "Bad address"), + MsgMapping(ENOTBLK, "Block device required"), + MsgMapping(EBUSY, "Device or resource busy"), + MsgMapping(EEXIST, "File exists"), + MsgMapping(EXDEV, "Invalid cross-device link"), + MsgMapping(ENODEV, "No such device"), + MsgMapping(ENOTDIR, "Not a directory"), + MsgMapping(EISDIR, "Is a directory"), + MsgMapping(EINVAL, "Invalid argument"), + MsgMapping(ENFILE, "Too many open files in system"), + MsgMapping(EMFILE, "Too many open files"), + MsgMapping(ENOTTY, "Inappropriate ioctl for device"), + MsgMapping(ETXTBSY, "Text file busy"), + MsgMapping(EFBIG, "File too large"), + MsgMapping(ENOSPC, "No space left on device"), + MsgMapping(ESPIPE, "Illegal seek"), + MsgMapping(EROFS, "Read-only file system"), + MsgMapping(EMLINK, "Too many links"), + MsgMapping(EPIPE, "Broken pipe"), + MsgMapping(EDOM, "Numerical argument out of domain"), + MsgMapping(ERANGE, "Numerical result out of range"), + MsgMapping(EDEADLK, "Resource deadlock avoided"), + MsgMapping(ENAMETOOLONG, "File name too long"), + MsgMapping(ENOLCK, "No locks available"), + MsgMapping(ENOSYS, "Function not implemented"), + MsgMapping(ENOTEMPTY, "Directory not empty"), + MsgMapping(ELOOP, "Too many levels of symbolic links"), + // No error for 41. Would be EWOULDBLOCK + MsgMapping(ENOMSG, "No message of desired type"), + MsgMapping(EIDRM, "Identifier removed"), + MsgMapping(ECHRNG, "Channel number out of range"), + MsgMapping(EL2NSYNC, "Level 2 not synchronized"), + MsgMapping(EL3HLT, "Level 3 halted"), + MsgMapping(EL3RST, "Level 3 reset"), + MsgMapping(ELNRNG, "Link number out of range"), + MsgMapping(EUNATCH, "Protocol driver not attached"), + MsgMapping(ENOCSI, "No CSI structure available"), + MsgMapping(EL2HLT, "Level 2 halted"), + MsgMapping(EBADE, "Invalid exchange"), + MsgMapping(EBADR, "Invalid request descriptor"), + MsgMapping(EXFULL, "Exchange full"), + MsgMapping(ENOANO, "No anode"), + MsgMapping(EBADRQC, "Invalid request code"), + MsgMapping(EBADSLT, "Invalid slot"), + // No error for 58. Would be EDEADLOCK. + MsgMapping(EBFONT, "Bad font file format"), + MsgMapping(ENOSTR, "Device not a stream"), + MsgMapping(ENODATA, "No data available"), + MsgMapping(ETIME, "Timer expired"), + MsgMapping(ENOSR, "Out of streams resources"), + MsgMapping(ENONET, "Machine is not on the network"), + MsgMapping(ENOPKG, "Package not installed"), + MsgMapping(EREMOTE, "Object is remote"), + MsgMapping(ENOLINK, "Link has been severed"), + MsgMapping(EADV, "Advertise error"), + MsgMapping(ESRMNT, "Srmount error"), + MsgMapping(ECOMM, "Communication error on send"), + MsgMapping(EPROTO, "Protocol error"), + MsgMapping(EMULTIHOP, "Multihop attempted"), + MsgMapping(EDOTDOT, "RFS specific error"), + MsgMapping(EBADMSG, "Bad message"), + MsgMapping(EOVERFLOW, "Value too large for defined data type"), + MsgMapping(ENOTUNIQ, "Name not unique on network"), + MsgMapping(EBADFD, "File descriptor in bad state"), + MsgMapping(EREMCHG, "Remote address changed"), + MsgMapping(ELIBACC, "Can not access a needed shared library"), + MsgMapping(ELIBBAD, "Accessing a corrupted shared library"), + MsgMapping(ELIBSCN, ".lib section in a.out corrupted"), + MsgMapping(ELIBMAX, "Attempting to link in too many shared libraries"), + MsgMapping(ELIBEXEC, "Cannot exec a shared library directly"), + MsgMapping(EILSEQ, "Invalid or incomplete multibyte or wide character"), + MsgMapping(ERESTART, "Interrupted system call should be restarted"), + MsgMapping(ESTRPIPE, "Streams pipe error"), + MsgMapping(EUSERS, "Too many users"), + MsgMapping(ENOTSOCK, "Socket operation on non-socket"), + MsgMapping(EDESTADDRREQ, "Destination address required"), + MsgMapping(EMSGSIZE, "Message too long"), + MsgMapping(EPROTOTYPE, "Protocol wrong type for socket"), + MsgMapping(ENOPROTOOPT, "Protocol not available"), + MsgMapping(EPROTONOSUPPORT, "Protocol not supported"), + MsgMapping(ESOCKTNOSUPPORT, "Socket type not supported"), + MsgMapping(ENOTSUP, "Operation not supported"), + MsgMapping(EPFNOSUPPORT, "Protocol family not supported"), + MsgMapping(EAFNOSUPPORT, "Address family not supported by protocol"), + MsgMapping(EADDRINUSE, "Address already in use"), + MsgMapping(EADDRNOTAVAIL, "Cannot assign requested address"), + MsgMapping(ENETDOWN, "Network is down"), + MsgMapping(ENETUNREACH, "Network is unreachable"), + MsgMapping(ENETRESET, "Network dropped connection on reset"), + MsgMapping(ECONNABORTED, "Software caused connection abort"), + MsgMapping(ECONNRESET, "Connection reset by peer"), + MsgMapping(ENOBUFS, "No buffer space available"), + MsgMapping(EISCONN, "Transport endpoint is already connected"), + MsgMapping(ENOTCONN, "Transport endpoint is not connected"), + MsgMapping(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), + MsgMapping(ETOOMANYREFS, "Too many references: cannot splice"), + MsgMapping(ETIMEDOUT, "Connection timed out"), + MsgMapping(ECONNREFUSED, "Connection refused"), + MsgMapping(EHOSTDOWN, "Host is down"), + MsgMapping(EHOSTUNREACH, "No route to host"), + MsgMapping(EALREADY, "Operation already in progress"), + MsgMapping(EINPROGRESS, "Operation now in progress"), + MsgMapping(ESTALE, "Stale file handle"), + MsgMapping(EUCLEAN, "Structure needs cleaning"), + MsgMapping(ENOTNAM, "Not a XENIX named type file"), + MsgMapping(ENAVAIL, "No XENIX semaphores available"), + MsgMapping(EISNAM, "Is a named type file"), + MsgMapping(EREMOTEIO, "Remote I/O error"), + MsgMapping(EDQUOT, "Disk quota exceeded"), + MsgMapping(ENOMEDIUM, "No medium found"), + MsgMapping(EMEDIUMTYPE, "Wrong medium type"), + MsgMapping(ECANCELED, "Operation canceled"), + MsgMapping(ENOKEY, "Required key not available"), + MsgMapping(EKEYEXPIRED, "Key has expired"), + MsgMapping(EKEYREVOKED, "Key has been revoked"), + MsgMapping(EKEYREJECTED, "Key was rejected by service"), + MsgMapping(EOWNERDEAD, "Owner died"), + MsgMapping(ENOTRECOVERABLE, "State not recoverable"), + MsgMapping(ERFKILL, "Operation not possible due to RF-kill"), + MsgMapping(EHWPOISON, "Memory page has hardware error"), +}; + +constexpr size_t RAW_ARRAY_LEN = sizeof(raw_err_array) / sizeof(MsgMapping); +constexpr size_t TOTAL_STR_LEN = total_str_len(raw_err_array, RAW_ARRAY_LEN); + +static constexpr MessageMapper + error_mapper(raw_err_array, RAW_ARRAY_LEN); + +cpp::string_view build_error_string(int err_num, cpp::span buffer) { + // if the buffer can't hold "Unknown error" + ' ' + num_str, then just + // return "Unknown error". + if (buffer.size() < + (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize())) + return const_cast("Unknown error"); + + cpp::StringStream buffer_stream( + {const_cast(buffer.data()), buffer.size()}); + buffer_stream << "Unknown error" << ' ' << err_num << '\0'; + return buffer_stream.str(); +} + +} // namespace internal + +cpp::string_view get_error_string(int err_num) { + return get_error_string(err_num, + {internal::error_buffer, internal::ERR_BUFFER_SIZE}); +} + +cpp::string_view get_error_string(int err_num, cpp::span buffer) { + auto opt_str = internal::error_mapper.get_str(err_num); + if (opt_str) + return *opt_str; + else + return internal::build_error_string(err_num, buffer); +} + +} // namespace __llvm_libc diff --git a/libc/src/__support/StringUtil/message_mapper.h b/libc/src/__support/StringUtil/message_mapper.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/StringUtil/message_mapper.h @@ -0,0 +1,77 @@ +//===-- A class for number to string mappings -------------------*- 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_SUPPORT_STRING_UTIL_MESSAGE_MAPPER +#define LLVM_LIBC_SRC_SUPPORT_STRING_UTIL_MESSAGE_MAPPER + +#include "src/__support/CPP/optional.h" +#include "src/__support/CPP/string_view.h" +#include + +namespace __llvm_libc { +namespace internal { + +struct MsgMapping { + int num; + cpp::string_view msg; + + constexpr MsgMapping(int init_num, const char *init_msg) + : num(init_num), msg(init_msg) { + ; + } +}; + +constexpr size_t total_str_len(const MsgMapping *array, size_t len) { + size_t total = 0; + for (size_t i = 0; i < len; ++i) { + // add 1 for the null terminator. + total += array[i].msg.size() + 1; + } + return total; +} + +template class MessageMapper { + int msg_offsets[ARR_SIZE] = {-1}; + char string_array[TOTAL_STR_LEN] = {'\0'}; + +public: + constexpr MessageMapper(const MsgMapping raw_array[], size_t raw_array_len) { + cpp::string_view string_mappings[ARR_SIZE] = {""}; + for (size_t i = 0; i < raw_array_len; ++i) + string_mappings[raw_array[i].num] = raw_array[i].msg; + + size_t string_array_index = 0; + for (size_t cur_num = 0; cur_num < ARR_SIZE; ++cur_num) { + if (string_mappings[cur_num].size() != 0) { + msg_offsets[cur_num] = string_array_index; + // No need to replace with proper strcpy, this is evaluated at compile + // time. + for (size_t i = 0; i < string_mappings[cur_num].size() + 1; + ++i, ++string_array_index) { + string_array[string_array_index] = string_mappings[cur_num][i]; + } + } else { + msg_offsets[cur_num] = -1; + } + } + } + + cpp::optional get_str(int num) const { + if (num >= 0 && static_cast(num) < ARR_SIZE && + msg_offsets[num] != -1) { + return {string_array + msg_offsets[num]}; + } else { + return cpp::optional(); + } + } +}; + +} // namespace internal +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_STRING_UTIL_MESSAGE_MAPPER diff --git a/libc/src/__support/error_to_string.h b/libc/src/__support/StringUtil/signal_to_string.h rename from libc/src/__support/error_to_string.h rename to libc/src/__support/StringUtil/signal_to_string.h --- a/libc/src/__support/error_to_string.h +++ b/libc/src/__support/StringUtil/signal_to_string.h @@ -1,4 +1,4 @@ -//===-- Definition of a class for mapping errors to strings -----*- C++ -*-===// +//===-- Function prototype for mapping signals to strings -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,18 +6,18 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/span.h" #include "src/__support/CPP/string_view.h" -#include "src/__support/integer_to_string.h" -#ifndef LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING -#define LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING +#ifndef LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING +#define LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING namespace __llvm_libc { -cpp::string_view get_error_string(int err_num); +cpp::string_view get_signal_string(int err_num); -cpp::string_view get_error_string(int err_num, cpp::string_view buffer); +cpp::string_view get_signal_string(int err_num, cpp::span buffer); } // namespace __llvm_libc -#endif // LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING +#endif // LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING diff --git a/libc/src/__support/StringUtil/signal_to_string.cpp b/libc/src/__support/StringUtil/signal_to_string.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/StringUtil/signal_to_string.cpp @@ -0,0 +1,117 @@ +//===-- Implementation of a class for mapping signals to strings ----------===// +// +// 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/StringUtil/signal_to_string.h" + +#include "src/__support/CPP/span.h" +#include "src/__support/CPP/string_view.h" +#include "src/__support/CPP/stringstream.h" +#include "src/__support/StringUtil/message_mapper.h" +#include "src/__support/integer_to_string.h" + +#include +#include + +namespace __llvm_libc { +namespace internal { + +constexpr size_t max_buff_size() { + constexpr size_t base_str_len = sizeof("Real-time signal"); + constexpr size_t max_num_len = + __llvm_libc::IntegerToString::dec_bufsize(); + // the buffer should be able to hold "Real-time signal" + ' ' + num_str + return (base_str_len + 1 + max_num_len) * sizeof(char); +} + +// This is to hold signal strings that have to be custom built. It may be +// rewritten on every call to strsignal (or other signal to string function). +constexpr size_t SIG_BUFFER_SIZE = max_buff_size(); +thread_local char signal_buffer[SIG_BUFFER_SIZE]; + +constexpr MsgMapping raw_sig_array[] = { + MsgMapping(SIGHUP, "Hangup"), MsgMapping(SIGINT, "Interrupt"), + MsgMapping(SIGQUIT, "Quit"), MsgMapping(SIGILL, "Illegal instruction"), + MsgMapping(SIGTRAP, "Trace/breakpoint trap"), + MsgMapping(SIGABRT, "Aborted"), MsgMapping(SIGBUS, "Bus error"), + MsgMapping(SIGFPE, "Floating point exception"), + MsgMapping(SIGKILL, "Killed"), MsgMapping(SIGUSR1, "User defined signal 1"), + MsgMapping(SIGSEGV, "Segmentation fault"), + MsgMapping(SIGUSR2, "User defined signal 2"), + MsgMapping(SIGPIPE, "Broken pipe"), MsgMapping(SIGALRM, "Alarm clock"), + MsgMapping(SIGTERM, "Terminated"), + // SIGSTKFLT (may not exist) + MsgMapping(SIGCHLD, "Child exited"), MsgMapping(SIGCONT, "Continued"), + MsgMapping(SIGSTOP, "Stopped (signal)"), MsgMapping(SIGTSTP, "Stopped"), + MsgMapping(SIGTTIN, "Stopped (tty input)"), + MsgMapping(SIGTTOU, "Stopped (tty output)"), + MsgMapping(SIGURG, "Urgent I/O condition"), + MsgMapping(SIGXCPU, "CPU time limit exceeded"), + MsgMapping(SIGXFSZ, "File size limit exceeded"), + MsgMapping(SIGVTALRM, "Virtual timer expired"), + MsgMapping(SIGPROF, "Profiling timer expired"), + MsgMapping(SIGWINCH, "Window changed"), MsgMapping(SIGPOLL, "I/O possible"), + // SIGPWR (may not exist) + MsgMapping(SIGSYS, "Bad system call"), + +#ifdef SIGSTKFLT + MsgMapping(SIGSTKFLT, "Stack fault"), // unused +#endif +#ifdef SIGPWR + MsgMapping(SIGPWR, "Power failure"), // ignored +#endif +}; + +// Since the string_mappings array is a map from signal numbers to their +// corresponding strings, we have to have an array large enough we can use the +// signal numbers as indexes. The highest signal is SIGSYS at 31, so an array of +// 32 elements will be large enough to hold all of them. +constexpr size_t SIG_ARRAY_SIZE = 32; + +constexpr size_t RAW_ARRAY_LEN = sizeof(raw_sig_array) / sizeof(MsgMapping); +constexpr size_t TOTAL_STR_LEN = total_str_len(raw_sig_array, RAW_ARRAY_LEN); + +static constexpr MessageMapper + signal_mapper(raw_sig_array, RAW_ARRAY_LEN); + +cpp::string_view build_signal_string(int sig_num, cpp::span buffer) { + cpp::string_view base_str; + if (sig_num >= SIGRTMIN && sig_num <= SIGRTMAX) { + base_str = cpp::string_view("Real-time signal"); + sig_num -= SIGRTMIN; + } else { + base_str = cpp::string_view("Unknown signal"); + } + + // if the buffer can't hold "Unknown signal" + ' ' + num_str, then just + // return "Unknown signal". + if (buffer.size() < + (base_str.size() + 1 + IntegerToString::dec_bufsize())) + return base_str; + + cpp::StringStream buffer_stream( + {const_cast(buffer.data()), buffer.size()}); + buffer_stream << base_str << ' ' << sig_num << '\0'; + return buffer_stream.str(); +} + +} // namespace internal + +cpp::string_view get_signal_string(int sig_num) { + return get_signal_string( + sig_num, {internal::signal_buffer, internal::SIG_BUFFER_SIZE}); +} + +cpp::string_view get_signal_string(int sig_num, cpp::span buffer) { + auto opt_str = internal::signal_mapper.get_str(sig_num); + if (opt_str) + return *opt_str; + else + return internal::build_signal_string(sig_num, buffer); +} + +} // namespace __llvm_libc diff --git a/libc/src/__support/error_to_string.cpp b/libc/src/__support/error_to_string.cpp deleted file mode 100644 --- a/libc/src/__support/error_to_string.cpp +++ /dev/null @@ -1,257 +0,0 @@ -//===-- Implementation of a class for mapping errors to strings -----------===// -// -// 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/error_to_string.h" -#include "src/__support/CPP/string_view.h" -#include "src/__support/CPP/stringstream.h" -#include -#include - -namespace __llvm_libc { -namespace internal { - -constexpr size_t max_buff_size() { - constexpr size_t unknown_str_len = sizeof("Unknown error"); - constexpr size_t max_num_len = - __llvm_libc::IntegerToString::dec_bufsize(); - // the buffer should be able to hold "Unknown error" + ' ' + num_str - return (unknown_str_len + 1 + max_num_len) * sizeof(char); -} - -// This is to hold error strings that have to be custom built. It may be -// rewritten on every call to strerror (or other error to string function). -constexpr size_t BUFFER_SIZE = max_buff_size(); -thread_local char error_buffer[BUFFER_SIZE]; - -struct ErrMsgMapping { - int err_num; - cpp::string_view err_msg; - -public: - constexpr ErrMsgMapping(int num, const char *msg) - : err_num(num), err_msg(msg) { - ; - } -}; - -constexpr ErrMsgMapping raw_err_array[] = { - ErrMsgMapping(0, "Success"), - ErrMsgMapping(EPERM, "Operation not permitted"), - ErrMsgMapping(ENOENT, "No such file or directory"), - ErrMsgMapping(ESRCH, "No such process"), - ErrMsgMapping(EINTR, "Interrupted system call"), - ErrMsgMapping(EIO, "Input/output error"), - ErrMsgMapping(ENXIO, "No such device or address"), - ErrMsgMapping(E2BIG, "Argument list too long"), - ErrMsgMapping(ENOEXEC, "Exec format error"), - ErrMsgMapping(EBADF, "Bad file descriptor"), - ErrMsgMapping(ECHILD, "No child processes"), - ErrMsgMapping(EAGAIN, "Resource temporarily unavailable"), - ErrMsgMapping(ENOMEM, "Cannot allocate memory"), - ErrMsgMapping(EACCES, "Permission denied"), - ErrMsgMapping(EFAULT, "Bad address"), - ErrMsgMapping(ENOTBLK, "Block device required"), - ErrMsgMapping(EBUSY, "Device or resource busy"), - ErrMsgMapping(EEXIST, "File exists"), - ErrMsgMapping(EXDEV, "Invalid cross-device link"), - ErrMsgMapping(ENODEV, "No such device"), - ErrMsgMapping(ENOTDIR, "Not a directory"), - ErrMsgMapping(EISDIR, "Is a directory"), - ErrMsgMapping(EINVAL, "Invalid argument"), - ErrMsgMapping(ENFILE, "Too many open files in system"), - ErrMsgMapping(EMFILE, "Too many open files"), - ErrMsgMapping(ENOTTY, "Inappropriate ioctl for device"), - ErrMsgMapping(ETXTBSY, "Text file busy"), - ErrMsgMapping(EFBIG, "File too large"), - ErrMsgMapping(ENOSPC, "No space left on device"), - ErrMsgMapping(ESPIPE, "Illegal seek"), - ErrMsgMapping(EROFS, "Read-only file system"), - ErrMsgMapping(EMLINK, "Too many links"), - ErrMsgMapping(EPIPE, "Broken pipe"), - ErrMsgMapping(EDOM, "Numerical argument out of domain"), - ErrMsgMapping(ERANGE, "Numerical result out of range"), - ErrMsgMapping(EDEADLK, "Resource deadlock avoided"), - ErrMsgMapping(ENAMETOOLONG, "File name too long"), - ErrMsgMapping(ENOLCK, "No locks available"), - ErrMsgMapping(ENOSYS, "Function not implemented"), - ErrMsgMapping(ENOTEMPTY, "Directory not empty"), - ErrMsgMapping(ELOOP, "Too many levels of symbolic links"), - // No error for 41. Would be EWOULDBLOCK - ErrMsgMapping(ENOMSG, "No message of desired type"), - ErrMsgMapping(EIDRM, "Identifier removed"), - ErrMsgMapping(ECHRNG, "Channel number out of range"), - ErrMsgMapping(EL2NSYNC, "Level 2 not synchronized"), - ErrMsgMapping(EL3HLT, "Level 3 halted"), - ErrMsgMapping(EL3RST, "Level 3 reset"), - ErrMsgMapping(ELNRNG, "Link number out of range"), - ErrMsgMapping(EUNATCH, "Protocol driver not attached"), - ErrMsgMapping(ENOCSI, "No CSI structure available"), - ErrMsgMapping(EL2HLT, "Level 2 halted"), - ErrMsgMapping(EBADE, "Invalid exchange"), - ErrMsgMapping(EBADR, "Invalid request descriptor"), - ErrMsgMapping(EXFULL, "Exchange full"), - ErrMsgMapping(ENOANO, "No anode"), - ErrMsgMapping(EBADRQC, "Invalid request code"), - ErrMsgMapping(EBADSLT, "Invalid slot"), - // No error for 58. Would be EDEADLOCK. - ErrMsgMapping(EBFONT, "Bad font file format"), - ErrMsgMapping(ENOSTR, "Device not a stream"), - ErrMsgMapping(ENODATA, "No data available"), - ErrMsgMapping(ETIME, "Timer expired"), - ErrMsgMapping(ENOSR, "Out of streams resources"), - ErrMsgMapping(ENONET, "Machine is not on the network"), - ErrMsgMapping(ENOPKG, "Package not installed"), - ErrMsgMapping(EREMOTE, "Object is remote"), - ErrMsgMapping(ENOLINK, "Link has been severed"), - ErrMsgMapping(EADV, "Advertise error"), - ErrMsgMapping(ESRMNT, "Srmount error"), - ErrMsgMapping(ECOMM, "Communication error on send"), - ErrMsgMapping(EPROTO, "Protocol error"), - ErrMsgMapping(EMULTIHOP, "Multihop attempted"), - ErrMsgMapping(EDOTDOT, "RFS specific error"), - ErrMsgMapping(EBADMSG, "Bad message"), - ErrMsgMapping(EOVERFLOW, "Value too large for defined data type"), - ErrMsgMapping(ENOTUNIQ, "Name not unique on network"), - ErrMsgMapping(EBADFD, "File descriptor in bad state"), - ErrMsgMapping(EREMCHG, "Remote address changed"), - ErrMsgMapping(ELIBACC, "Can not access a needed shared library"), - ErrMsgMapping(ELIBBAD, "Accessing a corrupted shared library"), - ErrMsgMapping(ELIBSCN, ".lib section in a.out corrupted"), - ErrMsgMapping(ELIBMAX, "Attempting to link in too many shared libraries"), - ErrMsgMapping(ELIBEXEC, "Cannot exec a shared library directly"), - ErrMsgMapping(EILSEQ, "Invalid or incomplete multibyte or wide character"), - ErrMsgMapping(ERESTART, "Interrupted system call should be restarted"), - ErrMsgMapping(ESTRPIPE, "Streams pipe error"), - ErrMsgMapping(EUSERS, "Too many users"), - ErrMsgMapping(ENOTSOCK, "Socket operation on non-socket"), - ErrMsgMapping(EDESTADDRREQ, "Destination address required"), - ErrMsgMapping(EMSGSIZE, "Message too long"), - ErrMsgMapping(EPROTOTYPE, "Protocol wrong type for socket"), - ErrMsgMapping(ENOPROTOOPT, "Protocol not available"), - ErrMsgMapping(EPROTONOSUPPORT, "Protocol not supported"), - ErrMsgMapping(ESOCKTNOSUPPORT, "Socket type not supported"), - ErrMsgMapping(ENOTSUP, "Operation not supported"), - ErrMsgMapping(EPFNOSUPPORT, "Protocol family not supported"), - ErrMsgMapping(EAFNOSUPPORT, "Address family not supported by protocol"), - ErrMsgMapping(EADDRINUSE, "Address already in use"), - ErrMsgMapping(EADDRNOTAVAIL, "Cannot assign requested address"), - ErrMsgMapping(ENETDOWN, "Network is down"), - ErrMsgMapping(ENETUNREACH, "Network is unreachable"), - ErrMsgMapping(ENETRESET, "Network dropped connection on reset"), - ErrMsgMapping(ECONNABORTED, "Software caused connection abort"), - ErrMsgMapping(ECONNRESET, "Connection reset by peer"), - ErrMsgMapping(ENOBUFS, "No buffer space available"), - ErrMsgMapping(EISCONN, "Transport endpoint is already connected"), - ErrMsgMapping(ENOTCONN, "Transport endpoint is not connected"), - ErrMsgMapping(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), - ErrMsgMapping(ETOOMANYREFS, "Too many references: cannot splice"), - ErrMsgMapping(ETIMEDOUT, "Connection timed out"), - ErrMsgMapping(ECONNREFUSED, "Connection refused"), - ErrMsgMapping(EHOSTDOWN, "Host is down"), - ErrMsgMapping(EHOSTUNREACH, "No route to host"), - ErrMsgMapping(EALREADY, "Operation already in progress"), - ErrMsgMapping(EINPROGRESS, "Operation now in progress"), - ErrMsgMapping(ESTALE, "Stale file handle"), - ErrMsgMapping(EUCLEAN, "Structure needs cleaning"), - ErrMsgMapping(ENOTNAM, "Not a XENIX named type file"), - ErrMsgMapping(ENAVAIL, "No XENIX semaphores available"), - ErrMsgMapping(EISNAM, "Is a named type file"), - ErrMsgMapping(EREMOTEIO, "Remote I/O error"), - ErrMsgMapping(EDQUOT, "Disk quota exceeded"), - ErrMsgMapping(ENOMEDIUM, "No medium found"), - ErrMsgMapping(EMEDIUMTYPE, "Wrong medium type"), - ErrMsgMapping(ECANCELED, "Operation canceled"), - ErrMsgMapping(ENOKEY, "Required key not available"), - ErrMsgMapping(EKEYEXPIRED, "Key has expired"), - ErrMsgMapping(EKEYREVOKED, "Key has been revoked"), - ErrMsgMapping(EKEYREJECTED, "Key was rejected by service"), - ErrMsgMapping(EOWNERDEAD, "Owner died"), - ErrMsgMapping(ENOTRECOVERABLE, "State not recoverable"), - ErrMsgMapping(ERFKILL, "Operation not possible due to RF-kill"), - ErrMsgMapping(EHWPOISON, "Memory page has hardware error"), -}; - -constexpr size_t total_str_len(const ErrMsgMapping *array, size_t len) { - size_t total = 0; - for (size_t i = 0; i < len; ++i) { - // add 1 for the null terminator. - total += array[i].err_msg.size() + 1; - } - return total; -} - -// Since the StringMappings array is a map from error numbers to their -// corresponding strings, we have to have an array large enough we can use the -// error numbers as indexes. Thankfully there are 132 errors in the above list -// (41 and 58 are skipped) and the highest number is 133. If other platforms use -// different error numbers, then this number may need to be adjusted. -// Also if negative numbers or particularly large numbers are used, then the -// array should be turned into a proper hashmap. -constexpr size_t ERR_ARRAY_SIZE = 134; - -class ErrorMapper { - - // const char *StringMappings[ERR_ARRAY_SIZE] = {""}; - int err_offsets[ERR_ARRAY_SIZE] = {-1}; - char string_array[total_str_len( - raw_err_array, sizeof(raw_err_array) / sizeof(ErrMsgMapping))] = {'\0'}; - -public: - constexpr ErrorMapper() { - cpp::string_view string_mappings[ERR_ARRAY_SIZE] = {""}; - for (size_t i = 0; i < (sizeof(raw_err_array) / sizeof(ErrMsgMapping)); ++i) - string_mappings[raw_err_array[i].err_num] = raw_err_array[i].err_msg; - - size_t string_array_index = 0; - for (size_t cur_err = 0; cur_err < ERR_ARRAY_SIZE; ++cur_err) { - if (string_mappings[cur_err].size() != 0) { - err_offsets[cur_err] = string_array_index; - // No need to replace with proper strcpy, this is evaluated at compile - // time. - for (size_t i = 0; i < string_mappings[cur_err].size() + 1; - ++i, ++string_array_index) { - string_array[string_array_index] = string_mappings[cur_err][i]; - } - } else { - err_offsets[cur_err] = -1; - } - } - } - - cpp::string_view get_str(int err_num, cpp::string_view buffer) const { - if (err_num >= 0 && static_cast(err_num) < ERR_ARRAY_SIZE && - err_offsets[err_num] != -1) { - return const_cast(string_array + err_offsets[err_num]); - } else { - // if the buffer can't hold "Unknown error" + ' ' + num_str, then just - // return "Unknown error". - if (buffer.size() < - (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize())) - return const_cast("Unknown error"); - - cpp::StringStream buffer_stream( - {const_cast(buffer.data()), buffer.size()}); - buffer_stream << "Unknown error" << ' ' << err_num << '\0'; - return buffer_stream.str(); - } - } -}; - -static constexpr ErrorMapper error_mapper; - -} // namespace internal - -cpp::string_view get_error_string(int err_num) { - return internal::error_mapper.get_str( - err_num, {internal::error_buffer, internal::BUFFER_SIZE}); -} - -cpp::string_view get_error_string(int err_num, cpp::string_view buffer) { - return internal::error_mapper.get_str(err_num, buffer); -} -} // namespace __llvm_libc diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -135,7 +135,7 @@ HDRS strerror.h DEPENDS - libc.src.__support.error_to_string + libc.src.__support.StringUtil.error_to_string ) add_entrypoint_object( @@ -145,7 +145,7 @@ HDRS strerror_r.h DEPENDS - libc.src.__support.error_to_string + libc.src.__support.StringUtil.error_to_string ) add_entrypoint_object( @@ -247,6 +247,16 @@ strrchr.h ) +add_entrypoint_object( + strsignal + SRCS + strsignal.cpp + HDRS + strsignal.h + DEPENDS + libc.src.__support.StringUtil.signal_to_string +) + add_entrypoint_object( strspn SRCS diff --git a/libc/src/string/strerror.h b/libc/src/string/strerror.h --- a/libc/src/string/strerror.h +++ b/libc/src/string/strerror.h @@ -11,7 +11,7 @@ namespace __llvm_libc { -char *strerror(int errnum); +char *strerror(int err_num); } // namespace __llvm_libc diff --git a/libc/src/string/strerror.cpp b/libc/src/string/strerror.cpp --- a/libc/src/string/strerror.cpp +++ b/libc/src/string/strerror.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "src/string/strerror.h" +#include "src/__support/StringUtil/error_to_string.h" #include "src/__support/common.h" -#include "src/__support/error_to_string.h" namespace __llvm_libc { diff --git a/libc/src/string/strerror_r.cpp b/libc/src/string/strerror_r.cpp --- a/libc/src/string/strerror_r.cpp +++ b/libc/src/string/strerror_r.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "src/string/strerror_r.h" +#include "src/__support/StringUtil/error_to_string.h" #include "src/__support/common.h" -#include "src/__support/error_to_string.h" #include diff --git a/libc/src/string/strerror.h b/libc/src/string/strsignal.h copy from libc/src/string/strerror.h copy to libc/src/string/strsignal.h --- a/libc/src/string/strerror.h +++ b/libc/src/string/strsignal.h @@ -1,4 +1,5 @@ -//===-- Implementation header for strerror ----------------------*- C++ -*-===// +//===-- Implementation header for strsignal ----------------------*- C++ +//-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +7,13 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_SRC_STRING_STRERROR_H -#define LLVM_LIBC_SRC_STRING_STRERROR_H +#ifndef LLVM_LIBC_SRC_STRING_STRSIGNAL_H +#define LLVM_LIBC_SRC_STRING_STRSIGNAL_H namespace __llvm_libc { -char *strerror(int errnum); +char *strsignal(int sig_num); } // namespace __llvm_libc -#endif // LLVM_LIBC_SRC_STRING_STRERROR_H +#endif // LLVM_LIBC_SRC_STRING_STRSIGNAL_H diff --git a/libc/src/string/strerror.cpp b/libc/src/string/strsignal.cpp copy from libc/src/string/strerror.cpp copy to libc/src/string/strsignal.cpp --- a/libc/src/string/strerror.cpp +++ b/libc/src/string/strsignal.cpp @@ -1,4 +1,5 @@ -//===-- Implementation of strerror ----------------------------------------===// +//===-- Implementation of strsignal +//----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,14 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "src/string/strerror.h" +#include "src/string/strsignal.h" +#include "src/__support/StringUtil/signal_to_string.h" #include "src/__support/common.h" -#include "src/__support/error_to_string.h" namespace __llvm_libc { -LLVM_LIBC_FUNCTION(char *, strerror, (int err_num)) { - return const_cast(get_error_string(err_num).data()); +LLVM_LIBC_FUNCTION(char *, strsignal, (int sig_num)) { + return const_cast(get_signal_string(sig_num).data()); } } // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -245,6 +245,16 @@ libc.src.string.strrchr ) +add_libc_unittest( + strsignal_test + SUITE + libc_string_unittests + SRCS + strsignal_test.cpp + DEPENDS + libc.src.string.strsignal +) + add_libc_unittest( strspn_test SUITE diff --git a/libc/test/src/string/strsignal_test.cpp b/libc/test/src/string/strsignal_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strsignal_test.cpp @@ -0,0 +1,83 @@ +//===-- Unittests for strsignal -------------------------------------------===// +// +// 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/string/strsignal.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcStrSignalTest, KnownSignals) { + ASSERT_STREQ(__llvm_libc::strsignal(1), "Hangup"); + + const char *message_array[] = { + "Unknown signal 0", // unknown + "Hangup", + "Interrupt", + "Quit", + "Illegal instruction", + "Trace/breakpoint trap", + "Aborted", + "Bus error", + "Floating point exception", + "Killed", + "User defined signal 1", + "Segmentation fault", + "User defined signal 2", + "Broken pipe", + "Alarm clock", + "Terminated", + "Stack fault", + "Child exited", + "Continued", + "Stopped (signal)", + "Stopped", + "Stopped (tty input)", + "Stopped (tty output)", + "Urgent I/O condition", + "CPU time limit exceeded", + "File size limit exceeded", + "Virtual timer expired", + "Profiling timer expired", + "Window changed", + "I/O possible", + "Power failure", + "Bad system call", + }; + + // There are supposed to be 32 of these, but sometimes SIGRTMIN is shifted to + // reserve some. + const char *rt_message_array[] = { + "Real-time signal 0", "Real-time signal 1", "Real-time signal 2", + "Real-time signal 3", "Real-time signal 4", "Real-time signal 5", + "Real-time signal 6", "Real-time signal 7", "Real-time signal 8", + "Real-time signal 9", "Real-time signal 10", "Real-time signal 11", + "Real-time signal 12", "Real-time signal 13", "Real-time signal 14", + "Real-time signal 15", "Real-time signal 16", "Real-time signal 17", + "Real-time signal 18", "Real-time signal 19", "Real-time signal 20", + "Real-time signal 21", "Real-time signal 22", "Real-time signal 23", + "Real-time signal 24", "Real-time signal 25", "Real-time signal 26", + "Real-time signal 27", "Real-time signal 28", "Real-time signal 29", + "Real-time signal 30", "Real-time signal 31", "Real-time signal 32", + }; + + for (size_t i = 0; i < (sizeof(message_array) / sizeof(char *)); ++i) { + EXPECT_STREQ(__llvm_libc::strsignal(i), message_array[i]); + } + + for (size_t i = 0; i < SIGRTMAX - SIGRTMIN; ++i) { + EXPECT_STREQ(__llvm_libc::strsignal(i + SIGRTMIN), rt_message_array[i]); + } +} + +TEST(LlvmLibcStrsignalTest, UnknownSignals) { + ASSERT_STREQ(__llvm_libc::strsignal(-1), "Unknown signal -1"); + ASSERT_STREQ(__llvm_libc::strsignal(65), "Unknown signal 65"); + ASSERT_STREQ(__llvm_libc::strsignal(2147483647), "Unknown signal 2147483647"); + ASSERT_STREQ(__llvm_libc::strsignal(-2147483648), + "Unknown signal -2147483648"); +}