mirror of
https://github.com/haproxy/haproxy.git
synced 2026-02-03 20:39:41 -05:00
This makes the code more readable and less prone to copy-paste errors.
In addition, it allows to place some __builtin_constant_p() predicates
to trigger a link-time error in case the compiler knows that the freed
area is constant. It will also produce compile-time error if trying to
free something that is not a regular pointer (e.g. a function).
The DEBUG_MEM_STATS macro now also defines an instance for ha_free()
so that all these calls can be checked.
178 occurrences were converted. The vast majority of them were handled
by the following Coccinelle script, some slightly refined to better deal
with "&*x" or with long lines:
@ rule @
expression E;
@@
- free(E);
- E = NULL;
+ ha_free(&E);
It was verified that the resulting code is the same, more or less a
handful of cases where the compiler optimized slightly differently
the temporary variable that holds the copy of the pointer.
A non-negligible amount of {free(str);str=NULL;str_len=0;} are still
present in the config part (mostly header names in proxies). These
ones should also be cleaned for the same reasons, and probably be
turned into ist strings.
213 lines
6.8 KiB
C
213 lines
6.8 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() 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
|
|
|
|
/* 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))) { \
|
|
/* provoke a build-time error */ \
|
|
extern volatile int call_to_ha_free_attempts_to_free_a_constant; \
|
|
call_to_ha_free_attempts_to_free_a_constant = 1; \
|
|
} \
|
|
free(*__x); \
|
|
*__x = NULL; \
|
|
} while (0)
|
|
|
|
#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 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))) { \
|
|
/* provoke a build-time error */ \
|
|
extern volatile int call_to_ha_free_attempts_to_free_a_constant; \
|
|
call_to_ha_free_attempts_to_free_a_constant = 1; \
|
|
} \
|
|
if (*__x) \
|
|
_HA_ATOMIC_ADD(&_.calls, 1); \
|
|
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_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:
|
|
*/
|