wireshark/wsutil/ws_assert.h

158 lines
5.9 KiB
C
Raw Permalink Normal View History

/** @file
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __WS_ASSERT_H__
#define __WS_ASSERT_H__
#include <ws_symbol_export.h>
#include <ws_attributes.h>
#include <stdbool.h>
dfilter: Require double-quoted strings with "matches" Matches is a special case that looks on the RHS and tries to convert every unparsed value to a string, regardless of the LHS type. This is not how types work in the display filter. Require double-quotes to avoid ambiguity, because matches doesn't follow normal Wireshark display filter type rules. It doesn't need nor benefit from the flexibility provided by unparsed strings in the syntax. For matches the RHS is always a literal strings except if the RHS is also a field name, then it complains of an incompatible type. This is confusing. No type can be compatible because no type rules are ever considered. Every unparsed value is a text string except if it happens to coincide with a field name it also requires double-quoting or it throws a syntax error, just to be difficult. We could remove this odd quirk but requiring double-quotes for regular expressions is a better, more elegant fix. Before: Filter: tcp matches "udp" Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp.srcport dftest: tcp and udp.srcport are not of compatible types. Filter: tcp matches udp.srcportt Constants: 00000 PUT_PCRE udp.srcportt -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN After: Filter: tcp matches "udp" Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp dftest: "udp" was unexpected in this context. Filter: tcp matches udp.srcport dftest: "udp.srcport" was unexpected in this context. Filter: tcp matches udp.srcportt dftest: "udp.srcportt" was unexpected in this context. The error message could still be improved.
2021-10-09 16:40:08 +01:00
#include <string.h>
2022-01-04 12:20:21 +00:00
#include <wsutil/wslog.h>
2023-09-16 07:06:55 +01:00
#include <wsutil/wmem/wmem.h>
/*
* XXX - WS_ASSERT_ENABLED is tested in various if statements
* below, so that we don't test various assertions unless
* assertions are enabled. Compilers will often partially
* evaluate (CONSTANT && (expression)) at compile time, so
* that if CONSTANT is 0 the rest of the test isn't evaluated
* and assumed to result in a false result, with the code in
* the if branch being removed, and if CONSTANT is 1, the
* code is treated as an if that tests the expression.
*
* This could mean that, if "defined but not used" tests are
* being done, any variable tested in the expression may be warked
* as "defined but not used" if WS_ASSERT_ENABLED is 0, causing
* a pile of warnings if the variable isn't marked as unused
* (especially true of parametre variables).
*
* However, some compilers - Clang, in my tests, and probably GCC,
* due to tests in builds not failing - treate "if (0 && (expression))"
* specially, pretending hat all variables in the expression are used,
* even if they aren't used in the generated code. (At least in
* Apple clang version 15.0.0 (clang-1500.1.0.2.5), it must be
* exactly 0 - (0) doesn't have the same effect.)
*
* That's all very well, but, unfortunately Microsoft Visual Studio's
* C compiler doesn't do that, so the variables have to be marked as
* unused, which may cause warnings "used, but marked as unused"
* warnings if the code is compiled with assertions enabled.
*/
#if defined(ENABLE_ASSERT)
#define WS_ASSERT_ENABLED 1
#elif defined(NDEBUG)
#define WS_ASSERT_ENABLED 0
#else
#define WS_ASSERT_ENABLED 1
2022-01-04 02:25:59 +00:00
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
2022-01-04 02:25:59 +00:00
/*
* We don't want to execute the expression without assertions because
2022-01-04 02:25:59 +00:00
* it might be time and space costly and the goal here is to optimize for
* that case. However removing it completely is not good enough
2022-01-04 02:25:59 +00:00
* because it might generate many unused variable warnings. So we use
* if (false) and let the compiler optimize away the dead execution branch.
*/
#define ws_assert_if_active(active, expr) \
2022-01-04 12:20:21 +00:00
do { \
if ((active) && !(expr)) \
ws_error("assertion failed: %s", #expr); \
2022-01-04 02:25:59 +00:00
} while (0)
/*
* ws_abort_if_fail() is not conditional on having assertions enabled.
* Usually used to appease a static analyzer.
*/
#define ws_abort_if_fail(expr) \
ws_assert_if_active(true, expr)
/*
* ws_assert() cannot produce side effects, otherwise code will
* behave differently because of having assertions enabled/disabled, and
* probably introduce some difficult to track bugs.
*/
2022-01-04 02:25:59 +00:00
#define ws_assert(expr) \
ws_assert_if_active(WS_ASSERT_ENABLED, expr)
2022-01-04 02:25:59 +00:00
dfilter: Require double-quoted strings with "matches" Matches is a special case that looks on the RHS and tries to convert every unparsed value to a string, regardless of the LHS type. This is not how types work in the display filter. Require double-quotes to avoid ambiguity, because matches doesn't follow normal Wireshark display filter type rules. It doesn't need nor benefit from the flexibility provided by unparsed strings in the syntax. For matches the RHS is always a literal strings except if the RHS is also a field name, then it complains of an incompatible type. This is confusing. No type can be compatible because no type rules are ever considered. Every unparsed value is a text string except if it happens to coincide with a field name it also requires double-quoting or it throws a syntax error, just to be difficult. We could remove this odd quirk but requiring double-quotes for regular expressions is a better, more elegant fix. Before: Filter: tcp matches "udp" Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp.srcport dftest: tcp and udp.srcport are not of compatible types. Filter: tcp matches udp.srcportt Constants: 00000 PUT_PCRE udp.srcportt -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN After: Filter: tcp matches "udp" Constants: 00000 PUT_PCRE udp -> reg#1 Instructions: 00000 READ_TREE tcp -> reg#0 00001 IF-FALSE-GOTO 3 00002 ANY_MATCHES reg#0 matches reg#1 00003 RETURN Filter: tcp matches udp dftest: "udp" was unexpected in this context. Filter: tcp matches udp.srcport dftest: "udp.srcport" was unexpected in this context. Filter: tcp matches udp.srcportt dftest: "udp.srcportt" was unexpected in this context. The error message could still be improved.
2021-10-09 16:40:08 +01:00
#define ws_assert_streq(s1, s2) \
ws_assert((s1) && (s2) && strcmp((s1), (s2)) == 0)
#define ws_assert_utf8(str, len) \
do { \
const char *__assert_endptr; \
if (WS_ASSERT_ENABLED && \
!g_utf8_validate(str, len, &__assert_endptr)) { \
ws_log_utf8_full(LOG_DOMAIN_UTF_8, LOG_LEVEL_ERROR, \
__FILE__, __LINE__, __func__, \
str, len, __assert_endptr); \
} \
} while (0)
/*
* We don't want to disable ws_assert_not_reached() with (optional) assertions
* disabled.
* That would blast compiler warnings everywhere for no benefit, not
* even a miniscule performance gain. Reaching this function is always
* a programming error and will unconditionally abort execution.
*
* Note: With g_assert_not_reached() if the compiler supports unreachable
* built-ins (which recent versions of GCC and MSVC do) there is no warning
* blast with g_assert_not_reached() and G_DISABLE_ASSERT. However if that
* is not the case then g_assert_not_reached() is simply (void)0 and that
* causes the spurious warnings, because the compiler can't tell anymore
* that a certain code path is not used. We avoid that with
* ws_assert_not_reached(). There is no reason to ever use a no-op here.
*/
#define ws_assert_not_reached() \
2022-01-04 12:20:21 +00:00
ws_error("assertion \"not reached\" failed")
2023-09-16 07:06:55 +01:00
/*
* These macros can be used as an alternative to ws_assert() to
* assert some condition on function arguments. This must only be used
* to catch programming errors, in situations where an assertion is
* appropriate. And it should only be used if failing the condition
* doesn't necessarily lead to an inconsistent state for the program.
*
* It is possible to set the fatal log domain to "InvalidArg" to abort
* execution for debugging purposes, if one of these checks fail.
*/
#define ws_warn_badarg(str) \
ws_log_full(LOG_DOMAIN_EINVAL, LOG_LEVEL_WARNING, \
2023-09-16 07:06:55 +01:00
__FILE__, __LINE__, __func__, \
"invalid argument: %s", str)
2023-09-16 07:06:55 +01:00
#define ws_return_str_if(expr, scope) \
do { \
if (WS_ASSERT_ENABLED && (expr)) { \
2023-09-16 07:06:55 +01:00
ws_warn_badarg(#expr); \
return wmem_strdup_printf(scope, "(invalid argument: %s)", #expr); \
2023-09-16 07:06:55 +01:00
} \
} while (0)
#define ws_return_val_if(expr, val) \
do { \
if (WS_ASSERT_ENABLED && (expr)) { \
2023-09-16 07:06:55 +01:00
ws_warn_badarg(#expr); \
return (val); \
} \
} while (0)
#ifdef __cplusplus
}
#endif /* __cplusplus */
2021-05-20 02:54:35 +01:00
#endif /* __WS_ASSERT_H__ */