cannam@49: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@49: // Licensed under the MIT License: cannam@49: // cannam@49: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@49: // of this software and associated documentation files (the "Software"), to deal cannam@49: // in the Software without restriction, including without limitation the rights cannam@49: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@49: // copies of the Software, and to permit persons to whom the Software is cannam@49: // furnished to do so, subject to the following conditions: cannam@49: // cannam@49: // The above copyright notice and this permission notice shall be included in cannam@49: // all copies or substantial portions of the Software. cannam@49: // cannam@49: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@49: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@49: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@49: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@49: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@49: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@49: // THE SOFTWARE. cannam@49: cannam@49: #ifndef KJ_EXCEPTION_H_ cannam@49: #define KJ_EXCEPTION_H_ cannam@49: cannam@49: #if defined(__GNUC__) && !KJ_HEADER_WARNINGS cannam@49: #pragma GCC system_header cannam@49: #endif cannam@49: cannam@49: #include "memory.h" cannam@49: #include "array.h" cannam@49: #include "string.h" cannam@49: cannam@49: namespace kj { cannam@49: cannam@49: class ExceptionImpl; cannam@49: cannam@49: class Exception { cannam@49: // Exception thrown in case of fatal errors. cannam@49: // cannam@49: // Actually, a subclass of this which also implements std::exception will be thrown, but we hide cannam@49: // that fact from the interface to avoid #including . cannam@49: cannam@49: public: cannam@49: enum class Type { cannam@49: // What kind of failure? cannam@49: cannam@49: FAILED = 0, cannam@49: // Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this cannam@49: // error type. cannam@49: cannam@49: OVERLOADED = 1, cannam@49: // The call failed because of a temporary lack of resources. This could be space resources cannam@49: // (out of memory, out of disk space) or time resources (request queue overflow, operation cannam@49: // timed out). cannam@49: // cannam@49: // The operation might work if tried again, but it should NOT be repeated immediately as this cannam@49: // may simply exacerbate the problem. cannam@49: cannam@49: DISCONNECTED = 2, cannam@49: // The call required communication over a connection that has been lost. The callee will need cannam@49: // to re-establish connections and try again. cannam@49: cannam@49: UNIMPLEMENTED = 3 cannam@49: // The requested method is not implemented. The caller may wish to revert to a fallback cannam@49: // approach based on other methods. cannam@49: cannam@49: // IF YOU ADD A NEW VALUE: cannam@49: // - Update the stringifier. cannam@49: // - Update Cap'n Proto's RPC protocol's Exception.Type enum. cannam@49: }; cannam@49: cannam@49: Exception(Type type, const char* file, int line, String description = nullptr) noexcept; cannam@49: Exception(Type type, String file, int line, String description = nullptr) noexcept; cannam@49: Exception(const Exception& other) noexcept; cannam@49: Exception(Exception&& other) = default; cannam@49: ~Exception() noexcept; cannam@49: cannam@49: const char* getFile() const { return file; } cannam@49: int getLine() const { return line; } cannam@49: Type getType() const { return type; } cannam@49: StringPtr getDescription() const { return description; } cannam@49: ArrayPtr getStackTrace() const { return arrayPtr(trace, traceCount); } cannam@49: cannam@49: struct Context { cannam@49: // Describes a bit about what was going on when the exception was thrown. cannam@49: cannam@49: const char* file; cannam@49: int line; cannam@49: String description; cannam@49: Maybe> next; cannam@49: cannam@49: Context(const char* file, int line, String&& description, Maybe>&& next) cannam@49: : file(file), line(line), description(mv(description)), next(mv(next)) {} cannam@49: Context(const Context& other) noexcept; cannam@49: }; cannam@49: cannam@49: inline Maybe getContext() const { cannam@49: KJ_IF_MAYBE(c, context) { cannam@49: return **c; cannam@49: } else { cannam@49: return nullptr; cannam@49: } cannam@49: } cannam@49: cannam@49: void wrapContext(const char* file, int line, String&& description); cannam@49: // Wraps the context in a new node. This becomes the head node returned by getContext() -- it cannam@49: // is expected that contexts will be added in reverse order as the exception passes up the cannam@49: // callback stack. cannam@49: cannam@49: KJ_NOINLINE void extendTrace(uint ignoreCount); cannam@49: // Append the current stack trace to the exception's trace, ignoring the first `ignoreCount` cannam@49: // frames (see `getStackTrace()` for discussion of `ignoreCount`). cannam@49: cannam@49: KJ_NOINLINE void truncateCommonTrace(); cannam@49: // Remove the part of the stack trace which the exception shares with the caller of this method. cannam@49: // This is used by the async library to remove the async infrastructure from the stack trace cannam@49: // before replacing it with the async trace. cannam@49: cannam@49: void addTrace(void* ptr); cannam@49: // Append the given pointer to the backtrace, if it is not already full. This is used by the cannam@49: // async library to trace through the promise chain that led to the exception. cannam@49: cannam@49: private: cannam@49: String ownFile; cannam@49: const char* file; cannam@49: int line; cannam@49: Type type; cannam@49: String description; cannam@49: Maybe> context; cannam@49: void* trace[32]; cannam@49: uint traceCount; cannam@49: cannam@49: friend class ExceptionImpl; cannam@49: }; cannam@49: cannam@49: StringPtr KJ_STRINGIFY(Exception::Type type); cannam@49: String KJ_STRINGIFY(const Exception& e); cannam@49: cannam@49: // ======================================================================================= cannam@49: cannam@49: enum class LogSeverity { cannam@49: INFO, // Information describing what the code is up to, which users may request to see cannam@49: // with a flag like `--verbose`. Does not indicate a problem. Not printed by cannam@49: // default; you must call setLogLevel(INFO) to enable. cannam@49: WARNING, // A problem was detected but execution can continue with correct output. cannam@49: ERROR, // Something is wrong, but execution can continue with garbage output. cannam@49: FATAL, // Something went wrong, and execution cannot continue. cannam@49: DBG // Temporary debug logging. See KJ_DBG. cannam@49: cannam@49: // Make sure to update the stringifier if you add a new severity level. cannam@49: }; cannam@49: cannam@49: StringPtr KJ_STRINGIFY(LogSeverity severity); cannam@49: cannam@49: class ExceptionCallback { cannam@49: // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order cannam@49: // to perform your own exception handling. For example, a reasonable thing to do is to have cannam@49: // onRecoverableException() set a flag indicating that an error occurred, and then check for that cannam@49: // flag just before writing to storage and/or returning results to the user. If the flag is set, cannam@49: // discard whatever you have and return an error instead. cannam@49: // cannam@49: // ExceptionCallbacks must always be allocated on the stack. When an exception is thrown, the cannam@49: // newest ExceptionCallback on the calling thread's stack is called. The default implementation cannam@49: // of each method calls the next-oldest ExceptionCallback for that thread. Thus the callbacks cannam@49: // behave a lot like try/catch blocks, except that they are called before any stack unwinding cannam@49: // occurs. cannam@49: cannam@49: public: cannam@49: ExceptionCallback(); cannam@49: KJ_DISALLOW_COPY(ExceptionCallback); cannam@49: virtual ~ExceptionCallback() noexcept(false); cannam@49: cannam@49: virtual void onRecoverableException(Exception&& exception); cannam@49: // Called when an exception has been raised, but the calling code has the ability to continue by cannam@49: // producing garbage output. This method _should_ throw the exception, but is allowed to simply cannam@49: // return if garbage output is acceptable. cannam@49: // cannam@49: // The global default implementation throws an exception unless the library was compiled with cannam@49: // -fno-exceptions, in which case it logs an error and returns. cannam@49: cannam@49: virtual void onFatalException(Exception&& exception); cannam@49: // Called when an exception has been raised and the calling code cannot continue. If this method cannam@49: // returns normally, abort() will be called. The method must throw the exception to avoid cannam@49: // aborting. cannam@49: // cannam@49: // The global default implementation throws an exception unless the library was compiled with cannam@49: // -fno-exceptions, in which case it logs an error and returns. cannam@49: cannam@49: virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, cannam@49: String&& text); cannam@49: // Called when something wants to log some debug text. `contextDepth` indicates how many levels cannam@49: // of context the message passed through; it may make sense to indent the message accordingly. cannam@49: // cannam@49: // The global default implementation writes the text to stderr. cannam@49: cannam@49: protected: cannam@49: ExceptionCallback& next; cannam@49: cannam@49: private: cannam@49: ExceptionCallback(ExceptionCallback& next); cannam@49: cannam@49: class RootExceptionCallback; cannam@49: friend ExceptionCallback& getExceptionCallback(); cannam@49: }; cannam@49: cannam@49: ExceptionCallback& getExceptionCallback(); cannam@49: // Returns the current exception callback. cannam@49: cannam@49: KJ_NOINLINE KJ_NORETURN(void throwFatalException(kj::Exception&& exception, uint ignoreCount = 0)); cannam@49: // Invoke the exception callback to throw the given fatal exception. If the exception callback cannam@49: // returns, abort. cannam@49: cannam@49: KJ_NOINLINE void throwRecoverableException(kj::Exception&& exception, uint ignoreCount = 0); cannam@49: // Invoke the exception callback to throw the given recoverable exception. If the exception cannam@49: // callback returns, return normally. cannam@49: cannam@49: // ======================================================================================= cannam@49: cannam@49: namespace _ { class Runnable; } cannam@49: cannam@49: template cannam@49: Maybe runCatchingExceptions(Func&& func) noexcept; cannam@49: // Executes the given function (usually, a lambda returning nothing) catching any exceptions that cannam@49: // are thrown. Returns the Exception if there was one, or null if the operation completed normally. cannam@49: // Non-KJ exceptions will be wrapped. cannam@49: // cannam@49: // If exception are disabled (e.g. with -fno-exceptions), this will still detect whether any cannam@49: // recoverable exceptions occurred while running the function and will return those. cannam@49: cannam@49: class UnwindDetector { cannam@49: // Utility for detecting when a destructor is called due to unwind. Useful for: cannam@49: // - Avoiding throwing exceptions in this case, which would terminate the program. cannam@49: // - Detecting whether to commit or roll back a transaction. cannam@49: // cannam@49: // To use this class, either inherit privately from it or declare it as a member. The detector cannam@49: // works by comparing the exception state against that when the constructor was called, so for cannam@49: // an object that was actually constructed during exception unwind, it will behave as if no cannam@49: // unwind is taking place. This is usually the desired behavior. cannam@49: cannam@49: public: cannam@49: UnwindDetector(); cannam@49: cannam@49: bool isUnwinding() const; cannam@49: // Returns true if the current thread is in a stack unwind that it wasn't in at the time the cannam@49: // object was constructed. cannam@49: cannam@49: template cannam@49: void catchExceptionsIfUnwinding(Func&& func) const; cannam@49: // Runs the given function (e.g., a lambda). If isUnwinding() is true, any exceptions are cannam@49: // caught and treated as secondary faults, meaning they are considered to be side-effects of the cannam@49: // exception that is unwinding the stack. Otherwise, exceptions are passed through normally. cannam@49: cannam@49: private: cannam@49: uint uncaughtCount; cannam@49: cannam@49: void catchExceptionsAsSecondaryFaults(_::Runnable& runnable) const; cannam@49: }; cannam@49: cannam@49: namespace _ { // private cannam@49: cannam@49: class Runnable { cannam@49: public: cannam@49: virtual void run() = 0; cannam@49: }; cannam@49: cannam@49: template cannam@49: class RunnableImpl: public Runnable { cannam@49: public: cannam@49: RunnableImpl(Func&& func): func(kj::mv(func)) {} cannam@49: void run() override { cannam@49: func(); cannam@49: } cannam@49: private: cannam@49: Func func; cannam@49: }; cannam@49: cannam@49: Maybe runCatchingExceptions(Runnable& runnable) noexcept; cannam@49: cannam@49: } // namespace _ (private) cannam@49: cannam@49: template cannam@49: Maybe runCatchingExceptions(Func&& func) noexcept { cannam@49: _::RunnableImpl> runnable(kj::fwd(func)); cannam@49: return _::runCatchingExceptions(runnable); cannam@49: } cannam@49: cannam@49: template cannam@49: void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const { cannam@49: if (isUnwinding()) { cannam@49: _::RunnableImpl> runnable(kj::fwd(func)); cannam@49: catchExceptionsAsSecondaryFaults(runnable); cannam@49: } else { cannam@49: func(); cannam@49: } cannam@49: } cannam@49: cannam@49: #define KJ_ON_SCOPE_SUCCESS(code) \ cannam@49: ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \ cannam@49: KJ_DEFER(if (!KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) cannam@49: // Runs `code` if the current scope is exited normally (not due to an exception). cannam@49: cannam@49: #define KJ_ON_SCOPE_FAILURE(code) \ cannam@49: ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \ cannam@49: KJ_DEFER(if (KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) cannam@49: // Runs `code` if the current scope is exited due to an exception. cannam@49: cannam@49: // ======================================================================================= cannam@49: cannam@49: KJ_NOINLINE ArrayPtr getStackTrace(ArrayPtr space, uint ignoreCount); cannam@49: // Attempt to get the current stack trace, returning a list of pointers to instructions. The cannam@49: // returned array is a slice of `space`. Provide a larger `space` to get a deeper stack trace. cannam@49: // If the platform doesn't support stack traces, returns an empty array. cannam@49: // cannam@49: // `ignoreCount` items will be truncated from the front of the trace. This is useful for chopping cannam@49: // off a prefix of the trace that is uninteresting to the developer because it's just locations cannam@49: // inside the debug infrastructure that is requesting the trace. Be careful to mark functions as cannam@49: // KJ_NOINLINE if you intend to count them in `ignoreCount`. Note that, unfortunately, the cannam@49: // ignored entries will still waste space in the `space` array (and the returned array's `begin()` cannam@49: // is never exactly equal to `space.begin()` due to this effect, even if `ignoreCount` is zero cannam@49: // since `getStackTrace()` needs to ignore its own internal frames). cannam@49: cannam@49: String stringifyStackTrace(ArrayPtr); cannam@49: // Convert the stack trace to a string with file names and line numbers. This may involve executing cannam@49: // suprocesses. cannam@49: cannam@49: void printStackTraceOnCrash(); cannam@49: // Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print cannam@49: // a stack trace. You should call this as early as possible on program startup. Programs using cannam@49: // KJ_MAIN get this automatically. cannam@49: cannam@49: kj::StringPtr trimSourceFilename(kj::StringPtr filename); cannam@49: // Given a source code file name, trim off noisy prefixes like "src/" or cannam@49: // "/ekam-provider/canonical/". cannam@49: cannam@49: } // namespace kj cannam@49: cannam@49: #endif // KJ_EXCEPTION_H_