blob: 5bf77642cb8b2bb06a5d6927ae891d1fb51117c5 [file] [log] [blame]
// Copyright (c) 2013, Kenton Varda <[email protected]>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "exception.h"
#include "util.h"
#include "logging.h"
#include <unistd.h>
#include <execinfo.h>
#include <stdlib.h>
namespace kj {
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
static const char* NATURE_STRINGS[] = {
"precondition not met",
"bug in code",
"invalid input data",
"error from OS",
"network failure",
"error"
};
const char* s = NATURE_STRINGS[static_cast<uint>(nature)];
return arrayPtr(s, strlen(s));
}
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability) {
static const char* DURABILITY_STRINGS[] = {
"temporary",
"permanent"
};
const char* s = DURABILITY_STRINGS[static_cast<uint>(durability)];
return arrayPtr(s, strlen(s));
}
Exception::Exception(Nature nature, Durability durability, const char* file, int line,
Array<char> description) noexcept
: file(file), line(line), nature(nature), durability(durability),
description(mv(description)) {
traceCount = backtrace(trace, 16);
}
Exception::Exception(const Exception& other) noexcept
: file(other.file), line(other.line), nature(other.nature), durability(other.durability),
description(str(other.description)), traceCount(other.traceCount) {
memcpy(trace, other.trace, sizeof(trace[0]) * traceCount);
KJ_IF_MAYBE(c, other.context) {
context = heap<Context>(**c);
}
}
Exception::~Exception() noexcept {}
Exception::Context::Context(const Context& other) noexcept
: file(other.file), line(other.line), description(str(other.description)) {
KJ_IF_MAYBE(n, other.next) {
next = heap<Context>(**n);
}
}
void Exception::wrapContext(const char* file, int line, Array<char>&& description) {
context = heap<Context>(file, line, mv(description), mv(context));
}
const char* Exception::what() const noexcept {
uint contextDepth = 0;
const Maybe<Own<Context>>* contextPtr = &context;
for (;;) {
KJ_IF_MAYBE(c, *contextPtr) {
++contextDepth;
contextPtr = &(*c)->next;
} else {
break;
}
}
Array<Array<char>> contextText = newArray<Array<char>>(contextDepth);
contextDepth = 0;
contextPtr = &context;
for (;;) {
KJ_IF_MAYBE(c, *contextPtr) {
const Context& node = **c;
contextText[contextDepth++] =
str(node.file, ":", node.line, ": context: ", node.description, "\n");
contextPtr = &node.next;
} else {
break;
}
}
// Must be careful to NUL-terminate this.
whatBuffer = str(strArray(contextText, ""),
file, ":", line, ": ", nature,
durability == Durability::TEMPORARY ? " (temporary)" : "",
this->description == nullptr ? "" : ": ", this->description,
"\nstack: ", strArray(arrayPtr(trace, traceCount), " "), '\0');
return whatBuffer.begin();
}
// =======================================================================================
namespace {
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
#define thread_local __thread
#endif
thread_local ExceptionCallback::ScopedRegistration* threadLocalCallback = nullptr;
ExceptionCallback* globalCallback = nullptr;
} // namespace
ExceptionCallback::ExceptionCallback() {}
ExceptionCallback::~ExceptionCallback() {
if (globalCallback == this) {
globalCallback = nullptr;
}
}
void ExceptionCallback::onRecoverableException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n'));
#else
if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n'));
} else {
throw std::move(exception);
}
#endif
}
void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n'));
#else
throw std::move(exception);
#endif
}
void ExceptionCallback::logMessage(ArrayPtr<const char> text) {
while (text != nullptr) {
ssize_t n = write(STDERR_FILENO, text.begin(), text.size());
if (n <= 0) {
// stderr is broken. Give up.
return;
}
text = text.slice(n, text.size());
}
}
void ExceptionCallback::useProcessWide() {
RECOVERABLE_PRECOND(globalCallback == nullptr,
"Can't register multiple global ExceptionCallbacks at once.") {
return;
}
globalCallback = this;
}
ExceptionCallback::ScopedRegistration::ScopedRegistration(ExceptionCallback& callback)
: callback(callback) {
old = threadLocalCallback;
threadLocalCallback = this;
}
ExceptionCallback::ScopedRegistration::~ScopedRegistration() {
threadLocalCallback = old;
}
ExceptionCallback& getExceptionCallback() {
static ExceptionCallback defaultCallback;
ExceptionCallback::ScopedRegistration* scoped = threadLocalCallback;
return scoped != nullptr ? scoped->getCallback() :
globalCallback != nullptr ? *globalCallback : defaultCallback;
}
} // namespace kj