[libc-commits] [libc] 07793f9 - [libc] add strsignal and refactor message mapping

Michael Jones via libc-commits libc-commits at lists.llvm.org
Fri Oct 7 11:12:00 PDT 2022


Author: Michael Jones
Date: 2022-10-07T11:11:53-07:00
New Revision: 07793f95c45b8606a21e1bdc948cd6f3b0e6461f

URL: https://github.com/llvm/llvm-project/commit/07793f95c45b8606a21e1bdc948cd6f3b0e6461f
DIFF: https://github.com/llvm/llvm-project/commit/07793f95c45b8606a21e1bdc948cd6f3b0e6461f.diff

LOG: [libc] add strsignal and refactor message mapping

The logic for strsignal and strerror is very similar, so I've moved them
both to use a shared utility (MessageMapper) for the basic
functionality.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D135322

Added: 
    libc/src/__support/StringUtil/CMakeLists.txt
    libc/src/__support/StringUtil/error_to_string.cpp
    libc/src/__support/StringUtil/error_to_string.h
    libc/src/__support/StringUtil/message_mapper.h
    libc/src/__support/StringUtil/signal_to_string.cpp
    libc/src/__support/StringUtil/signal_to_string.h
    libc/src/string/strsignal.cpp
    libc/src/string/strsignal.h
    libc/test/src/string/strsignal_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/posix.td
    libc/src/__support/CMakeLists.txt
    libc/src/string/CMakeLists.txt
    libc/src/string/strerror.cpp
    libc/src/string/strerror.h
    libc/src/string/strerror_r.cpp
    libc/test/src/string/CMakeLists.txt

Removed: 
    libc/src/__support/error_to_string.cpp
    libc/src/__support/error_to_string.h


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5ed5f0cd9747f..9874cb80d74d6 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -55,6 +55,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     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
index ee5a15980edc2..912d39ff42830 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -604,6 +604,11 @@ def POSIX : StandardSpec<"POSIX"> {
              ArgSpec<ConstRestrictedCharPtr>,
              ArgSpec<CharRestrictedDoublePtr>]
         >,
+        FunctionSpec<
+            "strsignal",
+            RetValSpec<CharPtr>,
+            [ArgSpec<IntType>]
+        >,
     ]
   >;
 

diff  --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 78d5df553946f..0a159723dbc3a 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -72,19 +72,6 @@ add_header_library(
     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_header_library(
 
 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
index 0000000000000..5c4c69f2bc6ff
--- /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/StringUtil/error_to_string.cpp b/libc/src/__support/StringUtil/error_to_string.cpp
new file mode 100644
index 0000000000000..98e2fbdee82c3
--- /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 <errno.h>
+#include <stddef.h>
+
+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<int>();
+  // 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
+// 
diff erent 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<ERR_ARRAY_SIZE, TOTAL_STR_LEN>
+    error_mapper(raw_err_array, RAW_ARRAY_LEN);
+
+cpp::string_view build_error_string(int err_num, cpp::span<char> 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<int>()))
+    return const_cast<char *>("Unknown error");
+
+  cpp::StringStream buffer_stream(
+      {const_cast<char *>(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<char> 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/error_to_string.h b/libc/src/__support/StringUtil/error_to_string.h
similarity index 84%
rename from libc/src/__support/error_to_string.h
rename to libc/src/__support/StringUtil/error_to_string.h
index afc5e39dab8a4..8b07ee2c17ab2 100644
--- 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 @@ namespace __llvm_libc {
 
 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<char> 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
index 0000000000000..ae8e7593edd2d
--- /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 <stddef.h>
+
+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 <size_t ARR_SIZE, size_t TOTAL_STR_LEN> 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<cpp::string_view> get_str(int num) const {
+    if (num >= 0 && static_cast<size_t>(num) < ARR_SIZE &&
+        msg_offsets[num] != -1) {
+      return {string_array + msg_offsets[num]};
+    } else {
+      return cpp::optional<cpp::string_view>();
+    }
+  }
+};
+
+} // namespace internal
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_STRING_UTIL_MESSAGE_MAPPER

diff  --git a/libc/src/__support/StringUtil/signal_to_string.cpp b/libc/src/__support/StringUtil/signal_to_string.cpp
new file mode 100644
index 0000000000000..a64f26adca650
--- /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 <signal.h>
+#include <stddef.h>
+
+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<int>();
+  // 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<SIG_ARRAY_SIZE, TOTAL_STR_LEN>
+    signal_mapper(raw_sig_array, RAW_ARRAY_LEN);
+
+cpp::string_view build_signal_string(int sig_num, cpp::span<char> 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<int>()))
+    return base_str;
+
+  cpp::StringStream buffer_stream(
+      {const_cast<char *>(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<char> 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/StringUtil/signal_to_string.h b/libc/src/__support/StringUtil/signal_to_string.h
new file mode 100644
index 0000000000000..ad04f36977504
--- /dev/null
+++ b/libc/src/__support/StringUtil/signal_to_string.h
@@ -0,0 +1,23 @@
+//===-- 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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
+
+#ifndef LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING
+#define LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING
+
+namespace __llvm_libc {
+
+cpp::string_view get_signal_string(int err_num);
+
+cpp::string_view get_signal_string(int err_num, cpp::span<char> buffer);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_SIGNAL_TO_STRING

diff  --git a/libc/src/__support/error_to_string.cpp b/libc/src/__support/error_to_string.cpp
deleted file mode 100644
index f84819ccc7c28..0000000000000
--- 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 <errno.h>
-#include <stddef.h>
-
-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<int>();
-  // 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
-// 
diff erent 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<size_t>(err_num) < ERR_ARRAY_SIZE &&
-        err_offsets[err_num] != -1) {
-      return const_cast<char *>(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<int>()))
-        return const_cast<char *>("Unknown error");
-
-      cpp::StringStream buffer_stream(
-          {const_cast<char *>(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
index 1af5472c218dd..3525e87240495 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -135,7 +135,7 @@ add_entrypoint_object(
   HDRS
     strerror.h
   DEPENDS
-    libc.src.__support.error_to_string
+    libc.src.__support.StringUtil.error_to_string
 )
 
 add_entrypoint_object(
@@ -145,7 +145,7 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
     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.cpp b/libc/src/string/strerror.cpp
index e99793fb3591c..014a51a622fde 100644
--- 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.h b/libc/src/string/strerror.h
index b9ddd71d62850..ce3366db529d3 100644
--- 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_r.cpp b/libc/src/string/strerror_r.cpp
index 30147152c4485..efdc8d3723c40 100644
--- 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 <stddef.h>
 

diff  --git a/libc/src/string/strsignal.cpp b/libc/src/string/strsignal.cpp
new file mode 100644
index 0000000000000..bad69b6f5415e
--- /dev/null
+++ b/libc/src/string/strsignal.cpp
@@ -0,0 +1,20 @@
+//===-- 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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/string/strsignal.h"
+#include "src/__support/StringUtil/signal_to_string.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(char *, strsignal, (int sig_num)) {
+  return const_cast<char *>(get_signal_string(sig_num).data());
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/string/strsignal.h b/libc/src/string/strsignal.h
new file mode 100644
index 0000000000000..346f3960d3aa3
--- /dev/null
+++ b/libc/src/string/strsignal.h
@@ -0,0 +1,19 @@
+//===-- 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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STRING_STRSIGNAL_H
+#define LLVM_LIBC_SRC_STRING_STRSIGNAL_H
+
+namespace __llvm_libc {
+
+char *strsignal(int sig_num);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STRING_STRSIGNAL_H

diff  --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt
index 723fbd502ba4f..bbd990935aa5d 100644
--- a/libc/test/src/string/CMakeLists.txt
+++ b/libc/test/src/string/CMakeLists.txt
@@ -245,6 +245,16 @@ add_libc_unittest(
     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
index 0000000000000..959d0f4bc04c4
--- /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 <signal.h>
+
+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");
+}


        


More information about the libc-commits mailing list