| /* |
| * include/haproxy/bug.h |
| * Assertions and instant crash macros needed everywhere. |
| * |
| * Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
| * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifndef _HAPROXY_BUG_H |
| #define _HAPROXY_BUG_H |
| |
| #include <haproxy/atomic.h> |
| #include <haproxy/compiler.h> |
| |
| /* quick debugging hack, should really be removed ASAP */ |
| #ifdef DEBUG_FULL |
| #define DPRINTF(x...) fprintf(x) |
| #else |
| #define DPRINTF(x...) |
| #endif |
| |
| #define DUMP_TRACE() do { extern void ha_backtrace_to_stderr(void); ha_backtrace_to_stderr(); } while (0) |
| |
| #ifdef DEBUG_USE_ABORT |
| /* abort() is better recognized by code analysis tools */ |
| #define ABORT_NOW() do { DUMP_TRACE(); abort(); } while (0) |
| #else |
| /* More efficient than abort() because it does not mangle the |
| * stack and stops at the exact location we need. |
| */ |
| #define ABORT_NOW() do { DUMP_TRACE(); (*(volatile int*)1=0); my_unreachable(); } while (0) |
| #endif |
| |
| /* This is the generic low-level macro dealing with conditional warnings and |
| * bugs. The caller decides whether to crash or not and what prefix and suffix |
| * to pass. The macro returns the boolean value of the condition as an int for |
| * the case where it wouldn't die. |
| */ |
| #define _BUG_ON(cond, file, line, crash, pfx, sfx) \ |
| __BUG_ON(cond, file, line, crash, pfx, sfx) |
| |
| #define __BUG_ON(cond, file, line, crash, pfx, sfx) \ |
| ({ \ |
| int __bug_cond = !!(cond); \ |
| if (unlikely(__bug_cond)) { \ |
| const char msg[] = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ |
| DISGUISE(write(2, msg, __builtin_strlen(msg))); \ |
| if (crash) \ |
| ABORT_NOW(); \ |
| else \ |
| DUMP_TRACE(); \ |
| } \ |
| __bug_cond; /* let's return the condition */ \ |
| }) |
| |
| /* This one is equivalent except that it only emits the message once by |
| * maintaining a static counter. This may be used with warnings to detect |
| * certain unexpected conditions in field. Later on, in cores it will be |
| * possible to verify these counters. |
| */ |
| #define _BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \ |
| __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) |
| |
| #define __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \ |
| ({ \ |
| static int __match_count_##line; \ |
| int __bug_cond = !!(cond); \ |
| if (unlikely(__bug_cond) && \ |
| !_HA_ATOMIC_FETCH_ADD(&__match_count_##line, 1)) { \ |
| const char msg[] = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ |
| DISGUISE(write(2, msg, __builtin_strlen(msg))); \ |
| if (crash) \ |
| ABORT_NOW(); \ |
| else \ |
| DUMP_TRACE(); \ |
| } \ |
| __bug_cond; /* let's return the condition */ \ |
| }) |
| |
| /* BUG_ON: complains if <cond> is true when DEBUG_STRICT or DEBUG_STRICT_NOCRASH |
| * are set, does nothing otherwise. With DEBUG_STRICT in addition it immediately |
| * crashes using ABORT_NOW() above. |
| */ |
| #if defined(DEBUG_STRICT) |
| #define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: bug ", "") |
| #define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") |
| #define WARN_ON_ONCE(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") |
| #elif defined(DEBUG_STRICT_NOCRASH) |
| #define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "FATAL: bug ", " (not crashing but process is untrusted now)") |
| #define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") |
| #define WARN_ON_ONCE(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: ", " (please report to developers)") |
| #else |
| #define BUG_ON(cond) |
| #define WARN_ON(cond) |
| #define WARN_ON_ONCE(cond) |
| #endif |
| |
| /* When not optimizing, clang won't remove that code, so only compile it in when optimizing */ |
| #if defined(__GNUC__) && defined(__OPTIMIZE__) |
| #define HA_LINK_ERROR(what) \ |
| do { \ |
| /* provoke a build-time error */ \ |
| extern volatile int what; \ |
| what = 1; \ |
| } while (0) |
| #else |
| #define HA_LINK_ERROR(what) \ |
| do { \ |
| } while (0) |
| #endif /* __OPTIMIZE__ */ |
| |
| /* more reliable free() that clears the pointer */ |
| #define ha_free(x) do { \ |
| typeof(x) __x = (x); \ |
| if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ |
| HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ |
| } \ |
| free(*__x); \ |
| *__x = NULL; \ |
| } while (0) |
| |
| |
| /* handle 'tainted' status */ |
| enum tainted_flags { |
| TAINTED_CONFIG_EXP_KW_DECLARED = 0x00000001, |
| TAINTED_ACTION_EXP_EXECUTED = 0x00000002, |
| TAINTED_CLI_EXPERT_MODE = 0x00000004, |
| TAINTED_CLI_EXPERIMENTAL_MODE = 0x00000008, |
| }; |
| |
| /* this is a bit field made of TAINTED_*, and is declared in haproxy.c */ |
| extern unsigned int tainted; |
| |
| static inline void mark_tainted(const enum tainted_flags flag) |
| { |
| HA_ATOMIC_OR(&tainted, flag); |
| } |
| |
| static inline unsigned int get_tainted() |
| { |
| return HA_ATOMIC_LOAD(&tainted); |
| } |
| |
| #if defined(DEBUG_MEM_STATS) |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /* Memory allocation statistics are centralized into a global "mem_stats" |
| * section. This will not work with some linkers. |
| */ |
| enum { |
| MEM_STATS_TYPE_UNSET = 0, |
| MEM_STATS_TYPE_CALLOC, |
| MEM_STATS_TYPE_FREE, |
| MEM_STATS_TYPE_MALLOC, |
| MEM_STATS_TYPE_REALLOC, |
| MEM_STATS_TYPE_STRDUP, |
| }; |
| |
| struct mem_stats { |
| size_t calls; |
| size_t size; |
| const char *file; |
| int line; |
| int type; |
| }; |
| |
| #undef calloc |
| #define calloc(x,y) ({ \ |
| size_t __x = (x); size_t __y = (y); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_CALLOC, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| _HA_ATOMIC_ADD(&_.size, __x * __y); \ |
| calloc(__x,__y); \ |
| }) |
| |
| /* note: we can't redefine free() because we have a few variables and struct |
| * members called like this. |
| */ |
| #undef __free |
| #define __free(x) ({ \ |
| void *__x = (x); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_FREE, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| if (__x) \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| free(__x); \ |
| }) |
| |
| #undef ha_free |
| #define ha_free(x) ({ \ |
| typeof(x) __x = (x); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_FREE, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ |
| HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ |
| } \ |
| if (*__x) \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| free(*__x); \ |
| *__x = NULL; \ |
| }) |
| |
| #undef malloc |
| #define malloc(x) ({ \ |
| size_t __x = (x); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_MALLOC, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| _HA_ATOMIC_ADD(&_.size, __x); \ |
| malloc(__x); \ |
| }) |
| |
| #undef realloc |
| #define realloc(x,y) ({ \ |
| void *__x = (x); size_t __y = (y); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_REALLOC, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| _HA_ATOMIC_ADD(&_.size, __y); \ |
| realloc(__x,__y); \ |
| }) |
| |
| #undef strdup |
| #define strdup(x) ({ \ |
| const char *__x = (x); size_t __y = strlen(__x); \ |
| static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ |
| .file = __FILE__, .line = __LINE__, \ |
| .type = MEM_STATS_TYPE_STRDUP, \ |
| }; \ |
| __asm__(".globl __start_mem_stats"); \ |
| __asm__(".globl __stop_mem_stats"); \ |
| _HA_ATOMIC_INC(&_.calls); \ |
| _HA_ATOMIC_ADD(&_.size, __y); \ |
| strdup(__x); \ |
| }) |
| #endif /* DEBUG_MEM_STATS*/ |
| |
| #endif /* _HAPROXY_BUG_H */ |
| |
| /* |
| * Local variables: |
| * c-indent-level: 8 |
| * c-basic-offset: 8 |
| * End: |
| */ |