The question of whether it is necessary to check what returns malloc
is controversial and always generates heated debate.
Some people think that we should try to handle all kinds of runtime errors, incl. and OOM situations. Others believe that there is still little that can be done with OOM, and it is better to let the application just crash. On the side of the second group of people, there is also the fact that the additional OOM processing logic is difficult to test. And if the code is not tested, then it almost certainly does not work.
I totally agree that you shouldn't implement error handling logic that you are not going to test. It almost certainly won't improve anything, or even worse, it will ruin everything.
The question of whether or not to try to handle OOM situations in libraries / applications is controversial and we will not touch on it here. As part of this publication, I just want to share my experience of how you can test the implemented logic for handling OOM situations in applications written in C / C ++. The conversation will be about Linux and macOS operating systems. For a number of reasons, Windows will be bypassed.
Introduction
We all wish that OOM never happened, but in real life this is not always possible due to the following reasons:
- RAM is always limited.
- SWAP is not always enabled.
- Applications do not always behave adequately and sometimes try to allocate unrealistically large amounts of memory interfering with themselves and others.
- 32-bit applications still exist.
- overcommit is not always enabled.
- Memory consumption can be limited using
ulimit
, for example. -
LD_PRELOAD
.
, , , OOM . , , :
- , - .
- OOM . .
- . , .
, , SQLite. , . . SQLite .
, , , . OOM Killer, , . , C++, .
1.
, OOM . my_malloc
my_free
malloc
free
.
my_free
. my_realloc
.
my_malloc
malloc
. my_malloc
, NULL
.
, :
- 3rd party .
-
malloc
. -strdup
. -
malloc
’ , , . - C++
malloc
free
.
- .
2.
Linux LD_PRELOAD
. . malloc
. , malloc
/realloc
/free
(weak
). , macOS LD_PRELOAD
, DYLD_INSERT_LIBRARIES
.
, , LD_PRELOAD
DYLD_INSERT_LIBRARIES
malloc
/realloc
NULL
.
, "" . , .
, "" , , . :
-
main
. , . - Runtime macOS " ". , , , .
-
printf
macOSSIGSEGV
/SIGBUS
. - ,
std::bad_alloc
, . , , , OOM.std::terminate
. . -
std::thread
std::terminate
macOS.
UPDATE: Travis CI , macOS / Xcode , std::bad_alloc
, std::thread
std::terminate
.
Overthrower. - malloc
NULL
. Overthrower - , .
3.
main
, main
, main
runtime . main
, , .. - main
.
, main
. Overthrower, OOM . Overthrower , .
:
activateOverthrower
deactivateOverthrower
:
#ifdef __cplusplus
extern "C" {
#endif
void activateOverthrower() __attribute__((weak));
unsigned int deactivateOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif
Overthrower LD_PRELOAD
, NULL
, , .
, , :
int main(int argc, char** argv)
{
activateOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
activateOverthrower
/deactivateOverthrower
, :
TEST(Foo, Bar)
{
activateOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
, -, , Overthrower , :
#ifdef __cplusplus
extern "C" {
#endif
void pauseOverthrower(unsigned int duration) __attribute__((weak));
void resumeOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif
:
TEST(Foo, Bar)
{
activateOverthrower();
// Some code we want to test ...
pauseOverthrower(0);
// Some fragile code we can not fix ...
resumeOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
__cxa_allocate_exception
, , , malloc
, NULL
. , Linux, malloc
, __cxa_allocate_exception
(emergency buffer), , . .
macOS , , , , std::bad_alloc
, std::terminate
.
UPDATE: , macOS / Xcode .
, , , __cxa_allocate_exception
malloc
. - Overthrower’ malloc
. Overthrower malloc
__cxa_allocate_exception
.
, , , macOS __cxa_atexit
, Linux dlerror
. .
Overthrower , malloc
free
. Overthrower’ , activateOverthrower
deactivateOverthrower
, :
overthrower got deactivation signal.
overthrower will not fail allocations anymore.
overthrower has detected not freed memory blocks with following addresses:
0x0000000000dd1e70 - 2 - 128
0x0000000000dd1de0 - 1 - 128
0x0000000000dd1030 - 0 - 128
^^^^^^^^^^^^^^^^^^ | ^^^^^^ | ^^^^^^^^^^
pointer | malloc | block size
|invocation|
| number |
Overthrower , , .
Overthrower’, , valgrind. , OOM. , , Overthrower . Overthrower , , deactivateOverthrower
, stderr
.
Overthrower 3 :
- Random —
rand() % duty_cycle == 0
.duty_cycle
, . - Step — (
malloc_seq_num >= delay
),delay
.
<--- delay --->
--------------+
|
| All further allocations fail
|
+------------------------------
- Pulse — (
malloc_seq_num > delay && malloc_seq_num <= delay + duration
),delay
duration
.
<--- delay --->
--------------+ +------------------------------
| |
| | All further allocations pass
| |
+----------------+
<--- duration --->
:
OVERTHROWER_STRATEGY
OVERTHROWER_SEED
OVERTHROWER_DUTY_CYCLE
OVERTHROWER_DELAY
OVERTHROWER_DURATION
activateOverthrower
. , Overthrower , /dev/urandom
.
- Overthrower
malloc
/. - .
- Overthrower .
- Overthrower .
- Overthrower , .
- Overthrower’ .
- Overthrower Overthrower-aware . , .
- Overthrower itself is tested on Ubuntu (since 14.04) and macOS (since Sierra (10.12) and Xcode 8.3). During testing, Overthrower tries to drop itself, among other things.
- If a real OOM appears in the system, Overthrower does everything possible not to fall himself.