haproxy/include/haproxy/bug.h
Willy Tarreau 9dd7f4fb4b MINOR: debug: don't count free(NULL) in memstats
The mem stats are pretty convenient to spot leaks, except that they count
free(NULL) as 1, and the code does actually have quite a number of free(foo)
guards where foo is NULL if the object was already freed. Let's just not
count these ones so that the stats remain consistent. Now it's possible
to compare the strdup()/malloc() and free() and verify they are consistent.
2020-11-03 16:46:48 +01:00

183 lines
5.5 KiB
C

/*
* 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() abort()
#else
/* More efficient than abort() because it does not mangle the
* stack and stops at the exact location we need.
*/
#define ABORT_NOW() (*(volatile int*)1=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()
#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:
*/