jpayne@69: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors jpayne@69: // Licensed under the MIT License: jpayne@69: // jpayne@69: // Permission is hereby granted, free of charge, to any person obtaining a copy jpayne@69: // of this software and associated documentation files (the "Software"), to deal jpayne@69: // in the Software without restriction, including without limitation the rights jpayne@69: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell jpayne@69: // copies of the Software, and to permit persons to whom the Software is jpayne@69: // furnished to do so, subject to the following conditions: jpayne@69: // jpayne@69: // The above copyright notice and this permission notice shall be included in jpayne@69: // all copies or substantial portions of the Software. jpayne@69: // jpayne@69: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR jpayne@69: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, jpayne@69: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE jpayne@69: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER jpayne@69: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, jpayne@69: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN jpayne@69: // THE SOFTWARE. jpayne@69: jpayne@69: #pragma once jpayne@69: jpayne@69: #include "memory.h" jpayne@69: #include "array.h" jpayne@69: #include "string.h" jpayne@69: #include "windows-sanity.h" // work-around macro conflict with `ERROR` jpayne@69: jpayne@69: KJ_BEGIN_HEADER jpayne@69: jpayne@69: namespace kj { jpayne@69: jpayne@69: class ExceptionImpl; jpayne@69: template class Function; jpayne@69: jpayne@69: class Exception { jpayne@69: // Exception thrown in case of fatal errors. jpayne@69: // jpayne@69: // Actually, a subclass of this which also implements std::exception will be thrown, but we hide jpayne@69: // that fact from the interface to avoid #including . jpayne@69: jpayne@69: public: jpayne@69: enum class Type { jpayne@69: // What kind of failure? jpayne@69: jpayne@69: FAILED = 0, jpayne@69: // Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this jpayne@69: // error type. jpayne@69: jpayne@69: OVERLOADED = 1, jpayne@69: // The call failed because of a temporary lack of resources. This could be space resources jpayne@69: // (out of memory, out of disk space) or time resources (request queue overflow, operation jpayne@69: // timed out). jpayne@69: // jpayne@69: // The operation might work if tried again, but it should NOT be repeated immediately as this jpayne@69: // may simply exacerbate the problem. jpayne@69: jpayne@69: DISCONNECTED = 2, jpayne@69: // The call required communication over a connection that has been lost. The callee will need jpayne@69: // to re-establish connections and try again. jpayne@69: jpayne@69: UNIMPLEMENTED = 3 jpayne@69: // The requested method is not implemented. The caller may wish to revert to a fallback jpayne@69: // approach based on other methods. jpayne@69: jpayne@69: // IF YOU ADD A NEW VALUE: jpayne@69: // - Update the stringifier. jpayne@69: // - Update Cap'n Proto's RPC protocol's Exception.Type enum. jpayne@69: }; jpayne@69: jpayne@69: Exception(Type type, const char* file, int line, String description = nullptr) noexcept; jpayne@69: Exception(Type type, String file, int line, String description = nullptr) noexcept; jpayne@69: Exception(const Exception& other) noexcept; jpayne@69: Exception(Exception&& other) = default; jpayne@69: ~Exception() noexcept; jpayne@69: jpayne@69: const char* getFile() const { return file; } jpayne@69: int getLine() const { return line; } jpayne@69: Type getType() const { return type; } jpayne@69: StringPtr getDescription() const { return description; } jpayne@69: ArrayPtr getStackTrace() const { return arrayPtr(trace, traceCount); } jpayne@69: jpayne@69: void setDescription(kj::String&& desc) { description = kj::mv(desc); } jpayne@69: jpayne@69: StringPtr getRemoteTrace() const { return remoteTrace; } jpayne@69: void setRemoteTrace(kj::String&& value) { remoteTrace = kj::mv(value); } jpayne@69: // Additional stack trace data originating from a remote server. If present, then jpayne@69: // `getStackTrace()` only traces up until entry into the RPC system, and the remote trace jpayne@69: // contains any trace information returned over the wire. This string is human-readable but the jpayne@69: // format is otherwise unspecified. jpayne@69: jpayne@69: struct Context { jpayne@69: // Describes a bit about what was going on when the exception was thrown. jpayne@69: jpayne@69: const char* file; jpayne@69: int line; jpayne@69: String description; jpayne@69: Maybe> next; jpayne@69: jpayne@69: Context(const char* file, int line, String&& description, Maybe>&& next) jpayne@69: : file(file), line(line), description(mv(description)), next(mv(next)) {} jpayne@69: Context(const Context& other) noexcept; jpayne@69: }; jpayne@69: jpayne@69: inline Maybe getContext() const { jpayne@69: KJ_IF_MAYBE(c, context) { jpayne@69: return **c; jpayne@69: } else { jpayne@69: return nullptr; jpayne@69: } jpayne@69: } jpayne@69: jpayne@69: void wrapContext(const char* file, int line, String&& description); jpayne@69: // Wraps the context in a new node. This becomes the head node returned by getContext() -- it jpayne@69: // is expected that contexts will be added in reverse order as the exception passes up the jpayne@69: // callback stack. jpayne@69: jpayne@69: KJ_NOINLINE void extendTrace(uint ignoreCount, uint limit = kj::maxValue); jpayne@69: // Append the current stack trace to the exception's trace, ignoring the first `ignoreCount` jpayne@69: // frames (see `getStackTrace()` for discussion of `ignoreCount`). jpayne@69: // jpayne@69: // If `limit` is set, limit the number of frames added to the given number. jpayne@69: jpayne@69: KJ_NOINLINE void truncateCommonTrace(); jpayne@69: // Remove the part of the stack trace which the exception shares with the caller of this method. jpayne@69: // This is used by the async library to remove the async infrastructure from the stack trace jpayne@69: // before replacing it with the async trace. jpayne@69: jpayne@69: void addTrace(void* ptr); jpayne@69: // Append the given pointer to the backtrace, if it is not already full. This is used by the jpayne@69: // async library to trace through the promise chain that led to the exception. jpayne@69: jpayne@69: KJ_NOINLINE void addTraceHere(); jpayne@69: // Adds the location that called this method to the stack trace. jpayne@69: jpayne@69: private: jpayne@69: String ownFile; jpayne@69: const char* file; jpayne@69: int line; jpayne@69: Type type; jpayne@69: String description; jpayne@69: Maybe> context; jpayne@69: String remoteTrace; jpayne@69: void* trace[32]; jpayne@69: uint traceCount; jpayne@69: jpayne@69: bool isFullTrace = false; jpayne@69: // Is `trace` a full trace to the top of the stack (or as close as we could get before we ran jpayne@69: // out of space)? If this is false, then `trace` is instead a partial trace covering just the jpayne@69: // frames between where the exception was thrown and where it was caught. jpayne@69: // jpayne@69: // extendTrace() transitions this to true, and truncateCommonTrace() changes it back to false. jpayne@69: // jpayne@69: // In theory, an exception should only hold a full trace when it is in the process of being jpayne@69: // thrown via the C++ exception handling mechanism -- extendTrace() is called before the throw jpayne@69: // and truncateCommonTrace() after it is caught. Note that when exceptions propagate through jpayne@69: // async promises, the trace is extended one frame at a time instead, so isFullTrace should jpayne@69: // remain false. jpayne@69: jpayne@69: friend class ExceptionImpl; jpayne@69: }; jpayne@69: jpayne@69: struct CanceledException { }; jpayne@69: // This exception is thrown to force-unwind a stack in order to immediately cancel whatever that jpayne@69: // stack was doing. It is used in the implementation of fibers in particular. Application code jpayne@69: // should almost never catch this exception, unless you need to modify stack unwinding for some jpayne@69: // reason. kj::runCatchingExceptions() does not catch it. jpayne@69: jpayne@69: StringPtr KJ_STRINGIFY(Exception::Type type); jpayne@69: String KJ_STRINGIFY(const Exception& e); jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: jpayne@69: enum class LogSeverity { jpayne@69: INFO, // Information describing what the code is up to, which users may request to see jpayne@69: // with a flag like `--verbose`. Does not indicate a problem. Not printed by jpayne@69: // default; you must call setLogLevel(INFO) to enable. jpayne@69: WARNING, // A problem was detected but execution can continue with correct output. jpayne@69: ERROR, // Something is wrong, but execution can continue with garbage output. jpayne@69: FATAL, // Something went wrong, and execution cannot continue. jpayne@69: DBG // Temporary debug logging. See KJ_DBG. jpayne@69: jpayne@69: // Make sure to update the stringifier if you add a new severity level. jpayne@69: }; jpayne@69: jpayne@69: StringPtr KJ_STRINGIFY(LogSeverity severity); jpayne@69: jpayne@69: class ExceptionCallback { jpayne@69: // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order jpayne@69: // to perform your own exception handling. For example, a reasonable thing to do is to have jpayne@69: // onRecoverableException() set a flag indicating that an error occurred, and then check for that jpayne@69: // flag just before writing to storage and/or returning results to the user. If the flag is set, jpayne@69: // discard whatever you have and return an error instead. jpayne@69: // jpayne@69: // ExceptionCallbacks must always be allocated on the stack. When an exception is thrown, the jpayne@69: // newest ExceptionCallback on the calling thread's stack is called. The default implementation jpayne@69: // of each method calls the next-oldest ExceptionCallback for that thread. Thus the callbacks jpayne@69: // behave a lot like try/catch blocks, except that they are called before any stack unwinding jpayne@69: // occurs. jpayne@69: jpayne@69: public: jpayne@69: ExceptionCallback(); jpayne@69: KJ_DISALLOW_COPY_AND_MOVE(ExceptionCallback); jpayne@69: virtual ~ExceptionCallback() noexcept(false); jpayne@69: jpayne@69: virtual void onRecoverableException(Exception&& exception); jpayne@69: // Called when an exception has been raised, but the calling code has the ability to continue by jpayne@69: // producing garbage output. This method _should_ throw the exception, but is allowed to simply jpayne@69: // return if garbage output is acceptable. jpayne@69: // jpayne@69: // The global default implementation throws an exception unless the library was compiled with jpayne@69: // -fno-exceptions, in which case it logs an error and returns. jpayne@69: jpayne@69: virtual void onFatalException(Exception&& exception); jpayne@69: // Called when an exception has been raised and the calling code cannot continue. If this method jpayne@69: // returns normally, abort() will be called. The method must throw the exception to avoid jpayne@69: // aborting. jpayne@69: // jpayne@69: // The global default implementation throws an exception unless the library was compiled with jpayne@69: // -fno-exceptions, in which case it logs an error and returns. jpayne@69: jpayne@69: virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, jpayne@69: String&& text); jpayne@69: // Called when something wants to log some debug text. `contextDepth` indicates how many levels jpayne@69: // of context the message passed through; it may make sense to indent the message accordingly. jpayne@69: // jpayne@69: // The global default implementation writes the text to stderr. jpayne@69: jpayne@69: enum class StackTraceMode { jpayne@69: FULL, jpayne@69: // Stringifying a stack trace will attempt to determine source file and line numbers. This may jpayne@69: // be expensive. For example, on Linux, this shells out to `addr2line`. jpayne@69: // jpayne@69: // This is the default in debug builds. jpayne@69: jpayne@69: ADDRESS_ONLY, jpayne@69: // Stringifying a stack trace will only generate a list of code addresses. jpayne@69: // jpayne@69: // This is the default in release builds. jpayne@69: jpayne@69: NONE jpayne@69: // Generating a stack trace will always return an empty array. jpayne@69: // jpayne@69: // This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library jpayne@69: // has been observed to be pretty slow, so exception-heavy code might benefit significantly jpayne@69: // from this setting. (But exceptions should be rare...) jpayne@69: }; jpayne@69: jpayne@69: virtual StackTraceMode stackTraceMode(); jpayne@69: // Returns the current preferred stack trace mode. jpayne@69: jpayne@69: virtual Function)> getThreadInitializer(); jpayne@69: // Called just before a new thread is spawned using kj::Thread. Returns a function which should jpayne@69: // be invoked inside the new thread to initialize the thread's ExceptionCallback. The initializer jpayne@69: // function itself receives, as its parameter, the thread's main function, which it must call. jpayne@69: jpayne@69: protected: jpayne@69: ExceptionCallback& next; jpayne@69: jpayne@69: private: jpayne@69: ExceptionCallback(ExceptionCallback& next); jpayne@69: jpayne@69: class RootExceptionCallback; jpayne@69: friend ExceptionCallback& getExceptionCallback(); jpayne@69: jpayne@69: friend class Thread; jpayne@69: }; jpayne@69: jpayne@69: ExceptionCallback& getExceptionCallback(); jpayne@69: // Returns the current exception callback. jpayne@69: jpayne@69: KJ_NOINLINE KJ_NORETURN(void throwFatalException(kj::Exception&& exception, uint ignoreCount = 0)); jpayne@69: // Invoke the exception callback to throw the given fatal exception. If the exception callback jpayne@69: // returns, abort. jpayne@69: jpayne@69: KJ_NOINLINE void throwRecoverableException(kj::Exception&& exception, uint ignoreCount = 0); jpayne@69: // Invoke the exception callback to throw the given recoverable exception. If the exception jpayne@69: // callback returns, return normally. jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: jpayne@69: namespace _ { class Runnable; } jpayne@69: jpayne@69: template jpayne@69: Maybe runCatchingExceptions(Func&& func); jpayne@69: // Executes the given function (usually, a lambda returning nothing) catching any exceptions that jpayne@69: // are thrown. Returns the Exception if there was one, or null if the operation completed normally. jpayne@69: // Non-KJ exceptions will be wrapped. jpayne@69: // jpayne@69: // If exception are disabled (e.g. with -fno-exceptions), this will still detect whether any jpayne@69: // recoverable exceptions occurred while running the function and will return those. jpayne@69: jpayne@69: #if !KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: kj::Exception getCaughtExceptionAsKj(); jpayne@69: // Call from the catch block of a try/catch to get a `kj::Exception` representing the exception jpayne@69: // that was caught, the same way that `kj::runCatchingExceptions` would when catching an exception. jpayne@69: // This is sometimes useful if `runCatchingExceptions()` doesn't quite fit your use case. You can jpayne@69: // call this from any catch block, including `catch (...)`. jpayne@69: // jpayne@69: // Some exception types will actually be rethrown by this function, rather than returned. The most jpayne@69: // common example is `CanceledException`, whose purpose is to unwind the stack and is not meant to jpayne@69: // be caught. jpayne@69: jpayne@69: #endif // !KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: class UnwindDetector { jpayne@69: // Utility for detecting when a destructor is called due to unwind. Useful for: jpayne@69: // - Avoiding throwing exceptions in this case, which would terminate the program. jpayne@69: // - Detecting whether to commit or roll back a transaction. jpayne@69: // jpayne@69: // To use this class, either inherit privately from it or declare it as a member. The detector jpayne@69: // works by comparing the exception state against that when the constructor was called, so for jpayne@69: // an object that was actually constructed during exception unwind, it will behave as if no jpayne@69: // unwind is taking place. This is usually the desired behavior. jpayne@69: jpayne@69: public: jpayne@69: UnwindDetector(); jpayne@69: jpayne@69: bool isUnwinding() const; jpayne@69: // Returns true if the current thread is in a stack unwind that it wasn't in at the time the jpayne@69: // object was constructed. jpayne@69: jpayne@69: template jpayne@69: void catchExceptionsIfUnwinding(Func&& func) const; jpayne@69: // Runs the given function (e.g., a lambda). If isUnwinding() is true, any exceptions are jpayne@69: // caught and treated as secondary faults, meaning they are considered to be side-effects of the jpayne@69: // exception that is unwinding the stack. Otherwise, exceptions are passed through normally. jpayne@69: jpayne@69: private: jpayne@69: uint uncaughtCount; jpayne@69: jpayne@69: #if !KJ_NO_EXCEPTIONS jpayne@69: void catchThrownExceptionAsSecondaryFault() const; jpayne@69: #endif jpayne@69: }; jpayne@69: jpayne@69: #if KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: namespace _ { // private jpayne@69: jpayne@69: class Runnable { jpayne@69: public: jpayne@69: virtual void run() = 0; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: class RunnableImpl: public Runnable { jpayne@69: public: jpayne@69: RunnableImpl(Func&& func): func(kj::fwd(func)) {} jpayne@69: void run() override { jpayne@69: func(); jpayne@69: } jpayne@69: private: jpayne@69: Func func; jpayne@69: }; jpayne@69: jpayne@69: Maybe runCatchingExceptions(Runnable& runnable); jpayne@69: jpayne@69: } // namespace _ (private) jpayne@69: jpayne@69: #endif // KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: template jpayne@69: Maybe runCatchingExceptions(Func&& func) { jpayne@69: #if KJ_NO_EXCEPTIONS jpayne@69: _::RunnableImpl runnable(kj::fwd(func)); jpayne@69: return _::runCatchingExceptions(runnable); jpayne@69: #else jpayne@69: try { jpayne@69: func(); jpayne@69: return nullptr; jpayne@69: } catch (...) { jpayne@69: return getCaughtExceptionAsKj(); jpayne@69: } jpayne@69: #endif jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const { jpayne@69: #if KJ_NO_EXCEPTIONS jpayne@69: // Can't possibly be unwinding... jpayne@69: func(); jpayne@69: #else jpayne@69: if (isUnwinding()) { jpayne@69: try { jpayne@69: func(); jpayne@69: } catch (...) { jpayne@69: catchThrownExceptionAsSecondaryFault(); jpayne@69: } jpayne@69: } else { jpayne@69: func(); jpayne@69: } jpayne@69: #endif jpayne@69: } jpayne@69: jpayne@69: #define KJ_ON_SCOPE_SUCCESS(code) \ jpayne@69: ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \ jpayne@69: KJ_DEFER(if (!KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) jpayne@69: // Runs `code` if the current scope is exited normally (not due to an exception). jpayne@69: jpayne@69: #define KJ_ON_SCOPE_FAILURE(code) \ jpayne@69: ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \ jpayne@69: KJ_DEFER(if (KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; }) jpayne@69: // Runs `code` if the current scope is exited due to an exception. jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: jpayne@69: KJ_NOINLINE ArrayPtr getStackTrace(ArrayPtr space, uint ignoreCount); jpayne@69: // Attempt to get the current stack trace, returning a list of pointers to instructions. The jpayne@69: // returned array is a slice of `space`. Provide a larger `space` to get a deeper stack trace. jpayne@69: // If the platform doesn't support stack traces, returns an empty array. jpayne@69: // jpayne@69: // `ignoreCount` items will be truncated from the front of the trace. This is useful for chopping jpayne@69: // off a prefix of the trace that is uninteresting to the developer because it's just locations jpayne@69: // inside the debug infrastructure that is requesting the trace. Be careful to mark functions as jpayne@69: // KJ_NOINLINE if you intend to count them in `ignoreCount`. Note that, unfortunately, the jpayne@69: // ignored entries will still waste space in the `space` array (and the returned array's `begin()` jpayne@69: // is never exactly equal to `space.begin()` due to this effect, even if `ignoreCount` is zero jpayne@69: // since `getStackTrace()` needs to ignore its own internal frames). jpayne@69: jpayne@69: String stringifyStackTrace(ArrayPtr); jpayne@69: // Convert the stack trace to a string with file names and line numbers. This may involve executing jpayne@69: // suprocesses. jpayne@69: jpayne@69: String stringifyStackTraceAddresses(ArrayPtr trace); jpayne@69: StringPtr stringifyStackTraceAddresses(ArrayPtr trace, ArrayPtr scratch); jpayne@69: // Construct a string containing just enough information about a stack trace to be able to convert jpayne@69: // it to file and line numbers later using offline tools. This produces a sequence of jpayne@69: // space-separated code location identifiers. Each identifier may be an absolute address jpayne@69: // (hex number starting with 0x) or may be a module-relative address "@0x". The jpayne@69: // latter case is preferred when ASLR is in effect and has loaded different modules at different jpayne@69: // addresses. jpayne@69: jpayne@69: String getStackTrace(); jpayne@69: // Get a stack trace right now and stringify it. Useful for debugging. jpayne@69: jpayne@69: void printStackTraceOnCrash(); jpayne@69: // Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print jpayne@69: // a stack trace. You should call this as early as possible on program startup. Programs using jpayne@69: // KJ_MAIN get this automatically. jpayne@69: jpayne@69: void resetCrashHandlers(); jpayne@69: // Resets all signal handlers set by printStackTraceOnCrash(). jpayne@69: jpayne@69: kj::StringPtr trimSourceFilename(kj::StringPtr filename); jpayne@69: // Given a source code file name, trim off noisy prefixes like "src/" or jpayne@69: // "/ekam-provider/canonical/". jpayne@69: jpayne@69: kj::String getCaughtExceptionType(); jpayne@69: // Utility function which attempts to return the human-readable type name of the exception jpayne@69: // currently being thrown. This can be called inside a catch block, including a catch (...) block, jpayne@69: // for the purpose of error logging. This function is best-effort; on some platforms it may simply jpayne@69: // return "(unknown)". jpayne@69: jpayne@69: #if !KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: class InFlightExceptionIterator { jpayne@69: // A class that can be used to iterate over exceptions that are in-flight in the current thread, jpayne@69: // meaning they are either uncaught, or caught by a catch block that is current executing. jpayne@69: // jpayne@69: // This is meant for debugging purposes, and the results are best-effort. The C++ standard jpayne@69: // library does not provide any way to inspect uncaught exceptions, so this class can only jpayne@69: // discover KJ exceptions thrown using throwFatalException() or throwRecoverableException(). jpayne@69: // All KJ code uses those two functions to throw exceptions, but if your own code uses a bare jpayne@69: // `throw`, or if the standard library throws an exception, these cannot be inspected. jpayne@69: // jpayne@69: // This class is safe to use in a signal handler. jpayne@69: jpayne@69: public: jpayne@69: InFlightExceptionIterator(); jpayne@69: jpayne@69: Maybe next(); jpayne@69: jpayne@69: private: jpayne@69: const Exception* ptr; jpayne@69: }; jpayne@69: jpayne@69: #endif // !KJ_NO_EXCEPTIONS jpayne@69: jpayne@69: kj::Exception getDestructionReason(void* traceSeparator, jpayne@69: kj::Exception::Type defaultType, const char* defaultFile, int defaultLine, jpayne@69: kj::StringPtr defaultDescription); jpayne@69: // Returns an exception that attempts to capture why a destructor has been invoked. If a KJ jpayne@69: // exception is currently in-flight (see InFlightExceptionIterator), then that exception is jpayne@69: // returned. Otherwise, an exception is constructed using the current stack trace and the type, jpayne@69: // file, line, and description provided. In the latter case, `traceSeparator` is appended to the jpayne@69: // stack trace; this should be a pointer to some dummy symbol which acts as a separator between the jpayne@69: // original stack trace and any new trace frames added later. jpayne@69: jpayne@69: kj::ArrayPtr computeRelativeTrace( jpayne@69: kj::ArrayPtr trace, kj::ArrayPtr relativeTo); jpayne@69: // Given two traces expected to have started from the same root, try to find the part of `trace` jpayne@69: // that is different from `relativeTo`, considering that either or both traces might be truncated. jpayne@69: // jpayne@69: // This is useful for debugging, when reporting several related traces at once. jpayne@69: jpayne@69: void requireOnStack(void* ptr, kj::StringPtr description); jpayne@69: // Throw an exception if `ptr` does not appear to point to something near the top of the stack. jpayne@69: // Used as a safety check for types that must be stack-allocated, like ExceptionCallback. jpayne@69: jpayne@69: } // namespace kj jpayne@69: jpayne@69: KJ_END_HEADER