1//===-- Status.cpp -----------------------------------------------*- C++
2//-*-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Utility/Status.h"
11
12#include "lldb/Utility/VASPrintf.h"
13#include "lldb/lldb-defines.h"
14#include "lldb/lldb-enumerations.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Support/Errno.h"
18#include "llvm/Support/FormatProviders.h"
19
20#include <cerrno>
21#include <cstdarg>
22#include <string>
23#include <system_error>
24
25#ifdef __APPLE__
26#include <mach/mach.h>
27#endif
28
29#ifdef _WIN32
30#include <windows.h>
31#endif
32#include <stdint.h>
33
34namespace llvm {
35class raw_ostream;
36}
37
38using namespace lldb;
39using namespace lldb_private;
40
41Status::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {}
42
43Status::Status(ValueType err, ErrorType type)
44    : m_code(err), m_type(type), m_string() {}
45
46Status::Status(std::error_code EC)
47    : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric),
48      m_string(EC.message()) {}
49
50Status::Status(const char *format, ...)
51    : m_code(0), m_type(eErrorTypeInvalid), m_string() {
52  va_list args;
53  va_start(args, format);
54  SetErrorToGenericError();
55  SetErrorStringWithVarArg(format, args);
56  va_end(args);
57}
58
59const Status &Status::operator=(llvm::Error error) {
60  if (!error) {
61    Clear();
62    return *this;
63  }
64
65  // if the error happens to be a errno error, preserve the error code
66  error = llvm::handleErrors(
67      std::move(error), [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error {
68        std::error_code ec = e->convertToErrorCode();
69        if (ec.category() == std::generic_category()) {
70          m_code = ec.value();
71          m_type = ErrorType::eErrorTypePOSIX;
72          return llvm::Error::success();
73        }
74        return llvm::Error(std::move(e));
75      });
76
77  // Otherwise, just preserve the message
78  if (error) {
79    SetErrorToGenericError();
80    SetErrorString(llvm::toString(std::move(error)));
81  }
82
83  return *this;
84}
85
86llvm::Error Status::ToError() const {
87  if (Success())
88    return llvm::Error::success();
89  if (m_type == ErrorType::eErrorTypePOSIX)
90    return llvm::errorCodeToError(
91        std::error_code(m_code, std::generic_category()));
92  return llvm::make_error<llvm::StringError>(AsCString(),
93                                             llvm::inconvertibleErrorCode());
94}
95
96Status::~Status() = default;
97
98#ifdef _WIN32
99static std::string RetrieveWin32ErrorString(uint32_t error_code) {
100  char *buffer = nullptr;
101  std::string message;
102  // Retrieve win32 system error.
103  // First, attempt to load a en-US message
104  if (::FormatMessageA(
105          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
106              FORMAT_MESSAGE_MAX_WIDTH_MASK,
107          NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
108          (LPSTR)&buffer, 0, NULL)) {
109    message.assign(buffer);
110    ::LocalFree(buffer);
111  }
112  // If the previous didn't work, use the default OS language
113  else if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
114                                FORMAT_MESSAGE_FROM_SYSTEM |
115                                FORMAT_MESSAGE_MAX_WIDTH_MASK,
116                            NULL, error_code, 0, (LPSTR)&buffer, 0, NULL)) {
117    message.assign(buffer);
118    ::LocalFree(buffer);
119  }
120  return message;
121}
122#endif
123
124// Get the error value as a NULL C string. The error string will be fetched and
125// cached on demand. The cached error string value will remain until the error
126// value is changed or cleared.
127const char *Status::AsCString(const char *default_error_str) const {
128  if (Success())
129    return nullptr;
130
131  if (m_string.empty()) {
132    switch (m_type) {
133    case eErrorTypeMachKernel:
134#if defined(__APPLE__)
135      if (const char *s = ::mach_error_string(m_code))
136        m_string.assign(s);
137#endif
138      break;
139
140    case eErrorTypePOSIX:
141      m_string = llvm::sys::StrError(m_code);
142      break;
143
144    case eErrorTypeWin32:
145#if defined(_WIN32)
146      m_string = RetrieveWin32ErrorString(m_code);
147#endif
148      break;
149
150    default:
151      break;
152    }
153  }
154  if (m_string.empty()) {
155    if (default_error_str)
156      m_string.assign(default_error_str);
157    else
158      return nullptr; // User wanted a nullptr string back...
159  }
160  return m_string.c_str();
161}
162
163// Clear the error and any cached error string that it might contain.
164void Status::Clear() {
165  m_code = 0;
166  m_type = eErrorTypeInvalid;
167  m_string.clear();
168}
169
170// Access the error value.
171Status::ValueType Status::GetError() const { return m_code; }
172
173// Access the error type.
174ErrorType Status::GetType() const { return m_type; }
175
176// Returns true if this object contains a value that describes an error or
177// otherwise non-success result.
178bool Status::Fail() const { return m_code != 0; }
179
180// Set accessor for the error value to "err" and the type to
181// "eErrorTypeMachKernel"
182void Status::SetMachError(uint32_t err) {
183  m_code = err;
184  m_type = eErrorTypeMachKernel;
185  m_string.clear();
186}
187
188void Status::SetExpressionError(lldb::ExpressionResults result,
189                                const char *mssg) {
190  m_code = result;
191  m_type = eErrorTypeExpression;
192  m_string = mssg;
193}
194
195int Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result,
196                                         const char *format, ...) {
197  int length = 0;
198
199  if (format != nullptr && format[0]) {
200    va_list args;
201    va_start(args, format);
202    length = SetErrorStringWithVarArg(format, args);
203    va_end(args);
204  } else {
205    m_string.clear();
206  }
207  m_code = result;
208  m_type = eErrorTypeExpression;
209  return length;
210}
211
212// Set accessor for the error value and type.
213void Status::SetError(ValueType err, ErrorType type) {
214  m_code = err;
215  m_type = type;
216  m_string.clear();
217}
218
219// Update the error value to be "errno" and update the type to be "POSIX".
220void Status::SetErrorToErrno() {
221  m_code = errno;
222  m_type = eErrorTypePOSIX;
223  m_string.clear();
224}
225
226// Update the error value to be LLDB_GENERIC_ERROR and update the type to be
227// "Generic".
228void Status::SetErrorToGenericError() {
229  m_code = LLDB_GENERIC_ERROR;
230  m_type = eErrorTypeGeneric;
231  m_string.clear();
232}
233
234// Set accessor for the error string value for a specific error. This allows
235// any string to be supplied as an error explanation. The error string value
236// will remain until the error value is cleared or a new error value/type is
237// assigned.
238void Status::SetErrorString(llvm::StringRef err_str) {
239  if (!err_str.empty()) {
240    // If we have an error string, we should always at least have an error set
241    // to a generic value.
242    if (Success())
243      SetErrorToGenericError();
244  }
245  m_string = err_str;
246}
247
248/// Set the current error string to a formatted error string.
249///
250/// \param format
251///     A printf style format string
252int Status::SetErrorStringWithFormat(const char *format, ...) {
253  if (format != nullptr && format[0]) {
254    va_list args;
255    va_start(args, format);
256    int length = SetErrorStringWithVarArg(format, args);
257    va_end(args);
258    return length;
259  } else {
260    m_string.clear();
261  }
262  return 0;
263}
264
265int Status::SetErrorStringWithVarArg(const char *format, va_list args) {
266  if (format != nullptr && format[0]) {
267    // If we have an error string, we should always at least have an error set
268    // to a generic value.
269    if (Success())
270      SetErrorToGenericError();
271
272    llvm::SmallString<1024> buf;
273    VASprintf(buf, format, args);
274    m_string = buf.str();
275    return buf.size();
276  } else {
277    m_string.clear();
278  }
279  return 0;
280}
281
282// Returns true if the error code in this object is considered a successful
283// return value.
284bool Status::Success() const { return m_code == 0; }
285
286bool Status::WasInterrupted() const {
287  return (m_type == eErrorTypePOSIX && m_code == EINTR);
288}
289
290void llvm::format_provider<lldb_private::Status>::format(
291    const lldb_private::Status &error, llvm::raw_ostream &OS,
292    llvm::StringRef Options) {
293  llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS,
294                                                 Options);
295}
296