/*
 * 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/compiler.h>

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

#ifdef DEBUG_USE_ABORT
/* abort() is better recognized by code analysis tools */
#define ABORT_NOW() do { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); 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 { extern void ha_backtrace_to_stderr(); ha_backtrace_to_stderr(); (*(volatile int*)1=0); } while (0)
#endif

/* 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) || defined(DEBUG_STRICT_NOCRASH)
#if defined(DEBUG_STRICT)
#define CRASH_NOW() ABORT_NOW()
#else
#define CRASH_NOW() do { ha_backtrace_to_stderr(); } while (0)
#endif

#define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__)
#define _BUG_ON(cond, file, line) __BUG_ON(cond, file, line)
#define __BUG_ON(cond, file, line)                                             \
	do {                                                                   \
		if (unlikely(cond)) {					       \
			const char msg[] = "\nFATAL: bug condition \"" #cond "\" matched at " file ":" #line "\n"; \
			DISGUISE(write(2, msg, __builtin_strlen(msg)));        \
			CRASH_NOW();                                           \
		}                                                              \
	} while (0)
#else
#undef CRASH_NOW
#define BUG_ON(cond)
#endif


#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_ADD(&_.calls, 1);					\
	_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_ADD(&_.calls, 1);				\
	free(__x);							\
})

#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_ADD(&_.calls, 1);					\
	_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_ADD(&_.calls, 1);					\
	_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_ADD(&_.calls, 1);					\
	_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:
 */
