| 1 | //===-- Status.cpp --------------------------------------------------------===// |
| 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 "lldb/Utility/Status.h" |
| 10 | |
| 11 | #include "lldb/Utility/LLDBLog.h" |
| 12 | #include "lldb/Utility/Log.h" |
| 13 | #include "lldb/Utility/VASPrintf.h" |
| 14 | #include "lldb/lldb-defines.h" |
| 15 | #include "lldb/lldb-enumerations.h" |
| 16 | #include "llvm/ADT/SmallString.h" |
| 17 | #include "llvm/ADT/StringRef.h" |
| 18 | #include "llvm/Support/Errno.h" |
| 19 | #include "llvm/Support/FormatProviders.h" |
| 20 | |
| 21 | #include <cerrno> |
| 22 | #include <cstdarg> |
| 23 | #include <string> |
| 24 | #include <system_error> |
| 25 | |
| 26 | #ifdef __APPLE__ |
| 27 | #include <mach/mach.h> |
| 28 | #endif |
| 29 | |
| 30 | #ifdef _WIN32 |
| 31 | #include <windows.h> |
| 32 | #endif |
| 33 | #include <cstdint> |
| 34 | |
| 35 | namespace llvm { |
| 36 | class raw_ostream; |
| 37 | } |
| 38 | |
| 39 | using namespace lldb; |
| 40 | using namespace lldb_private; |
| 41 | |
| 42 | char CloneableError::ID; |
| 43 | char CloneableECError::ID; |
| 44 | char MachKernelError::ID; |
| 45 | char Win32Error::ID; |
| 46 | |
| 47 | namespace { |
| 48 | /// A std::error_code category for eErrorTypeGeneric. |
| 49 | class LLDBGenericCategory : public std::error_category { |
| 50 | const char *name() const noexcept override { return "LLDBGenericCategory" ; } |
| 51 | std::string message(int __ev) const override { return "generic LLDB error" ; }; |
| 52 | }; |
| 53 | LLDBGenericCategory &lldb_generic_category() { |
| 54 | static LLDBGenericCategory g_generic_category; |
| 55 | return g_generic_category; |
| 56 | } |
| 57 | } // namespace |
| 58 | |
| 59 | Status::Status() : m_error(llvm::Error::success()) {} |
| 60 | |
| 61 | static llvm::Error (Status::ValueType err, ErrorType type, |
| 62 | std::string msg) { |
| 63 | switch (type) { |
| 64 | case eErrorTypeMachKernel: |
| 65 | return llvm::make_error<MachKernelError>( |
| 66 | Args: std::error_code(err, std::system_category())); |
| 67 | case eErrorTypeWin32: |
| 68 | #ifdef _WIN32 |
| 69 | if (err == NO_ERROR) |
| 70 | return llvm::Error::success(); |
| 71 | #endif |
| 72 | return llvm::make_error<Win32Error>( |
| 73 | Args: std::error_code(err, std::system_category())); |
| 74 | case eErrorTypePOSIX: |
| 75 | if (msg.empty()) |
| 76 | return llvm::errorCodeToError( |
| 77 | EC: std::error_code(err, std::generic_category())); |
| 78 | return llvm::createStringError( |
| 79 | Msg: std::move(msg), EC: std::error_code(err, std::generic_category())); |
| 80 | default: |
| 81 | return llvm::createStringError( |
| 82 | Msg: std::move(msg), EC: std::error_code(err, lldb_generic_category())); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | Status::Status(ValueType err, ErrorType type, std::string msg) |
| 87 | : m_error(ErrorFromEnums(err, type, msg)) {} |
| 88 | |
| 89 | // This logic is confusing because C++ calls the traditional (posix) errno codes |
| 90 | // "generic errors", while we use the term "generic" to mean completely |
| 91 | // arbitrary (text-based) errors. |
| 92 | Status::Status(std::error_code EC) |
| 93 | : m_error(!EC ? llvm::Error::success() : llvm::errorCodeToError(EC)) {} |
| 94 | |
| 95 | Status::Status(std::string err_str) |
| 96 | : m_error( |
| 97 | llvm::createStringError(EC: llvm::inconvertibleErrorCode(), S: err_str)) {} |
| 98 | |
| 99 | const Status &Status::operator=(Status &&other) { |
| 100 | Clear(); |
| 101 | llvm::consumeError(Err: std::move(m_error)); |
| 102 | m_error = std::move(other.m_error); |
| 103 | return *this; |
| 104 | } |
| 105 | |
| 106 | Status Status::FromErrorStringWithFormat(const char *format, ...) { |
| 107 | std::string string; |
| 108 | va_list args; |
| 109 | va_start(args, format); |
| 110 | if (format != nullptr && format[0]) { |
| 111 | llvm::SmallString<1024> buf; |
| 112 | VASprintf(buf, fmt: format, args); |
| 113 | string = std::string(buf.str()); |
| 114 | } |
| 115 | va_end(args); |
| 116 | return Status(string); |
| 117 | } |
| 118 | |
| 119 | /// Creates a deep copy of all known errors and converts all other |
| 120 | /// errors to a new llvm::StringError. |
| 121 | static llvm::Error CloneError(const llvm::Error &error) { |
| 122 | llvm::Error result = llvm::Error::success(); |
| 123 | auto clone = [](const llvm::ErrorInfoBase &e) { |
| 124 | if (e.isA<CloneableError>()) |
| 125 | return llvm::Error(static_cast<const CloneableError &>(e).Clone()); |
| 126 | if (e.isA<llvm::ECError>()) |
| 127 | return llvm::errorCodeToError(EC: e.convertToErrorCode()); |
| 128 | return llvm::make_error<llvm::StringError>(Args: e.message(), |
| 129 | Args: e.convertToErrorCode(), Args: true); |
| 130 | }; |
| 131 | llvm::visitErrors(E: error, H: [&](const llvm::ErrorInfoBase &e) { |
| 132 | result = joinErrors(E1: std::move(result), E2: clone(e)); |
| 133 | }); |
| 134 | return result; |
| 135 | } |
| 136 | |
| 137 | Status Status::FromError(llvm::Error error) { return Status(std::move(error)); } |
| 138 | |
| 139 | llvm::Error Status::ToError() const { return CloneError(error: m_error); } |
| 140 | |
| 141 | Status::~Status() { llvm::consumeError(Err: std::move(m_error)); } |
| 142 | |
| 143 | #ifdef _WIN32 |
| 144 | static std::string RetrieveWin32ErrorString(uint32_t error_code) { |
| 145 | char *buffer = nullptr; |
| 146 | std::string message; |
| 147 | // Retrieve win32 system error. |
| 148 | // First, attempt to load a en-US message |
| 149 | if (::FormatMessageA( |
| 150 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
| 151 | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
| 152 | NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), |
| 153 | (LPSTR)&buffer, 0, NULL)) { |
| 154 | message.assign(buffer); |
| 155 | ::LocalFree(buffer); |
| 156 | } |
| 157 | // If the previous didn't work, use the default OS language |
| 158 | else if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 159 | FORMAT_MESSAGE_FROM_SYSTEM | |
| 160 | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
| 161 | NULL, error_code, 0, (LPSTR)&buffer, 0, NULL)) { |
| 162 | message.assign(buffer); |
| 163 | ::LocalFree(buffer); |
| 164 | } |
| 165 | return message; |
| 166 | } |
| 167 | #endif |
| 168 | |
| 169 | std::string MachKernelError::message() const { |
| 170 | #if defined(__APPLE__) |
| 171 | if (const char *s = ::mach_error_string(convertToErrorCode().value())) |
| 172 | return s; |
| 173 | #endif |
| 174 | return "MachKernelError" ; |
| 175 | } |
| 176 | |
| 177 | std::string Win32Error::message() const { |
| 178 | #if defined(_WIN32) |
| 179 | return RetrieveWin32ErrorString(convertToErrorCode().value()); |
| 180 | #endif |
| 181 | return "Win32Error" ; |
| 182 | } |
| 183 | |
| 184 | std::unique_ptr<CloneableError> MachKernelError::Clone() const { |
| 185 | return std::make_unique<MachKernelError>(args: convertToErrorCode()); |
| 186 | } |
| 187 | |
| 188 | std::unique_ptr<CloneableError> Win32Error::Clone() const { |
| 189 | return std::make_unique<Win32Error>(args: convertToErrorCode()); |
| 190 | } |
| 191 | |
| 192 | // Get the error value as a NULL C string. The error string will be fetched and |
| 193 | // cached on demand. The cached error string value will remain until the error |
| 194 | // value is changed or cleared. |
| 195 | const char *Status::AsCString(const char *default_error_str) const { |
| 196 | if (Success()) |
| 197 | return nullptr; |
| 198 | |
| 199 | m_string = llvm::toStringWithoutConsuming(E: m_error); |
| 200 | // Backwards compatibility with older implementations of Status. |
| 201 | if (m_error.isA<llvm::ECError>()) |
| 202 | if (!m_string.empty() && m_string[m_string.size() - 1] == '\n') |
| 203 | m_string.pop_back(); |
| 204 | |
| 205 | if (m_string.empty()) { |
| 206 | if (default_error_str) |
| 207 | m_string.assign(s: default_error_str); |
| 208 | else |
| 209 | return nullptr; // User wanted a nullptr string back... |
| 210 | } |
| 211 | return m_string.c_str(); |
| 212 | } |
| 213 | |
| 214 | // Clear the error and any cached error string that it might contain. |
| 215 | void Status::Clear() { |
| 216 | if (m_error) |
| 217 | LLDB_LOG_ERRORV(GetLog(LLDBLog::API), std::move(m_error), |
| 218 | "dropping error {0}" ); |
| 219 | m_error = llvm::Error::success(); |
| 220 | } |
| 221 | |
| 222 | Status::ValueType Status::GetError() const { |
| 223 | Status::ValueType result = 0; |
| 224 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
| 225 | // Return the first only. |
| 226 | if (result) |
| 227 | return; |
| 228 | std::error_code ec = error.convertToErrorCode(); |
| 229 | result = ec.value(); |
| 230 | }); |
| 231 | return result; |
| 232 | } |
| 233 | |
| 234 | static ErrorType ErrorCodeToErrorType(std::error_code ec) { |
| 235 | if (ec.category() == std::generic_category()) |
| 236 | return eErrorTypePOSIX; |
| 237 | if (ec.category() == lldb_generic_category() || |
| 238 | ec == llvm::inconvertibleErrorCode()) |
| 239 | return eErrorTypeGeneric; |
| 240 | return eErrorTypeInvalid; |
| 241 | } |
| 242 | |
| 243 | ErrorType CloneableECError::GetErrorType() const { |
| 244 | return ErrorCodeToErrorType(ec: EC); |
| 245 | } |
| 246 | |
| 247 | lldb::ErrorType MachKernelError::GetErrorType() const { |
| 248 | return lldb::eErrorTypeMachKernel; |
| 249 | } |
| 250 | |
| 251 | lldb::ErrorType Win32Error::GetErrorType() const { |
| 252 | return lldb::eErrorTypeWin32; |
| 253 | } |
| 254 | |
| 255 | StructuredData::ObjectSP Status::GetAsStructuredData() const { |
| 256 | auto dict_up = std::make_unique<StructuredData::Dictionary>(); |
| 257 | auto array_up = std::make_unique<StructuredData::Array>(); |
| 258 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
| 259 | if (error.isA<CloneableError>()) |
| 260 | array_up->AddItem( |
| 261 | item: static_cast<const CloneableError &>(error).GetAsStructuredData()); |
| 262 | else |
| 263 | array_up->AddStringItem(value: error.message()); |
| 264 | }); |
| 265 | dict_up->AddIntegerItem(key: "version" , value: 1u); |
| 266 | dict_up->AddIntegerItem(key: "type" , value: (unsigned)GetType()); |
| 267 | dict_up->AddItem(key: "errors" , value_sp: std::move(array_up)); |
| 268 | return dict_up; |
| 269 | } |
| 270 | |
| 271 | StructuredData::ObjectSP CloneableECError::GetAsStructuredData() const { |
| 272 | auto dict_up = std::make_unique<StructuredData::Dictionary>(); |
| 273 | dict_up->AddIntegerItem(key: "version" , value: 1u); |
| 274 | dict_up->AddIntegerItem(key: "error_code" , value: EC.value()); |
| 275 | dict_up->AddStringItem(key: "message" , value: message()); |
| 276 | return dict_up; |
| 277 | } |
| 278 | |
| 279 | ErrorType Status::GetType() const { |
| 280 | ErrorType result = eErrorTypeInvalid; |
| 281 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
| 282 | // Return the first only. |
| 283 | if (result != eErrorTypeInvalid) |
| 284 | return; |
| 285 | if (error.isA<CloneableError>()) |
| 286 | result = static_cast<const CloneableError &>(error).GetErrorType(); |
| 287 | else |
| 288 | result = ErrorCodeToErrorType(ec: error.convertToErrorCode()); |
| 289 | |
| 290 | }); |
| 291 | return result; |
| 292 | } |
| 293 | |
| 294 | bool Status::Fail() const { |
| 295 | // Note that this does not clear the checked flag in |
| 296 | // m_error. Otherwise we'd need to make this thread-safe. |
| 297 | return m_error.isA<llvm::ErrorInfoBase>(); |
| 298 | } |
| 299 | |
| 300 | Status Status::FromErrno() { return Status(llvm::errnoAsErrorCode()); } |
| 301 | |
| 302 | // Returns true if the error code in this object is considered a successful |
| 303 | // return value. |
| 304 | bool Status::Success() const { return !Fail(); } |
| 305 | |
| 306 | void llvm::format_provider<lldb_private::Status>::format( |
| 307 | const lldb_private::Status &error, llvm::raw_ostream &OS, |
| 308 | llvm::StringRef Options) { |
| 309 | llvm::format_provider<llvm::StringRef>::format(V: error.AsCString(), Stream&: OS, |
| 310 | Style: Options); |
| 311 | } |
| 312 | |