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