Wireshark 4.5.0
The Wireshark network protocol analyzer
Loading...
Searching...
No Matches
ws_assert.h
Go to the documentation of this file.
1
10#ifndef __WS_ASSERT_H__
11#define __WS_ASSERT_H__
12
13#include <ws_symbol_export.h>
14#include <ws_attributes.h>
15#include <stdbool.h>
16#include <string.h>
17#include <wsutil/wslog.h>
18#include <wsutil/wmem/wmem.h>
19
20/*
21 * XXX - WS_ASSERT_ENABLED is tested in various if statements
22 * below, so that we don't test various assertions unless
23 * assertions are enabled. Compilers will often partially
24 * evaluate (CONSTANT && (expression)) at compile time, so
25 * that if CONSTANT is 0 the rest of the test isn't evaluated
26 * and assumed to result in a false result, with the code in
27 * the if branch being removed, and if CONSTANT is 1, the
28 * code is treated as an if that tests the expression.
29 *
30 * This could mean that, if "defined but not used" tests are
31 * being done, any variable tested in the expression may be warked
32 * as "defined but not used" if WS_ASSERT_ENABLED is 0, causing
33 * a pile of warnings if the variable isn't marked as unused
34 * (especially true of parametre variables).
35 *
36 * However, some compilers - Clang, in my tests, and probably GCC,
37 * due to tests in builds not failing - treate "if (0 && (expression))"
38 * specially, pretending hat all variables in the expression are used,
39 * even if they aren't used in the generated code. (At least in
40 * Apple clang version 15.0.0 (clang-1500.1.0.2.5), it must be
41 * exactly 0 - (0) doesn't have the same effect.)
42 *
43 * That's all very well, but, unfortunately Microsoft Visual Studio's
44 * C compiler doesn't do that, so the variables have to be marked as
45 * unused, which may cause warnings "used, but marked as unused"
46 * warnings if the code is compiled with assertions enabled.
47 */
48#if defined(ENABLE_ASSERT)
49#define WS_ASSERT_ENABLED 1
50#elif defined(NDEBUG)
51#define WS_ASSERT_ENABLED 0
52#else
53#define WS_ASSERT_ENABLED 1
54#endif
55
56#ifdef __cplusplus
57extern "C" {
58#endif /* __cplusplus */
59
60/*
61 * We don't want to execute the expression without assertions because
62 * it might be time and space costly and the goal here is to optimize for
63 * that case. However removing it completely is not good enough
64 * because it might generate many unused variable warnings. So we use
65 * if (false) and let the compiler optimize away the dead execution branch.
66 */
67#define ws_assert_if_active(active, expr) \
68 do { \
69 if ((active) && !(expr)) \
70 ws_error("assertion failed: %s", #expr); \
71 } while (0)
72
73/*
74 * ws_abort_if_fail() is not conditional on having assertions enabled.
75 * Usually used to appease a static analyzer.
76 */
77#define ws_abort_if_fail(expr) \
78 ws_assert_if_active(true, expr)
79
80/*
81 * ws_assert() cannot produce side effects, otherwise code will
82 * behave differently because of having assertions enabled/disabled, and
83 * probably introduce some difficult to track bugs.
84 */
85#define ws_assert(expr) \
86 ws_assert_if_active(WS_ASSERT_ENABLED, expr)
87
88
89#define ws_assert_streq(s1, s2) \
90 ws_assert((s1) && (s2) && strcmp((s1), (s2)) == 0)
91
92#define ws_assert_utf8(str, len) \
93 do { \
94 const char *__assert_endptr; \
95 if (WS_ASSERT_ENABLED && \
96 !g_utf8_validate(str, len, &__assert_endptr)) { \
97 ws_log_utf8_full(LOG_DOMAIN_UTF_8, LOG_LEVEL_ERROR, \
98 __FILE__, __LINE__, __func__, \
99 str, len, __assert_endptr); \
100 } \
101 } while (0)
102
103/*
104 * We don't want to disable ws_assert_not_reached() with (optional) assertions
105 * disabled.
106 * That would blast compiler warnings everywhere for no benefit, not
107 * even a miniscule performance gain. Reaching this function is always
108 * a programming error and will unconditionally abort execution.
109 *
110 * Note: With g_assert_not_reached() if the compiler supports unreachable
111 * built-ins (which recent versions of GCC and MSVC do) there is no warning
112 * blast with g_assert_not_reached() and G_DISABLE_ASSERT. However if that
113 * is not the case then g_assert_not_reached() is simply (void)0 and that
114 * causes the spurious warnings, because the compiler can't tell anymore
115 * that a certain code path is not used. We avoid that with
116 * ws_assert_not_reached(). There is no reason to ever use a no-op here.
117 */
118#define ws_assert_not_reached() \
119 ws_error("assertion \"not reached\" failed")
120
121/*
122 * These macros can be used as an alternative to ws_assert() to
123 * assert some condition on function arguments. This must only be used
124 * to catch programming errors, in situations where an assertion is
125 * appropriate. And it should only be used if failing the condition
126 * doesn't necessarily lead to an inconsistent state for the program.
127 *
128 * It is possible to set the fatal log domain to "InvalidArg" to abort
129 * execution for debugging purposes, if one of these checks fail.
130 */
131
132#define ws_warn_badarg(str) \
133 ws_log_full(LOG_DOMAIN_EINVAL, LOG_LEVEL_WARNING, \
134 __FILE__, __LINE__, __func__, \
135 "invalid argument: %s", str)
136
137#define ws_return_str_if(expr, scope) \
138 do { \
139 if (WS_ASSERT_ENABLED && (expr)) { \
140 ws_warn_badarg(#expr); \
141 return wmem_strdup_printf(scope, "(invalid argument: %s)", #expr); \
142 } \
143 } while (0)
144
145#define ws_return_val_if(expr, val) \
146 do { \
147 if (WS_ASSERT_ENABLED && (expr)) { \
148 ws_warn_badarg(#expr); \
149 return (val); \
150 } \
151 } while (0)
152
153#ifdef __cplusplus
154}
155#endif /* __cplusplus */
156
157#endif /* __WS_ASSERT_H__ */