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