cannam@49
|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
cannam@49
|
2 // Licensed under the MIT License:
|
cannam@49
|
3 //
|
cannam@49
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@49
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@49
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@49
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@49
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@49
|
9 // furnished to do so, subject to the following conditions:
|
cannam@49
|
10 //
|
cannam@49
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@49
|
12 // all copies or substantial portions of the Software.
|
cannam@49
|
13 //
|
cannam@49
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@49
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@49
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@49
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@49
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@49
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@49
|
20 // THE SOFTWARE.
|
cannam@49
|
21
|
cannam@49
|
22 #ifndef KJ_EXCEPTION_H_
|
cannam@49
|
23 #define KJ_EXCEPTION_H_
|
cannam@49
|
24
|
cannam@49
|
25 #if defined(__GNUC__) && !KJ_HEADER_WARNINGS
|
cannam@49
|
26 #pragma GCC system_header
|
cannam@49
|
27 #endif
|
cannam@49
|
28
|
cannam@49
|
29 #include "memory.h"
|
cannam@49
|
30 #include "array.h"
|
cannam@49
|
31 #include "string.h"
|
cannam@49
|
32
|
cannam@49
|
33 namespace kj {
|
cannam@49
|
34
|
cannam@49
|
35 class ExceptionImpl;
|
cannam@49
|
36
|
cannam@49
|
37 class Exception {
|
cannam@49
|
38 // Exception thrown in case of fatal errors.
|
cannam@49
|
39 //
|
cannam@49
|
40 // Actually, a subclass of this which also implements std::exception will be thrown, but we hide
|
cannam@49
|
41 // that fact from the interface to avoid #including <exception>.
|
cannam@49
|
42
|
cannam@49
|
43 public:
|
cannam@49
|
44 enum class Type {
|
cannam@49
|
45 // What kind of failure?
|
cannam@49
|
46
|
cannam@49
|
47 FAILED = 0,
|
cannam@49
|
48 // Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this
|
cannam@49
|
49 // error type.
|
cannam@49
|
50
|
cannam@49
|
51 OVERLOADED = 1,
|
cannam@49
|
52 // The call failed because of a temporary lack of resources. This could be space resources
|
cannam@49
|
53 // (out of memory, out of disk space) or time resources (request queue overflow, operation
|
cannam@49
|
54 // timed out).
|
cannam@49
|
55 //
|
cannam@49
|
56 // The operation might work if tried again, but it should NOT be repeated immediately as this
|
cannam@49
|
57 // may simply exacerbate the problem.
|
cannam@49
|
58
|
cannam@49
|
59 DISCONNECTED = 2,
|
cannam@49
|
60 // The call required communication over a connection that has been lost. The callee will need
|
cannam@49
|
61 // to re-establish connections and try again.
|
cannam@49
|
62
|
cannam@49
|
63 UNIMPLEMENTED = 3
|
cannam@49
|
64 // The requested method is not implemented. The caller may wish to revert to a fallback
|
cannam@49
|
65 // approach based on other methods.
|
cannam@49
|
66
|
cannam@49
|
67 // IF YOU ADD A NEW VALUE:
|
cannam@49
|
68 // - Update the stringifier.
|
cannam@49
|
69 // - Update Cap'n Proto's RPC protocol's Exception.Type enum.
|
cannam@49
|
70 };
|
cannam@49
|
71
|
cannam@49
|
72 Exception(Type type, const char* file, int line, String description = nullptr) noexcept;
|
cannam@49
|
73 Exception(Type type, String file, int line, String description = nullptr) noexcept;
|
cannam@49
|
74 Exception(const Exception& other) noexcept;
|
cannam@49
|
75 Exception(Exception&& other) = default;
|
cannam@49
|
76 ~Exception() noexcept;
|
cannam@49
|
77
|
cannam@49
|
78 const char* getFile() const { return file; }
|
cannam@49
|
79 int getLine() const { return line; }
|
cannam@49
|
80 Type getType() const { return type; }
|
cannam@49
|
81 StringPtr getDescription() const { return description; }
|
cannam@49
|
82 ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
|
cannam@49
|
83
|
cannam@49
|
84 struct Context {
|
cannam@49
|
85 // Describes a bit about what was going on when the exception was thrown.
|
cannam@49
|
86
|
cannam@49
|
87 const char* file;
|
cannam@49
|
88 int line;
|
cannam@49
|
89 String description;
|
cannam@49
|
90 Maybe<Own<Context>> next;
|
cannam@49
|
91
|
cannam@49
|
92 Context(const char* file, int line, String&& description, Maybe<Own<Context>>&& next)
|
cannam@49
|
93 : file(file), line(line), description(mv(description)), next(mv(next)) {}
|
cannam@49
|
94 Context(const Context& other) noexcept;
|
cannam@49
|
95 };
|
cannam@49
|
96
|
cannam@49
|
97 inline Maybe<const Context&> getContext() const {
|
cannam@49
|
98 KJ_IF_MAYBE(c, context) {
|
cannam@49
|
99 return **c;
|
cannam@49
|
100 } else {
|
cannam@49
|
101 return nullptr;
|
cannam@49
|
102 }
|
cannam@49
|
103 }
|
cannam@49
|
104
|
cannam@49
|
105 void wrapContext(const char* file, int line, String&& description);
|
cannam@49
|
106 // Wraps the context in a new node. This becomes the head node returned by getContext() -- it
|
cannam@49
|
107 // is expected that contexts will be added in reverse order as the exception passes up the
|
cannam@49
|
108 // callback stack.
|
cannam@49
|
109
|
cannam@49
|
110 KJ_NOINLINE void extendTrace(uint ignoreCount);
|
cannam@49
|
111 // Append the current stack trace to the exception's trace, ignoring the first `ignoreCount`
|
cannam@49
|
112 // frames (see `getStackTrace()` for discussion of `ignoreCount`).
|
cannam@49
|
113
|
cannam@49
|
114 KJ_NOINLINE void truncateCommonTrace();
|
cannam@49
|
115 // Remove the part of the stack trace which the exception shares with the caller of this method.
|
cannam@49
|
116 // This is used by the async library to remove the async infrastructure from the stack trace
|
cannam@49
|
117 // before replacing it with the async trace.
|
cannam@49
|
118
|
cannam@49
|
119 void addTrace(void* ptr);
|
cannam@49
|
120 // Append the given pointer to the backtrace, if it is not already full. This is used by the
|
cannam@49
|
121 // async library to trace through the promise chain that led to the exception.
|
cannam@49
|
122
|
cannam@49
|
123 private:
|
cannam@49
|
124 String ownFile;
|
cannam@49
|
125 const char* file;
|
cannam@49
|
126 int line;
|
cannam@49
|
127 Type type;
|
cannam@49
|
128 String description;
|
cannam@49
|
129 Maybe<Own<Context>> context;
|
cannam@49
|
130 void* trace[32];
|
cannam@49
|
131 uint traceCount;
|
cannam@49
|
132
|
cannam@49
|
133 friend class ExceptionImpl;
|
cannam@49
|
134 };
|
cannam@49
|
135
|
cannam@49
|
136 StringPtr KJ_STRINGIFY(Exception::Type type);
|
cannam@49
|
137 String KJ_STRINGIFY(const Exception& e);
|
cannam@49
|
138
|
cannam@49
|
139 // =======================================================================================
|
cannam@49
|
140
|
cannam@49
|
141 enum class LogSeverity {
|
cannam@49
|
142 INFO, // Information describing what the code is up to, which users may request to see
|
cannam@49
|
143 // with a flag like `--verbose`. Does not indicate a problem. Not printed by
|
cannam@49
|
144 // default; you must call setLogLevel(INFO) to enable.
|
cannam@49
|
145 WARNING, // A problem was detected but execution can continue with correct output.
|
cannam@49
|
146 ERROR, // Something is wrong, but execution can continue with garbage output.
|
cannam@49
|
147 FATAL, // Something went wrong, and execution cannot continue.
|
cannam@49
|
148 DBG // Temporary debug logging. See KJ_DBG.
|
cannam@49
|
149
|
cannam@49
|
150 // Make sure to update the stringifier if you add a new severity level.
|
cannam@49
|
151 };
|
cannam@49
|
152
|
cannam@49
|
153 StringPtr KJ_STRINGIFY(LogSeverity severity);
|
cannam@49
|
154
|
cannam@49
|
155 class ExceptionCallback {
|
cannam@49
|
156 // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
|
cannam@49
|
157 // to perform your own exception handling. For example, a reasonable thing to do is to have
|
cannam@49
|
158 // onRecoverableException() set a flag indicating that an error occurred, and then check for that
|
cannam@49
|
159 // flag just before writing to storage and/or returning results to the user. If the flag is set,
|
cannam@49
|
160 // discard whatever you have and return an error instead.
|
cannam@49
|
161 //
|
cannam@49
|
162 // ExceptionCallbacks must always be allocated on the stack. When an exception is thrown, the
|
cannam@49
|
163 // newest ExceptionCallback on the calling thread's stack is called. The default implementation
|
cannam@49
|
164 // of each method calls the next-oldest ExceptionCallback for that thread. Thus the callbacks
|
cannam@49
|
165 // behave a lot like try/catch blocks, except that they are called before any stack unwinding
|
cannam@49
|
166 // occurs.
|
cannam@49
|
167
|
cannam@49
|
168 public:
|
cannam@49
|
169 ExceptionCallback();
|
cannam@49
|
170 KJ_DISALLOW_COPY(ExceptionCallback);
|
cannam@49
|
171 virtual ~ExceptionCallback() noexcept(false);
|
cannam@49
|
172
|
cannam@49
|
173 virtual void onRecoverableException(Exception&& exception);
|
cannam@49
|
174 // Called when an exception has been raised, but the calling code has the ability to continue by
|
cannam@49
|
175 // producing garbage output. This method _should_ throw the exception, but is allowed to simply
|
cannam@49
|
176 // return if garbage output is acceptable.
|
cannam@49
|
177 //
|
cannam@49
|
178 // The global default implementation throws an exception unless the library was compiled with
|
cannam@49
|
179 // -fno-exceptions, in which case it logs an error and returns.
|
cannam@49
|
180
|
cannam@49
|
181 virtual void onFatalException(Exception&& exception);
|
cannam@49
|
182 // Called when an exception has been raised and the calling code cannot continue. If this method
|
cannam@49
|
183 // returns normally, abort() will be called. The method must throw the exception to avoid
|
cannam@49
|
184 // aborting.
|
cannam@49
|
185 //
|
cannam@49
|
186 // The global default implementation throws an exception unless the library was compiled with
|
cannam@49
|
187 // -fno-exceptions, in which case it logs an error and returns.
|
cannam@49
|
188
|
cannam@49
|
189 virtual void logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
|
cannam@49
|
190 String&& text);
|
cannam@49
|
191 // Called when something wants to log some debug text. `contextDepth` indicates how many levels
|
cannam@49
|
192 // of context the message passed through; it may make sense to indent the message accordingly.
|
cannam@49
|
193 //
|
cannam@49
|
194 // The global default implementation writes the text to stderr.
|
cannam@49
|
195
|
cannam@49
|
196 protected:
|
cannam@49
|
197 ExceptionCallback& next;
|
cannam@49
|
198
|
cannam@49
|
199 private:
|
cannam@49
|
200 ExceptionCallback(ExceptionCallback& next);
|
cannam@49
|
201
|
cannam@49
|
202 class RootExceptionCallback;
|
cannam@49
|
203 friend ExceptionCallback& getExceptionCallback();
|
cannam@49
|
204 };
|
cannam@49
|
205
|
cannam@49
|
206 ExceptionCallback& getExceptionCallback();
|
cannam@49
|
207 // Returns the current exception callback.
|
cannam@49
|
208
|
cannam@49
|
209 KJ_NOINLINE KJ_NORETURN(void throwFatalException(kj::Exception&& exception, uint ignoreCount = 0));
|
cannam@49
|
210 // Invoke the exception callback to throw the given fatal exception. If the exception callback
|
cannam@49
|
211 // returns, abort.
|
cannam@49
|
212
|
cannam@49
|
213 KJ_NOINLINE void throwRecoverableException(kj::Exception&& exception, uint ignoreCount = 0);
|
cannam@49
|
214 // Invoke the exception callback to throw the given recoverable exception. If the exception
|
cannam@49
|
215 // callback returns, return normally.
|
cannam@49
|
216
|
cannam@49
|
217 // =======================================================================================
|
cannam@49
|
218
|
cannam@49
|
219 namespace _ { class Runnable; }
|
cannam@49
|
220
|
cannam@49
|
221 template <typename Func>
|
cannam@49
|
222 Maybe<Exception> runCatchingExceptions(Func&& func) noexcept;
|
cannam@49
|
223 // Executes the given function (usually, a lambda returning nothing) catching any exceptions that
|
cannam@49
|
224 // are thrown. Returns the Exception if there was one, or null if the operation completed normally.
|
cannam@49
|
225 // Non-KJ exceptions will be wrapped.
|
cannam@49
|
226 //
|
cannam@49
|
227 // If exception are disabled (e.g. with -fno-exceptions), this will still detect whether any
|
cannam@49
|
228 // recoverable exceptions occurred while running the function and will return those.
|
cannam@49
|
229
|
cannam@49
|
230 class UnwindDetector {
|
cannam@49
|
231 // Utility for detecting when a destructor is called due to unwind. Useful for:
|
cannam@49
|
232 // - Avoiding throwing exceptions in this case, which would terminate the program.
|
cannam@49
|
233 // - Detecting whether to commit or roll back a transaction.
|
cannam@49
|
234 //
|
cannam@49
|
235 // To use this class, either inherit privately from it or declare it as a member. The detector
|
cannam@49
|
236 // works by comparing the exception state against that when the constructor was called, so for
|
cannam@49
|
237 // an object that was actually constructed during exception unwind, it will behave as if no
|
cannam@49
|
238 // unwind is taking place. This is usually the desired behavior.
|
cannam@49
|
239
|
cannam@49
|
240 public:
|
cannam@49
|
241 UnwindDetector();
|
cannam@49
|
242
|
cannam@49
|
243 bool isUnwinding() const;
|
cannam@49
|
244 // Returns true if the current thread is in a stack unwind that it wasn't in at the time the
|
cannam@49
|
245 // object was constructed.
|
cannam@49
|
246
|
cannam@49
|
247 template <typename Func>
|
cannam@49
|
248 void catchExceptionsIfUnwinding(Func&& func) const;
|
cannam@49
|
249 // Runs the given function (e.g., a lambda). If isUnwinding() is true, any exceptions are
|
cannam@49
|
250 // caught and treated as secondary faults, meaning they are considered to be side-effects of the
|
cannam@49
|
251 // exception that is unwinding the stack. Otherwise, exceptions are passed through normally.
|
cannam@49
|
252
|
cannam@49
|
253 private:
|
cannam@49
|
254 uint uncaughtCount;
|
cannam@49
|
255
|
cannam@49
|
256 void catchExceptionsAsSecondaryFaults(_::Runnable& runnable) const;
|
cannam@49
|
257 };
|
cannam@49
|
258
|
cannam@49
|
259 namespace _ { // private
|
cannam@49
|
260
|
cannam@49
|
261 class Runnable {
|
cannam@49
|
262 public:
|
cannam@49
|
263 virtual void run() = 0;
|
cannam@49
|
264 };
|
cannam@49
|
265
|
cannam@49
|
266 template <typename Func>
|
cannam@49
|
267 class RunnableImpl: public Runnable {
|
cannam@49
|
268 public:
|
cannam@49
|
269 RunnableImpl(Func&& func): func(kj::mv(func)) {}
|
cannam@49
|
270 void run() override {
|
cannam@49
|
271 func();
|
cannam@49
|
272 }
|
cannam@49
|
273 private:
|
cannam@49
|
274 Func func;
|
cannam@49
|
275 };
|
cannam@49
|
276
|
cannam@49
|
277 Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept;
|
cannam@49
|
278
|
cannam@49
|
279 } // namespace _ (private)
|
cannam@49
|
280
|
cannam@49
|
281 template <typename Func>
|
cannam@49
|
282 Maybe<Exception> runCatchingExceptions(Func&& func) noexcept {
|
cannam@49
|
283 _::RunnableImpl<Decay<Func>> runnable(kj::fwd<Func>(func));
|
cannam@49
|
284 return _::runCatchingExceptions(runnable);
|
cannam@49
|
285 }
|
cannam@49
|
286
|
cannam@49
|
287 template <typename Func>
|
cannam@49
|
288 void UnwindDetector::catchExceptionsIfUnwinding(Func&& func) const {
|
cannam@49
|
289 if (isUnwinding()) {
|
cannam@49
|
290 _::RunnableImpl<Decay<Func>> runnable(kj::fwd<Func>(func));
|
cannam@49
|
291 catchExceptionsAsSecondaryFaults(runnable);
|
cannam@49
|
292 } else {
|
cannam@49
|
293 func();
|
cannam@49
|
294 }
|
cannam@49
|
295 }
|
cannam@49
|
296
|
cannam@49
|
297 #define KJ_ON_SCOPE_SUCCESS(code) \
|
cannam@49
|
298 ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
cannam@49
|
299 KJ_DEFER(if (!KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; })
|
cannam@49
|
300 // Runs `code` if the current scope is exited normally (not due to an exception).
|
cannam@49
|
301
|
cannam@49
|
302 #define KJ_ON_SCOPE_FAILURE(code) \
|
cannam@49
|
303 ::kj::UnwindDetector KJ_UNIQUE_NAME(_kjUnwindDetector); \
|
cannam@49
|
304 KJ_DEFER(if (KJ_UNIQUE_NAME(_kjUnwindDetector).isUnwinding()) { code; })
|
cannam@49
|
305 // Runs `code` if the current scope is exited due to an exception.
|
cannam@49
|
306
|
cannam@49
|
307 // =======================================================================================
|
cannam@49
|
308
|
cannam@49
|
309 KJ_NOINLINE ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount);
|
cannam@49
|
310 // Attempt to get the current stack trace, returning a list of pointers to instructions. The
|
cannam@49
|
311 // returned array is a slice of `space`. Provide a larger `space` to get a deeper stack trace.
|
cannam@49
|
312 // If the platform doesn't support stack traces, returns an empty array.
|
cannam@49
|
313 //
|
cannam@49
|
314 // `ignoreCount` items will be truncated from the front of the trace. This is useful for chopping
|
cannam@49
|
315 // off a prefix of the trace that is uninteresting to the developer because it's just locations
|
cannam@49
|
316 // inside the debug infrastructure that is requesting the trace. Be careful to mark functions as
|
cannam@49
|
317 // KJ_NOINLINE if you intend to count them in `ignoreCount`. Note that, unfortunately, the
|
cannam@49
|
318 // ignored entries will still waste space in the `space` array (and the returned array's `begin()`
|
cannam@49
|
319 // is never exactly equal to `space.begin()` due to this effect, even if `ignoreCount` is zero
|
cannam@49
|
320 // since `getStackTrace()` needs to ignore its own internal frames).
|
cannam@49
|
321
|
cannam@49
|
322 String stringifyStackTrace(ArrayPtr<void* const>);
|
cannam@49
|
323 // Convert the stack trace to a string with file names and line numbers. This may involve executing
|
cannam@49
|
324 // suprocesses.
|
cannam@49
|
325
|
cannam@49
|
326 void printStackTraceOnCrash();
|
cannam@49
|
327 // Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print
|
cannam@49
|
328 // a stack trace. You should call this as early as possible on program startup. Programs using
|
cannam@49
|
329 // KJ_MAIN get this automatically.
|
cannam@49
|
330
|
cannam@49
|
331 kj::StringPtr trimSourceFilename(kj::StringPtr filename);
|
cannam@49
|
332 // Given a source code file name, trim off noisy prefixes like "src/" or
|
cannam@49
|
333 // "/ekam-provider/canonical/".
|
cannam@49
|
334
|
cannam@49
|
335 } // namespace kj
|
cannam@49
|
336
|
cannam@49
|
337 #endif // KJ_EXCEPTION_H_
|