1// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22#include "debug.h"
23#include <stdlib.h>
24#include <ctype.h>
25#include <string.h>
26#include <errno.h>
27
28#if _WIN32
29#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
30#define NOMINMAX 1
31#define WIN32_LEAN_AND_MEAN 1
32#define NOSERVICE 1
33#define NOMCX 1
34#define NOIME 1
35#include <windows.h>
36#include "windows-sanity.h"
37#include "encoding.h"
38#endif
39
40namespace kj {
41namespace _ { // private
42
43LogSeverity Debug::minSeverity = LogSeverity::WARNING;
44
45namespace {
46
47Exception::Type typeOfErrno(int error) {
48 switch (error) {
49#ifdef EDQUOT
50 case EDQUOT:
51#endif
52#ifdef EMFILE
53 case EMFILE:
54#endif
55#ifdef ENFILE
56 case ENFILE:
57#endif
58#ifdef ENOBUFS
59 case ENOBUFS:
60#endif
61#ifdef ENOLCK
62 case ENOLCK:
63#endif
64#ifdef ENOMEM
65 case ENOMEM:
66#endif
67#ifdef ENOSPC
68 case ENOSPC:
69#endif
70#ifdef ETIMEDOUT
71 case ETIMEDOUT:
72#endif
73#ifdef EUSERS
74 case EUSERS:
75#endif
76 return Exception::Type::OVERLOADED;
77
78#ifdef ENOTCONN
79 case ENOTCONN:
80#endif
81#ifdef ECONNABORTED
82 case ECONNABORTED:
83#endif
84#ifdef ECONNREFUSED
85 case ECONNREFUSED:
86#endif
87#ifdef ECONNRESET
88 case ECONNRESET:
89#endif
90#ifdef EHOSTDOWN
91 case EHOSTDOWN:
92#endif
93#ifdef EHOSTUNREACH
94 case EHOSTUNREACH:
95#endif
96#ifdef ENETDOWN
97 case ENETDOWN:
98#endif
99#ifdef ENETRESET
100 case ENETRESET:
101#endif
102#ifdef ENETUNREACH
103 case ENETUNREACH:
104#endif
105#ifdef ENONET
106 case ENONET:
107#endif
108#ifdef EPIPE
109 case EPIPE:
110#endif
111 return Exception::Type::DISCONNECTED;
112
113#ifdef ENOSYS
114 case ENOSYS:
115#endif
116#ifdef ENOTSUP
117 case ENOTSUP:
118#endif
119#if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP
120 case EOPNOTSUPP:
121#endif
122#ifdef ENOPROTOOPT
123 case ENOPROTOOPT:
124#endif
125#ifdef ENOTSOCK
126 // This is really saying "syscall not implemented for non-sockets".
127 case ENOTSOCK:
128#endif
129 return Exception::Type::UNIMPLEMENTED;
130
131 default:
132 return Exception::Type::FAILED;
133 }
134}
135
136#if _WIN32
137
138Exception::Type typeOfWin32Error(DWORD error) {
139 switch (error) {
140 // TODO(someday): This needs more work.
141
142 case WSAETIMEDOUT:
143 return Exception::Type::OVERLOADED;
144
145 case WSAENOTCONN:
146 case WSAECONNABORTED:
147 case WSAECONNREFUSED:
148 case WSAECONNRESET:
149 case WSAEHOSTDOWN:
150 case WSAEHOSTUNREACH:
151 case WSAENETDOWN:
152 case WSAENETRESET:
153 case WSAENETUNREACH:
154 case WSAESHUTDOWN:
155 return Exception::Type::DISCONNECTED;
156
157 case WSAEOPNOTSUPP:
158 case WSAENOPROTOOPT:
159 case WSAENOTSOCK: // This is really saying "syscall not implemented for non-sockets".
160 return Exception::Type::UNIMPLEMENTED;
161
162 default:
163 return Exception::Type::FAILED;
164 }
165}
166
167#endif // _WIN32
168
169enum DescriptionStyle {
170 LOG,
171 ASSERTION,
172 SYSCALL
173};
174
175static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber,
176 const char* sysErrorString, const char* macroArgs,
177 ArrayPtr<String> argValues) {
178 KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
179
180 if (argValues.size() > 0) {
181 size_t index = 0;
182 const char* start = macroArgs;
183 while (isspace(*start)) ++start;
184 const char* pos = start;
185 uint depth = 0;
186 bool quoted = false;
187 while (char c = *pos++) {
188 if (quoted) {
189 if (c == '\\' && *pos != '\0') {
190 ++pos;
191 } else if (c == '\"') {
192 quoted = false;
193 }
194 } else {
195 if (c == '(') {
196 ++depth;
197 } else if (c == ')') {
198 --depth;
199 } else if (c == '\"') {
200 quoted = true;
201 } else if (c == ',' && depth == 0) {
202 if (index < argValues.size()) {
203 argNames[index] = arrayPtr(start, pos - 1);
204 }
205 ++index;
206 while (isspace(*pos)) ++pos;
207 start = pos;
208 }
209 }
210 }
211 if (index < argValues.size()) {
212 argNames[index] = arrayPtr(start, pos - 1);
213 }
214 ++index;
215
216 if (index != argValues.size()) {
217 getExceptionCallback().logMessage(LogSeverity::ERROR, __FILE__, __LINE__, 0,
218 str("Failed to parse logging macro args into ",
219 argValues.size(), " names: ", macroArgs, '\n'));
220 }
221 }
222
223 if (style == SYSCALL) {
224 // Strip off leading "foo = " from code, since callers will sometimes write things like:
225 // ssize_t n;
226 // RECOVERABLE_SYSCALL(n = read(fd, buffer, sizeof(buffer))) { return ""; }
227 // return std::string(buffer, n);
228 const char* equalsPos = strchr(code, '=');
229 if (equalsPos != nullptr && equalsPos[1] != '=') {
230 code = equalsPos + 1;
231 while (isspace(*code)) ++code;
232 }
233 }
234
235 if (style == ASSERTION && code == nullptr) {
236 style = LOG;
237 }
238
239 {
240 StringPtr expected = "expected ";
241 StringPtr codeArray = style == LOG ? nullptr : StringPtr(code);
242 StringPtr sep = " = ";
243 StringPtr delim = "; ";
244 StringPtr colon = ": ";
245
246 StringPtr sysErrorArray;
247// On android before marshmallow only the posix version of stderror_r was
248// available, even with __USE_GNU.
249#if __USE_GNU && !(defined(__ANDROID_API__) && __ANDROID_API__ < 23)
250 char buffer[256];
251 if (style == SYSCALL) {
252 if (sysErrorString == nullptr) {
253 sysErrorArray = strerror_r(errorNumber, buffer, sizeof(buffer));
254 } else {
255 sysErrorArray = sysErrorString;
256 }
257 }
258#else
259 char buffer[256];
260 if (style == SYSCALL) {
261 if (sysErrorString == nullptr) {
262 strerror_r(errorNumber, buffer, sizeof(buffer));
263 sysErrorArray = buffer;
264 } else {
265 sysErrorArray = sysErrorString;
266 }
267 }
268#endif
269
270 size_t totalSize = 0;
271 switch (style) {
272 case LOG:
273 break;
274 case ASSERTION:
275 totalSize += expected.size() + codeArray.size();
276 break;
277 case SYSCALL:
278 totalSize += codeArray.size() + colon.size() + sysErrorArray.size();
279 break;
280 }
281
282 for (size_t i = 0; i < argValues.size(); i++) {
283 if (i > 0 || style != LOG) {
284 totalSize += delim.size();
285 }
286 if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
287 totalSize += argNames[i].size() + sep.size();
288 }
289 totalSize += argValues[i].size();
290 }
291
292 String result = heapString(totalSize);
293 char* pos = result.begin();
294
295 switch (style) {
296 case LOG:
297 break;
298 case ASSERTION:
299 pos = _::fill(pos, expected, codeArray);
300 break;
301 case SYSCALL:
302 pos = _::fill(pos, codeArray, colon, sysErrorArray);
303 break;
304 }
305
306 for (size_t i = 0; i < argValues.size(); i++) {
307 if (i > 0 || style != LOG) {
308 pos = _::fill(pos, delim);
309 }
310 if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
311 pos = _::fill(pos, argNames[i], sep);
312 }
313 pos = _::fill(pos, argValues[i]);
314 }
315
316 return result;
317 }
318}
319
320} // namespace
321
322void Debug::logInternal(const char* file, int line, LogSeverity severity, const char* macroArgs,
323 ArrayPtr<String> argValues) {
324 getExceptionCallback().logMessage(severity, trimSourceFilename(file).cStr(), line, 0,
325 makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues));
326}
327
328Debug::Fault::~Fault() noexcept(false) {
329 if (exception != nullptr) {
330 Exception copy = mv(*exception);
331 delete exception;
332 throwRecoverableException(mv(copy), 2);
333 }
334}
335
336void Debug::Fault::fatal() {
337 Exception copy = mv(*exception);
338 delete exception;
339 exception = nullptr;
340 throwFatalException(mv(copy), 2);
341 abort();
342}
343
344void Debug::Fault::init(
345 const char* file, int line, Exception::Type type,
346 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
347 exception = new Exception(type, file, line,
348 makeDescriptionImpl(ASSERTION, condition, 0, nullptr, macroArgs, argValues));
349}
350
351void Debug::Fault::init(
352 const char* file, int line, int osErrorNumber,
353 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
354 exception = new Exception(typeOfErrno(osErrorNumber), file, line,
355 makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues));
356}
357
358#if _WIN32
359void Debug::Fault::init(
360 const char* file, int line, Win32Result osErrorNumber,
361 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
362 LPVOID ptr;
363 // TODO(someday): Why doesn't this work for winsock errors?
364 DWORD result = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
365 FORMAT_MESSAGE_FROM_SYSTEM |
366 FORMAT_MESSAGE_IGNORE_INSERTS,
367 NULL, osErrorNumber.number,
368 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
369 (LPWSTR) &ptr, 0, NULL);
370
371 String message;
372 if (result > 0) {
373 KJ_DEFER(LocalFree(ptr));
374 const wchar_t* desc = reinterpret_cast<wchar_t*>(ptr);
375 size_t len = wcslen(desc);
376 if (len > 0 && desc[len-1] == '\n') --len;
377 if (len > 0 && desc[len-1] == '\r') --len;
378 message = kj::str('#', osErrorNumber.number, ' ',
379 decodeWideString(arrayPtr(desc, len)));
380 } else {
381 message = kj::str("win32 error code: ", osErrorNumber.number);
382 }
383
384 exception = new Exception(typeOfWin32Error(osErrorNumber.number), file, line,
385 makeDescriptionImpl(SYSCALL, condition, 0, message.cStr(),
386 macroArgs, argValues));
387}
388#endif
389
390String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
391 return makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues);
392}
393
394int Debug::getOsErrorNumber(bool nonblocking) {
395 int result = errno;
396
397 // On many systems, EAGAIN and EWOULDBLOCK have the same value, but this is not strictly required
398 // by POSIX, so we need to check both.
399 return result == EINTR ? -1
400 : nonblocking && (result == EAGAIN || result == EWOULDBLOCK) ? 0
401 : result;
402}
403
404#if _WIN32
405uint Debug::getWin32ErrorCode() {
406 return ::GetLastError();
407}
408#endif
409
410Debug::Context::Context(): logged(false) {}
411Debug::Context::~Context() noexcept(false) {}
412
413Debug::Context::Value Debug::Context::ensureInitialized() {
414 KJ_IF_MAYBE(v, value) {
415 return Value(v->file, v->line, heapString(v->description));
416 } else {
417 Value result = evaluate();
418 value = Value(result.file, result.line, heapString(result.description));
419 return result;
420 }
421}
422
423void Debug::Context::onRecoverableException(Exception&& exception) {
424 Value v = ensureInitialized();
425 exception.wrapContext(v.file, v.line, mv(v.description));
426 next.onRecoverableException(kj::mv(exception));
427}
428void Debug::Context::onFatalException(Exception&& exception) {
429 Value v = ensureInitialized();
430 exception.wrapContext(v.file, v.line, mv(v.description));
431 next.onFatalException(kj::mv(exception));
432}
433void Debug::Context::logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
434 String&& text) {
435 if (!logged) {
436 Value v = ensureInitialized();
437 next.logMessage(LogSeverity::INFO, v.file, v.line, 0,
438 str("context: ", mv(v.description), '\n'));
439 logged = true;
440 }
441
442 next.logMessage(severity, file, line, contextDepth + 1, mv(text));
443}
444
445} // namespace _ (private)
446} // namespace kj
447