/*
 * 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 <stddef.h>
#include <sys/types.h>
#include <haproxy/atomic.h>
#include <haproxy/compiler.h>

/* Counting the number of matches on a given BUG_ON()/WARN_ON()/CHECK_IF()/
 * COUNT_IF() invocation requires a special section ("dbg_cnt") hence a modern
 * linker.
 */
extern unsigned int debug_enable_counters;

/* this is a bit field made of TAINTED_*, and is declared in haproxy.c */
extern unsigned int tainted;

/* 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)

/* describes a call place in the code, for example for tracing memory
 * allocations or task wakeups. These must be declared static const.
 */
struct ha_caller {
	const char *func;  // function name
	const char *file;  // file name
	uint16_t line;     // line number
	uint8_t what;      // description of the call, usage specific
	uint8_t arg8;      // optional argument, usage specific
	uint32_t arg32;    // optional argument, usage specific
};

/* makes a struct ha_caller from the caller */
#define MK_CALLER(_what, _arg8, _arg32)					\
	({ static const struct ha_caller _ = {				\
		.func = __func__, .file = __FILE__, .line = __LINE__,	\
		.what = _what, .arg8 = _arg8, .arg32 = _arg32 };	\
		&_; })

/* 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,
	MEM_STATS_TYPE_P_ALLOC,
	MEM_STATS_TYPE_P_FREE,
};

struct mem_stats {
	size_t calls;
	size_t size;
	struct ha_caller caller;
	const void *extra; // extra info specific to this call (e.g. pool ptr)
} ALIGNED(sizeof(void*));

/* 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,
	TAINTED_WARN                   = 0x00000010, /* a WARN_ON triggered */
	TAINTED_BUG                    = 0x00000020, /* a BUG_ON triggered */
	TAINTED_SHARED_LIBS            = 0x00000040, /* a shared library was loaded */
	TAINTED_REDEFINITION           = 0x00000080, /* symbol redefinition detected */
	TAINTED_REPLACED_MEM_ALLOCATOR = 0x00000100, /* memory allocator was replaced using LD_PRELOAD */
	TAINTED_PANIC                  = 0x00000200, /* a panic dump has started */
	TAINTED_LUA_STUCK              = 0x00000400, /* stuck in a Lua context */
	TAINTED_LUA_STUCK_SHARED       = 0x00000800, /* stuck in a shared Lua context */
	TAINTED_MEM_TRIMMING_STUCK     = 0x00001000, /* stuck while trimming memory */
	TAINTED_WARN_BLOCKED_TRAFFIC   = 0x00002000, /* emitted a warning about blocked traffic */
};

static inline unsigned int mark_tainted(const enum tainted_flags flag)
{
	return HA_ATOMIC_FETCH_OR(&tainted, flag);
}

static inline unsigned int get_tainted()
{
	return HA_ATOMIC_LOAD(&tainted);
}

/* type of checks that can be verified. We cannot really distinguish between
 * BUG/WARN/CHECK_IF as they all pass through __BUG_ON() at a different level,
 * but there's at least a difference between __BUG_ON() and __BUG_ON_ONCE()
 * (and of course COUNT_IF).
 */
enum debug_counter_type {
	DBG_BUG,
	DBG_BUG_ONCE,
	DBG_COUNT_IF,
	DBG_GLITCH,
	DBG_COUNTER_TYPES // must be last
};

/* this is the struct that we store in section "dbg_cnt". Better keep it
 * well aligned.
 */
struct debug_count {
	const char *file;
	const char *func;
	const char *desc;
	uint16_t    line;
	uint8_t     type;
	uint8_t  details;
	uint32_t   count;
};

/* 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__ */

/* quick debugging hack, should really be removed ASAP */
#ifdef DEBUG_FULL
#define DPRINTF(x...) fprintf(x)
#else
#define DPRINTF(x...)
#endif

/* report a bug on stderr */
void complain(uint details, const char *msg);
void complain_with_dbg(struct debug_count *dbg);
void ha_backtrace_to_stderr(int hint);

/* Let's make DEBUG_STRESS equal to zero if not set or not valid, or to
 * 1 if set. This way it is always set and should be easy to use in "if ()"
 * statements without requiring ifdefs, while remaining compatible with
 * "#if DEBUG_STRESS > 0". We also force DEBUG_STRICT and DEBUG_STRICT_ACTION
 * when stressed.
 */
#if !defined(DEBUG_STRESS)
# define DEBUG_STRESS 0
#elif DEBUG_STRESS != 0
# undef DEBUG_STRESS
# define DEBUG_STRESS 1        // make sure comparison >0 always works
# undef DEBUG_STRICT
# define DEBUG_STRICT 2        // enable BUG_ON
# undef DEBUG_STRICT_ACTION
# define DEBUG_STRICT_ACTION 3 // enable crash on match
#endif

/* First, let's try to handle some arch-specific crashing methods. We prefer
 * the macro to the function because when opening the core, the debugger will
 * directly show the calling point (e.g. the BUG_ON() condition) based on the
 * line number, while the function will create new line numbers. But the
 * function is needed e.g. if some pragmas are needed.
 */

#if defined(__i386__) || defined(__x86_64__)
#define ha_crash_now() do {						\
		/* ud2 opcode: 2 bytes, raises illegal instruction */	\
		__asm__ volatile(".byte 0x0f,0x0b\n");			\
		DO_NOT_FOLD();						\
		my_unreachable();					\
	} while (0)

#elif defined(__aarch64__)
#define ha_crash_now() do {						\
		/* udf#imm16: 4 bytes (), raises illegal instruction */	\
		__asm__ volatile(".byte 0x00,0x00,0x00,0x00\n");	\
		DO_NOT_FOLD();						\
		my_unreachable();					\
	} while (0)

#else // not x86

/* generic implementation, causes a segfault */
static inline __attribute((always_inline,noreturn,unused)) void ha_crash_now(void)
{
#if __GNUC_PREREQ__(5, 0)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#if __GNUC_PREREQ__(6, 0)
#pragma GCC diagnostic ignored "-Wnull-dereference"
#endif
#endif
	*(volatile char *)1 = 0;
#if __GNUC_PREREQ__(5, 0)
#pragma GCC diagnostic pop
#endif
	DO_NOT_FOLD();
	my_unreachable();
}

#endif // end of arch-specific ha_crash_now() definitions


/* details of a debug event passed to _BUG_ON*(), allows to distinguish some
 * causes (check, warn, bug, abort) and the fatality (none, warning, fatal).
 * These are bit fields that can be ORed.
 */
#define DBG_DET_NONE            0x00000000
#define DBG_DET_TYP_CHK         0x00000001
#define DBG_DET_TYP_WRN         0x00000002
#define DBG_DET_TYP_BUG         0x00000004
#define DBG_DET_TYP_ABT         0x00000008
#define DBG_DET_FAT_WARN        0x00000010
#define DBG_DET_FAT_FATL        0x00000020
// combinations
#define DBG_WARN_CHK            (DBG_DET_FAT_WARN | DBG_DET_TYP_CHK)
#define DBG_WARN_WRN            (DBG_DET_FAT_WARN | DBG_DET_TYP_WRN)
#define DBG_WARN_BUG            (DBG_DET_FAT_WARN | DBG_DET_TYP_BUG)
#define DBG_WARN_ABT            (DBG_DET_FAT_WARN | DBG_DET_TYP_ABT)
#define DBG_FATL_CHK            (DBG_DET_FAT_FATL | DBG_DET_TYP_CHK)
#define DBG_FATL_WRN            (DBG_DET_FAT_FATL | DBG_DET_TYP_WRN)
#define DBG_FATL_BUG            (DBG_DET_FAT_FATL | DBG_DET_TYP_BUG)
#define DBG_FATL_ABT            (DBG_DET_FAT_FATL | DBG_DET_TYP_ABT)

/* ABORT_NOW() usually takes no argument and will cause the program to abort
 * exactly where it is. We prefer to emit an invalid instruction to preserve
 * all registers, but it may fall back to a regular abort depending on the
 * platform. An optional argument can be a message string that will cause
 * the emission of a message saying "ABORT at" followed by the file and line
 * number then that message followed by a final line feed. This can be helpful
 * in situations where the core cannot be retrieved for example. However it
 * will definitely cause the loss of some registers, so should be avoided when
 * not strictly necessary.
 */
#define ABORT_NOW(...)							\
	_ABORT_NOW(__FILE__, __LINE__, __VA_ARGS__)

#define _ABORT_NOW(file, line, ...)					\
	__ABORT_NOW(file, line, __VA_ARGS__)

#ifdef DEBUG_USE_ABORT
/* abort() is better recognized by code analysis tools */

/* abort() is generally tagged noreturn, so there's no 100% safe way to prevent
 * the compiler from doing a tail-merge here. Tests show that stopping folding
 * just before calling abort() does work in practice at -O2, increasing the
 * number of abort() calls in h3.o from 18 to 26, probably because there's no
 * more savings to be made by replacing a call with a jump. However, as -Os it
 * drops to 5 regardless of the build option. In order to help here, instead we
 * wrap abort() into another function, with the line number stored into a local
 * variable on the stack and we pretend to use it, so that unwinding the stack
 * from abort() will reveal its value even if the call was folded.
 */
static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
{
	DISGUISE(&line);
	abort();
}

#else

/* More efficient than abort() because it does not mangle the
 * stack and stops at the exact location we need.
 */
#define abort_with_line(...) ha_crash_now()

#endif

#define __ABORT_NOW(file, line, ...) do {				\
		if (sizeof("" __VA_ARGS__) > 1)				\
			complain(DBG_FATL_ABT, file ":" #line ": " __VA_ARGS__); \
		abort_with_line(__LINE__);				\
	} while (0)

#if !defined(USE_OBSOLETE_LINKER)

/**** USE_OBSOLETE_LINKER not defined -- we're working with a modern linker ****/

/* Declare a section for condition counters. The start and stop pointers are
 * set by the linker itself, which is why they're declared extern here. The
 * weak attribute is used so that we declare them ourselves if the section is
 * empty. The corresponding section must contain exclusively struct debug_count
 * to make sure each location may safely be visited by incrementing a pointer.
 */
extern __attribute__((__weak__)) struct debug_count __start_dbg_cnt HA_SECTION_START("dbg_cnt");
extern __attribute__((__weak__)) struct debug_count __stop_dbg_cnt  HA_SECTION_STOP("dbg_cnt");

/* This macro adds a pass counter at the line where it's declared. It can be
 * used by the various BUG_ON, COUNT_IF etc flavors. The condition is only
 * passed for the sake of being turned into a string; the caller is expected
 * to have already verified it. The descripton and optional comments are
 * delimited by \x1e (record separator).
 */
#define __DBG_COUNT(_cond, _file, _line, _type, ...) do {			\
		static struct debug_count __dbg_cnt_##_line HA_SECTION("dbg_cnt") \
		__attribute__((__used__,__aligned__(sizeof(void*)))) = { 	\
			.file = _file,						\
			.func = __func__,					\
			.line = _line,						\
			.type = _type,						\
			.desc = (sizeof("" #_cond) > 1) ?			\
				  (sizeof("" __VA_ARGS__) > 1) ?		\
				  "" #_cond "\x1e" __VA_ARGS__ :		\
				  "" #_cond :					\
				"" __VA_ARGS__,					\
			.count = 0,						\
		};								\
		HA_WEAK(__start_dbg_cnt);					\
		HA_WEAK(__stop_dbg_cnt);					\
		_HA_ATOMIC_INC(&__dbg_cnt_##_line.count);			\
	} while (0)


/* This is called by _BUG_ON() and _BUG_ON_ONCE() to count and handle the event. */
#define __BUG_ON(_cond, _file, _line, _type, _details, ...) {			\
		static struct debug_count __dbg_cnt_##_line HA_SECTION("dbg_cnt") \
		__attribute__((__used__,__aligned__(sizeof(void*)))) = { 	\
			.file = _file,						\
			.func = __func__,					\
			.line = _line,						\
			.type = _type,						\
			.details = _details,					\
			.desc = (sizeof("" #_cond) > 1) ?			\
				  (sizeof("" __VA_ARGS__) > 1) ?		\
				  "" #_cond "\x1e" __VA_ARGS__ :		\
				  "" #_cond :					\
				"" __VA_ARGS__,					\
			.count = 0,						\
		};								\
		HA_WEAK(__start_dbg_cnt);					\
		HA_WEAK(__stop_dbg_cnt);					\
		complain_with_dbg(&__dbg_cnt_##_line);				\
		if (_details & DBG_DET_FAT_FATL)				\
			abort_with_line(__LINE__);				\
	}

/* Matrix for DEBUG_COUNTERS:
 *    0 : only BUG_ON() and CHECK_IF() are reported (super rare)
 *    1 : COUNT_GLITCH() are also reported (rare)
 *        COUNT_IF() are also reported only if debug_enable_counters was set
 *    2 : COUNT_IF() are also reported unless debug_enable_counters was reset
 */

/* Core of the COUNT_IF() macro, checks the condition and counts one hit if
 * true. It's only enabled at DEBUG_COUNTERS >= 1, and enabled by default if
 * DEBUG_COUNTERS >= 2.
 */
# if defined(DEBUG_COUNTERS) && (DEBUG_COUNTERS >= 1)
#  define _COUNT_IF(cond, file, line, ...)					  \
	(unlikely(cond) ? ({							  \
		if (debug_enable_counters)					  \
			__DBG_COUNT(cond, file, line, DBG_COUNT_IF, __VA_ARGS__); \
		1; /* let's return the true condition */			  \
	}) : 0)
# else
#  define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0)
# endif

/* DEBUG_COUNTERS enables counting the number of glitches per line of code. The
 * condition is empty (nothing to write there), except maybe __VA_ARGS at the
 * end.
 */
# if !defined(DEBUG_COUNTERS) || (DEBUG_COUNTERS == 0)
#  define _COUNT_GLITCH(file, line, ...) do { } while (0)
# else
#  define _COUNT_GLITCH(file, line, ...) do {						\
		__DBG_COUNT(, file, line, DBG_GLITCH, __VA_ARGS__); 	\
	} while (0)
# endif

#else /* USE_OBSOLETE_LINKER  */

/**** USE_OBSOLETE_LINKER *is* defined -- we're working with an old linker ****/

# define __DBG_COUNT(cond, file, line, type, ...) do { } while (0)
# define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0)
# define _COUNT_GLITCH(file, line, ...) do { } while (0)

/* This is called by _BUG_ON() and _BUG_ON_ONCE() to handle the event. */
#define __BUG_ON(cond, file, line, type, details, ...) {		\
		const char *msg;					\
		if (sizeof("" __VA_ARGS__) > 1)				\
			msg = "\"" #cond "\" matched at " file ":" #line "\x1e" __VA_ARGS__; \
		else							\
			msg = "\"" #cond "\" matched at " file ":" #line; \
		if (type != DBG_BUG_ONCE ||				\
		    ({ static int __match_count_##line;			\
		       !_HA_ATOMIC_FETCH_ADD(&__match_count_##line, 1); })) { \
			complain(details, msg);				\
			if (details & DBG_DET_FAT_FATL)			\
				abort_with_line(__LINE__);		\
		}							\
	}

#endif /* USE_OBSOLETE_LINKER  */

/* reports a glitch for current file and line, optionally with an explanation */
#define COUNT_GLITCH(...) _COUNT_GLITCH(__FILE__, __LINE__, __VA_ARGS__)

/* This is the generic low-level macro dealing with conditional warnings and
 * bugs. The caller decides whether to crash or not.
 * The macro returns the boolean value of the condition as an int for the case
 * where it wouldn't die. The <details> flag is made of:
 *  - details & DBG_DET_FAT_FATL: crash yes/no;
 *  - details & DBG_DET_TYP_BUG: taint as bug instead of warn
 * The optional argument must be a single constant string that will be appended
 * on a second line after the condition message, to give a bit more context
 * about the problem.
 */
#define _BUG_ON(cond, file, line, details, ...)					\
	(void)(unlikely(cond) ? ({						\
		__BUG_ON(cond, file, line, DBG_BUG, details, __VA_ARGS__); 	\
		1; /* let's return the true condition */			\
	}) : 0)

/* 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, details, ...)				\
	(void)(unlikely(cond) ? ({						\
		__BUG_ON(cond, file, line, DBG_BUG_ONCE, details, __VA_ARGS__); \
		1; /* let's return the true condition */			\
	}) : 0)

/* DEBUG_STRICT enables/disables runtime checks on condition <cond>
 * DEBUG_STRICT_ACTION indicates the level of verification on the rules when
 * <cond> is true:
 *
 *    macro   BUG_ON()    WARN_ON()    CHECK_IF()
 * value  0    warn         warn         warn
 *        1    CRASH        warn         warn
 *        2    CRASH        CRASH        warn
 *        3    CRASH        CRASH        CRASH
 */

/* The macros below are for general use */
#if defined(DEBUG_STRICT) && (DEBUG_STRICT > 0)
# if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1)
/* Lowest level: BUG_ON() warns, WARN_ON() warns, CHECK_IF() warns */
#  define BUG_ON(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_WARN_BUG, __VA_ARGS__)
#  define WARN_ON(cond, ...)  _BUG_ON     (cond, __FILE__, __LINE__, DBG_WARN_WRN, __VA_ARGS__)
#  define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_CHK, __VA_ARGS__)
#  define COUNT_IF(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION == 1)
/* default level: BUG_ON() crashes, WARN_ON() warns, CHECK_IF() warns */
#  define BUG_ON(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_BUG, __VA_ARGS__)
#  define WARN_ON(cond, ...)  _BUG_ON     (cond, __FILE__, __LINE__, DBG_WARN_WRN, __VA_ARGS__)
#  define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_CHK, __VA_ARGS__)
#  define COUNT_IF(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION == 2)
/* Stricter level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() warns */
#  define BUG_ON(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_BUG, __VA_ARGS__)
#  define WARN_ON(cond, ...)  _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_WRN, __VA_ARGS__)
#  define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_CHK, __VA_ARGS__)
#  define COUNT_IF(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3)
/* Developer/CI level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() crashes */
#  define BUG_ON(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_BUG, __VA_ARGS__)
#  define WARN_ON(cond, ...)  _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_WRN, __VA_ARGS__)
#  define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_FATL_CHK, __VA_ARGS__)
#  define COUNT_IF(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# endif
#else /* DEBUG_STRICT not defined below */
/* We want BUG_ON() to evaluate the expression sufficiently for next lines
 * of codes not to complain about suspicious dereferences for example.
 * GCC-11 tends to fail to validate that in combined expressions such as
 * "BUG_ON(!a || !b)", but it works fine when using a temporary assignment
 * like below, without hurting the generated code.
 */
#  define BUG_ON(cond, ...)   ({ typeof(cond) __cond = (cond); ASSUME(!__cond); })
#  define WARN_ON(cond, ...)  do { (void)sizeof(cond); } while (0)
#  define CHECK_IF(cond, ...) do { (void)sizeof(cond); } while (0)
#  define COUNT_IF(cond, ...) DISGUISE(cond)
#endif

/* These macros are only for hot paths and remain disabled unless DEBUG_STRICT is 2 or above.
 * Only developers/CI should use these levels as they may significantly impact performance by
 * enabling checks in sensitive areas.
 */
#if defined(DEBUG_STRICT) && (DEBUG_STRICT > 1)
# if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1)
/* Lowest level: BUG_ON() warns, CHECK_IF() warns */
#  define BUG_ON_HOT(cond, ...)   _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_BUG, __VA_ARGS__)
#  define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_CHK, __VA_ARGS__)
#  define COUNT_IF_HOT(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION < 3)
/* default level: BUG_ON() crashes, CHECK_IF() warns */
#  define BUG_ON_HOT(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_BUG, __VA_ARGS__)
#  define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_WARN_CHK, __VA_ARGS__)
#  define COUNT_IF_HOT(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3)
/* Developer/CI level: BUG_ON() crashes, CHECK_IF() crashes */
#  define BUG_ON_HOT(cond, ...)   _BUG_ON     (cond, __FILE__, __LINE__, DBG_FATL_BUG, __VA_ARGS__)
#  define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, DBG_FATL_CHK, __VA_ARGS__)
#  define COUNT_IF_HOT(cond, ...) _COUNT_IF   (cond, __FILE__, __LINE__, __VA_ARGS__)
# endif
#else /* DEBUG_STRICT <= 1 below */
/* Contrary to BUG_ON(), we do *NOT* want BUG_ON_HOT() to evaluate the
 * expression unless explicitly enabled, since it is located in hot code paths.
 * We just validate that the expression results in a valid type.
 */
#  define BUG_ON_HOT(cond, ...)   do { (void)sizeof(cond) ; } while (0)
#  define CHECK_IF_HOT(cond, ...) do { (void)sizeof(cond) ; } while (0)
#  define COUNT_IF_HOT(cond, ...) DISGUISE(cond)
#endif

/* turn BUG_ON_STRESS() into a real statement when DEBUG_STRESS is set,
 * otherwise simply ignore it, at the risk of failing to notice if the
 * condition would build at all. We don't really care if BUG_ON_STRESS
 * doesn't always build, because it's meant to be used only in certain
 * scenarios, possibly requiring certain combinations of options. We
 * just want to be certain that the condition is not implemented at all
 * when not used, so as to encourage developers to put a lot of them at
 * zero cost.
 */
#if DEBUG_STRESS > 0
# define BUG_ON_STRESS(cond, ...)  BUG_ON(cond, __VA_ARGS__)
#else
# define BUG_ON_STRESS(cond, ...)  do { } while (0)
#endif

/* BUG_ON_STATIC(cond): fail the build if <cond> is true. */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#define BUG_ON_STATIC(cond) \
	_Static_assert(!(cond), "bug condition \"" #cond "\" matched")
#else
#define BUG_ON_STATIC(cond) \
	extern char CONCAT(bug_on_static_, __LINE__)[(cond) ? -1 : 1] __maybe_unused
#endif

#if defined(DEBUG_MEM_STATS)
#include <stdlib.h>
#include <string.h>

#undef calloc
#define calloc(x,y)  ({							\
	size_t __x = (x); size_t __y = (y);				\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_CALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__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. This one may be used before a call to free(),
 * and when known, the size should be indicated, otherwise pass zero. The
 * pointer is used to know whether the call should be accounted for (null is
 * ignored).
 */
#undef will_free
#define will_free(x, y)  ({						\
	void *__x = (x); size_t __y = (y);				\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_FREE,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	if (__x) {							\
		_HA_ATOMIC_INC(&_.calls);				\
		_HA_ATOMIC_ADD(&_.size, __y);				\
	}								\
})

#undef ha_free
#define ha_free(x)  ({							\
	typeof(x) __x = (x);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_FREE,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__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"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_MALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__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"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_REALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__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"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_STRDUP,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __y);					\
	strdup(__x);							\
})

#undef ha_aligned_alloc
#define ha_aligned_alloc(a,s)  ({					\
	size_t __a = (a);						\
	size_t __s = (s);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_MALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	_ha_aligned_alloc(__a, __s);					\
})

#undef ha_aligned_zalloc
#define ha_aligned_zalloc(a,s)  ({					\
	size_t __a = (a);						\
	size_t __s = (s);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_CALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	_ha_aligned_zalloc(__a, __s);					\
})

#undef ha_aligned_alloc_safe
#define ha_aligned_alloc_safe(a,s)  ({					\
	size_t __a = (a);						\
	size_t __s = (s);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_MALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	_ha_aligned_alloc_safe(__a, __s);				\
})

#undef ha_aligned_zalloc_safe
#define ha_aligned_zalloc_safe(a,s)  ({					\
	size_t __a = (a);						\
	size_t __s = (s);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_CALLOC,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	_ha_aligned_zalloc_safe(__a, __s);				\
})

// Since the type is known, the .extra field will contain its name
#undef ha_aligned_alloc_typed
#define ha_aligned_alloc_typed(cnt,type)  ({				\
	size_t __a = __alignof__(type);					\
	size_t __s = ((size_t)cnt) * sizeof(type);			\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_MALLOC,			\
			.func = __func__,				\
		},							\
		.extra = #type,						\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	(type*)_ha_aligned_alloc(__a, __s);				\
})

// Since the type is known, the .extra field will contain its name
#undef ha_aligned_zalloc_typed
#define ha_aligned_zalloc_typed(cnt,type)  ({				\
	size_t __a = __alignof__(type);					\
	size_t __s = ((size_t)cnt) * sizeof(type);			\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_CALLOC,			\
			.func = __func__,				\
		},							\
		.extra = #type,						\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	_HA_ATOMIC_INC(&_.calls);					\
	_HA_ATOMIC_ADD(&_.size, __s);					\
	(type*)_ha_aligned_zalloc_safe(__a, __s);			\
})

#undef ha_aligned_free
#define ha_aligned_free(x)  ({							\
	typeof(x) __x = (x);						\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_FREE,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	if (__builtin_constant_p((x))) {  \
		HA_LINK_ERROR(call_to_ha_aligned_free_attempts_to_free_a_constant); \
	}								\
	if (__x)							\
		_HA_ATOMIC_INC(&_.calls);				\
	_ha_aligned_free(__x);						\
})

#undef ha_aligned_free_size
#define ha_aligned_free_size(p,s)  ({					\
	void *__p = (p); size_t __s = (s);				\
	static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \
		.caller = {						\
			.file = __FILE__, .line = __LINE__,		\
			.what = MEM_STATS_TYPE_FREE,			\
			.func = __func__,				\
		},							\
	};								\
	HA_WEAK(__start_mem_stats);					\
	HA_WEAK(__stop_mem_stats);					\
	if (__builtin_constant_p((p))) {  \
		HA_LINK_ERROR(call_to_ha_aligned_free_attempts_to_free_a_constant); \
	}								\
	if (__p) {							\
		_HA_ATOMIC_INC(&_.calls);				\
		_HA_ATOMIC_ADD(&_.size, __s);				\
	}								\
	_ha_aligned_free(__p);						\
})

#else // DEBUG_MEM_STATS

#define will_free(x, y) do { } while (0)
#define ha_aligned_alloc(a,s) _ha_aligned_alloc(a, s)
#define ha_aligned_zalloc(a,s) _ha_aligned_zalloc(a, s)
#define ha_aligned_alloc_safe(a,s) _ha_aligned_alloc_safe(a, s)
#define ha_aligned_zalloc_safe(a,s) _ha_aligned_zalloc_safe(a, s)
#define ha_aligned_alloc_typed(cnt,type) ((type*)_ha_aligned_alloc(__alignof__(type), ((size_t)cnt) * sizeof(type)))
#define ha_aligned_zalloc_typed(cnt,type) ((type*)_ha_aligned_zalloc(__alignof__(type), ((size_t)cnt) * sizeof(type)))
#define ha_aligned_free(p) _ha_aligned_free(p)
#define ha_aligned_free_size(p,s) _ha_aligned_free(p)

#endif /* DEBUG_MEM_STATS*/

/* Add warnings to users of such functions. These will be reported at link time
 * indicating what file name and line used them. The goal is to remind their
 * users that these are extremely unsafe functions that never have a valid
 * reason for being used.
 */
#undef strcat
__attribute__warning("\n"
"  * WARNING! strcat() must never be used, because there is no convenient way\n"
"  *          to use it that is safe. Use memcpy() instead!\n")
extern char *strcat(char *__restrict dest, const char *__restrict src);

#undef strcpy
__attribute__warning("\n"
"  * WARNING! strcpy() must never be used, because there is no convenient way\n"
"  *          to use it that is safe. Use memcpy() or strlcpy2() instead!\n")
extern char *strcpy(char *__restrict dest, const char *__restrict src);

#undef strncat
__attribute__warning("\n"
"  * WARNING! strncat() must never be used, because there is no convenient way\n"
"  *          to use it that is safe. Use memcpy() instead!\n")
extern char *strncat(char *__restrict dest, const char *__restrict src, size_t n);

#undef sprintf
__attribute__warning("\n"
"  * WARNING! sprintf() must never be used, because there is no convenient way\n"
"  *          to use it that is safe. Use snprintf() instead!\n")
extern int sprintf(char *__restrict dest, const char *__restrict fmt, ...);

#if defined(_VA_LIST_DEFINED) || defined(_VA_LIST_DECLARED) || defined(_VA_LIST)
#undef vsprintf
__attribute__warning("\n"
"  * WARNING! vsprintf() must never be used, because there is no convenient way\n"
"  *          to use it that is safe. Use vsnprintf() instead!\n")
extern int vsprintf(char *__restrict dest, const char *__restrict fmt, va_list ap);
#endif

#endif /* _HAPROXY_BUG_H */

/*
 * Local variables:
 *  c-indent-level: 8
 *  c-basic-offset: 8
 * End:
 */
