Status.cpp revision 320041
1318378Sdim//===-- Status.cpp -----------------------------------------------*- C++
2318378Sdim//-*-===//
3318378Sdim//
4318378Sdim//                     The LLVM Compiler Infrastructure
5318378Sdim//
6318378Sdim// This file is distributed under the University of Illinois Open Source
7318378Sdim// License. See LICENSE.TXT for details.
8318378Sdim//
9318378Sdim//===----------------------------------------------------------------------===//
10318378Sdim
11318378Sdim#include "lldb/Utility/Status.h"
12318378Sdim
13318378Sdim#include "lldb/Utility/VASPrintf.h"
14319799Sdim#include "lldb/lldb-defines.h"      // for LLDB_GENERIC_ERROR
15319799Sdim#include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType::eErr...
16319799Sdim#include "llvm/ADT/SmallString.h"   // for SmallString
17319799Sdim#include "llvm/ADT/StringRef.h"     // for StringRef
18319799Sdim#include "llvm/Support/Errno.h"
19318378Sdim#include "llvm/Support/FormatProviders.h" // for format_provider
20318378Sdim
21318378Sdim#include <cerrno>
22318378Sdim#include <cstdarg>
23318378Sdim#include <string> // for string
24318378Sdim#include <system_error>
25318378Sdim
26318378Sdim#ifdef __APPLE__
27318378Sdim#include <mach/mach.h>
28318378Sdim#endif
29318378Sdim
30318378Sdim#include <stdint.h> // for uint32_t
31318378Sdim
32318378Sdimnamespace llvm {
33318378Sdimclass raw_ostream;
34318378Sdim}
35318378Sdim
36318378Sdimusing namespace lldb;
37318378Sdimusing namespace lldb_private;
38318378Sdim
39318378SdimStatus::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {}
40318378Sdim
41318378SdimStatus::Status(ValueType err, ErrorType type)
42318378Sdim    : m_code(err), m_type(type), m_string() {}
43318378Sdim
44318378SdimStatus::Status(std::error_code EC)
45318378Sdim    : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric),
46318378Sdim      m_string(EC.message()) {}
47318378Sdim
48318378SdimStatus::Status(const Status &rhs) = default;
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)
90318681Sdim    return llvm::errorCodeToError(std::error_code(m_code, std::generic_category()));
91318681Sdim  return llvm::make_error<llvm::StringError>(AsCString(),
92318681Sdim                                             llvm::inconvertibleErrorCode());
93318681Sdim}
94318681Sdim
95318378Sdim//----------------------------------------------------------------------
96318378Sdim// Assignment operator
97318378Sdim//----------------------------------------------------------------------
98318378Sdimconst Status &Status::operator=(const Status &rhs) {
99318378Sdim  if (this != &rhs) {
100318378Sdim    m_code = rhs.m_code;
101318378Sdim    m_type = rhs.m_type;
102318378Sdim    m_string = rhs.m_string;
103318378Sdim  }
104318378Sdim  return *this;
105318378Sdim}
106318378Sdim
107318378Sdim//----------------------------------------------------------------------
108318378Sdim// Assignment operator
109318378Sdim//----------------------------------------------------------------------
110318378Sdimconst Status &Status::operator=(uint32_t err) {
111318378Sdim  m_code = err;
112318378Sdim  m_type = eErrorTypeMachKernel;
113318378Sdim  m_string.clear();
114318378Sdim  return *this;
115318378Sdim}
116318378Sdim
117318378SdimStatus::~Status() = default;
118318378Sdim
119318378Sdim//----------------------------------------------------------------------
120318378Sdim// Get the error value as a NULL C string. The error string will be
121318378Sdim// fetched and cached on demand. The cached error string value will
122318378Sdim// remain until the error value is changed or cleared.
123318378Sdim//----------------------------------------------------------------------
124318378Sdimconst char *Status::AsCString(const char *default_error_str) const {
125318378Sdim  if (Success())
126318378Sdim    return nullptr;
127318378Sdim
128318378Sdim  if (m_string.empty()) {
129318378Sdim    switch (m_type) {
130318378Sdim    case eErrorTypeMachKernel:
131318378Sdim#if defined(__APPLE__)
132319799Sdim      if (const char *s = ::mach_error_string(m_code))
133319799Sdim        m_string.assign(s);
134318378Sdim#endif
135318378Sdim      break;
136318378Sdim
137318378Sdim    case eErrorTypePOSIX:
138319799Sdim      m_string = llvm::sys::StrError(m_code);
139318378Sdim      break;
140318378Sdim
141318378Sdim    default:
142318378Sdim      break;
143318378Sdim    }
144318378Sdim  }
145318378Sdim  if (m_string.empty()) {
146318378Sdim    if (default_error_str)
147318378Sdim      m_string.assign(default_error_str);
148318378Sdim    else
149318378Sdim      return nullptr; // User wanted a nullptr string back...
150318378Sdim  }
151318378Sdim  return m_string.c_str();
152318378Sdim}
153318378Sdim
154318378Sdim//----------------------------------------------------------------------
155318378Sdim// Clear the error and any cached error string that it might contain.
156318378Sdim//----------------------------------------------------------------------
157318378Sdimvoid Status::Clear() {
158318378Sdim  m_code = 0;
159318378Sdim  m_type = eErrorTypeInvalid;
160318378Sdim  m_string.clear();
161318378Sdim}
162318378Sdim
163318378Sdim//----------------------------------------------------------------------
164318378Sdim// Access the error value.
165318378Sdim//----------------------------------------------------------------------
166318378SdimStatus::ValueType Status::GetError() const { return m_code; }
167318378Sdim
168318378Sdim//----------------------------------------------------------------------
169318378Sdim// Access the error type.
170318378Sdim//----------------------------------------------------------------------
171318378SdimErrorType Status::GetType() const { return m_type; }
172318378Sdim
173318378Sdim//----------------------------------------------------------------------
174318378Sdim// Returns true if this object contains a value that describes an
175318378Sdim// error or otherwise non-success result.
176318378Sdim//----------------------------------------------------------------------
177318378Sdimbool Status::Fail() const { return m_code != 0; }
178318378Sdim
179318378Sdim//----------------------------------------------------------------------
180318378Sdim// Set accesssor for the error value to "err" and the type to
181318378Sdim// "eErrorTypeMachKernel"
182318378Sdim//----------------------------------------------------------------------
183318378Sdimvoid Status::SetMachError(uint32_t err) {
184318378Sdim  m_code = err;
185318378Sdim  m_type = eErrorTypeMachKernel;
186318378Sdim  m_string.clear();
187318378Sdim}
188318378Sdim
189318378Sdimvoid Status::SetExpressionError(lldb::ExpressionResults result,
190318378Sdim                                const char *mssg) {
191318378Sdim  m_code = result;
192318378Sdim  m_type = eErrorTypeExpression;
193318378Sdim  m_string = mssg;
194318378Sdim}
195318378Sdim
196318378Sdimint Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result,
197318378Sdim                                         const char *format, ...) {
198318378Sdim  int length = 0;
199318378Sdim
200318378Sdim  if (format != nullptr && format[0]) {
201318378Sdim    va_list args;
202318378Sdim    va_start(args, format);
203318378Sdim    length = SetErrorStringWithVarArg(format, args);
204318378Sdim    va_end(args);
205318378Sdim  } else {
206318378Sdim    m_string.clear();
207318378Sdim  }
208318378Sdim  m_code = result;
209318378Sdim  m_type = eErrorTypeExpression;
210318378Sdim  return length;
211318378Sdim}
212318378Sdim
213318378Sdim//----------------------------------------------------------------------
214318378Sdim// Set accesssor for the error value and type.
215318378Sdim//----------------------------------------------------------------------
216318378Sdimvoid Status::SetError(ValueType err, ErrorType type) {
217318378Sdim  m_code = err;
218318378Sdim  m_type = type;
219318378Sdim  m_string.clear();
220318378Sdim}
221318378Sdim
222318378Sdim//----------------------------------------------------------------------
223318378Sdim// Update the error value to be "errno" and update the type to
224318378Sdim// be "POSIX".
225318378Sdim//----------------------------------------------------------------------
226318378Sdimvoid Status::SetErrorToErrno() {
227318378Sdim  m_code = errno;
228318378Sdim  m_type = eErrorTypePOSIX;
229318378Sdim  m_string.clear();
230318378Sdim}
231318378Sdim
232318378Sdim//----------------------------------------------------------------------
233318378Sdim// Update the error value to be LLDB_GENERIC_ERROR and update the type
234318378Sdim// to be "Generic".
235318378Sdim//----------------------------------------------------------------------
236318378Sdimvoid Status::SetErrorToGenericError() {
237318378Sdim  m_code = LLDB_GENERIC_ERROR;
238318378Sdim  m_type = eErrorTypeGeneric;
239318378Sdim  m_string.clear();
240318378Sdim}
241318378Sdim
242318378Sdim//----------------------------------------------------------------------
243318378Sdim// Set accessor for the error string value for a specific error.
244318378Sdim// This allows any string to be supplied as an error explanation.
245318378Sdim// The error string value will remain until the error value is
246318378Sdim// cleared or a new error value/type is assigned.
247318378Sdim//----------------------------------------------------------------------
248318378Sdimvoid Status::SetErrorString(llvm::StringRef err_str) {
249318378Sdim  if (!err_str.empty()) {
250318378Sdim    // If we have an error string, we should always at least have an error
251318378Sdim    // set to a generic value.
252318378Sdim    if (Success())
253318378Sdim      SetErrorToGenericError();
254318378Sdim  }
255318378Sdim  m_string = err_str;
256318378Sdim}
257318378Sdim
258318378Sdim//------------------------------------------------------------------
259318378Sdim/// Set the current error string to a formatted error string.
260318378Sdim///
261318378Sdim/// @param format
262318378Sdim///     A printf style format string
263318378Sdim//------------------------------------------------------------------
264318378Sdimint Status::SetErrorStringWithFormat(const char *format, ...) {
265318378Sdim  if (format != nullptr && format[0]) {
266318378Sdim    va_list args;
267318378Sdim    va_start(args, format);
268318378Sdim    int length = SetErrorStringWithVarArg(format, args);
269318378Sdim    va_end(args);
270318378Sdim    return length;
271318378Sdim  } else {
272318378Sdim    m_string.clear();
273318378Sdim  }
274318378Sdim  return 0;
275318378Sdim}
276318378Sdim
277318378Sdimint Status::SetErrorStringWithVarArg(const char *format, va_list args) {
278318378Sdim  if (format != nullptr && format[0]) {
279318378Sdim    // If we have an error string, we should always at least have
280318378Sdim    // an error set to a generic value.
281318378Sdim    if (Success())
282318378Sdim      SetErrorToGenericError();
283318378Sdim
284318378Sdim    llvm::SmallString<1024> buf;
285318378Sdim    VASprintf(buf, format, args);
286318378Sdim    m_string = buf.str();
287318378Sdim    return buf.size();
288318378Sdim  } else {
289318378Sdim    m_string.clear();
290318378Sdim  }
291318378Sdim  return 0;
292318378Sdim}
293318378Sdim
294318378Sdim//----------------------------------------------------------------------
295318378Sdim// Returns true if the error code in this object is considered a
296318378Sdim// successful return value.
297318378Sdim//----------------------------------------------------------------------
298318378Sdimbool Status::Success() const { return m_code == 0; }
299318378Sdim
300318378Sdimbool Status::WasInterrupted() const {
301318378Sdim  return (m_type == eErrorTypePOSIX && m_code == EINTR);
302318378Sdim}
303318378Sdim
304318378Sdimvoid llvm::format_provider<lldb_private::Status>::format(
305318378Sdim    const lldb_private::Status &error, llvm::raw_ostream &OS,
306318378Sdim    llvm::StringRef Options) {
307318378Sdim  llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS,
308318378Sdim                                                 Options);
309318378Sdim}
310