77 changes: 77 additions & 0 deletions libc/src/__support/StringUtil/message_mapper.h
Original file line number Diff line number Diff line change
@@ -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
117 changes: 117 additions & 0 deletions libc/src/__support/StringUtil/signal_to_string.cpp
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions libc/src/__support/StringUtil/signal_to_string.h
Original file line number Diff line number Diff line change
@@ -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
257 changes: 0 additions & 257 deletions libc/src/__support/error_to_string.cpp

This file was deleted.

14 changes: 12 additions & 2 deletions libc/src/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion libc/src/string/strerror.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
2 changes: 1 addition & 1 deletion libc/src/string/strerror.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace __llvm_libc {

char *strerror(int errnum);
char *strerror(int err_num);

} // namespace __llvm_libc

Expand Down
2 changes: 1 addition & 1 deletion libc/src/string/strerror_r.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
20 changes: 20 additions & 0 deletions libc/src/string/strsignal.cpp
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions libc/src/string/strsignal.h
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions libc/test/src/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 83 additions & 0 deletions libc/test/src/string/strsignal_test.cpp
Original file line number Diff line number Diff line change
@@ -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");
}