1318378Sdim//===-- Status.cpp -----------------------------------------------*- C++ 2318378Sdim//-*-===// 3318378Sdim// 4353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5353358Sdim// See https://llvm.org/LICENSE.txt for license information. 6353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7318378Sdim// 8318378Sdim//===----------------------------------------------------------------------===// 9318378Sdim 10318378Sdim#include "lldb/Utility/Status.h" 11318378Sdim 12318378Sdim#include "lldb/Utility/VASPrintf.h" 13344779Sdim#include "lldb/lldb-defines.h" 14344779Sdim#include "lldb/lldb-enumerations.h" 15344779Sdim#include "llvm/ADT/SmallString.h" 16344779Sdim#include "llvm/ADT/StringRef.h" 17319799Sdim#include "llvm/Support/Errno.h" 18344779Sdim#include "llvm/Support/FormatProviders.h" 19318378Sdim 20318378Sdim#include <cerrno> 21318378Sdim#include <cstdarg> 22344779Sdim#include <string> 23318378Sdim#include <system_error> 24318378Sdim 25318378Sdim#ifdef __APPLE__ 26318378Sdim#include <mach/mach.h> 27318378Sdim#endif 28318378Sdim 29344779Sdim#ifdef _WIN32 30344779Sdim#include <windows.h> 31344779Sdim#endif 32344779Sdim#include <stdint.h> 33318378Sdim 34318378Sdimnamespace llvm { 35318378Sdimclass raw_ostream; 36318378Sdim} 37318378Sdim 38318378Sdimusing namespace lldb; 39318378Sdimusing namespace lldb_private; 40318378Sdim 41318378SdimStatus::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {} 42318378Sdim 43318378SdimStatus::Status(ValueType err, ErrorType type) 44318378Sdim : m_code(err), m_type(type), m_string() {} 45318378Sdim 46318378SdimStatus::Status(std::error_code EC) 47318378Sdim : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric), 48318378Sdim m_string(EC.message()) {} 49318378Sdim 50318378SdimStatus::Status(const char *format, ...) 51318378Sdim : m_code(0), m_type(eErrorTypeInvalid), m_string() { 52318378Sdim va_list args; 53318378Sdim va_start(args, format); 54318378Sdim SetErrorToGenericError(); 55318378Sdim SetErrorStringWithVarArg(format, args); 56318378Sdim va_end(args); 57318378Sdim} 58318378Sdim 59320041Sdimconst Status &Status::operator=(llvm::Error error) { 60320041Sdim if (!error) { 61320041Sdim Clear(); 62320041Sdim return *this; 63320041Sdim } 64318681Sdim 65318681Sdim // if the error happens to be a errno error, preserve the error code 66318681Sdim error = llvm::handleErrors( 67318681Sdim std::move(error), [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error { 68318681Sdim std::error_code ec = e->convertToErrorCode(); 69318681Sdim if (ec.category() == std::generic_category()) { 70318681Sdim m_code = ec.value(); 71318681Sdim m_type = ErrorType::eErrorTypePOSIX; 72318681Sdim return llvm::Error::success(); 73318681Sdim } 74318681Sdim return llvm::Error(std::move(e)); 75318681Sdim }); 76318681Sdim 77318681Sdim // Otherwise, just preserve the message 78320041Sdim if (error) { 79320041Sdim SetErrorToGenericError(); 80318681Sdim SetErrorString(llvm::toString(std::move(error))); 81320041Sdim } 82320041Sdim 83320041Sdim return *this; 84318681Sdim} 85318681Sdim 86318681Sdimllvm::Error Status::ToError() const { 87318681Sdim if (Success()) 88318681Sdim return llvm::Error::success(); 89318681Sdim if (m_type == ErrorType::eErrorTypePOSIX) 90344779Sdim return llvm::errorCodeToError( 91344779Sdim std::error_code(m_code, std::generic_category())); 92318681Sdim return llvm::make_error<llvm::StringError>(AsCString(), 93318681Sdim llvm::inconvertibleErrorCode()); 94318681Sdim} 95318681Sdim 96318378SdimStatus::~Status() = default; 97318378Sdim 98344779Sdim#ifdef _WIN32 99344779Sdimstatic std::string RetrieveWin32ErrorString(uint32_t error_code) { 100344779Sdim char *buffer = nullptr; 101344779Sdim std::string message; 102344779Sdim // Retrieve win32 system error. 103360784Sdim // First, attempt to load a en-US message 104344779Sdim if (::FormatMessageA( 105344779Sdim FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 106344779Sdim FORMAT_MESSAGE_MAX_WIDTH_MASK, 107360784Sdim NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 108344779Sdim (LPSTR)&buffer, 0, NULL)) { 109344779Sdim message.assign(buffer); 110344779Sdim ::LocalFree(buffer); 111344779Sdim } 112360784Sdim // If the previous didn't work, use the default OS language 113360784Sdim else if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | 114360784Sdim FORMAT_MESSAGE_FROM_SYSTEM | 115360784Sdim FORMAT_MESSAGE_MAX_WIDTH_MASK, 116360784Sdim NULL, error_code, 0, (LPSTR)&buffer, 0, NULL)) { 117360784Sdim message.assign(buffer); 118360784Sdim ::LocalFree(buffer); 119360784Sdim } 120344779Sdim return message; 121344779Sdim} 122344779Sdim#endif 123344779Sdim 124341825Sdim// Get the error value as a NULL C string. The error string will be fetched and 125341825Sdim// cached on demand. The cached error string value will remain until the error 126341825Sdim// value is changed or cleared. 127318378Sdimconst char *Status::AsCString(const char *default_error_str) const { 128318378Sdim if (Success()) 129318378Sdim return nullptr; 130318378Sdim 131318378Sdim if (m_string.empty()) { 132318378Sdim switch (m_type) { 133318378Sdim case eErrorTypeMachKernel: 134318378Sdim#if defined(__APPLE__) 135319799Sdim if (const char *s = ::mach_error_string(m_code)) 136319799Sdim m_string.assign(s); 137318378Sdim#endif 138318378Sdim break; 139318378Sdim 140318378Sdim case eErrorTypePOSIX: 141319799Sdim m_string = llvm::sys::StrError(m_code); 142318378Sdim break; 143318378Sdim 144344779Sdim case eErrorTypeWin32: 145344779Sdim#if defined(_WIN32) 146344779Sdim m_string = RetrieveWin32ErrorString(m_code); 147344779Sdim#endif 148344779Sdim break; 149344779Sdim 150318378Sdim default: 151318378Sdim break; 152318378Sdim } 153318378Sdim } 154318378Sdim if (m_string.empty()) { 155318378Sdim if (default_error_str) 156318378Sdim m_string.assign(default_error_str); 157318378Sdim else 158318378Sdim return nullptr; // User wanted a nullptr string back... 159318378Sdim } 160318378Sdim return m_string.c_str(); 161318378Sdim} 162318378Sdim 163318378Sdim// Clear the error and any cached error string that it might contain. 164318378Sdimvoid Status::Clear() { 165318378Sdim m_code = 0; 166318378Sdim m_type = eErrorTypeInvalid; 167318378Sdim m_string.clear(); 168318378Sdim} 169318378Sdim 170318378Sdim// Access the error value. 171318378SdimStatus::ValueType Status::GetError() const { return m_code; } 172318378Sdim 173318378Sdim// Access the error type. 174318378SdimErrorType Status::GetType() const { return m_type; } 175318378Sdim 176341825Sdim// Returns true if this object contains a value that describes an error or 177341825Sdim// otherwise non-success result. 178318378Sdimbool Status::Fail() const { return m_code != 0; } 179318378Sdim 180341825Sdim// Set accessor for the error value to "err" and the type to 181318378Sdim// "eErrorTypeMachKernel" 182318378Sdimvoid Status::SetMachError(uint32_t err) { 183318378Sdim m_code = err; 184318378Sdim m_type = eErrorTypeMachKernel; 185318378Sdim m_string.clear(); 186318378Sdim} 187318378Sdim 188318378Sdimvoid Status::SetExpressionError(lldb::ExpressionResults result, 189318378Sdim const char *mssg) { 190318378Sdim m_code = result; 191318378Sdim m_type = eErrorTypeExpression; 192318378Sdim m_string = mssg; 193318378Sdim} 194318378Sdim 195318378Sdimint Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result, 196318378Sdim const char *format, ...) { 197318378Sdim int length = 0; 198318378Sdim 199318378Sdim if (format != nullptr && format[0]) { 200318378Sdim va_list args; 201318378Sdim va_start(args, format); 202318378Sdim length = SetErrorStringWithVarArg(format, args); 203318378Sdim va_end(args); 204318378Sdim } else { 205318378Sdim m_string.clear(); 206318378Sdim } 207318378Sdim m_code = result; 208318378Sdim m_type = eErrorTypeExpression; 209318378Sdim return length; 210318378Sdim} 211318378Sdim 212341825Sdim// Set accessor for the error value and type. 213318378Sdimvoid Status::SetError(ValueType err, ErrorType type) { 214318378Sdim m_code = err; 215318378Sdim m_type = type; 216318378Sdim m_string.clear(); 217318378Sdim} 218318378Sdim 219341825Sdim// Update the error value to be "errno" and update the type to be "POSIX". 220318378Sdimvoid Status::SetErrorToErrno() { 221318378Sdim m_code = errno; 222318378Sdim m_type = eErrorTypePOSIX; 223318378Sdim m_string.clear(); 224318378Sdim} 225318378Sdim 226341825Sdim// Update the error value to be LLDB_GENERIC_ERROR and update the type to be 227341825Sdim// "Generic". 228318378Sdimvoid Status::SetErrorToGenericError() { 229318378Sdim m_code = LLDB_GENERIC_ERROR; 230318378Sdim m_type = eErrorTypeGeneric; 231318378Sdim m_string.clear(); 232318378Sdim} 233318378Sdim 234341825Sdim// Set accessor for the error string value for a specific error. This allows 235341825Sdim// any string to be supplied as an error explanation. The error string value 236341825Sdim// will remain until the error value is cleared or a new error value/type is 237341825Sdim// assigned. 238318378Sdimvoid Status::SetErrorString(llvm::StringRef err_str) { 239318378Sdim if (!err_str.empty()) { 240341825Sdim // If we have an error string, we should always at least have an error set 241341825Sdim // to a generic value. 242318378Sdim if (Success()) 243318378Sdim SetErrorToGenericError(); 244318378Sdim } 245318378Sdim m_string = err_str; 246318378Sdim} 247318378Sdim 248318378Sdim/// Set the current error string to a formatted error string. 249318378Sdim/// 250353358Sdim/// \param format 251318378Sdim/// A printf style format string 252318378Sdimint Status::SetErrorStringWithFormat(const char *format, ...) { 253318378Sdim if (format != nullptr && format[0]) { 254318378Sdim va_list args; 255318378Sdim va_start(args, format); 256318378Sdim int length = SetErrorStringWithVarArg(format, args); 257318378Sdim va_end(args); 258318378Sdim return length; 259318378Sdim } else { 260318378Sdim m_string.clear(); 261318378Sdim } 262318378Sdim return 0; 263318378Sdim} 264318378Sdim 265318378Sdimint Status::SetErrorStringWithVarArg(const char *format, va_list args) { 266318378Sdim if (format != nullptr && format[0]) { 267341825Sdim // If we have an error string, we should always at least have an error set 268341825Sdim // to a generic value. 269318378Sdim if (Success()) 270318378Sdim SetErrorToGenericError(); 271318378Sdim 272318378Sdim llvm::SmallString<1024> buf; 273318378Sdim VASprintf(buf, format, args); 274318378Sdim m_string = buf.str(); 275318378Sdim return buf.size(); 276318378Sdim } else { 277318378Sdim m_string.clear(); 278318378Sdim } 279318378Sdim return 0; 280318378Sdim} 281318378Sdim 282341825Sdim// Returns true if the error code in this object is considered a successful 283341825Sdim// return value. 284318378Sdimbool Status::Success() const { return m_code == 0; } 285318378Sdim 286318378Sdimbool Status::WasInterrupted() const { 287318378Sdim return (m_type == eErrorTypePOSIX && m_code == EINTR); 288318378Sdim} 289318378Sdim 290318378Sdimvoid llvm::format_provider<lldb_private::Status>::format( 291318378Sdim const lldb_private::Status &error, llvm::raw_ostream &OS, 292318378Sdim llvm::StringRef Options) { 293318378Sdim llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS, 294318378Sdim Options); 295318378Sdim} 296