/src/libjxl/lib/jxl/base/status.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #ifndef LIB_JXL_BASE_STATUS_H_ |
7 | | #define LIB_JXL_BASE_STATUS_H_ |
8 | | |
9 | | // Error handling: Status return type + helper macros. |
10 | | |
11 | | #include <stdarg.h> |
12 | | #include <stdint.h> |
13 | | #include <stdio.h> |
14 | | #include <stdlib.h> |
15 | | |
16 | | #include "lib/jxl/base/compiler_specific.h" |
17 | | #include "lib/jxl/base/sanitizer_definitions.h" |
18 | | |
19 | | #if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER |
20 | | #include "sanitizer/common_interface_defs.h" // __sanitizer_print_stack_trace |
21 | | #endif // defined(*_SANITIZER) |
22 | | |
23 | | namespace jxl { |
24 | | |
25 | | // Uncomment to abort when JXL_FAILURE or JXL_STATUS with a fatal error is |
26 | | // reached: |
27 | | // #define JXL_CRASH_ON_ERROR |
28 | | |
29 | | #ifndef JXL_ENABLE_ASSERT |
30 | | #define JXL_ENABLE_ASSERT 1 |
31 | | #endif |
32 | | |
33 | | #ifndef JXL_ENABLE_CHECK |
34 | | #define JXL_ENABLE_CHECK 1 |
35 | | #endif |
36 | | |
37 | | // Pass -DJXL_DEBUG_ON_ERROR at compile time to print debug messages when a |
38 | | // function returns JXL_FAILURE or calls JXL_NOTIFY_ERROR. Note that this is |
39 | | // irrelevant if you also pass -DJXL_CRASH_ON_ERROR. |
40 | | #if defined(JXL_DEBUG_ON_ERROR) || defined(JXL_CRASH_ON_ERROR) |
41 | | #undef JXL_DEBUG_ON_ERROR |
42 | | #define JXL_DEBUG_ON_ERROR 1 |
43 | | #else // JXL_DEBUG_ON_ERROR || JXL_CRASH_ON_ERROR |
44 | | #ifdef NDEBUG |
45 | 7.34k | #define JXL_DEBUG_ON_ERROR 0 |
46 | | #else // NDEBUG |
47 | | #define JXL_DEBUG_ON_ERROR 1 |
48 | | #endif // NDEBUG |
49 | | #endif // JXL_DEBUG_ON_ERROR || JXL_CRASH_ON_ERROR |
50 | | |
51 | | // Pass -DJXL_DEBUG_ON_ALL_ERROR at compile time to print debug messages on |
52 | | // all error (fatal and non-fatal) status. This implies JXL_DEBUG_ON_ERROR. |
53 | | #if defined(JXL_DEBUG_ON_ALL_ERROR) |
54 | | #undef JXL_DEBUG_ON_ALL_ERROR |
55 | | #define JXL_DEBUG_ON_ALL_ERROR 1 |
56 | | // JXL_DEBUG_ON_ALL_ERROR implies JXL_DEBUG_ON_ERROR too. |
57 | | #undef JXL_DEBUG_ON_ERROR |
58 | | #define JXL_DEBUG_ON_ERROR 1 |
59 | | #else // JXL_DEBUG_ON_ALL_ERROR |
60 | 0 | #define JXL_DEBUG_ON_ALL_ERROR 0 |
61 | | #endif // JXL_DEBUG_ON_ALL_ERROR |
62 | | |
63 | | // The Verbose level for the library |
64 | | #ifndef JXL_DEBUG_V_LEVEL |
65 | | #define JXL_DEBUG_V_LEVEL 0 |
66 | | #endif // JXL_DEBUG_V_LEVEL |
67 | | |
68 | | // Pass -DJXL_DEBUG_ON_ABORT=0 to disable the debug messages on JXL_ASSERT, |
69 | | // JXL_CHECK and JXL_ABORT. |
70 | | #ifndef JXL_DEBUG_ON_ABORT |
71 | 0 | #define JXL_DEBUG_ON_ABORT 1 |
72 | | #endif // JXL_DEBUG_ON_ABORT |
73 | | |
74 | | // Print a debug message on standard error. You should use the JXL_DEBUG macro |
75 | | // instead of calling Debug directly. This function returns false, so it can be |
76 | | // used as a return value in JXL_FAILURE. |
77 | | JXL_FORMAT(1, 2) |
78 | 0 | inline JXL_NOINLINE bool Debug(const char* format, ...) { |
79 | 0 | va_list args; |
80 | 0 | va_start(args, format); |
81 | 0 | vfprintf(stderr, format, args); |
82 | 0 | va_end(args); |
83 | 0 | return false; |
84 | 0 | } |
85 | | |
86 | | // Print a debug message on standard error if "enabled" is true. "enabled" is |
87 | | // normally a macro that evaluates to 0 or 1 at compile time, so the Debug |
88 | | // function is never called and optimized out in release builds. Note that the |
89 | | // arguments are compiled but not evaluated when enabled is false. The format |
90 | | // string must be a explicit string in the call, for example: |
91 | | // JXL_DEBUG(JXL_DEBUG_MYMODULE, "my module message: %d", some_var); |
92 | | // Add a header at the top of your module's .cc or .h file (depending on whether |
93 | | // you have JXL_DEBUG calls from the .h as well) like this: |
94 | | // #ifndef JXL_DEBUG_MYMODULE |
95 | | // #define JXL_DEBUG_MYMODULE 0 |
96 | | // #endif JXL_DEBUG_MYMODULE |
97 | | #define JXL_DEBUG(enabled, format, ...) \ |
98 | 193 | do { \ |
99 | 193 | if (enabled) { \ |
100 | 0 | ::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, \ |
101 | 0 | ##__VA_ARGS__); \ |
102 | 0 | } \ |
103 | 193 | } while (0) |
104 | | |
105 | | // JXL_DEBUG version that prints the debug message if the global verbose level |
106 | | // defined at compile time by JXL_DEBUG_V_LEVEL is greater or equal than the |
107 | | // passed level. |
108 | | #define JXL_DEBUG_V(level, format, ...) \ |
109 | | JXL_DEBUG(level <= JXL_DEBUG_V_LEVEL, format, ##__VA_ARGS__) |
110 | | |
111 | | // Warnings (via JXL_WARNING) are enabled by default in debug builds (opt and |
112 | | // debug). |
113 | | #ifdef JXL_DEBUG_WARNING |
114 | | #undef JXL_DEBUG_WARNING |
115 | | #define JXL_DEBUG_WARNING 1 |
116 | | #else // JXL_DEBUG_WARNING |
117 | | #ifdef NDEBUG |
118 | | #define JXL_DEBUG_WARNING 0 |
119 | | #else // JXL_DEBUG_WARNING |
120 | | #define JXL_DEBUG_WARNING 1 |
121 | | #endif // NDEBUG |
122 | | #endif // JXL_DEBUG_WARNING |
123 | | #define JXL_WARNING(format, ...) \ |
124 | 193 | JXL_DEBUG(JXL_DEBUG_WARNING, format, ##__VA_ARGS__) |
125 | | |
126 | | // Exits the program after printing a stack trace when possible. |
127 | 0 | JXL_NORETURN inline JXL_NOINLINE bool Abort() { |
128 | | #if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER |
129 | | // If compiled with any sanitizer print a stack trace. This call doesn't crash |
130 | | // the program, instead the trap below will crash it also allowing gdb to |
131 | | // break there. |
132 | | __sanitizer_print_stack_trace(); |
133 | | #endif // *_SANITIZER) |
134 | |
|
135 | | #if JXL_COMPILER_MSVC |
136 | | __debugbreak(); |
137 | | abort(); |
138 | | #else |
139 | 0 | __builtin_trap(); |
140 | 0 | #endif |
141 | 0 | } |
142 | | |
143 | | // Exits the program after printing file/line plus a formatted string. |
144 | | #define JXL_ABORT(format, ...) \ |
145 | 0 | ((JXL_DEBUG_ON_ABORT) && ::jxl::Debug(("%s:%d: JXL_ABORT: " format "\n"), \ |
146 | 0 | __FILE__, __LINE__, ##__VA_ARGS__), \ |
147 | 0 | ::jxl::Abort()) |
148 | | |
149 | | // Does not guarantee running the code, use only for debug mode checks. |
150 | | #if JXL_ENABLE_ASSERT |
151 | | #define JXL_ASSERT(condition) \ |
152 | 721k | do { \ |
153 | 721k | if (!(condition)) { \ |
154 | 0 | JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_ASSERT: %s", #condition); \ |
155 | 0 | ::jxl::Abort(); \ |
156 | 0 | } \ |
157 | 721k | } while (0) |
158 | | #else |
159 | | #define JXL_ASSERT(condition) \ |
160 | | do { \ |
161 | | } while (0) |
162 | | #endif |
163 | | |
164 | | // Define JXL_IS_DEBUG_BUILD that denotes asan, msan and other debug builds, |
165 | | // but not opt or release. |
166 | | #ifndef JXL_IS_DEBUG_BUILD |
167 | | #if !defined(NDEBUG) || defined(ADDRESS_SANITIZER) || \ |
168 | | defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ |
169 | | defined(__clang_analyzer__) |
170 | | #define JXL_IS_DEBUG_BUILD 1 |
171 | | #else |
172 | | #define JXL_IS_DEBUG_BUILD 0 |
173 | | #endif |
174 | | #endif // JXL_IS_DEBUG_BUILD |
175 | | |
176 | | // Same as above, but only runs in debug builds (builds where NDEBUG is not |
177 | | // defined). This is useful for slower asserts that we want to run more rarely |
178 | | // than usual. These will run on asan, msan and other debug builds, but not in |
179 | | // opt or release. |
180 | | #if JXL_IS_DEBUG_BUILD |
181 | | #define JXL_DASSERT(condition) \ |
182 | 17.7G | do { \ |
183 | 17.7G | if (!(condition)) { \ |
184 | 0 | JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_DASSERT: %s", #condition); \ |
185 | 0 | ::jxl::Abort(); \ |
186 | 0 | } \ |
187 | 17.7G | } while (0) |
188 | | #else |
189 | | #define JXL_DASSERT(condition) \ |
190 | | do { \ |
191 | | } while (0) |
192 | | #endif |
193 | | |
194 | | // Always runs the condition, so can be used for non-debug calls. |
195 | | #if JXL_ENABLE_CHECK |
196 | | #define JXL_CHECK(condition) \ |
197 | | do { \ |
198 | | if (!(condition)) { \ |
199 | | JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_CHECK: %s", #condition); \ |
200 | | ::jxl::Abort(); \ |
201 | | } \ |
202 | | } while (0) |
203 | | #else |
204 | | #define JXL_CHECK(condition) \ |
205 | | do { \ |
206 | | (void)(condition); \ |
207 | | } while (0) |
208 | | #endif |
209 | | |
210 | | // A jxl::Status value from a StatusCode or Status which prints a debug message |
211 | | // when enabled. |
212 | | #define JXL_STATUS(status, format, ...) \ |
213 | 2.09k | ::jxl::StatusMessage(::jxl::Status(status), "%s:%d: " format "\n", __FILE__, \ |
214 | 2.09k | __LINE__, ##__VA_ARGS__) |
215 | | |
216 | | // Notify of an error but discard the resulting Status value. This is only |
217 | | // useful for debug builds or when building with JXL_CRASH_ON_ERROR. |
218 | | #define JXL_NOTIFY_ERROR(format, ...) \ |
219 | | (void)JXL_STATUS(::jxl::StatusCode::kGenericError, "JXL_ERROR: " format, \ |
220 | | ##__VA_ARGS__) |
221 | | |
222 | | // An error Status with a message. The JXL_STATUS() macro will return a Status |
223 | | // object with a kGenericError code, but the comma operator helps with |
224 | | // clang-tidy inference and potentially with optimizations. |
225 | | #define JXL_FAILURE(format, ...) \ |
226 | 1.78k | ((void)JXL_STATUS(::jxl::StatusCode::kGenericError, "JXL_FAILURE: " format, \ |
227 | 1.78k | ##__VA_ARGS__), \ |
228 | 1.78k | ::jxl::Status(::jxl::StatusCode::kGenericError)) |
229 | | |
230 | | // Always evaluates the status exactly once, so can be used for non-debug calls. |
231 | | // Returns from the current context if the passed Status expression is an error |
232 | | // (fatal or non-fatal). The return value is the passed Status. |
233 | | #define JXL_RETURN_IF_ERROR(status) \ |
234 | 51.7k | do { \ |
235 | 51.7k | ::jxl::Status jxl_return_if_error_status = (status); \ |
236 | 51.7k | if (!jxl_return_if_error_status) { \ |
237 | 1.57k | (void)::jxl::StatusMessage( \ |
238 | 1.57k | jxl_return_if_error_status, \ |
239 | 1.57k | "%s:%d: JXL_RETURN_IF_ERROR code=%d: %s\n", __FILE__, __LINE__, \ |
240 | 1.57k | static_cast<int>(jxl_return_if_error_status.code()), #status); \ |
241 | 1.57k | return jxl_return_if_error_status; \ |
242 | 1.57k | } \ |
243 | 51.7k | } while (0) |
244 | | |
245 | | // As above, but without calling StatusMessage. Intended for bundles (see |
246 | | // fields.h), which have numerous call sites (-> relevant for code size) and do |
247 | | // not want to generate excessive messages when decoding partial headers. |
248 | | #define JXL_QUIET_RETURN_IF_ERROR(status) \ |
249 | 13.5k | do { \ |
250 | 13.5k | ::jxl::Status jxl_return_if_error_status = (status); \ |
251 | 13.5k | if (!jxl_return_if_error_status) { \ |
252 | 21 | return jxl_return_if_error_status; \ |
253 | 21 | } \ |
254 | 13.5k | } while (0) |
255 | | |
256 | | enum class StatusCode : int32_t { |
257 | | // Non-fatal errors (negative values). |
258 | | kNotEnoughBytes = -1, |
259 | | |
260 | | // The only non-error status code. |
261 | | kOk = 0, |
262 | | |
263 | | // Fatal-errors (positive values) |
264 | | kGenericError = 1, |
265 | | }; |
266 | | |
267 | | // Drop-in replacement for bool that raises compiler warnings if not used |
268 | | // after being returned from a function. Example: |
269 | | // Status LoadFile(...) { return true; } is more compact than |
270 | | // bool JXL_MUST_USE_RESULT LoadFile(...) { return true; } |
271 | | // In case of error, the status can carry an extra error code in its value which |
272 | | // is split between fatal and non-fatal error codes. |
273 | | class JXL_MUST_USE_RESULT Status { |
274 | | public: |
275 | | // We want implicit constructor from bool to allow returning "true" or "false" |
276 | | // on a function when using Status. "true" means kOk while "false" means a |
277 | | // generic fatal error. |
278 | | // NOLINTNEXTLINE(google-explicit-constructor) |
279 | | constexpr Status(bool ok) |
280 | 102k | : code_(ok ? StatusCode::kOk : StatusCode::kGenericError) {} |
281 | | |
282 | | // NOLINTNEXTLINE(google-explicit-constructor) |
283 | 3.87k | constexpr Status(StatusCode code) : code_(code) {} |
284 | | |
285 | | // We also want implicit cast to bool to check for return values of functions. |
286 | | // NOLINTNEXTLINE(google-explicit-constructor) |
287 | 109k | constexpr operator bool() const { return code_ == StatusCode::kOk; } |
288 | | |
289 | 1.57k | constexpr StatusCode code() const { return code_; } |
290 | | |
291 | | // Returns whether the status code is a fatal error. |
292 | 0 | constexpr bool IsFatalError() const { |
293 | 0 | return static_cast<int32_t>(code_) > 0; |
294 | 0 | } |
295 | | |
296 | | private: |
297 | | StatusCode code_; |
298 | | }; |
299 | | |
300 | | // Helper function to create a Status and print the debug message or abort when |
301 | | // needed. |
302 | | inline JXL_FORMAT(2, 3) Status |
303 | 3.67k | StatusMessage(const Status status, const char* format, ...) { |
304 | | // This block will be optimized out when JXL_DEBUG_ON_ERROR and |
305 | | // JXL_DEBUG_ON_ALL_ERROR are both disabled. |
306 | 3.67k | if ((JXL_DEBUG_ON_ERROR && status.IsFatalError()) || |
307 | 3.67k | (JXL_DEBUG_ON_ALL_ERROR && !status)) { |
308 | 0 | va_list args; |
309 | 0 | va_start(args, format); |
310 | 0 | vfprintf(stderr, format, args); |
311 | 0 | va_end(args); |
312 | 0 | } |
313 | | #ifdef JXL_CRASH_ON_ERROR |
314 | | // JXL_CRASH_ON_ERROR means to Abort() only on non-fatal errors. |
315 | | if (status.IsFatalError()) { |
316 | | Abort(); |
317 | | } |
318 | | #endif // JXL_CRASH_ON_ERROR |
319 | 3.67k | return status; |
320 | 3.67k | } |
321 | | |
322 | | } // namespace jxl |
323 | | |
324 | | #endif // LIB_JXL_BASE_STATUS_H_ |