jpayne@69
|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
jpayne@69
|
2 // Licensed under the MIT License:
|
jpayne@69
|
3 //
|
jpayne@69
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
jpayne@69
|
5 // of this software and associated documentation files (the "Software"), to deal
|
jpayne@69
|
6 // in the Software without restriction, including without limitation the rights
|
jpayne@69
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
jpayne@69
|
8 // copies of the Software, and to permit persons to whom the Software is
|
jpayne@69
|
9 // furnished to do so, subject to the following conditions:
|
jpayne@69
|
10 //
|
jpayne@69
|
11 // The above copyright notice and this permission notice shall be included in
|
jpayne@69
|
12 // all copies or substantial portions of the Software.
|
jpayne@69
|
13 //
|
jpayne@69
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
jpayne@69
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
jpayne@69
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
jpayne@69
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
jpayne@69
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
jpayne@69
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
jpayne@69
|
20 // THE SOFTWARE.
|
jpayne@69
|
21
|
jpayne@69
|
22 #pragma once
|
jpayne@69
|
23
|
jpayne@69
|
24 #include "memory.h"
|
jpayne@69
|
25 #include "array.h"
|
jpayne@69
|
26 #include "string.h"
|
jpayne@69
|
27 #include "windows-sanity.h" // work-around macro conflict with `ERROR`
|
jpayne@69
|
28
|
jpayne@69
|
29 KJ_BEGIN_HEADER
|
jpayne@69
|
30
|
jpayne@69
|
31 namespace kj {
|
jpayne@69
|
32
|
jpayne@69
|
33 class ExceptionImpl;
|
jpayne@69
|
34 template <typename T> class Function;
|
jpayne@69
|
35
|
jpayne@69
|
36 class Exception {
|
jpayne@69
|
37 // Exception thrown in case of fatal errors.
|
jpayne@69
|
38 //
|
jpayne@69
|
39 // Actually, a subclass of this which also implements std::exception will be thrown, but we hide
|
jpayne@69
|
40 // that fact from the interface to avoid #including <exception>.
|
jpayne@69
|
41
|
jpayne@69
|
42 public:
|
jpayne@69
|
43 enum class Type {
|
jpayne@69
|
44 // What kind of failure?
|
jpayne@69
|
45
|
jpayne@69
|
46 FAILED = 0,
|
jpayne@69
|
47 // Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this
|
jpayne@69
|
48 // error type.
|
jpayne@69
|
49
|
jpayne@69
|
50 OVERLOADED = 1,
|
jpayne@69
|
51 // The call failed because of a temporary lack of resources. This could be space resources
|
jpayne@69
|
52 // (out of memory, out of disk space) or time resources (request queue overflow, operation
|
jpayne@69
|
53 // timed out).
|
jpayne@69
|
54 //
|
jpayne@69
|
55 // The operation might work if tried again, but it should NOT be repeated immediately as this
|
jpayne@69
|
56 // may simply exacerbate the problem.
|
jpayne@69
|
57
|
jpayne@69
|
58 DISCONNECTED = 2,
|
jpayne@69
|
59 // The call required communication over a connection that has been lost. The callee will need
|
jpayne@69
|
60 // to re-establish connections and try again.
|
jpayne@69
|
61
|
jpayne@69
|
62 UNIMPLEMENTED = 3
|
jpayne@69
|
63 // The requested method is not implemented. The caller may wish to revert to a fallback
|
jpayne@69
|
64 // approach based on other methods.
|
jpayne@69
|
65
|
jpayne@69
|
66 // IF YOU ADD A NEW VALUE:
|
jpayne@69
|
67 // - Update the stringifier.
|
jpayne@69
|
68 // - Update Cap'n Proto's RPC protocol's Exception.Type enum.
|
jpayne@69
|
69 };
|
jpayne@69
|
70
|
jpayne@69
|
71 Exception(Type type, const char* file, int line, String description = nullptr) noexcept;
|
jpayne@69
|
72 Exception(Type type, String file, int line, String description = nullptr) noexcept;
|
jpayne@69
|
73 Exception(const Exception& other) noexcept;
|
jpayne@69
|
74 Exception(Exception&& other) = default;
|
jpayne@69
|
75 ~Exception() noexcept;
|
jpayne@69
|
76
|
jpayne@69
|
77 const char* getFile() const { return file; }
|
jpayne@69
|
78 int getLine() const { return line; }
|
jpayne@69
|
79 Type getType() const { return type; }
|
jpayne@69
|
80 StringPtr getDescription() const { return description; }
|
jpayne@69
|
81 ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
|
jpayne@69
|
82
|
jpayne@69
|
83 void setDescription(kj::String&& desc) { description = kj::mv(desc); }
|
jpayne@69
|
84
|
jpayne@69
|
85 StringPtr getRemoteTrace() const { return remoteTrace; }
|
jpayne@69
|
86 void setRemoteTrace(kj::String&& value) { remoteTrace = kj::mv(value); }
|
jpayne@69
|
87 // Additional stack trace data originating from a remote server. If present, then
|
jpayne@69
|
88 // `getStackTrace()` only traces up until entry into the RPC system, and the remote trace
|
jpayne@69
|
89 // contains any trace information returned over the wire. This string is human-readable but the
|
jpayne@69
|
90 // format is otherwise unspecified.
|
jpayne@69
|
91
|
jpayne@69
|
92 struct Context {
|
jpayne@69
|
93 // Describes a bit about what was going on when the exception was thrown.
|
jpayne@69
|
94
|
jpayne@69
|
95 const char* file;
|
jpayne@69
|
96 int line;
|
jpayne@69
|
97 String description;
|
jpayne@69
|
98 Maybe<Own<Context>> next;
|
jpayne@69
|
99
|
jpayne@69
|
100 Context(const char* file, int line, String&& description, Maybe<Own<Context>>&& next)
|
jpayne@69
|
101 : file(file), line(line), description(mv(description)), next(mv(next)) {}
|
jpayne@69
|
102 Context(const Context& other) noexcept;
|
jpayne@69
|
103 };
|
jpayne@69
|
104
|
jpayne@69
|
105 inline Maybe<const Context&> getContext() const {
|
jpayne@69
|
106 KJ_IF_MAYBE(c, context) {
|
jpayne@69
|
107 return **c;
|
jpayne@69
|
108 } else {
|
jpayne@69
|
109 return nullptr;
|
jpayne@69
|
110 }
|
jpayne@69
|
111 }
|
jpayne@69
|
112
|
jpayne@69
|
113 void wrapContext(const char* file, int line, String&& description);
|
jpayne@69
|
114 // Wraps the context in a new node. This becomes the head node returned by getContext() -- it
|
jpayne@69
|
115 // is expected that contexts will be added in reverse order as the exception passes up the
|
jpayne@69
|
116 // callback stack.
|
jpayne@69
|
117
|
jpayne@69
|
118 KJ_NOINLINE void extendTrace(uint ignoreCount, uint limit = kj::maxValue);
|
jpayne@69
|
119 // Append the current stack trace to the exception's trace, ignoring the first `ignoreCount`
|
jpayne@69
|
120 // frames (see `getStackTrace()` for discussion of `ignoreCount`).
|
jpayne@69
|
121 //
|
jpayne@69
|
122 // If `limit` is set, limit the number of frames added to the given number.
|
jpayne@69
|
123
|
jpayne@69
|
124 KJ_NOINLINE void truncateCommonTrace();
|
jpayne@69
|
125 // Remove the part of the stack trace which the exception shares with the caller of this method.
|
jpayne@69
|
126 // This is used by the async library to remove the async infrastructure from the stack trace
|
jpayne@69
|
127 // before replacing it with the async trace.
|
jpayne@69
|
128
|
jpayne@69
|
129 void addTrace(void* ptr);
|
jpayne@69
|
130 // Append the given pointer to the backtrace, if it is not already full. This is used by the
|
jpayne@69
|
131 // async library to trace through the promise chain that led to the exception.
|
jpayne@69
|
132
|
jpayne@69
|
133 KJ_NOINLINE void addTraceHere();
|
jpayne@69
|
134 // Adds the location that called this method to the stack trace.
|
jpayne@69
|
135
|
jpayne@69
|
136 private:
|
jpayne@69
|
137 String ownFile;
|
jpayne@69
|
138 const char* file;
|
jpayne@69
|
139 int line;
|
jpayne@69
|
140 Type type;
|
jpayne@69
|
141 String description;
|
jpayne@69
|
142 Maybe<Own<Context>> context;
|
jpayne@69
|
143 String remoteTrace;
|
jpayne@69
|
144 void* trace[32];
|
jpayne@69
|
145 uint traceCount;
|
jpayne@69
|
146
|
jpayne@69
|
147 bool isFullTrace = false;
|
jpayne@69
|
148 // Is `trace` a full trace to the top of the stack (or as close as we could get before we ran
|
jpayne@69
|
149 // out of space)? If this is false, then `trace` is instead a partial trace covering just the
|
jpayne@69
|
150 // frames between where the exception was thrown and where it was caught.
|
jpayne@69
|
151 //
|
jpayne@69
|
152 // extendTrace() transitions this to true, and truncateCommonTrace() changes it back to false.
|
jpayne@69
|
153 //
|
jpayne@69
|
154 // In theory, an exception should only hold a full trace when it is in the process of being
|
jpayne@69
|
155 // thrown via the C++ exception handling mechanism -- extendTrace() is called before the throw
|
jpayne@69
|
156 // and truncateCommonTrace() after it is caught. Note that when exceptions propagate through
|
jpayne@69
|
157 // async promises, the trace is extended one frame at a time instead, so isFullTrace should
|
jpayne@69
|
158 // remain false.
|
jpayne@69
|
159
|
jpayne@69
|
160 friend class ExceptionImpl;
|
jpayne@69
|
161 };
|
jpayne@69
|
162
|
jpayne@69
|
163 struct CanceledException { };
|
jpayne@69
|
164 // This exception is thrown to force-unwind a stack in order to immediately cancel whatever that
|
jpayne@69
|
165 // stack was doing. It is used in the implementation of fibers in particular. Application code
|
jpayne@69
|
166 // should almost never catch this exception, unless you need to modify stack unwinding for some
|
jpayne@69
|
167 // reason. kj::runCatchingExceptions() does not catch it.
|
jpayne@69
|
168
|
jpayne@69
|
169 StringPtr KJ_STRINGIFY(Exception::Type type);
|
jpayne@69
|
170 String KJ_STRINGIFY(const Exception& e);
|
jpayne@69
|
171
|
jpayne@69
|
172 // =======================================================================================
|
jpayne@69
|
173
|
jpayne@69
|
174 enum class LogSeverity {
|
jpayne@69
|
175 INFO, // Information describing what the code is up to, which users may request to see
|
jpayne@69
|
176 // with a flag like `--verbose`. Does not indicate a problem. Not printed by
|
jpayne@69
|
177 // default; you must call setLogLevel(INFO) to enable.
|
jpayne@69
|
178 WARNING, // A problem was detected but execution can continue with correct output.
|
jpayne@69
|
179 ERROR, // Something is wrong, but execution can continue with garbage output.
|
jpayne@69
|
180 FATAL, // Something went wrong, and execution cannot continue.
|
jpayne@69
|
181 DBG // Temporary debug logging. See KJ_DBG.
|
jpayne@69
|
182
|
jpayne@69
|
183 // Make sure to update the stringifier if you add a new severity level.
|
jpayne@69
|
184 };
|
jpayne@69
|
185
|
jpayne@69
|
186 StringPtr KJ_STRINGIFY(LogSeverity severity);
|
jpayne@69
|
187
|
jpayne@69
|
188 class ExceptionCallback {
|
jpayne@69
|
189 // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
|
jpayne@69
|
190 // to perform your own exception handling. For example, a reasonable thing to do is to have
|
jpayne@69
|
191 // onRecoverableException() set a flag indicating that an error occurred, and then check for that
|
jpayne@69
|
192 // flag just before writing to storage and/or returning results to the user. If the flag is set,
|
jpayne@69
|
193 // discard whatever you have and return an error instead.
|
jpayne@69
|
194 //
|
jpayne@69
|
195 // ExceptionCallbacks must always be allocated on the stack. When an exception is thrown, the
|
jpayne@69
|
196 // newest ExceptionCallback on the calling thread's stack is called. The default implementation
|
jpayne@69
|
197 // of each method calls the next-oldest ExceptionCallback for that thread. Thus the callbacks
|
jpayne@69
|
198 // behave a lot like try/catch blocks, except that they are called before any stack unwinding
|
jpayne@69
|
199 // occurs.
|
jpayne@69
|
200
|
jpayne@69
|
201 public:
|
jpayne@69
|
202 ExceptionCallback();
|
jpayne@69
|
203 KJ_DISALLOW_COPY_AND_MOVE(ExceptionCallback);
|
jpayne@69
|
204 virtual ~ExceptionCallback() noexcept(false);
|
jpayne@69
|
205
|
jpayne@69
|
206 virtual void onRecoverableException(Exception&& exception);
|
jpayne@69
|
207 // Called when an exception has been raised, but the calling code has the ability to continue by
|
jpayne@69
|
208 // producing garbage output. This method _should_ throw the exception, but is allowed to simply
|
jpayne@69
|
209 // return if garbage output is acceptable.
|
jpayne@69
|
210 //
|
jpayne@69
|
211 // The global default implementation throws an exception unless the library was compiled with
|
jpayne@69
|
212 // -fno-exceptions, in which case it logs an error and returns.
|
jpayne@69
|
213
|
jpayne@69
|
214 virtual void onFatalException(Exception&& exception);
|
jpayne@69
|
215 // Called when an exception has been raised and the calling code cannot continue. If this method
|
jpayne@69
|
216 // returns normally, abort() will be called. The method must throw the exception to avoid
|
jpayne@69
|
217 // aborting.
|
jpayne@69
|
218 //
|
jpayne@69
|
219 // The global default implementation throws an exception unless the library was compiled with
|
jpayne@69
|
220 // -fno-exceptions, in which case it logs an error and returns.
|
jpayne@69
|
221
|
jpayne@69
|
222 virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
|
jpayne@69
|
223 String&& text);
|
jpayne@69
|
224 // Called when something wants to log some debug text. `contextDepth` indicates how many levels
|
jpayne@69
|
225 // of context the message passed through; it may make sense to indent the message accordingly.
|
jpayne@69
|
226 //
|
jpayne@69
|
227 // The global default implementation writes the text to stderr.
|
jpayne@69
|
228
|
jpayne@69
|
229 enum class StackTraceMode {
|
jpayne@69
|
230 FULL,
|
jpayne@69
|
231 // Stringifying a stack trace will attempt to determine source file and line numbers. This may
|
jpayne@69
|
232 // be expensive. For example, on Linux, this shells out to `addr2line`.
|
jpayne@69
|
233 //
|
jpayne@69
|
234 // This is the default in debug builds.
|
jpayne@69
|
235
|
jpayne@69
|
236 ADDRESS_ONLY,
|
jpayne@69
|
237 // Stringifying a stack trace will only generate a list of code addresses.
|
jpayne@69
|
238 //
|
jpayne@69
|
239 // This is the default in release builds.
|
jpayne@69
|
240
|
jpayne@69
|
241 NONE
|
jpayne@69
|
242 // Generating a stack trace will always return an empty array.
|
jpayne@69
|
243 //
|
jpayne@69
|
244 // This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library
|
jpayne@69
|
245 // has been observed to be pretty slow, so exception-heavy code might benefit significantly
|
jpayne@69
|
246 // from this setting. (But exceptions should be rare...)
|
jpayne@69
|
247 };
|
jpayne@69
|
248
|
jpayne@69
|
249 virtual StackTraceMode stackTraceMode();
|
jpayne@69
|
250 // Returns the current preferred stack trace mode.
|
jpayne@69
|
251
|
jpayne@69
|
252 virtual Function<void(Function<void()>)> getThreadInitializer();
|
jpayne@69
|
253 // Called just before a new thread is spawned using kj::Thread. Returns a function which should
|
jpayne@69
|
254 // be invoked inside the new thread to initialize the thread's ExceptionCallback. The initializer
|
jpayne@69
|
255 // function itself receives, as its parameter, the thread's main function, which it must call.
|
jpayne@69
|
256
|
jpayne@69
|
257 protected:
|
jpayne@69
|
258 ExceptionCallback& next;
|
jpayne@69
|
259
|
jpayne@69
|
260 private:
|
jpayne@69
|
261 ExceptionCallback(ExceptionCallback& next);
|
jpayne@69
|
262
|
jpayne@69
|
263 class RootExceptionCallback;
|
jpayne@69
|
264 friend ExceptionCallback& getExceptionCallback();
|
jpayne@69
|
265
|
jpayne@69
|
266 friend class Thread;
|
jpayne@69
|
267 };
|
jpayne@69
|
268
|
jpayne@69
|
269 ExceptionCallback& getExceptionCallback();
|
jpayne@69
|
270 // Returns the current exception callback.
|
jpayne@69
|
271
|
jpayne@69
|
272 KJ_NOINLINE KJ_NORETURN(void throwFatalException(kj::Exception&& exception, uint ignoreCount = 0));
|
jpayne@69
|
273 // Invoke the exception callback to throw the given fatal exception. If the exception callback
|
jpayne@69
|
274 // returns, abort.
|
jpayne@69
|
275
|
jpayne@69
|
276 KJ_NOINLINE void throwRecoverableException(kj::Exception&& exception, uint ignoreCount = 0);
|
jpayne@69
|
277 // Invoke the exception callback to throw the given recoverable exception. If the exception
|
jpayne@69
|
278 // callback returns, return normally.
|
jpayne@69
|
279
|
jpayne@69
|
280 // =======================================================================================
|
jpayne@69
|
281
|
jpayne@69
|
282 namespace _ { class Runnable; }
|
jpayne@69
|
283
|
jpayne@69
|
284 template <typename Func>
|
jpayne@69
|
285 Maybe<Exception> runCatchingExceptions(Func&& func);
|
jpayne@69
|
286 // Executes the given function (usually, a lambda returning nothing) catching any exceptions that
|
jpayne@69
|
287 // are thrown. Returns the Exception if there was one, or null if the operation completed normally.
|
jpayne@69
|
288 // Non-KJ exceptions will be wrapped.
|
jpayne@69
|
289 //
|
jpayne@69
|
290 // If exception are disabled (e.g. with -fno-exceptions), this will still detect whether any
|
jpayne@69
|
291 // recoverable exceptions occurred while running the function and will return those.
|
jpayne@69
|
292
|
jpayne@69
|
293 #if !KJ_NO_EXCEPTIONS
|
jpayne@69
|
294
|
jpayne@69
|
295 kj::Exception getCaughtExceptionAsKj();
|
jpayne@69
|
296 // Call from the catch block of a try/catch to get a `kj::Exception` representing the exception
|
jpayne@69
|
297 // that was caught, the same way that `kj::runCatchingExceptions` would when catching an exception.
|
jpayne@69
|
298 // This is sometimes useful if `runCatchingExceptions()` doesn't quite fit your use case. You can
|
jpayne@69
|
299 // call this from any catch block, including `catch (...)`.
|
jpayne@69
|
300 //
|
jpayne@69
|
301 // Some exception types will actually be rethrown by this function, rather than returned. The most
|
jpayne@69
|
302 // common example is `CanceledException`, whose purpose is to unwind the stack and is not meant to
|
jpayne@69
|
303 // be caught.
|
jpayne@69
|
304
|
jpayne@69
|
305 #endif // !KJ_NO_EXCEPTIONS
|
jpayne@69
|
306
|
jpayne@69
|
307 class UnwindDetector {
|
jpayne@69
|
308 // Utility for detecting when a destructor is called due to unwind. Useful for:
|
jpayne@69
|
309 // - Avoiding throwing exceptions in this case, which would terminate the program.
|
jpayne@69
|
310 // - Detecting whether to commit or roll back a transaction.
|
jpayne@69
|
311 //
|
jpayne@69
|
312 // To use this class, either inherit privately from it or declare it as a member. The detector
|
jpayne@69
|
313 // works by comparing the exception state against that when the constructor was called, so for
|
jpayne@69
|
314 // an object that was actually constructed during exception unwind, it will behave as if no
|
jpayne@69
|
315 // unwind is taking place. This is usually the desired behavior.
|
jpayne@69
|
316
|
jpayne@69
|
317 public:
|
jpayne@69
|
318 UnwindDetector();
|
jpayne@69
|
319
|
jpayne@69
|
320 bool isUnwinding() const;
|
jpayne@69
|
321 // Returns true if the current thread is in a stack unwind that it wasn't in at the time the
|
jpayne@69
|
322 // object was constructed.
|
jpayne@69
|
323
|
jpayne@69
|
324 template <typename Func>
|
jpayne@69
|
325 void catchExceptionsIfUnwinding(Func&& func) const;
|
jpayne@69
|
326 // Runs the given function (e.g., a lambda). If isUnwinding() is true, any exceptions are
|
jpayne@69
|
327 // caught and treated as secondary faults, meaning they are considered to be side-effects of the
|
jpayne@69
|
328 // exception that is unwinding the stack. Otherwise, exceptions are passed through normally.
|
jpayne@69
|
329
|
jpayne@69
|
330 private:
|
jpayne@69
|
331 uint uncaughtCount;
|
jpayne@69
|
332
|
jpayne@69
|
333 #if !KJ_NO_EXCEPTIONS
|
jpayne@69
|
334 void catchThrownExceptionAsSecondaryFault() const;
|
jpayne@69
|
335 #endif
|
jpayne@69
|
336 };
|
jpayne@69
|
337
|
jpayne@69
|
338 #if KJ_NO_EXCEPTIONS
|
jpayne@69
|
339
|
jpayne@69
|
340 namespace _ { // private
|
jpayne@69
|
341
|
jpayne@69
|
342 class Runnable {
|
jpayne@69
|
343 public:
|
jpayne@69
|
344 virtual void run() = 0;
|
jpayne@69
|
345 };
|
jpayne@69
|
346
|
jpayne@69
|
347 template <typename Func>
|
jpayne@69
|
348 class RunnableImpl: public Runnable {
|
jpayne@69
|
349 public:
|
jpayne@69
|
350 RunnableImpl(Func&& func): func(kj::fwd<Func>(func)) {}
|
jpayne@69
|
351 void run() override {
|
jpayne@69
|
352 func();
|
jpayne@69
|
353 }
|
jpayne@69
|
354 private:
|
jpayne@69
|
355 Func func;
|
jpayne@69
|
356 };
|
jpayne@69
|
357
|
jpayne@69
|
358 Maybe<Exception> runCatchingExceptions(Runnable& runnable);
|
jpayne@69
|
359
|
jpayne@69
|
360 } // namespace _ (private)
|
jpayne@69
|
361
|
jpayne@69
|
362 #endif // KJ_NO_EXCEPTIONS
|
jpayne@69
|
363
|
jpayne@69
|
364 template <typename Func>
|
jpayne@69
|
365 Maybe<Exception> runCatchingExceptions(Func&& func) {
|
jpayne@69
|
366 #if KJ_NO_EXCEPTIONS
|
jpayne@69
|
367 _::RunnableImpl<Func> runnable(kj::fwd<Func>(func));
|
jpayne@69
|
368 return _::runCatchingExceptions(runnable);
|
jpayne@69
|
369 #else
|
jpayne@69
|
370 try {
|
jpayne@69
|
371 func();
|
jpayne@69
|
372 return nullptr;
|
jpayne@69
|
373 } catch (...) {
|
jpayne@69
|
374 return getCaughtExceptionAsKj();
|
jpayne@69
|
375 }
|
jpayne@69
|
376 #endif
|
jpayne@69
|
377 }
|
jpayne@69
|
378
|
jpayne@69
|
379 template <typename Func>
|
jpayne@69
|
380 void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const {
|
jpayne@69
|
381 #if KJ_NO_EXCEPTIONS
|
jpayne@69
|
382 // Can't possibly be unwinding...
|
jpayne@69
|
383 func();
|
jpayne@69
|
384 #else
|
jpayne@69
|
385 if (isUnwinding()) {
|
jpayne@69
|
386 try {
|
jpayne@69
|
387 func();
|
jpayne@69
|
388 } catch (...) {
|
jpayne@69
|
389 catchThrownExceptionAsSecondaryFault();
|
jpayne@69
|
390 }
|
jpayne@69
|
391 } else {
|
jpayne@69
|
392 func();
|
jpayne@69
|
393 }
|
jpayne@69
|
394 #endif
|
jpayne@69
|
395 }
|
jpayne@69
|
396
|
jpayne@69
|
397 #define KJ_ON_SCOPE_SUCCESS(code) \
|
jpayne@69
|
398 ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
jpayne@69
|
399 KJ_DEFER(if (!KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; })
|
jpayne@69
|
400 // Runs `code` if the current scope is exited normally (not due to an exception).
|
jpayne@69
|
401
|
jpayne@69
|
402 #define KJ_ON_SCOPE_FAILURE(code) \
|
jpayne@69
|
403 ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
jpayne@69
|
404 KJ_DEFER(if (KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; })
|
jpayne@69
|
405 // Runs `code` if the current scope is exited due to an exception.
|
jpayne@69
|
406
|
jpayne@69
|
407 // =======================================================================================
|
jpayne@69
|
408
|
jpayne@69
|
409 KJ_NOINLINE ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount);
|
jpayne@69
|
410 // Attempt to get the current stack trace, returning a list of pointers to instructions. The
|
jpayne@69
|
411 // returned array is a slice of `space`. Provide a larger `space` to get a deeper stack trace.
|
jpayne@69
|
412 // If the platform doesn't support stack traces, returns an empty array.
|
jpayne@69
|
413 //
|
jpayne@69
|
414 // `ignoreCount` items will be truncated from the front of the trace. This is useful for chopping
|
jpayne@69
|
415 // off a prefix of the trace that is uninteresting to the developer because it's just locations
|
jpayne@69
|
416 // inside the debug infrastructure that is requesting the trace. Be careful to mark functions as
|
jpayne@69
|
417 // KJ_NOINLINE if you intend to count them in `ignoreCount`. Note that, unfortunately, the
|
jpayne@69
|
418 // ignored entries will still waste space in the `space` array (and the returned array's `begin()`
|
jpayne@69
|
419 // is never exactly equal to `space.begin()` due to this effect, even if `ignoreCount` is zero
|
jpayne@69
|
420 // since `getStackTrace()` needs to ignore its own internal frames).
|
jpayne@69
|
421
|
jpayne@69
|
422 String stringifyStackTrace(ArrayPtr<void* const>);
|
jpayne@69
|
423 // Convert the stack trace to a string with file names and line numbers. This may involve executing
|
jpayne@69
|
424 // suprocesses.
|
jpayne@69
|
425
|
jpayne@69
|
426 String stringifyStackTraceAddresses(ArrayPtr<void* const> trace);
|
jpayne@69
|
427 StringPtr stringifyStackTraceAddresses(ArrayPtr<void* const> trace, ArrayPtr<char> scratch);
|
jpayne@69
|
428 // Construct a string containing just enough information about a stack trace to be able to convert
|
jpayne@69
|
429 // it to file and line numbers later using offline tools. This produces a sequence of
|
jpayne@69
|
430 // space-separated code location identifiers. Each identifier may be an absolute address
|
jpayne@69
|
431 // (hex number starting with 0x) or may be a module-relative address "<module>@0x<hex>". The
|
jpayne@69
|
432 // latter case is preferred when ASLR is in effect and has loaded different modules at different
|
jpayne@69
|
433 // addresses.
|
jpayne@69
|
434
|
jpayne@69
|
435 String getStackTrace();
|
jpayne@69
|
436 // Get a stack trace right now and stringify it. Useful for debugging.
|
jpayne@69
|
437
|
jpayne@69
|
438 void printStackTraceOnCrash();
|
jpayne@69
|
439 // Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print
|
jpayne@69
|
440 // a stack trace. You should call this as early as possible on program startup. Programs using
|
jpayne@69
|
441 // KJ_MAIN get this automatically.
|
jpayne@69
|
442
|
jpayne@69
|
443 void resetCrashHandlers();
|
jpayne@69
|
444 // Resets all signal handlers set by printStackTraceOnCrash().
|
jpayne@69
|
445
|
jpayne@69
|
446 kj::StringPtr trimSourceFilename(kj::StringPtr filename);
|
jpayne@69
|
447 // Given a source code file name, trim off noisy prefixes like "src/" or
|
jpayne@69
|
448 // "/ekam-provider/canonical/".
|
jpayne@69
|
449
|
jpayne@69
|
450 kj::String getCaughtExceptionType();
|
jpayne@69
|
451 // Utility function which attempts to return the human-readable type name of the exception
|
jpayne@69
|
452 // currently being thrown. This can be called inside a catch block, including a catch (...) block,
|
jpayne@69
|
453 // for the purpose of error logging. This function is best-effort; on some platforms it may simply
|
jpayne@69
|
454 // return "(unknown)".
|
jpayne@69
|
455
|
jpayne@69
|
456 #if !KJ_NO_EXCEPTIONS
|
jpayne@69
|
457
|
jpayne@69
|
458 class InFlightExceptionIterator {
|
jpayne@69
|
459 // A class that can be used to iterate over exceptions that are in-flight in the current thread,
|
jpayne@69
|
460 // meaning they are either uncaught, or caught by a catch block that is current executing.
|
jpayne@69
|
461 //
|
jpayne@69
|
462 // This is meant for debugging purposes, and the results are best-effort. The C++ standard
|
jpayne@69
|
463 // library does not provide any way to inspect uncaught exceptions, so this class can only
|
jpayne@69
|
464 // discover KJ exceptions thrown using throwFatalException() or throwRecoverableException().
|
jpayne@69
|
465 // All KJ code uses those two functions to throw exceptions, but if your own code uses a bare
|
jpayne@69
|
466 // `throw`, or if the standard library throws an exception, these cannot be inspected.
|
jpayne@69
|
467 //
|
jpayne@69
|
468 // This class is safe to use in a signal handler.
|
jpayne@69
|
469
|
jpayne@69
|
470 public:
|
jpayne@69
|
471 InFlightExceptionIterator();
|
jpayne@69
|
472
|
jpayne@69
|
473 Maybe<const Exception&> next();
|
jpayne@69
|
474
|
jpayne@69
|
475 private:
|
jpayne@69
|
476 const Exception* ptr;
|
jpayne@69
|
477 };
|
jpayne@69
|
478
|
jpayne@69
|
479 #endif // !KJ_NO_EXCEPTIONS
|
jpayne@69
|
480
|
jpayne@69
|
481 kj::Exception getDestructionReason(void* traceSeparator,
|
jpayne@69
|
482 kj::Exception::Type defaultType, const char* defaultFile, int defaultLine,
|
jpayne@69
|
483 kj::StringPtr defaultDescription);
|
jpayne@69
|
484 // Returns an exception that attempts to capture why a destructor has been invoked. If a KJ
|
jpayne@69
|
485 // exception is currently in-flight (see InFlightExceptionIterator), then that exception is
|
jpayne@69
|
486 // returned. Otherwise, an exception is constructed using the current stack trace and the type,
|
jpayne@69
|
487 // file, line, and description provided. In the latter case, `traceSeparator` is appended to the
|
jpayne@69
|
488 // stack trace; this should be a pointer to some dummy symbol which acts as a separator between the
|
jpayne@69
|
489 // original stack trace and any new trace frames added later.
|
jpayne@69
|
490
|
jpayne@69
|
491 kj::ArrayPtr<void* const> computeRelativeTrace(
|
jpayne@69
|
492 kj::ArrayPtr<void* const> trace, kj::ArrayPtr<void* const> relativeTo);
|
jpayne@69
|
493 // Given two traces expected to have started from the same root, try to find the part of `trace`
|
jpayne@69
|
494 // that is different from `relativeTo`, considering that either or both traces might be truncated.
|
jpayne@69
|
495 //
|
jpayne@69
|
496 // This is useful for debugging, when reporting several related traces at once.
|
jpayne@69
|
497
|
jpayne@69
|
498 void requireOnStack(void* ptr, kj::StringPtr description);
|
jpayne@69
|
499 // Throw an exception if `ptr` does not appear to point to something near the top of the stack.
|
jpayne@69
|
500 // Used as a safety check for types that must be stack-allocated, like ExceptionCallback.
|
jpayne@69
|
501
|
jpayne@69
|
502 } // namespace kj
|
jpayne@69
|
503
|
jpayne@69
|
504 KJ_END_HEADER
|