capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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