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/LLDBLog.h" |
12 | #include "lldb/Utility/Log.h" |
13 | #include "lldb/Utility/VASPrintf.h" |
14 | #include "lldb/lldb-defines.h" |
15 | #include "lldb/lldb-enumerations.h" |
16 | #include "llvm/ADT/SmallString.h" |
17 | #include "llvm/ADT/StringRef.h" |
18 | #include "llvm/Support/Errno.h" |
19 | #include "llvm/Support/FormatProviders.h" |
20 | |
21 | #include <cerrno> |
22 | #include <cstdarg> |
23 | #include <string> |
24 | #include <system_error> |
25 | |
26 | #ifdef __APPLE__ |
27 | #include <mach/mach.h> |
28 | #endif |
29 | |
30 | #ifdef _WIN32 |
31 | #include <windows.h> |
32 | #endif |
33 | #include <cstdint> |
34 | |
35 | namespace llvm { |
36 | class raw_ostream; |
37 | } |
38 | |
39 | using namespace lldb; |
40 | using namespace lldb_private; |
41 | |
42 | char CloneableError::ID; |
43 | char CloneableECError::ID; |
44 | char MachKernelError::ID; |
45 | char Win32Error::ID; |
46 | |
47 | namespace { |
48 | /// A std::error_code category for eErrorTypeGeneric. |
49 | class LLDBGenericCategory : public std::error_category { |
50 | const char *name() const noexcept override { return "LLDBGenericCategory"; } |
51 | std::string message(int __ev) const override { return "generic LLDB error"; }; |
52 | }; |
53 | LLDBGenericCategory &lldb_generic_category() { |
54 | static LLDBGenericCategory g_generic_category; |
55 | return g_generic_category; |
56 | } |
57 | } // namespace |
58 | |
59 | Status::Status() : m_error(llvm::Error::success()) {} |
60 | |
61 | static llvm::Error ErrorFromEnums(Status::ValueType err, ErrorType type, |
62 | std::string msg) { |
63 | switch (type) { |
64 | case eErrorTypeMachKernel: |
65 | return llvm::make_error<MachKernelError>( |
66 | Args: std::error_code(err, std::system_category())); |
67 | case eErrorTypeWin32: |
68 | #ifdef _WIN32 |
69 | if (err == NO_ERROR) |
70 | return llvm::Error::success(); |
71 | #endif |
72 | return llvm::make_error<Win32Error>( |
73 | Args: std::error_code(err, std::system_category())); |
74 | case eErrorTypePOSIX: |
75 | if (msg.empty()) |
76 | return llvm::errorCodeToError( |
77 | EC: std::error_code(err, std::generic_category())); |
78 | return llvm::createStringError( |
79 | Msg: std::move(msg), EC: std::error_code(err, std::generic_category())); |
80 | default: |
81 | return llvm::createStringError( |
82 | Msg: std::move(msg), EC: std::error_code(err, lldb_generic_category())); |
83 | } |
84 | } |
85 | |
86 | Status::Status(ValueType err, ErrorType type, std::string msg) |
87 | : m_error(ErrorFromEnums(err, type, msg)) {} |
88 | |
89 | // This logic is confusing because C++ calls the traditional (posix) errno codes |
90 | // "generic errors", while we use the term "generic" to mean completely |
91 | // arbitrary (text-based) errors. |
92 | Status::Status(std::error_code EC) |
93 | : m_error(!EC ? llvm::Error::success() : llvm::errorCodeToError(EC)) {} |
94 | |
95 | Status::Status(std::string err_str) |
96 | : m_error( |
97 | llvm::createStringError(EC: llvm::inconvertibleErrorCode(), S: err_str)) {} |
98 | |
99 | const Status &Status::operator=(Status &&other) { |
100 | Clear(); |
101 | llvm::consumeError(Err: std::move(m_error)); |
102 | m_error = std::move(other.m_error); |
103 | return *this; |
104 | } |
105 | |
106 | Status Status::FromErrorStringWithFormat(const char *format, ...) { |
107 | std::string string; |
108 | va_list args; |
109 | va_start(args, format); |
110 | if (format != nullptr && format[0]) { |
111 | llvm::SmallString<1024> buf; |
112 | VASprintf(buf, fmt: format, args); |
113 | string = std::string(buf.str()); |
114 | } |
115 | va_end(args); |
116 | return Status(string); |
117 | } |
118 | |
119 | /// Creates a deep copy of all known errors and converts all other |
120 | /// errors to a new llvm::StringError. |
121 | static llvm::Error CloneError(const llvm::Error &error) { |
122 | llvm::Error result = llvm::Error::success(); |
123 | auto clone = [](const llvm::ErrorInfoBase &e) { |
124 | if (e.isA<CloneableError>()) |
125 | return llvm::Error(static_cast<const CloneableError &>(e).Clone()); |
126 | if (e.isA<llvm::ECError>()) |
127 | return llvm::errorCodeToError(EC: e.convertToErrorCode()); |
128 | return llvm::make_error<llvm::StringError>(Args: e.message(), |
129 | Args: e.convertToErrorCode(), Args: true); |
130 | }; |
131 | llvm::visitErrors(E: error, H: [&](const llvm::ErrorInfoBase &e) { |
132 | result = joinErrors(E1: std::move(result), E2: clone(e)); |
133 | }); |
134 | return result; |
135 | } |
136 | |
137 | Status Status::FromError(llvm::Error error) { return Status(std::move(error)); } |
138 | |
139 | llvm::Error Status::ToError() const { return CloneError(error: m_error); } |
140 | |
141 | Status::~Status() { llvm::consumeError(Err: std::move(m_error)); } |
142 | |
143 | #ifdef _WIN32 |
144 | static std::string RetrieveWin32ErrorString(uint32_t error_code) { |
145 | char *buffer = nullptr; |
146 | std::string message; |
147 | // Retrieve win32 system error. |
148 | // First, attempt to load a en-US message |
149 | if (::FormatMessageA( |
150 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
151 | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
152 | NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), |
153 | (LPSTR)&buffer, 0, NULL)) { |
154 | message.assign(buffer); |
155 | ::LocalFree(buffer); |
156 | } |
157 | // If the previous didn't work, use the default OS language |
158 | else if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
159 | FORMAT_MESSAGE_FROM_SYSTEM | |
160 | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
161 | NULL, error_code, 0, (LPSTR)&buffer, 0, NULL)) { |
162 | message.assign(buffer); |
163 | ::LocalFree(buffer); |
164 | } |
165 | return message; |
166 | } |
167 | #endif |
168 | |
169 | std::string MachKernelError::message() const { |
170 | #if defined(__APPLE__) |
171 | if (const char *s = ::mach_error_string(convertToErrorCode().value())) |
172 | return s; |
173 | #endif |
174 | return "MachKernelError"; |
175 | } |
176 | |
177 | std::string Win32Error::message() const { |
178 | #if defined(_WIN32) |
179 | return RetrieveWin32ErrorString(convertToErrorCode().value()); |
180 | #endif |
181 | return "Win32Error"; |
182 | } |
183 | |
184 | std::unique_ptr<CloneableError> MachKernelError::Clone() const { |
185 | return std::make_unique<MachKernelError>(args: convertToErrorCode()); |
186 | } |
187 | |
188 | std::unique_ptr<CloneableError> Win32Error::Clone() const { |
189 | return std::make_unique<Win32Error>(args: convertToErrorCode()); |
190 | } |
191 | |
192 | // Get the error value as a NULL C string. The error string will be fetched and |
193 | // cached on demand. The cached error string value will remain until the error |
194 | // value is changed or cleared. |
195 | const char *Status::AsCString(const char *default_error_str) const { |
196 | if (Success()) |
197 | return nullptr; |
198 | |
199 | m_string = llvm::toStringWithoutConsuming(E: m_error); |
200 | // Backwards compatibility with older implementations of Status. |
201 | if (m_error.isA<llvm::ECError>()) |
202 | if (!m_string.empty() && m_string[m_string.size() - 1] == '\n') |
203 | m_string.pop_back(); |
204 | |
205 | if (m_string.empty()) { |
206 | if (default_error_str) |
207 | m_string.assign(s: default_error_str); |
208 | else |
209 | return nullptr; // User wanted a nullptr string back... |
210 | } |
211 | return m_string.c_str(); |
212 | } |
213 | |
214 | // Clear the error and any cached error string that it might contain. |
215 | void Status::Clear() { |
216 | if (m_error) |
217 | LLDB_LOG_ERRORV(GetLog(LLDBLog::API), std::move(m_error), |
218 | "dropping error {0}"); |
219 | m_error = llvm::Error::success(); |
220 | } |
221 | |
222 | Status::ValueType Status::GetError() const { |
223 | Status::ValueType result = 0; |
224 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
225 | // Return the first only. |
226 | if (result) |
227 | return; |
228 | std::error_code ec = error.convertToErrorCode(); |
229 | result = ec.value(); |
230 | }); |
231 | return result; |
232 | } |
233 | |
234 | static ErrorType ErrorCodeToErrorType(std::error_code ec) { |
235 | if (ec.category() == std::generic_category()) |
236 | return eErrorTypePOSIX; |
237 | if (ec.category() == lldb_generic_category() || |
238 | ec == llvm::inconvertibleErrorCode()) |
239 | return eErrorTypeGeneric; |
240 | return eErrorTypeInvalid; |
241 | } |
242 | |
243 | ErrorType CloneableECError::GetErrorType() const { |
244 | return ErrorCodeToErrorType(ec: EC); |
245 | } |
246 | |
247 | lldb::ErrorType MachKernelError::GetErrorType() const { |
248 | return lldb::eErrorTypeMachKernel; |
249 | } |
250 | |
251 | lldb::ErrorType Win32Error::GetErrorType() const { |
252 | return lldb::eErrorTypeWin32; |
253 | } |
254 | |
255 | StructuredData::ObjectSP Status::GetAsStructuredData() const { |
256 | auto dict_up = std::make_unique<StructuredData::Dictionary>(); |
257 | auto array_up = std::make_unique<StructuredData::Array>(); |
258 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
259 | if (error.isA<CloneableError>()) |
260 | array_up->AddItem( |
261 | item: static_cast<const CloneableError &>(error).GetAsStructuredData()); |
262 | else |
263 | array_up->AddStringItem(value: error.message()); |
264 | }); |
265 | dict_up->AddIntegerItem(key: "version", value: 1u); |
266 | dict_up->AddIntegerItem(key: "type", value: (unsigned)GetType()); |
267 | dict_up->AddItem(key: "errors", value_sp: std::move(array_up)); |
268 | return dict_up; |
269 | } |
270 | |
271 | StructuredData::ObjectSP CloneableECError::GetAsStructuredData() const { |
272 | auto dict_up = std::make_unique<StructuredData::Dictionary>(); |
273 | dict_up->AddIntegerItem(key: "version", value: 1u); |
274 | dict_up->AddIntegerItem(key: "error_code", value: EC.value()); |
275 | dict_up->AddStringItem(key: "message", value: message()); |
276 | return dict_up; |
277 | } |
278 | |
279 | ErrorType Status::GetType() const { |
280 | ErrorType result = eErrorTypeInvalid; |
281 | llvm::visitErrors(E: m_error, H: [&](const llvm::ErrorInfoBase &error) { |
282 | // Return the first only. |
283 | if (result != eErrorTypeInvalid) |
284 | return; |
285 | if (error.isA<CloneableError>()) |
286 | result = static_cast<const CloneableError &>(error).GetErrorType(); |
287 | else |
288 | result = ErrorCodeToErrorType(ec: error.convertToErrorCode()); |
289 | |
290 | }); |
291 | return result; |
292 | } |
293 | |
294 | bool Status::Fail() const { |
295 | // Note that this does not clear the checked flag in |
296 | // m_error. Otherwise we'd need to make this thread-safe. |
297 | return m_error.isA<llvm::ErrorInfoBase>(); |
298 | } |
299 | |
300 | Status Status::FromErrno() { return Status(llvm::errnoAsErrorCode()); } |
301 | |
302 | // Returns true if the error code in this object is considered a successful |
303 | // return value. |
304 | bool Status::Success() const { return !Fail(); } |
305 | |
306 | void llvm::format_provider<lldb_private::Status>::format( |
307 | const lldb_private::Status &error, llvm::raw_ostream &OS, |
308 | llvm::StringRef Options) { |
309 | llvm::format_provider<llvm::StringRef>::format(V: error.AsCString(), Stream&: OS, |
310 | Style: Options); |
311 | } |
312 |
Definitions
- ID
- ID
- ID
- ID
- LLDBGenericCategory
- name
- message
- lldb_generic_category
- Status
- ErrorFromEnums
- Status
- Status
- Status
- operator=
- FromErrorStringWithFormat
- CloneError
- FromError
- ToError
- ~Status
- message
- message
- Clone
- Clone
- AsCString
- Clear
- GetError
- ErrorCodeToErrorType
- GetErrorType
- GetErrorType
- GetErrorType
- GetAsStructuredData
- GetAsStructuredData
- GetType
- Fail
- FromErrno
- Success
Learn to use CMake with our Intro Training
Find out more