Coverage Report

Created: 2022-08-24 06:06

/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_