1#include "Exception.h"
2
3#include <string.h>
4#include <cxxabi.h>
5#include <Poco/String.h>
6#include <common/logger_useful.h>
7#include <IO/WriteHelpers.h>
8#include <IO/Operators.h>
9#include <IO/ReadBufferFromString.h>
10#include <common/demangle.h>
11#include <Common/config_version.h>
12#include <Common/formatReadable.h>
13#include <Common/filesystemHelpers.h>
14#include <filesystem>
15
16namespace DB
17{
18
19namespace ErrorCodes
20{
21 extern const int POCO_EXCEPTION;
22 extern const int STD_EXCEPTION;
23 extern const int UNKNOWN_EXCEPTION;
24 extern const int CANNOT_TRUNCATE_FILE;
25 extern const int NOT_IMPLEMENTED;
26}
27
28std::string errnoToString(int code, int e)
29{
30 const size_t buf_size = 128;
31 char buf[buf_size];
32#ifndef _GNU_SOURCE
33 int rc = strerror_r(e, buf, buf_size);
34#ifdef __APPLE__
35 if (rc != 0 && rc != EINVAL)
36#else
37 if (rc != 0)
38#endif
39 {
40 std::string tmp = std::to_string(code);
41 const char * code_str = tmp.c_str();
42 const char * unknown_message = "Unknown error ";
43 strcpy(buf, unknown_message);
44 strcpy(buf + strlen(unknown_message), code_str);
45 }
46 return "errno: " + toString(e) + ", strerror: " + std::string(buf);
47#else
48 (void)code;
49 return "errno: " + toString(e) + ", strerror: " + std::string(strerror_r(e, buf, sizeof(buf)));
50#endif
51}
52
53void throwFromErrno(const std::string & s, int code, int e)
54{
55 throw ErrnoException(s + ", " + errnoToString(code, e), code, e);
56}
57
58void throwFromErrnoWithPath(const std::string & s, const std::string & path, int code, int the_errno)
59{
60 throw ErrnoException(s + ", " + errnoToString(code, the_errno), code, the_errno, path);
61}
62
63void tryLogCurrentException(const char * log_name, const std::string & start_of_message)
64{
65 tryLogCurrentException(&Logger::get(log_name), start_of_message);
66}
67
68void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_message)
69{
70 try
71 {
72 LOG_ERROR(logger, start_of_message << (start_of_message.empty() ? "" : ": ") << getCurrentExceptionMessage(true));
73 }
74 catch (...)
75 {
76 }
77}
78
79static void getNoSpaceLeftInfoMessage(std::filesystem::path path, std::string & msg)
80{
81 path = std::filesystem::absolute(path);
82 /// It's possible to get ENOSPC for non existent file (e.g. if there are no free inodes and creat() fails)
83 /// So try to get info for existent parent directory.
84 while (!std::filesystem::exists(path) && path.has_relative_path())
85 path = path.parent_path();
86
87 auto fs = getStatVFS(path);
88 msg += "\nTotal space: " + formatReadableSizeWithBinarySuffix(fs.f_blocks * fs.f_bsize)
89 + "\nAvailable space: " + formatReadableSizeWithBinarySuffix(fs.f_bavail * fs.f_bsize)
90 + "\nTotal inodes: " + formatReadableQuantity(fs.f_files)
91 + "\nAvailable inodes: " + formatReadableQuantity(fs.f_favail);
92
93 auto mount_point = getMountPoint(path).string();
94 msg += "\nMount point: " + mount_point;
95#if defined(__linux__)
96 msg += "\nFilesystem: " + getFilesystemName(mount_point);
97#endif
98}
99
100static std::string getExtraExceptionInfo(const std::exception & e)
101{
102 String msg;
103 try
104 {
105 if (auto file_exception = dynamic_cast<const Poco::FileException *>(&e))
106 {
107 if (file_exception->code() == ENOSPC)
108 getNoSpaceLeftInfoMessage(file_exception->message(), msg);
109 }
110 else if (auto errno_exception = dynamic_cast<const DB::ErrnoException *>(&e))
111 {
112 if (errno_exception->getErrno() == ENOSPC && errno_exception->getPath())
113 getNoSpaceLeftInfoMessage(errno_exception->getPath().value(), msg);
114 }
115 }
116 catch (...)
117 {
118 msg += "\nCannot print extra info: " + getCurrentExceptionMessage(false, false, false);
119 }
120
121 return msg;
122}
123
124std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace /*= false*/, bool with_extra_info /*= true*/)
125{
126 std::stringstream stream;
127
128 try
129 {
130 throw;
131 }
132 catch (const Exception & e)
133 {
134 stream << getExceptionMessage(e, with_stacktrace, check_embedded_stacktrace)
135 << (with_extra_info ? getExtraExceptionInfo(e) : "")
136 << " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
137 }
138 catch (const Poco::Exception & e)
139 {
140 try
141 {
142 stream << "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
143 << ", e.displayText() = " << e.displayText()
144 << (with_extra_info ? getExtraExceptionInfo(e) : "")
145 << " (version " << VERSION_STRING << VERSION_OFFICIAL;
146 }
147 catch (...) {}
148 }
149 catch (const std::exception & e)
150 {
151 try
152 {
153 int status = 0;
154 auto name = demangle(typeid(e).name(), status);
155
156 if (status)
157 name += " (demangling status: " + toString(status) + ")";
158
159 stream << "std::exception. Code: " << ErrorCodes::STD_EXCEPTION << ", type: " << name << ", e.what() = " << e.what()
160 << (with_extra_info ? getExtraExceptionInfo(e) : "")
161 << ", version = " << VERSION_STRING << VERSION_OFFICIAL;
162 }
163 catch (...) {}
164 }
165 catch (...)
166 {
167 try
168 {
169 int status = 0;
170 auto name = demangle(abi::__cxa_current_exception_type()->name(), status);
171
172 if (status)
173 name += " (demangling status: " + toString(status) + ")";
174
175 stream << "Unknown exception. Code: " << ErrorCodes::UNKNOWN_EXCEPTION << ", type: " << name << " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
176 }
177 catch (...) {}
178 }
179
180 return stream.str();
181}
182
183
184int getCurrentExceptionCode()
185{
186 try
187 {
188 throw;
189 }
190 catch (const Exception & e)
191 {
192 return e.code();
193 }
194 catch (const Poco::Exception &)
195 {
196 return ErrorCodes::POCO_EXCEPTION;
197 }
198 catch (const std::exception &)
199 {
200 return ErrorCodes::STD_EXCEPTION;
201 }
202 catch (...)
203 {
204 return ErrorCodes::UNKNOWN_EXCEPTION;
205 }
206}
207
208
209void rethrowFirstException(const Exceptions & exceptions)
210{
211 for (size_t i = 0, size = exceptions.size(); i < size; ++i)
212 if (exceptions[i])
213 std::rethrow_exception(exceptions[i]);
214}
215
216
217void tryLogException(std::exception_ptr e, const char * log_name, const std::string & start_of_message)
218{
219 try
220 {
221 std::rethrow_exception(std::move(e));
222 }
223 catch (...)
224 {
225 tryLogCurrentException(log_name, start_of_message);
226 }
227}
228
229void tryLogException(std::exception_ptr e, Poco::Logger * logger, const std::string & start_of_message)
230{
231 try
232 {
233 std::rethrow_exception(std::move(e));
234 }
235 catch (...)
236 {
237 tryLogCurrentException(logger, start_of_message);
238 }
239}
240
241std::string getExceptionMessage(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace)
242{
243 std::stringstream stream;
244
245 try
246 {
247 std::string text = e.displayText();
248
249 bool has_embedded_stack_trace = false;
250 if (check_embedded_stacktrace)
251 {
252 auto embedded_stack_trace_pos = text.find("Stack trace");
253 has_embedded_stack_trace = embedded_stack_trace_pos != std::string::npos;
254 if (!with_stacktrace && has_embedded_stack_trace)
255 {
256 text.resize(embedded_stack_trace_pos);
257 Poco::trimRightInPlace(text);
258 }
259 }
260
261 stream << "Code: " << e.code() << ", e.displayText() = " << text;
262
263 if (with_stacktrace && !has_embedded_stack_trace)
264 stream << ", Stack trace (when copying this message, always include the lines below):\n\n" << e.getStackTrace().toString();
265 }
266 catch (...) {}
267
268 return stream.str();
269}
270
271std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace)
272{
273 try
274 {
275 std::rethrow_exception(std::move(e));
276 }
277 catch (...)
278 {
279 return getCurrentExceptionMessage(with_stacktrace);
280 }
281}
282
283
284std::string ExecutionStatus::serializeText() const
285{
286 WriteBufferFromOwnString wb;
287 wb << code << "\n" << escape << message;
288 return wb.str();
289}
290
291void ExecutionStatus::deserializeText(const std::string & data)
292{
293 ReadBufferFromString rb(data);
294 rb >> code >> "\n" >> escape >> message;
295}
296
297bool ExecutionStatus::tryDeserializeText(const std::string & data)
298{
299 try
300 {
301 deserializeText(data);
302 }
303 catch (...)
304 {
305 return false;
306 }
307
308 return true;
309}
310
311ExecutionStatus ExecutionStatus::fromCurrentException(const std::string & start_of_message)
312{
313 String msg = (start_of_message.empty() ? "" : (start_of_message + ": ")) + getCurrentExceptionMessage(false, true);
314 return ExecutionStatus(getCurrentExceptionCode(), msg);
315}
316
317
318}
319