debug.c++ (13661B)
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 #if _WIN32 || __CYGWIN__ 23 #include "win32-api-version.h" 24 #endif 25 26 #include "debug.h" 27 #include <stdlib.h> 28 #include <ctype.h> 29 #include <string.h> 30 #include <errno.h> 31 32 #if _WIN32 || __CYGWIN__ 33 #if !__CYGWIN__ 34 #define strerror_r(errno,buf,len) strerror_s(buf,len,errno) 35 #endif 36 #include <windows.h> 37 #include "windows-sanity.h" 38 #include "encoding.h" 39 #include <wchar.h> 40 #endif 41 42 namespace kj { 43 namespace _ { // private 44 45 LogSeverity Debug::minSeverity = LogSeverity::WARNING; 46 47 namespace { 48 49 Exception::Type typeOfErrno(int error) { 50 switch (error) { 51 #ifdef EDQUOT 52 case EDQUOT: 53 #endif 54 #ifdef EMFILE 55 case EMFILE: 56 #endif 57 #ifdef ENFILE 58 case ENFILE: 59 #endif 60 #ifdef ENOBUFS 61 case ENOBUFS: 62 #endif 63 #ifdef ENOLCK 64 case ENOLCK: 65 #endif 66 #ifdef ENOMEM 67 case ENOMEM: 68 #endif 69 #ifdef ENOSPC 70 case ENOSPC: 71 #endif 72 #ifdef ETIMEDOUT 73 case ETIMEDOUT: 74 #endif 75 #ifdef EUSERS 76 case EUSERS: 77 #endif 78 return Exception::Type::OVERLOADED; 79 80 #ifdef ENOTCONN 81 case ENOTCONN: 82 #endif 83 #ifdef ECONNABORTED 84 case ECONNABORTED: 85 #endif 86 #ifdef ECONNREFUSED 87 case ECONNREFUSED: 88 #endif 89 #ifdef ECONNRESET 90 case ECONNRESET: 91 #endif 92 #ifdef EHOSTDOWN 93 case EHOSTDOWN: 94 #endif 95 #ifdef EHOSTUNREACH 96 case EHOSTUNREACH: 97 #endif 98 #ifdef ENETDOWN 99 case ENETDOWN: 100 #endif 101 #ifdef ENETRESET 102 case ENETRESET: 103 #endif 104 #ifdef ENETUNREACH 105 case ENETUNREACH: 106 #endif 107 #ifdef ENONET 108 case ENONET: 109 #endif 110 #ifdef EPIPE 111 case EPIPE: 112 #endif 113 return Exception::Type::DISCONNECTED; 114 115 #ifdef ENOSYS 116 case ENOSYS: 117 #endif 118 #ifdef ENOTSUP 119 case ENOTSUP: 120 #endif 121 #if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP 122 case EOPNOTSUPP: 123 #endif 124 #ifdef ENOPROTOOPT 125 case ENOPROTOOPT: 126 #endif 127 #ifdef ENOTSOCK 128 // This is really saying "syscall not implemented for non-sockets". 129 case ENOTSOCK: 130 #endif 131 return Exception::Type::UNIMPLEMENTED; 132 133 default: 134 return Exception::Type::FAILED; 135 } 136 } 137 138 #if _WIN32 || __CYGWIN__ 139 140 Exception::Type typeOfWin32Error(DWORD error) { 141 switch (error) { 142 // TODO(someday): This needs more work. 143 144 case WSAETIMEDOUT: 145 return Exception::Type::OVERLOADED; 146 147 case WSAENOTCONN: 148 case WSAECONNABORTED: 149 case WSAECONNREFUSED: 150 case WSAECONNRESET: 151 case WSAEHOSTDOWN: 152 case WSAEHOSTUNREACH: 153 case WSAENETDOWN: 154 case WSAENETRESET: 155 case WSAENETUNREACH: 156 case WSAESHUTDOWN: 157 return Exception::Type::DISCONNECTED; 158 159 case WSAEOPNOTSUPP: 160 case WSAENOPROTOOPT: 161 case WSAENOTSOCK: // This is really saying "syscall not implemented for non-sockets". 162 return Exception::Type::UNIMPLEMENTED; 163 164 default: 165 return Exception::Type::FAILED; 166 } 167 } 168 169 #endif // _WIN32 170 171 enum DescriptionStyle { 172 LOG, 173 ASSERTION, 174 SYSCALL 175 }; 176 177 static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber, 178 const char* sysErrorString, const char* macroArgs, 179 ArrayPtr<String> argValues) { 180 KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64); 181 182 if (argValues.size() > 0) { 183 size_t index = 0; 184 const char* start = macroArgs; 185 while (isspace(*start)) ++start; 186 const char* pos = start; 187 uint depth = 0; 188 bool quoted = false; 189 while (char c = *pos++) { 190 if (quoted) { 191 if (c == '\\' && *pos != '\0') { 192 ++pos; 193 } else if (c == '\"') { 194 quoted = false; 195 } 196 } else { 197 if (c == '(') { 198 ++depth; 199 } else if (c == ')') { 200 --depth; 201 } else if (c == '\"') { 202 quoted = true; 203 } else if (c == ',' && depth == 0) { 204 if (index < argValues.size()) { 205 argNames[index++] = arrayPtr(start, pos - 1); 206 } 207 while (isspace(*pos)) ++pos; 208 start = pos; 209 if (*pos == '\0') { 210 // ignore trailing comma 211 break; 212 } 213 } 214 } 215 } 216 if (index < argValues.size()) { 217 argNames[index++] = arrayPtr(start, pos - 1); 218 } 219 220 if (index != argValues.size()) { 221 getExceptionCallback().logMessage(LogSeverity::ERROR, __FILE__, __LINE__, 0, 222 str("Failed to parse logging macro args into ", 223 argValues.size(), " names: ", macroArgs, '\n')); 224 } 225 } 226 227 if (style == SYSCALL) { 228 // Strip off leading "foo = " from code, since callers will sometimes write things like: 229 // ssize_t n; 230 // RECOVERABLE_SYSCALL(n = read(fd, buffer, sizeof(buffer))) { return ""; } 231 // return std::string(buffer, n); 232 const char* equalsPos = strchr(code, '='); 233 if (equalsPos != nullptr && equalsPos[1] != '=') { 234 code = equalsPos + 1; 235 while (isspace(*code)) ++code; 236 } 237 } 238 239 if (style == ASSERTION && code == nullptr) { 240 style = LOG; 241 } 242 243 { 244 StringPtr expected = "expected "; 245 StringPtr codeArray = style == LOG ? nullptr : StringPtr(code); 246 StringPtr sep = " = "; 247 StringPtr delim = "; "; 248 StringPtr colon = ": "; 249 StringPtr openBracket = " ["; 250 StringPtr closeBracket = "]"; 251 252 StringPtr sysErrorArray; 253 // On android before marshmallow only the posix version of stderror_r was 254 // available, even with __USE_GNU. 255 #if __USE_GNU && !(defined(__ANDROID_API__) && __ANDROID_API__ < 23) 256 char buffer[256]; 257 if (style == SYSCALL) { 258 if (sysErrorString == nullptr) { 259 sysErrorArray = strerror_r(errorNumber, buffer, sizeof(buffer)); 260 } else { 261 sysErrorArray = sysErrorString; 262 } 263 } 264 #else 265 char buffer[256]; 266 if (style == SYSCALL) { 267 if (sysErrorString == nullptr) { 268 strerror_r(errorNumber, buffer, sizeof(buffer)); 269 sysErrorArray = buffer; 270 } else { 271 sysErrorArray = sysErrorString; 272 } 273 } 274 #endif 275 276 size_t totalSize = 0; 277 switch (style) { 278 case LOG: 279 break; 280 case ASSERTION: 281 totalSize += expected.size() + codeArray.size(); 282 break; 283 case SYSCALL: 284 totalSize += codeArray.size() + colon.size() + sysErrorArray.size(); 285 break; 286 } 287 288 auto needsLabel = [](ArrayPtr<const char> &argName) -> bool { 289 return (argName.size() > 0 && argName[0] != '\"' && 290 !(argName.size() >= 8 && memcmp(argName.begin(), "kj::str(", 8) == 0)); 291 }; 292 293 for (size_t i = 0; i < argValues.size(); i++) { 294 if (argNames[i] == "_kjCondition"_kj) { 295 // Special handling: don't output delimiter, we want to append this to the previous item, 296 // in brackets. Also, if it's just "[false]" (meaning we didn't manage to extract a 297 // comparison), don't add it at all. 298 if (argValues[i] != "false") { 299 totalSize += openBracket.size() + argValues[i].size() + closeBracket.size(); 300 } 301 continue; 302 } 303 304 if (i > 0 || style != LOG) { 305 totalSize += delim.size(); 306 } 307 if (needsLabel(argNames[i])) { 308 totalSize += argNames[i].size() + sep.size(); 309 } 310 totalSize += argValues[i].size(); 311 } 312 313 String result = heapString(totalSize); 314 char* pos = result.begin(); 315 316 switch (style) { 317 case LOG: 318 break; 319 case ASSERTION: 320 pos = _::fill(pos, expected, codeArray); 321 break; 322 case SYSCALL: 323 pos = _::fill(pos, codeArray, colon, sysErrorArray); 324 break; 325 } 326 327 for (size_t i = 0; i < argValues.size(); i++) { 328 if (argNames[i] == "_kjCondition"_kj) { 329 // Special handling: don't output delimiter, we want to append this to the previous item, 330 // in brackets. Also, if it's just "[false]" (meaning we didn't manage to extract a 331 // comparison), don't add it at all. 332 if (argValues[i] != "false") { 333 pos = _::fill(pos, openBracket, argValues[i], closeBracket); 334 } 335 continue; 336 } 337 338 if (i > 0 || style != LOG) { 339 pos = _::fill(pos, delim); 340 } 341 if (needsLabel(argNames[i])) { 342 pos = _::fill(pos, argNames[i], sep); 343 } 344 pos = _::fill(pos, argValues[i]); 345 } 346 347 return result; 348 } 349 } 350 351 } // namespace 352 353 void Debug::logInternal(const char* file, int line, LogSeverity severity, const char* macroArgs, 354 ArrayPtr<String> argValues) { 355 getExceptionCallback().logMessage(severity, trimSourceFilename(file).cStr(), line, 0, 356 makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues)); 357 } 358 359 Debug::Fault::~Fault() noexcept(false) { 360 if (exception != nullptr) { 361 Exception copy = mv(*exception); 362 delete exception; 363 throwRecoverableException(mv(copy), 1); 364 } 365 } 366 367 void Debug::Fault::fatal() { 368 Exception copy = mv(*exception); 369 delete exception; 370 exception = nullptr; 371 throwFatalException(mv(copy), 1); 372 KJ_KNOWN_UNREACHABLE(abort()); 373 } 374 375 void Debug::Fault::init( 376 const char* file, int line, Exception::Type type, 377 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { 378 exception = new Exception(type, file, line, 379 makeDescriptionImpl(ASSERTION, condition, 0, nullptr, macroArgs, argValues)); 380 } 381 382 void Debug::Fault::init( 383 const char* file, int line, int osErrorNumber, 384 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { 385 exception = new Exception(typeOfErrno(osErrorNumber), file, line, 386 makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues)); 387 } 388 389 #if _WIN32 || __CYGWIN__ 390 void Debug::Fault::init( 391 const char* file, int line, Win32Result osErrorNumber, 392 const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { 393 LPVOID ptr; 394 // TODO(someday): Why doesn't this work for winsock errors? 395 DWORD result = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 396 FORMAT_MESSAGE_FROM_SYSTEM | 397 FORMAT_MESSAGE_IGNORE_INSERTS, 398 NULL, osErrorNumber.number, 399 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 400 (LPWSTR) &ptr, 0, NULL); 401 402 String message; 403 if (result > 0) { 404 KJ_DEFER(LocalFree(ptr)); 405 const wchar_t* desc = reinterpret_cast<wchar_t*>(ptr); 406 size_t len = wcslen(desc); 407 if (len > 0 && desc[len-1] == '\n') --len; 408 if (len > 0 && desc[len-1] == '\r') --len; 409 message = kj::str('#', osErrorNumber.number, ' ', 410 decodeWideString(arrayPtr(desc, len))); 411 } else { 412 message = kj::str("win32 error code: ", osErrorNumber.number); 413 } 414 415 exception = new Exception(typeOfWin32Error(osErrorNumber.number), file, line, 416 makeDescriptionImpl(SYSCALL, condition, 0, message.cStr(), 417 macroArgs, argValues)); 418 } 419 #endif 420 421 String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) { 422 return makeDescriptionImpl(LOG, nullptr, 0, nullptr, macroArgs, argValues); 423 } 424 425 int Debug::getOsErrorNumber(bool nonblocking) { 426 int result = errno; 427 428 // On many systems, EAGAIN and EWOULDBLOCK have the same value, but this is not strictly required 429 // by POSIX, so we need to check both. 430 return result == EINTR ? -1 431 : nonblocking && (result == EAGAIN || result == EWOULDBLOCK) ? 0 432 : result; 433 } 434 435 #if _WIN32 || __CYGWIN__ 436 uint Debug::getWin32ErrorCode() { 437 return ::GetLastError(); 438 } 439 #endif 440 441 Debug::Context::Context(): logged(false) {} 442 Debug::Context::~Context() noexcept(false) {} 443 444 Debug::Context::Value Debug::Context::ensureInitialized() { 445 KJ_IF_MAYBE(v, value) { 446 return Value(v->file, v->line, heapString(v->description)); 447 } else { 448 Value result = evaluate(); 449 value = Value(result.file, result.line, heapString(result.description)); 450 return result; 451 } 452 } 453 454 void Debug::Context::onRecoverableException(Exception&& exception) { 455 Value v = ensureInitialized(); 456 exception.wrapContext(v.file, v.line, mv(v.description)); 457 next.onRecoverableException(kj::mv(exception)); 458 } 459 void Debug::Context::onFatalException(Exception&& exception) { 460 Value v = ensureInitialized(); 461 exception.wrapContext(v.file, v.line, mv(v.description)); 462 next.onFatalException(kj::mv(exception)); 463 } 464 void Debug::Context::logMessage(LogSeverity severity, const char* file, int line, int contextDepth, 465 String&& text) { 466 if (!logged) { 467 Value v = ensureInitialized(); 468 next.logMessage(LogSeverity::INFO, trimSourceFilename(v.file).cStr(), v.line, 0, 469 str("context: ", mv(v.description), '\n')); 470 logged = true; 471 } 472 473 next.logMessage(severity, file, line, contextDepth + 1, mv(text)); 474 } 475 476 } // namespace _ (private) 477 } // namespace kj