exception.c++ 28.2 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 22

#include "exception.h"
Kenton Varda's avatar
Kenton Varda committed
23
#include "string.h"
Kenton Varda's avatar
Kenton Varda committed
24
#include "debug.h"
25
#include "threadlocal.h"
26
#include "miniposix.h"
27
#include <stdlib.h>
28
#include <exception>
29
#include <new>
30
#include <signal.h>
31
#ifndef _WIN32
32
#include <sys/mman.h>
33
#endif
34
#include "io.h"
35

36
#if (__linux__ && __GLIBC__) || __APPLE__
37
#define KJ_HAS_BACKTRACE 1
38 39 40
#include <execinfo.h>
#endif

41 42 43 44 45 46 47
#if _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "windows-sanity.h"
#include <dbghelp.h>
#endif

48
#if (__linux__ || __APPLE__)
49
#include <stdio.h>
50
#include <pthread.h>
51 52
#endif

53
namespace kj {
54

55 56 57 58 59 60 61 62 63 64 65 66
StringPtr KJ_STRINGIFY(LogSeverity severity) {
  static const char* SEVERITY_STRINGS[] = {
    "info",
    "warning",
    "error",
    "fatal",
    "debug"
  };

  return SEVERITY_STRINGS[static_cast<uint>(severity)];
}

67 68 69 70 71 72 73 74 75 76 77 78
#if _WIN32 && _M_X64
// Currently the Win32 stack-trace code only supports x86_64. We could easily extend it to support
// i386 as well but it requires some code changes around how we read the context to start the
// trace.

namespace {

struct Dbghelp {
  // Load dbghelp.dll dynamically since we don't really need it, it's just for debugging.

  HINSTANCE lib;

79 80
  BOOL (WINAPI *symInitialize)(HANDLE hProcess,PCSTR UserSearchPath,BOOL fInvadeProcess);
  BOOL (WINAPI *stackWalk64)(
81 82 83 84 85 86
      DWORD MachineType,HANDLE hProcess,HANDLE hThread,
      LPSTACKFRAME64 StackFrame,PVOID ContextRecord,
      PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
      PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
      PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
      PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
87 88 89
  PVOID (WINAPI *symFunctionTableAccess64)(HANDLE hProcess,DWORD64 AddrBase);
  DWORD64 (WINAPI *symGetModuleBase64)(HANDLE hProcess,DWORD64 qwAddr);
  BOOL (WINAPI *symGetLineFromAddr64)(
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
      HANDLE hProcess,DWORD64 qwAddr,PDWORD pdwDisplacement,PIMAGEHLP_LINE64 Line64);

  Dbghelp()
      : lib(LoadLibraryA("dbghelp.dll")),
        symInitialize(lib == nullptr ? nullptr :
            reinterpret_cast<decltype(symInitialize)>(
                GetProcAddress(lib, "SymInitialize"))),
        stackWalk64(symInitialize == nullptr ? nullptr :
            reinterpret_cast<decltype(stackWalk64)>(
                GetProcAddress(lib, "StackWalk64"))),
        symFunctionTableAccess64(symInitialize == nullptr ? nullptr :
            reinterpret_cast<decltype(symFunctionTableAccess64)>(
                GetProcAddress(lib, "SymFunctionTableAccess64"))),
        symGetModuleBase64(symInitialize == nullptr ? nullptr :
            reinterpret_cast<decltype(symGetModuleBase64)>(
                GetProcAddress(lib, "SymGetModuleBase64"))),
        symGetLineFromAddr64(symInitialize == nullptr ? nullptr :
            reinterpret_cast<decltype(symGetLineFromAddr64)>(
                GetProcAddress(lib, "SymGetLineFromAddr64"))) {
    if (symInitialize != nullptr) {
      symInitialize(GetCurrentProcess(), NULL, TRUE);
    }
  }
};

const Dbghelp& getDbghelp() {
  static Dbghelp dbghelp;
  return dbghelp;
}

ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
                                    HANDLE thread, CONTEXT& context) {
  const Dbghelp& dbghelp = getDbghelp();
  if (dbghelp.stackWalk64 == nullptr ||
      dbghelp.symFunctionTableAccess64 == nullptr ||
      dbghelp.symGetModuleBase64 == nullptr) {
    return nullptr;
  }

  STACKFRAME64 frame;
  memset(&frame, 0, sizeof(frame));

  frame.AddrPC.Offset = context.Rip;
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrStack.Offset = context.Rsp;
  frame.AddrStack.Mode = AddrModeFlat;
  frame.AddrFrame.Offset = context.Rbp;
  frame.AddrFrame.Mode = AddrModeFlat;

  HANDLE process = GetCurrentProcess();

  uint count = 0;
  for (; count < space.size(); count++) {
    if (!dbghelp.stackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread,
          &frame, &context, NULL, dbghelp.symFunctionTableAccess64,
          dbghelp.symGetModuleBase64, NULL)){
      break;
    }

    space[count] = reinterpret_cast<void*>(frame.AddrPC.Offset);
  }

  return space.slice(kj::min(ignoreCount, count), count);
}

}  // namespace
#endif

Kenton Varda's avatar
Kenton Varda committed
158
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
159 160 161 162
  if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) {
    return nullptr;
  }

163 164 165 166 167
#if _WIN32 && _M_X64
  CONTEXT context;
  RtlCaptureContext(&context);
  return getStackTrace(space, ignoreCount, GetCurrentThread(), context);
#elif KJ_HAS_BACKTRACE
Kenton Varda's avatar
Kenton Varda committed
168 169
  size_t size = backtrace(space.begin(), space.size());
  return space.slice(kj::min(ignoreCount + 1, size), size);
170 171
#else
  return nullptr;
172 173
#endif
}
174

175
String stringifyStackTrace(ArrayPtr<void* const> trace) {
Kenton Varda's avatar
Kenton Varda committed
176
  if (trace.size() == 0) return nullptr;
177 178 179
  if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) {
    return nullptr;
  }
Kenton Varda's avatar
Kenton Varda committed
180

181
#if _WIN32 && _M_X64 && _MSC_VER
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

  // Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
  // this requires MSVC debug info.
  //
  // TODO(someday): We could perhaps shell out to addr2line on MinGW.

  const Dbghelp& dbghelp = getDbghelp();
  if (dbghelp.symGetLineFromAddr64 == nullptr) return nullptr;

  HANDLE process = GetCurrentProcess();

  KJ_STACK_ARRAY(String, lines, trace.size(), 32, 32);

  for (auto i: kj::indices(trace)) {
    IMAGEHLP_LINE64 lineInfo;
    memset(&lineInfo, 0, sizeof(lineInfo));
    lineInfo.SizeOfStruct = sizeof(lineInfo);
    if (dbghelp.symGetLineFromAddr64(process, reinterpret_cast<DWORD64>(trace[i]), NULL, &lineInfo)) {
      lines[i] = kj::str('\n', lineInfo.FileName, ':', lineInfo.LineNumber);
    }
  }

  return strArray(lines, "");

#elif (__linux__ || __APPLE__) && !__ANDROID__
207 208
  // We want to generate a human-readable stack trace.

209 210
  // TODO(someday):  It would be really great if we could avoid farming out to another process
  //   and do this all in-process, but that may involve onerous requirements like large library
211 212 213 214 215 216 217 218
  //   dependencies or using -rdynamic.

  // The environment manipulation is not thread-safe, so lock a mutex.  This could still be
  // problematic if another thread is manipulating the environment in unrelated code, but there's
  // not much we can do about that.  This is debug-only anyway and only an issue when LD_PRELOAD
  // is in use.
  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  pthread_mutex_lock(&mutex);
219
  KJ_DEFER(pthread_mutex_unlock(&mutex));
220

221
  // Don't heapcheck / intercept syscalls.
222 223 224 225 226 227
  const char* preload = getenv("LD_PRELOAD");
  String oldPreload;
  if (preload != nullptr) {
    oldPreload = heapString(preload);
    unsetenv("LD_PRELOAD");
  }
228
  KJ_DEFER(if (oldPreload != nullptr) { setenv("LD_PRELOAD", oldPreload.cStr(), true); });
229

230
  String lines[32];
231
  FILE* p = nullptr;
232
  auto strTrace = strArray(trace, " ");
233 234

#if __linux__
235 236
  if (access("/proc/self/exe", R_OK) < 0) {
    // Apparently /proc is not available?
237
    return nullptr;
238 239
  }

240 241 242 243
  // Obtain symbolic stack trace using addr2line.
  // TODO(cleanup): Use fork() and exec() or maybe our own Subprocess API (once it exists), to
  //   avoid depending on a shell.
  p = popen(str("addr2line -e /proc/", getpid(), "/exe ", strTrace).cStr(), "r");
244 245 246
#elif __APPLE__
  // The Mac OS X equivalent of addr2line is atos.
  // (Internally, it uses the private CoreSymbolication.framework library.)
247
  p = popen(str("xcrun atos -p ", getpid(), ' ', strTrace).cStr(), "r");
248
#endif
249 250

  if (p == nullptr) {
251
    return nullptr;
252 253 254
  }

  char line[512];
255 256
  size_t i = 0;
  while (i < kj::size(lines) && fgets(line, sizeof(line), p) != nullptr) {
Kenton Varda's avatar
Kenton Varda committed
257
    // Don't include exception-handling infrastructure or promise infrastructure in stack trace.
258
    // addr2line output matches file names; atos output matches symbol names.
Kenton Varda's avatar
Kenton Varda committed
259 260 261 262 263 264 265 266
    if (strstr(line, "kj/common.c++") != nullptr ||
        strstr(line, "kj/exception.") != nullptr ||
        strstr(line, "kj/debug.") != nullptr ||
        strstr(line, "kj/async.") != nullptr ||
        strstr(line, "kj/async-prelude.h") != nullptr ||
        strstr(line, "kj/async-inl.h") != nullptr ||
        strstr(line, "kj::Exception") != nullptr ||
        strstr(line, "kj::_::Debug") != nullptr) {
267 268 269 270 271
      continue;
    }

    size_t len = strlen(line);
    if (len > 0 && line[len-1] == '\n') line[len-1] = '\0';
Kenton Varda's avatar
Kenton Varda committed
272
    lines[i++] = str("\n    ", trimSourceFilename(line), ": returning here");
273 274 275 276 277 278 279
  }

  // Skip remaining input.
  while (fgets(line, sizeof(line), p) != nullptr) {}

  pclose(p);

280
  return strArray(arrayPtr(lines, i), "");
281

282 283 284 285 286
#else
  return nullptr;
#endif
}

287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
String getStackTrace() {
  void* space[32];
  auto trace = getStackTrace(space, 2);
  return kj::str(kj::strArray(trace, " "), stringifyStackTrace(trace));
}

#if _WIN32 && _M_X64
namespace {

DWORD mainThreadId = 0;

BOOL WINAPI breakHandler(DWORD type) {
  switch (type) {
    case CTRL_C_EVENT:
    case CTRL_BREAK_EVENT: {
      HANDLE thread = OpenThread(THREAD_ALL_ACCESS, FALSE, mainThreadId);
      if (thread != NULL) {
        if (SuspendThread(thread) != (DWORD)-1) {
          CONTEXT context;
          memset(&context, 0, sizeof(context));
          context.ContextFlags = CONTEXT_FULL;
          if (GetThreadContext(thread, &context)) {
            void* traceSpace[32];
            auto trace = getStackTrace(traceSpace, 2, thread, context);
            ResumeThread(thread);
            auto message = kj::str("*** Received CTRL+C. stack: ", strArray(trace, " "),
                                   stringifyStackTrace(trace), '\n');
            FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
          } else {
            ResumeThread(thread);
          }
        }
        CloseHandle(thread);
      }
      break;
    }
    default:
      break;
  }

  return FALSE;  // still crash
}

}  // namespace

void printStackTraceOnCrash() {
  mainThreadId = GetCurrentThreadId();
  KJ_WIN32(SetConsoleCtrlHandler(breakHandler, TRUE));
}

#elif KJ_HAS_BACKTRACE
338 339 340 341 342
namespace {

void crashHandler(int signo, siginfo_t* info, void* context) {
  void* traceSpace[32];

Kenton Varda's avatar
Kenton Varda committed
343 344
  // ignoreCount = 2 to ignore crashHandler() and signal trampoline.
  auto trace = getStackTrace(traceSpace, 2);
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

  auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
                         "\nstack: ", strArray(trace, " "),
                         stringifyStackTrace(trace), '\n');

  FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
  _exit(1);
}

}  // namespace

void printStackTraceOnCrash() {
  // Set up alternate signal stack so that stack overflows can be handled.
  stack_t stack;
  memset(&stack, 0, sizeof(stack));

#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#ifndef MAP_GROWSDOWN
#define MAP_GROWSDOWN 0
#endif

  stack.ss_size = 65536;
  // Note: ss_sp is char* on FreeBSD, void* on Linux and OSX.
  stack.ss_sp = reinterpret_cast<char*>(mmap(
      nullptr, stack.ss_size, PROT_READ | PROT_WRITE,
      MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0));
  KJ_SYSCALL(sigaltstack(&stack, nullptr));

  // Catch all relevant signals.
  struct sigaction action;
  memset(&action, 0, sizeof(action));

  action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND;
  action.sa_sigaction = &crashHandler;

  // Dump stack on common "crash" signals.
  KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr));
  KJ_SYSCALL(sigaction(SIGBUS, &action, nullptr));
  KJ_SYSCALL(sigaction(SIGFPE, &action, nullptr));
  KJ_SYSCALL(sigaction(SIGABRT, &action, nullptr));
387
  KJ_SYSCALL(sigaction(SIGILL, &action, nullptr));
388 389 390 391 392 393 394 395 396 397

  // Dump stack on unimplemented syscalls -- useful in seccomp sandboxes.
  KJ_SYSCALL(sigaction(SIGSYS, &action, nullptr));

#ifdef KJ_DEBUG
  // Dump stack on keyboard interrupt -- useful for infinite loops. Only in debug mode, though,
  // because stack traces on ctrl+c can be obnoxious for, say, command-line tools.
  KJ_SYSCALL(sigaction(SIGINT, &action, nullptr));
#endif
}
398 399 400 401
#else
void printStackTraceOnCrash() {
}
#endif
402

403 404
kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
  // Removes noisy prefixes from source code file name.
405 406 407 408 409 410 411 412 413
  //
  // The goal here is to produce the "canonical" filename given the filename returned by e.g.
  // addr2line. addr2line gives us the full path of the file as passed on the compiler
  // command-line, which in turn is affected by build system and by whether and where we're
  // performing an out-of-tree build.
  //
  // To deal with all this, we look for directory names in the path which we recognize to be
  // locations that represent roots of the source tree. We strip said root and everything before
  // it.
414 415 416 417
  //
  // On Windows, we often get filenames containing backslashes. Since we aren't allowed to allocate
  // a new string here, we can't do much about this, so our returned "canonical" name will
  // unfortunately end up with backslashes.
418 419 420 421 422

  static constexpr const char* ROOTS[] = {
    "ekam-provider/canonical/",  // Ekam source file.
    "ekam-provider/c++header/",  // Ekam include file.
    "src/",                      // Non-Ekam source root.
423 424 425 426 427
    "tmp/",                      // Non-Ekam generated code.
#if _WIN32
    "src\\",                     // Win32 source root.
    "tmp\\",                     // Win32 generated code.
#endif
428 429 430
  };

retry:
431
  for (size_t i: kj::indices(filename)) {
432 433 434 435 436
    if (i == 0 || filename[i-1] == '/'
#if _WIN32
               || filename[i-1] == '\\'
#endif
        ) {
437 438 439 440 441 442 443 444 445 446
      // We're at the start of a directory name. Check for valid prefixes.
      for (kj::StringPtr root: ROOTS) {
        if (filename.slice(i).startsWith(root)) {
          filename = filename.slice(i + root.size());

          // We should keep searching to find the last instance of a root name. `i` is no longer
          // a valid index for `filename` so start the loop over.
          goto retry;
        }
      }
447 448 449 450 451 452
    }
  }

  return filename;
}

453
StringPtr KJ_STRINGIFY(Exception::Type type) {
454 455 456 457 458
  static const char* TYPE_STRINGS[] = {
    "failed",
    "overloaded",
    "disconnected",
    "unimplemented"
459 460
  };

461
  return TYPE_STRINGS[static_cast<uint>(type)];
462 463
}

464 465 466 467 468 469 470
String KJ_STRINGIFY(const Exception& e) {
  uint contextDepth = 0;

  Maybe<const Exception::Context&> contextPtr = e.getContext();
  for (;;) {
    KJ_IF_MAYBE(c, contextPtr) {
      ++contextDepth;
471
      contextPtr = c->next;
472 473 474 475 476 477 478 479 480 481 482 483 484
    } else {
      break;
    }
  }

  Array<String> contextText = heapArray<String>(contextDepth);

  contextDepth = 0;
  contextPtr = e.getContext();
  for (;;) {
    KJ_IF_MAYBE(c, contextPtr) {
      contextText[contextDepth++] =
          str(c->file, ":", c->line, ": context: ", c->description, "\n");
485
      contextPtr = c->next;
486 487 488 489 490 491
    } else {
      break;
    }
  }

  return str(strArray(contextText, ""),
492
             e.getFile(), ":", e.getLine(), ": ", e.getType(),
493
             e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
494
             e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
495
             stringifyStackTrace(e.getStackTrace()));
496 497
}

498
Exception::Exception(Type type, const char* file, int line, String description) noexcept
Kenton Varda's avatar
Kenton Varda committed
499 500
    : file(trimSourceFilename(file).cStr()), line(line), type(type), description(mv(description)),
      traceCount(0) {}
501

502
Exception::Exception(Type type, String file, int line, String description) noexcept
503
    : ownFile(kj::mv(file)), file(trimSourceFilename(ownFile).cStr()), line(line), type(type),
Kenton Varda's avatar
Kenton Varda committed
504
      description(mv(description)), traceCount(0) {}
505

506
Exception::Exception(const Exception& other) noexcept
507
    : file(other.file), line(other.line), type(other.type),
508 509 510 511 512 513
      description(heapString(other.description)), traceCount(other.traceCount) {
  if (file == other.ownFile.cStr()) {
    ownFile = heapString(other.ownFile);
    file = ownFile.cStr();
  }

514
  memcpy(trace, other.trace, sizeof(trace[0]) * traceCount);
515

516
  KJ_IF_MAYBE(c, other.context) {
517
    context = heap(**c);
518
  }
519
}
520 521 522

Exception::~Exception() noexcept {}

523 524
Exception::Context::Context(const Context& other) noexcept
    : file(other.file), line(other.line), description(str(other.description)) {
525
  KJ_IF_MAYBE(n, other.next) {
526
    next = heap(**n);
527 528 529
  }
}

Kenton Varda's avatar
Kenton Varda committed
530
void Exception::wrapContext(const char* file, int line, String&& description) {
Kenton Varda's avatar
Kenton Varda committed
531
  context = heap<Context>(file, line, mv(description), mv(context));
532 533
}

Kenton Varda's avatar
Kenton Varda committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
void Exception::extendTrace(uint ignoreCount) {
  KJ_STACK_ARRAY(void*, newTraceSpace, kj::size(trace) + ignoreCount + 1,
      sizeof(trace)/sizeof(trace[0]) + 8, 128);

  auto newTrace = kj::getStackTrace(newTraceSpace, ignoreCount + 1);
  if (newTrace.size() > ignoreCount + 2) {
    // Remove suffix that won't fit into our static-sized trace.
    newTrace = newTrace.slice(0, kj::min(kj::size(trace) - traceCount, newTrace.size()));

    // Copy the rest into our trace.
    memcpy(trace + traceCount, newTrace.begin(), newTrace.asBytes().size());
    traceCount += newTrace.size();
  }
}

void Exception::truncateCommonTrace() {
  if (traceCount > 0) {
    // Create a "reference" stack trace that is a little bit deeper than the one in the exception.
    void* refTraceSpace[sizeof(this->trace) / sizeof(this->trace[0]) + 4];
    auto refTrace = kj::getStackTrace(refTraceSpace, 0);

    // We expect that the deepest frame in the exception's stack trace should be somewhere in our
    // own trace, since our own trace has a deeper limit. Search for it.
    for (uint i = refTrace.size(); i > 0; i--) {
      if (refTrace[i-1] == trace[traceCount-1]) {
        // See how many frames match.
        for (uint j = 0; j < i; j++) {
          if (j >= traceCount) {
            // We matched the whole trace, apparently?
            traceCount = 0;
            return;
          } else if (refTrace[i-j-1] != trace[traceCount-j-1]) {
            // Found mismatching entry.

            // If we matched more than half of the reference trace, guess that this is in fact
            // the prefix we're looking for.
            if (j > refTrace.size() / 2) {
              // Delete the matching suffix. Also delete one non-matched entry on the assumption
              // that both traces contain that stack frame but are simply at different points in
              // the function.
              traceCount -= j + 1;
              return;
            }
          }
        }
      }
    }

    // No match. Ignore.
  }
}

void Exception::addTrace(void* ptr) {
  if (traceCount < kj::size(trace)) {
    trace[traceCount++] = ptr;
  }
}

592 593 594 595 596
class ExceptionImpl: public Exception, public std::exception {
public:
  inline ExceptionImpl(Exception&& other): Exception(mv(other)) {}
  ExceptionImpl(const ExceptionImpl& other): Exception(other) {
    // No need to copy whatBuffer since it's just to hold the return value of what().
597 598
  }

599
  const char* what() const noexcept override;
600

601 602 603
private:
  mutable String whatBuffer;
};
604

605 606
const char* ExceptionImpl::what() const noexcept {
  whatBuffer = str(*this);
607
  return whatBuffer.begin();
608 609 610 611 612 613
}

// =======================================================================================

namespace {

614
KJ_THREADLOCAL_PTR(ExceptionCallback) threadLocalCallback = nullptr;
615

616 617
}  // namespace

618 619 620
ExceptionCallback::ExceptionCallback(): next(getExceptionCallback()) {
  char stackVar;
  ptrdiff_t offset = reinterpret_cast<char*>(this) - &stackVar;
Kenton Varda's avatar
Kenton Varda committed
621
  KJ_ASSERT(offset < 65536 && offset > -65536,
622 623 624 625 626 627 628
            "ExceptionCallback must be allocated on the stack.");

  threadLocalCallback = this;
}

ExceptionCallback::ExceptionCallback(ExceptionCallback& next): next(next) {}

629
ExceptionCallback::~ExceptionCallback() noexcept(false) {
630 631
  if (&next != this) {
    threadLocalCallback = &next;
632 633 634 635
  }
}

void ExceptionCallback::onRecoverableException(Exception&& exception) {
636 637 638 639 640 641 642
  next.onRecoverableException(mv(exception));
}

void ExceptionCallback::onFatalException(Exception&& exception) {
  next.onFatalException(mv(exception));
}

643 644 645
void ExceptionCallback::logMessage(
    LogSeverity severity, const char* file, int line, int contextDepth, String&& text) {
  next.logMessage(severity, file, line, contextDepth, mv(text));
646 647
}

648 649 650 651
ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() {
  return next.stackTraceMode();
}

652 653 654 655 656
class ExceptionCallback::RootExceptionCallback: public ExceptionCallback {
public:
  RootExceptionCallback(): ExceptionCallback(*this) {}

  void onRecoverableException(Exception&& exception) override {
Kenton Varda's avatar
Kenton Varda committed
657
#if KJ_NO_EXCEPTIONS
658
    logException(LogSeverity::ERROR, mv(exception));
659
#else
660 661
    if (std::uncaught_exception()) {
      // Bad time to throw an exception.  Just log instead.
662 663 664 665 666
      //
      // TODO(someday): We should really compare uncaughtExceptionCount() against the count at
      //   the innermost runCatchingExceptions() frame in this thread to tell if exceptions are
      //   being caught correctly.
      logException(LogSeverity::ERROR, mv(exception));
667 668 669
    } else {
      throw ExceptionImpl(mv(exception));
    }
670
#endif
671
  }
672

673
  void onFatalException(Exception&& exception) override {
Kenton Varda's avatar
Kenton Varda committed
674
#if KJ_NO_EXCEPTIONS
675
    logException(LogSeverity::FATAL, mv(exception));
676
#else
677
    throw ExceptionImpl(mv(exception));
678
#endif
679
  }
680

681 682 683 684
  void logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
                  String&& text) override {
    text = str(kj::repeat('_', contextDepth), file, ":", line, ": ", severity, ": ",
               mv(text), '\n');
685

686
    StringPtr textPtr = text;
687

688
    while (text != nullptr) {
689
      miniposix::ssize_t n = miniposix::write(STDERR_FILENO, textPtr.begin(), textPtr.size());
690 691 692 693 694 695 696
      if (n <= 0) {
        // stderr is broken.  Give up.
        return;
      }
      textPtr = textPtr.slice(n);
    }
  }
697

698 699 700 701 702 703 704 705
  StackTraceMode stackTraceMode() override {
#ifdef KJ_DEBUG
    return StackTraceMode::FULL;
#else
    return StackTraceMode::ADDRESS_ONLY;
#endif
  }

706
private:
707
  void logException(LogSeverity severity, Exception&& e) {
708 709 710 711 712
    // We intentionally go back to the top exception callback on the stack because we don't want to
    // bypass whatever log processing is in effect.
    //
    // We intentionally don't log the context since it should get re-added by the exception callback
    // anyway.
713
    getExceptionCallback().logMessage(severity, e.getFile(), e.getLine(), 0, str(
714
        e.getType(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
715
        e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
716
        stringifyStackTrace(e.getStackTrace()), "\n"));
717 718
  }
};
719

720
ExceptionCallback& getExceptionCallback() {
721 722 723
  static ExceptionCallback::RootExceptionCallback defaultCallback;
  ExceptionCallback* scoped = threadLocalCallback;
  return scoped != nullptr ? *scoped : defaultCallback;
724 725
}

Kenton Varda's avatar
Kenton Varda committed
726 727
void throwFatalException(kj::Exception&& exception, uint ignoreCount) {
  exception.extendTrace(ignoreCount + 1);
728 729 730 731
  getExceptionCallback().onFatalException(kj::mv(exception));
  abort();
}

Kenton Varda's avatar
Kenton Varda committed
732 733
void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) {
  exception.extendTrace(ignoreCount + 1);
734 735 736
  getExceptionCallback().onRecoverableException(kj::mv(exception));
}

737 738
// =======================================================================================

739
namespace _ {  // private
740 741 742

#if __GNUC__

743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
// Horrible -- but working -- hack:  We can dig into __cxa_get_globals() in order to extract the
// count of uncaught exceptions.  This function is part of the C++ ABI implementation used on Linux,
// OSX, and probably other platforms that use GCC.  Unfortunately, __cxa_get_globals() is only
// actually defined in cxxabi.h on some platforms (e.g. Linux, but not OSX), and even where it is
// defined, it returns an incomplete type.  Here we use the same hack used by Evgeny Panasyuk:
//   https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp
//
// Notice that a similar hack is possible on MSVC -- if its C++11 support ever gets to the point of
// supporting KJ in the first place.
//
// It appears likely that a future version of the C++ standard may include an
// uncaught_exception_count() function in the standard library, or an equivalent language feature.
// Some discussion:
//   https://groups.google.com/a/isocpp.org/d/msg/std-proposals/HglEslyZFYs/kKdu5jJw5AgJ

struct FakeEhGlobals {
  // Fake

  void* caughtExceptions;
762 763 764
  uint uncaughtExceptions;
};

765 766 767 768 769 770 771
// Because of the 'extern "C"', the symbol name is not mangled and thus the namespace is effectively
// ignored for linking.  Thus it doesn't matter that we are declaring __cxa_get_globals() in a
// different namespace from the ABI's definition.
extern "C" {
FakeEhGlobals* __cxa_get_globals();
}

772 773 774
uint uncaughtExceptionCount() {
  // TODO(perf):  Use __cxa_get_globals_fast()?  Requires that __cxa_get_globals() has been called
  //   from somewhere.
775
  return __cxa_get_globals()->uncaughtExceptions;
776 777
}

778 779
#elif _MSC_VER

780
#if _MSC_VER >= 1900
781
// MSVC14 has a refactored CRT which now provides a direct accessor for this value.
782
// See https://svn.boost.org/trac/boost/ticket/10158 for a brief discussion.
783
extern "C" int *__cdecl __processing_throw();
784 785

uint uncaughtExceptionCount() {
786
  return static_cast<uint>(*__processing_throw());
787 788 789 790 791
}

#elif _MSC_VER >= 1400
// The below was copied from:
// https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp
792

793 794 795 796
extern "C" char *__cdecl _getptd();

uint uncaughtExceptionCount() {
  return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90));
797
}
798
#else
799 800 801 802
uint uncaughtExceptionCount() {
  // Since the above doesn't work, fall back to uncaught_exception(). This will produce incorrect
  // results in very obscure cases that Cap'n Proto doesn't really rely on anyway.
  return std::uncaught_exception();
803
}
804
#endif
805

806 807 808 809
#else
#error "This needs to be ported to your compiler / C++ ABI."
#endif

810
}  // namespace _ (private)
811

812
UnwindDetector::UnwindDetector(): uncaughtCount(_::uncaughtExceptionCount()) {}
813 814

bool UnwindDetector::isUnwinding() const {
815
  return _::uncaughtExceptionCount() > uncaughtCount;
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
}

void UnwindDetector::catchExceptionsAsSecondaryFaults(_::Runnable& runnable) const {
  // TODO(someday):  Attach the secondary exception to whatever primary exception is causing
  //   the unwind.  For now we just drop it on the floor as this is probably fine most of the
  //   time.
  runCatchingExceptions(runnable);
}

namespace _ {  // private

class RecoverableExceptionCatcher: public ExceptionCallback {
  // Catches a recoverable exception without using try/catch.  Used when compiled with
  // -fno-exceptions.

public:
832
  virtual ~RecoverableExceptionCatcher() noexcept(false) {}
833 834 835 836 837 838 839 840 841 842 843 844

  void onRecoverableException(Exception&& exception) override {
    if (caught == nullptr) {
      caught = mv(exception);
    } else {
      // TODO(someday):  Consider it a secondary fault?
    }
  }

  Maybe<Exception> caught;
};

845
Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept {
846 847 848
#if KJ_NO_EXCEPTIONS
  RecoverableExceptionCatcher catcher;
  runnable.run();
Kenton Varda's avatar
Kenton Varda committed
849 850 851
  KJ_IF_MAYBE(e, catcher.caught) {
    e->truncateCommonTrace();
  }
852 853 854 855 856 857
  return mv(catcher.caught);
#else
  try {
    runnable.run();
    return nullptr;
  } catch (Exception& e) {
Kenton Varda's avatar
Kenton Varda committed
858
    e.truncateCommonTrace();
859
    return kj::mv(e);
860 861 862
  } catch (std::bad_alloc& e) {
    return Exception(Exception::Type::OVERLOADED,
                     "(unknown)", -1, str("std::bad_alloc: ", e.what()));
863
  } catch (std::exception& e) {
864
    return Exception(Exception::Type::FAILED,
865 866
                     "(unknown)", -1, str("std::exception: ", e.what()));
  } catch (...) {
867
    return Exception(Exception::Type::FAILED,
868 869 870 871 872 873 874
                     "(unknown)", -1, str("Unknown non-KJ exception."));
  }
#endif
}

}  // namespace _ (private)

875
}  // namespace kj