|
| 1 | +//===-- Implementation of a class for mapping errors to strings -----------===// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | + |
| 9 | +#include "src/__support/error_to_string.h" |
| 10 | +#include "src/__support/CPP/string_view.h" |
| 11 | +#include "src/__support/CPP/stringstream.h" |
| 12 | +#include <errno.h> |
| 13 | +#include <stddef.h> |
| 14 | + |
| 15 | +namespace __llvm_libc { |
| 16 | +namespace internal { |
| 17 | + |
| 18 | +constexpr size_t max_buff_size() { |
| 19 | + constexpr size_t unknown_str_len = sizeof("Unknown error"); |
| 20 | + constexpr size_t max_num_len = |
| 21 | + __llvm_libc::IntegerToString::dec_bufsize<int>(); |
| 22 | + // the buffer should be able to hold "Unknown error" + ' ' + num_str |
| 23 | + return (unknown_str_len + 1 + max_num_len) * sizeof(char); |
| 24 | +} |
| 25 | + |
| 26 | +// This is to hold error strings that have to be custom built. It may be |
| 27 | +// rewritten on every call to strerror (or other error to string function). |
| 28 | +constexpr size_t BUFFER_SIZE = max_buff_size(); |
| 29 | +thread_local char error_buffer[BUFFER_SIZE]; |
| 30 | + |
| 31 | +struct ErrMsgMapping { |
| 32 | + int err_num; |
| 33 | + cpp::string_view err_msg; |
| 34 | + |
| 35 | +public: |
| 36 | + constexpr ErrMsgMapping(int num, const char *msg) |
| 37 | + : err_num(num), err_msg(msg) { |
| 38 | + ; |
| 39 | + } |
| 40 | +}; |
| 41 | + |
| 42 | +constexpr ErrMsgMapping raw_err_array[] = { |
| 43 | + ErrMsgMapping(0, "Success"), |
| 44 | + ErrMsgMapping(EPERM, "Operation not permitted"), |
| 45 | + ErrMsgMapping(ENOENT, "No such file or directory"), |
| 46 | + ErrMsgMapping(ESRCH, "No such process"), |
| 47 | + ErrMsgMapping(EINTR, "Interrupted system call"), |
| 48 | + ErrMsgMapping(EIO, "Input/output error"), |
| 49 | + ErrMsgMapping(ENXIO, "No such device or address"), |
| 50 | + ErrMsgMapping(E2BIG, "Argument list too long"), |
| 51 | + ErrMsgMapping(ENOEXEC, "Exec format error"), |
| 52 | + ErrMsgMapping(EBADF, "Bad file descriptor"), |
| 53 | + ErrMsgMapping(ECHILD, "No child processes"), |
| 54 | + ErrMsgMapping(EAGAIN, "Resource temporarily unavailable"), |
| 55 | + ErrMsgMapping(ENOMEM, "Cannot allocate memory"), |
| 56 | + ErrMsgMapping(EACCES, "Permission denied"), |
| 57 | + ErrMsgMapping(EFAULT, "Bad address"), |
| 58 | + ErrMsgMapping(ENOTBLK, "Block device required"), |
| 59 | + ErrMsgMapping(EBUSY, "Device or resource busy"), |
| 60 | + ErrMsgMapping(EEXIST, "File exists"), |
| 61 | + ErrMsgMapping(EXDEV, "Invalid cross-device link"), |
| 62 | + ErrMsgMapping(ENODEV, "No such device"), |
| 63 | + ErrMsgMapping(ENOTDIR, "Not a directory"), |
| 64 | + ErrMsgMapping(EISDIR, "Is a directory"), |
| 65 | + ErrMsgMapping(EINVAL, "Invalid argument"), |
| 66 | + ErrMsgMapping(ENFILE, "Too many open files in system"), |
| 67 | + ErrMsgMapping(EMFILE, "Too many open files"), |
| 68 | + ErrMsgMapping(ENOTTY, "Inappropriate ioctl for device"), |
| 69 | + ErrMsgMapping(ETXTBSY, "Text file busy"), |
| 70 | + ErrMsgMapping(EFBIG, "File too large"), |
| 71 | + ErrMsgMapping(ENOSPC, "No space left on device"), |
| 72 | + ErrMsgMapping(ESPIPE, "Illegal seek"), |
| 73 | + ErrMsgMapping(EROFS, "Read-only file system"), |
| 74 | + ErrMsgMapping(EMLINK, "Too many links"), |
| 75 | + ErrMsgMapping(EPIPE, "Broken pipe"), |
| 76 | + ErrMsgMapping(EDOM, "Numerical argument out of domain"), |
| 77 | + ErrMsgMapping(ERANGE, "Numerical result out of range"), |
| 78 | + ErrMsgMapping(EDEADLK, "Resource deadlock avoided"), |
| 79 | + ErrMsgMapping(ENAMETOOLONG, "File name too long"), |
| 80 | + ErrMsgMapping(ENOLCK, "No locks available"), |
| 81 | + ErrMsgMapping(ENOSYS, "Function not implemented"), |
| 82 | + ErrMsgMapping(ENOTEMPTY, "Directory not empty"), |
| 83 | + ErrMsgMapping(ELOOP, "Too many levels of symbolic links"), |
| 84 | + // No error for 41. Would be EWOULDBLOCK |
| 85 | + ErrMsgMapping(ENOMSG, "No message of desired type"), |
| 86 | + ErrMsgMapping(EIDRM, "Identifier removed"), |
| 87 | + ErrMsgMapping(ECHRNG, "Channel number out of range"), |
| 88 | + ErrMsgMapping(EL2NSYNC, "Level 2 not synchronized"), |
| 89 | + ErrMsgMapping(EL3HLT, "Level 3 halted"), |
| 90 | + ErrMsgMapping(EL3RST, "Level 3 reset"), |
| 91 | + ErrMsgMapping(ELNRNG, "Link number out of range"), |
| 92 | + ErrMsgMapping(EUNATCH, "Protocol driver not attached"), |
| 93 | + ErrMsgMapping(ENOCSI, "No CSI structure available"), |
| 94 | + ErrMsgMapping(EL2HLT, "Level 2 halted"), |
| 95 | + ErrMsgMapping(EBADE, "Invalid exchange"), |
| 96 | + ErrMsgMapping(EBADR, "Invalid request descriptor"), |
| 97 | + ErrMsgMapping(EXFULL, "Exchange full"), |
| 98 | + ErrMsgMapping(ENOANO, "No anode"), |
| 99 | + ErrMsgMapping(EBADRQC, "Invalid request code"), |
| 100 | + ErrMsgMapping(EBADSLT, "Invalid slot"), |
| 101 | + // No error for 58. Would be EDEADLOCK. |
| 102 | + ErrMsgMapping(EBFONT, "Bad font file format"), |
| 103 | + ErrMsgMapping(ENOSTR, "Device not a stream"), |
| 104 | + ErrMsgMapping(ENODATA, "No data available"), |
| 105 | + ErrMsgMapping(ETIME, "Timer expired"), |
| 106 | + ErrMsgMapping(ENOSR, "Out of streams resources"), |
| 107 | + ErrMsgMapping(ENONET, "Machine is not on the network"), |
| 108 | + ErrMsgMapping(ENOPKG, "Package not installed"), |
| 109 | + ErrMsgMapping(EREMOTE, "Object is remote"), |
| 110 | + ErrMsgMapping(ENOLINK, "Link has been severed"), |
| 111 | + ErrMsgMapping(EADV, "Advertise error"), |
| 112 | + ErrMsgMapping(ESRMNT, "Srmount error"), |
| 113 | + ErrMsgMapping(ECOMM, "Communication error on send"), |
| 114 | + ErrMsgMapping(EPROTO, "Protocol error"), |
| 115 | + ErrMsgMapping(EMULTIHOP, "Multihop attempted"), |
| 116 | + ErrMsgMapping(EDOTDOT, "RFS specific error"), |
| 117 | + ErrMsgMapping(EBADMSG, "Bad message"), |
| 118 | + ErrMsgMapping(EOVERFLOW, "Value too large for defined data type"), |
| 119 | + ErrMsgMapping(ENOTUNIQ, "Name not unique on network"), |
| 120 | + ErrMsgMapping(EBADFD, "File descriptor in bad state"), |
| 121 | + ErrMsgMapping(EREMCHG, "Remote address changed"), |
| 122 | + ErrMsgMapping(ELIBACC, "Can not access a needed shared library"), |
| 123 | + ErrMsgMapping(ELIBBAD, "Accessing a corrupted shared library"), |
| 124 | + ErrMsgMapping(ELIBSCN, ".lib section in a.out corrupted"), |
| 125 | + ErrMsgMapping(ELIBMAX, "Attempting to link in too many shared libraries"), |
| 126 | + ErrMsgMapping(ELIBEXEC, "Cannot exec a shared library directly"), |
| 127 | + ErrMsgMapping(EILSEQ, "Invalid or incomplete multibyte or wide character"), |
| 128 | + ErrMsgMapping(ERESTART, "Interrupted system call should be restarted"), |
| 129 | + ErrMsgMapping(ESTRPIPE, "Streams pipe error"), |
| 130 | + ErrMsgMapping(EUSERS, "Too many users"), |
| 131 | + ErrMsgMapping(ENOTSOCK, "Socket operation on non-socket"), |
| 132 | + ErrMsgMapping(EDESTADDRREQ, "Destination address required"), |
| 133 | + ErrMsgMapping(EMSGSIZE, "Message too long"), |
| 134 | + ErrMsgMapping(EPROTOTYPE, "Protocol wrong type for socket"), |
| 135 | + ErrMsgMapping(ENOPROTOOPT, "Protocol not available"), |
| 136 | + ErrMsgMapping(EPROTONOSUPPORT, "Protocol not supported"), |
| 137 | + ErrMsgMapping(ESOCKTNOSUPPORT, "Socket type not supported"), |
| 138 | + ErrMsgMapping(ENOTSUP, "Operation not supported"), |
| 139 | + ErrMsgMapping(EPFNOSUPPORT, "Protocol family not supported"), |
| 140 | + ErrMsgMapping(EAFNOSUPPORT, "Address family not supported by protocol"), |
| 141 | + ErrMsgMapping(EADDRINUSE, "Address already in use"), |
| 142 | + ErrMsgMapping(EADDRNOTAVAIL, "Cannot assign requested address"), |
| 143 | + ErrMsgMapping(ENETDOWN, "Network is down"), |
| 144 | + ErrMsgMapping(ENETUNREACH, "Network is unreachable"), |
| 145 | + ErrMsgMapping(ENETRESET, "Network dropped connection on reset"), |
| 146 | + ErrMsgMapping(ECONNABORTED, "Software caused connection abort"), |
| 147 | + ErrMsgMapping(ECONNRESET, "Connection reset by peer"), |
| 148 | + ErrMsgMapping(ENOBUFS, "No buffer space available"), |
| 149 | + ErrMsgMapping(EISCONN, "Transport endpoint is already connected"), |
| 150 | + ErrMsgMapping(ENOTCONN, "Transport endpoint is not connected"), |
| 151 | + ErrMsgMapping(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), |
| 152 | + ErrMsgMapping(ETOOMANYREFS, "Too many references: cannot splice"), |
| 153 | + ErrMsgMapping(ETIMEDOUT, "Connection timed out"), |
| 154 | + ErrMsgMapping(ECONNREFUSED, "Connection refused"), |
| 155 | + ErrMsgMapping(EHOSTDOWN, "Host is down"), |
| 156 | + ErrMsgMapping(EHOSTUNREACH, "No route to host"), |
| 157 | + ErrMsgMapping(EALREADY, "Operation already in progress"), |
| 158 | + ErrMsgMapping(EINPROGRESS, "Operation now in progress"), |
| 159 | + ErrMsgMapping(ESTALE, "Stale file handle"), |
| 160 | + ErrMsgMapping(EUCLEAN, "Structure needs cleaning"), |
| 161 | + ErrMsgMapping(ENOTNAM, "Not a XENIX named type file"), |
| 162 | + ErrMsgMapping(ENAVAIL, "No XENIX semaphores available"), |
| 163 | + ErrMsgMapping(EISNAM, "Is a named type file"), |
| 164 | + ErrMsgMapping(EREMOTEIO, "Remote I/O error"), |
| 165 | + ErrMsgMapping(EDQUOT, "Disk quota exceeded"), |
| 166 | + ErrMsgMapping(ENOMEDIUM, "No medium found"), |
| 167 | + ErrMsgMapping(EMEDIUMTYPE, "Wrong medium type"), |
| 168 | + ErrMsgMapping(ECANCELED, "Operation canceled"), |
| 169 | + ErrMsgMapping(ENOKEY, "Required key not available"), |
| 170 | + ErrMsgMapping(EKEYEXPIRED, "Key has expired"), |
| 171 | + ErrMsgMapping(EKEYREVOKED, "Key has been revoked"), |
| 172 | + ErrMsgMapping(EKEYREJECTED, "Key was rejected by service"), |
| 173 | + ErrMsgMapping(EOWNERDEAD, "Owner died"), |
| 174 | + ErrMsgMapping(ENOTRECOVERABLE, "State not recoverable"), |
| 175 | + ErrMsgMapping(ERFKILL, "Operation not possible due to RF-kill"), |
| 176 | + ErrMsgMapping(EHWPOISON, "Memory page has hardware error"), |
| 177 | +}; |
| 178 | + |
| 179 | +constexpr size_t total_str_len(const ErrMsgMapping *array, size_t len) { |
| 180 | + size_t total = 0; |
| 181 | + for (size_t i = 0; i < len; ++i) { |
| 182 | + // add 1 for the null terminator. |
| 183 | + total += array[i].err_msg.size() + 1; |
| 184 | + } |
| 185 | + return total; |
| 186 | +} |
| 187 | + |
| 188 | +// Since the StringMappings array is a map from error numbers to their |
| 189 | +// corresponding strings, we have to have an array large enough we can use the |
| 190 | +// error numbers as indexes. Thankfully there are 132 errors in the above list |
| 191 | +// (41 and 58 are skipped) and the highest number is 133. If other platforms use |
| 192 | +// different error numbers, then this number may need to be adjusted. |
| 193 | +// Also if negative numbers or particularly large numbers are used, then the |
| 194 | +// array should be turned into a proper hashmap. |
| 195 | +constexpr size_t ERR_ARRAY_SIZE = 134; |
| 196 | + |
| 197 | +class ErrorMapper { |
| 198 | + |
| 199 | + // const char *StringMappings[ERR_ARRAY_SIZE] = {""}; |
| 200 | + int err_offsets[ERR_ARRAY_SIZE] = {-1}; |
| 201 | + char string_array[total_str_len( |
| 202 | + raw_err_array, sizeof(raw_err_array) / sizeof(ErrMsgMapping))] = {'\0'}; |
| 203 | + |
| 204 | +public: |
| 205 | + constexpr ErrorMapper() { |
| 206 | + cpp::string_view string_mappings[ERR_ARRAY_SIZE] = {""}; |
| 207 | + for (size_t i = 0; i < (sizeof(raw_err_array) / sizeof(ErrMsgMapping)); ++i) |
| 208 | + string_mappings[raw_err_array[i].err_num] = raw_err_array[i].err_msg; |
| 209 | + |
| 210 | + size_t string_array_index = 0; |
| 211 | + for (size_t cur_err = 0; cur_err < ERR_ARRAY_SIZE; ++cur_err) { |
| 212 | + if (string_mappings[cur_err].size() != 0) { |
| 213 | + err_offsets[cur_err] = string_array_index; |
| 214 | + // No need to replace with proper strcpy, this is evaluated at compile |
| 215 | + // time. |
| 216 | + for (size_t i = 0; i < string_mappings[cur_err].size() + 1; |
| 217 | + ++i, ++string_array_index) { |
| 218 | + string_array[string_array_index] = string_mappings[cur_err][i]; |
| 219 | + } |
| 220 | + } else { |
| 221 | + err_offsets[cur_err] = -1; |
| 222 | + } |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + cpp::string_view get_str(int err_num) const { |
| 227 | + if (err_num >= 0 && static_cast<size_t>(err_num) < ERR_ARRAY_SIZE && |
| 228 | + err_offsets[err_num] != -1) { |
| 229 | + return const_cast<char *>(string_array + err_offsets[err_num]); |
| 230 | + } else { |
| 231 | + // if the buffer can't hold "Unknown error" + ' ' + num_str, then just |
| 232 | + // return "Unknown error". |
| 233 | + if (BUFFER_SIZE < |
| 234 | + (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize<int>())) |
| 235 | + return const_cast<char *>("Unknown error"); |
| 236 | + |
| 237 | + cpp::StringStream buffer_stream({error_buffer, BUFFER_SIZE}); |
| 238 | + buffer_stream << "Unknown error" << ' ' << err_num << '\0'; |
| 239 | + return buffer_stream.str(); |
| 240 | + } |
| 241 | + } |
| 242 | +}; |
| 243 | + |
| 244 | +static constexpr ErrorMapper error_mapper; |
| 245 | + |
| 246 | +} // namespace internal |
| 247 | + |
| 248 | +cpp::string_view get_error_string(int err_num) { |
| 249 | + return internal::error_mapper.get_str(err_num); |
| 250 | +} |
| 251 | +} // namespace __llvm_libc |
0 commit comments