debug.c++ 9.55 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21

Kenton Varda's avatar
Kenton Varda committed
22
#include "debug.h"
23 24
#include <stdlib.h>
#include <ctype.h>
25 26
#include <string.h>
#include <errno.h>
27

28 29 30 31
#if _WIN32
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
#endif

32
namespace kj {
Kenton Varda's avatar
Kenton Varda committed
33
namespace _ {  // private
34

Kenton Varda's avatar
Kenton Varda committed
35
Debug::Severity Debug::minSeverity = Debug::Severity::WARNING;
36

Kenton Varda's avatar
Kenton Varda committed
37
ArrayPtr<const char> KJ_STRINGIFY(Debug::Severity severity) {
38 39 40 41
  static const char* SEVERITY_STRINGS[] = {
    "info",
    "warning",
    "error",
42 43
    "fatal",
    "debug"
44 45 46 47 48 49
  };

  const char* s = SEVERITY_STRINGS[static_cast<uint>(severity)];
  return arrayPtr(s, strlen(s));
}

50 51
namespace {

52 53
Exception::Type typeOfErrno(int error) {
  switch (error) {
54
#ifdef EDQUOT
55
    case EDQUOT:
56 57
#endif
#ifdef EMFILE
58
    case EMFILE:
59 60
#endif
#ifdef ENFILE
61
    case ENFILE:
62 63
#endif
#ifdef ENOBUFS
64
    case ENOBUFS:
65 66
#endif
#ifdef ENOLCK
67
    case ENOLCK:
68 69
#endif
#ifdef ENOMEM
70
    case ENOMEM:
71 72
#endif
#ifdef ENOSPC
73
    case ENOSPC:
74 75
#endif
#ifdef ETIMEDOUT
76
    case ETIMEDOUT:
77 78
#endif
#ifdef EUSERS
79
    case EUSERS:
80
#endif
81 82
      return Exception::Type::OVERLOADED;

83 84 85
#ifdef ENOTCONN:
    case ENOTCONN:
#endif
86
#ifdef ECONNABORTED
87
    case ECONNABORTED:
88 89
#endif
#ifdef ECONNREFUSED
90
    case ECONNREFUSED:
91 92
#endif
#ifdef ECONNRESET
93
    case ECONNRESET:
94 95
#endif
#ifdef EHOSTDOWN
96
    case EHOSTDOWN:
97 98
#endif
#ifdef EHOSTUNREACH
99
    case EHOSTUNREACH:
100 101
#endif
#ifdef ENETDOWN
102
    case ENETDOWN:
103 104
#endif
#ifdef ENETRESET
105
    case ENETRESET:
106 107
#endif
#ifdef ENETUNREACH
108
    case ENETUNREACH:
109 110
#endif
#ifdef ENONET
111
    case ENONET:
112 113
#endif
#ifdef EPIPE
114
    case EPIPE:
115
#endif
116 117
      return Exception::Type::DISCONNECTED;

118
#ifdef ENOSYS
119
    case ENOSYS:
120 121
#endif
#ifdef ENOTSUP
122 123
    case ENOTSUP:
#endif
124
#if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP
125
    case EOPNOTSUPP:
126 127 128 129 130 131 132
#endif
#ifdef ENOPROTOOPT
    case ENOPROTOOPT:
#endif
#ifdef ENOTSOCK
    // This is really saying "syscall not implemented for non-sockets".
    case ENOTSOCK:
133 134 135 136 137 138 139 140
#endif
      return Exception::Type::UNIMPLEMENTED;

    default:
      return Exception::Type::FAILED;
  }
}

141 142 143 144 145 146
enum DescriptionStyle {
  LOG,
  ASSERTION,
  SYSCALL
};

147 148
static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber,
                                  const char* macroArgs, ArrayPtr<String> argValues) {
149
  KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

  if (argValues.size() > 0) {
    size_t index = 0;
    const char* start = macroArgs;
    while (isspace(*start)) ++start;
    const char* pos = start;
    uint depth = 0;
    bool quoted = false;
    while (char c = *pos++) {
      if (quoted) {
        if (c == '\\' && *pos != '\0') {
          ++pos;
        } else if (c == '\"') {
          quoted = false;
        }
      } else {
        if (c == '(') {
          ++depth;
        } else if (c == ')') {
          --depth;
        } else if (c == '\"') {
          quoted = true;
        } else if (c == ',' && depth == 0) {
          if (index < argValues.size()) {
            argNames[index] = arrayPtr(start, pos - 1);
          }
          ++index;
          while (isspace(*pos)) ++pos;
          start = pos;
        }
      }
    }
    if (index < argValues.size()) {
      argNames[index] = arrayPtr(start, pos - 1);
    }
    ++index;

    if (index != argValues.size()) {
188 189
      getExceptionCallback().logMessage(__FILE__, __LINE__, 0,
          str("Failed to parse logging macro args into ",
190 191 192 193
              argValues.size(), " names: ", macroArgs, '\n'));
    }
  }

194 195 196 197 198 199 200 201 202 203 204 205
  if (style == SYSCALL) {
    // Strip off leading "foo = " from code, since callers will sometimes write things like:
    //   ssize_t n;
    //   RECOVERABLE_SYSCALL(n = read(fd, buffer, sizeof(buffer))) { return ""; }
    //   return std::string(buffer, n);
    const char* equalsPos = strchr(code, '=');
    if (equalsPos != nullptr && equalsPos[1] != '=') {
      code = equalsPos + 1;
      while (isspace(*code)) ++code;
    }
  }

206 207 208 209
  if (style == ASSERTION && code == nullptr) {
    style = LOG;
  }

210
  {
Kenton Varda's avatar
Kenton Varda committed
211 212 213 214 215
    StringPtr expected = "expected ";
    StringPtr codeArray = style == LOG ? nullptr : StringPtr(code);
    StringPtr sep = " = ";
    StringPtr delim = "; ";
    StringPtr colon = ": ";
216

Kenton Varda's avatar
Kenton Varda committed
217
    StringPtr sysErrorArray;
218 219 220
#if __USE_GNU
    char buffer[256];
    if (style == SYSCALL) {
Kenton Varda's avatar
Kenton Varda committed
221
      sysErrorArray = strerror_r(errorNumber, buffer, sizeof(buffer));
222 223
    }
#else
Kenton Varda's avatar
Kenton Varda committed
224 225 226 227 228
    char buffer[256];
    if (style == SYSCALL) {
      strerror_r(errorNumber, buffer, sizeof(buffer));
      sysErrorArray = buffer;
    }
229
#endif
230 231

    size_t totalSize = 0;
232 233 234 235 236 237 238 239 240
    switch (style) {
      case LOG:
        break;
      case ASSERTION:
        totalSize += expected.size() + codeArray.size();
        break;
      case SYSCALL:
        totalSize += codeArray.size() + colon.size() + sysErrorArray.size();
        break;
241
    }
242

243
    for (size_t i = 0; i < argValues.size(); i++) {
244 245 246
      if (i > 0 || style != LOG) {
        totalSize += delim.size();
      }
247 248 249 250 251 252
      if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
        totalSize += argNames[i].size() + sep.size();
      }
      totalSize += argValues[i].size();
    }

Kenton Varda's avatar
Kenton Varda committed
253 254
    String result = heapString(totalSize);
    char* pos = result.begin();
255

256 257 258 259
    switch (style) {
      case LOG:
        break;
      case ASSERTION:
260
        pos = _::fill(pos, expected, codeArray);
261 262
        break;
      case SYSCALL:
263
        pos = _::fill(pos, codeArray, colon, sysErrorArray);
264
        break;
265 266
    }
    for (size_t i = 0; i < argValues.size(); i++) {
267
      if (i > 0 || style != LOG) {
268
        pos = _::fill(pos, delim);
269
      }
270
      if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
271
        pos = _::fill(pos, argNames[i], sep);
272
      }
273
      pos = _::fill(pos, argValues[i]);
274 275
    }

Kenton Varda's avatar
Kenton Varda committed
276
    return result;
277 278 279
  }
}

280 281
}  // namespace

Kenton Varda's avatar
Kenton Varda committed
282 283
void Debug::logInternal(const char* file, int line, Severity severity, const char* macroArgs,
                        ArrayPtr<String> argValues) {
284
  getExceptionCallback().logMessage(file, line, 0,
285
      str(severity, ": ", makeDescriptionImpl(LOG, nullptr, 0, macroArgs, argValues), '\n'));
286 287
}

Kenton Varda's avatar
Kenton Varda committed
288
Debug::Fault::~Fault() noexcept(false) {
289 290 291
  if (exception != nullptr) {
    Exception copy = mv(*exception);
    delete exception;
292
    throwRecoverableException(mv(copy));
293
  }
294 295
}

Kenton Varda's avatar
Kenton Varda committed
296
void Debug::Fault::fatal() {
297 298 299
  Exception copy = mv(*exception);
  delete exception;
  exception = nullptr;
300
  throwFatalException(mv(copy));
301 302 303
  abort();
}

304 305 306 307 308 309 310
void Debug::Fault::init(
    const char* file, int line, Exception::Type type,
    const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
  exception = new Exception(type, file, line,
      makeDescriptionImpl(ASSERTION, condition, 0, macroArgs, argValues));
}

Kenton Varda's avatar
Kenton Varda committed
311
void Debug::Fault::init(
312
    const char* file, int line, int osErrorNumber,
313
    const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
314
  exception = new Exception(typeOfErrno(osErrorNumber), file, line,
315
      makeDescriptionImpl(SYSCALL, condition, osErrorNumber, macroArgs, argValues));
316 317
}

318 319
String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
  return makeDescriptionImpl(LOG, nullptr, 0, macroArgs, argValues);
320 321
}

322
int Debug::getOsErrorNumber(bool nonblocking) {
323
  int result = errno;
324 325 326 327 328 329

  // On many systems, EAGAIN and EWOULDBLOCK have the same value, but this is not strictly required
  // by POSIX, so we need to check both.
  return result == EINTR ? -1
       : nonblocking && (result == EAGAIN || result == EWOULDBLOCK) ? 0
       : result;
330 331
}

Kenton Varda's avatar
Kenton Varda committed
332
Debug::Context::Context(): logged(false) {}
333
Debug::Context::~Context() noexcept(false) {}
334

Kenton Varda's avatar
Kenton Varda committed
335
Debug::Context::Value Debug::Context::ensureInitialized() {
336 337 338 339 340 341 342 343 344
  KJ_IF_MAYBE(v, value) {
    return Value(v->file, v->line, heapString(v->description));
  } else {
    Value result = evaluate();
    value = Value(result.file, result.line, heapString(result.description));
    return result;
  }
}

Kenton Varda's avatar
Kenton Varda committed
345
void Debug::Context::onRecoverableException(Exception&& exception) {
346 347
  Value v = ensureInitialized();
  exception.wrapContext(v.file, v.line, mv(v.description));
Kenton Varda's avatar
Kenton Varda committed
348
  next.onRecoverableException(kj::mv(exception));
349
}
Kenton Varda's avatar
Kenton Varda committed
350
void Debug::Context::onFatalException(Exception&& exception) {
351 352
  Value v = ensureInitialized();
  exception.wrapContext(v.file, v.line, mv(v.description));
Kenton Varda's avatar
Kenton Varda committed
353
  next.onFatalException(kj::mv(exception));
354
}
Kenton Varda's avatar
Kenton Varda committed
355
void Debug::Context::logMessage(const char* file, int line, int contextDepth, String&& text) {
356 357 358 359 360 361 362
  if (!logged) {
    Value v = ensureInitialized();
    next.logMessage(v.file, v.line, 0, str("context: ", mv(v.description), '\n'));
    logged = true;
  }

  next.logMessage(file, line, contextDepth + 1, mv(text));
363 364
}

Kenton Varda's avatar
Kenton Varda committed
365
}  // namespace _ (private)
366
}  // namespace kj