2003-04-24 20:06:47 +04:00
/*
* testOOM . c : Test out - of - memory handling
*
* See Copyright for the status of this software .
*
* Copyright 2003 Red Hat , Inc .
* Written by : hp @ redhat . com
*/
# include "testOOMlib.h"
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
# include <string.h>
# define _TEST_INT_MAX 2147483647
# ifndef TRUE
# define TRUE (1)
# endif
# ifndef FALSE
# define FALSE (0)
# endif
# ifndef NULL
# define NULL ((void*)0)
# endif
# include <libxml/xmlmemory.h>
static int fail_alloc_counter = _TEST_INT_MAX ;
static int n_failures_per_failure = 1 ;
static int n_failures_this_failure = 0 ;
static int n_blocks_outstanding = 0 ;
/**
2003-11-16 09:25:42 +03:00
* set_fail_alloc_counter :
* @ until_next_fail : number of successful allocs before one fails
*
2003-04-24 20:06:47 +04:00
* Sets the number of allocations until we simulate a failed
* allocation . If set to 0 , the next allocation to run
* fails ; if set to 1 , one succeeds then the next fails ; etc .
2012-09-11 09:26:36 +04:00
* Set to _TEST_INT_MAX to not fail anything .
2003-04-24 20:06:47 +04:00
*/
static void
set_fail_alloc_counter ( int until_next_fail )
{
fail_alloc_counter = until_next_fail ;
}
/**
2003-11-16 09:25:42 +03:00
* get_fail_alloc_counter :
2003-04-24 20:06:47 +04:00
*
2003-11-16 09:25:42 +03:00
* Returns the number of successful allocs until we ' ll simulate
* a failed alloc .
2003-04-24 20:06:47 +04:00
*/
static int
get_fail_alloc_counter ( void )
{
return fail_alloc_counter ;
}
/**
2003-11-16 09:25:42 +03:00
* set_fail_alloc_failures :
* @ failures_per_failure : number to fail
*
2003-04-24 20:06:47 +04:00
* Sets how many mallocs to fail when the fail alloc counter reaches
* 0.
*
*/
static void
set_fail_alloc_failures ( int failures_per_failure )
{
n_failures_per_failure = failures_per_failure ;
}
/**
2003-11-16 09:25:42 +03:00
* decrement_fail_alloc_counter :
*
2003-04-24 20:06:47 +04:00
* Called when about to alloc some memory ; if
* it returns # TRUE , then the allocation should
* fail . If it returns # FALSE , then the allocation
* should not fail .
*
2003-11-16 09:25:42 +03:00
* returns # TRUE if this alloc should fail
2003-04-24 20:06:47 +04:00
*/
static int
decrement_fail_alloc_counter ( void )
{
if ( fail_alloc_counter < = 0 )
{
n_failures_this_failure + = 1 ;
if ( n_failures_this_failure > = n_failures_per_failure )
{
fail_alloc_counter = _TEST_INT_MAX ;
n_failures_this_failure = 0 ;
}
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
return TRUE ;
}
else
{
fail_alloc_counter - = 1 ;
return FALSE ;
}
}
/**
2003-11-16 09:25:42 +03:00
* test_get_malloc_blocks_outstanding :
*
2003-04-24 20:06:47 +04:00
* Get the number of outstanding malloc ( ) ' d blocks .
*
2003-11-16 09:25:42 +03:00
* Returns number of blocks
2003-04-24 20:06:47 +04:00
*/
int
test_get_malloc_blocks_outstanding ( void )
{
return n_blocks_outstanding ;
}
void *
test_malloc ( size_t bytes )
2012-09-11 09:26:36 +04:00
{
2003-04-24 20:06:47 +04:00
if ( decrement_fail_alloc_counter ( ) )
{
/* FAIL the malloc */
return NULL ;
}
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
if ( bytes = = 0 ) /* some system mallocs handle this, some don't */
return NULL ;
else
{
void * mem ;
mem = xmlMemMalloc ( bytes ) ;
if ( mem )
n_blocks_outstanding + = 1 ;
return mem ;
}
}
void *
test_realloc ( void * memory ,
size_t bytes )
{
if ( decrement_fail_alloc_counter ( ) )
{
/* FAIL */
return NULL ;
}
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
if ( bytes = = 0 ) /* guarantee this is safe */
{
test_free ( memory ) ;
return NULL ;
}
else
{
void * mem ;
mem = xmlMemRealloc ( memory , bytes ) ;
if ( memory = = NULL & & mem ! = NULL )
n_blocks_outstanding + = 1 ;
return mem ;
}
}
void
test_free ( void * memory )
{
if ( memory ) /* we guarantee it's safe to free (NULL) */
{
n_blocks_outstanding - = 1 ;
xmlMemFree ( memory ) ;
}
}
char *
test_strdup ( const char * str )
{
int len ;
char * copy ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
if ( str = = NULL )
return NULL ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
len = strlen ( str ) ;
copy = test_malloc ( len + 1 ) ;
if ( copy = = NULL )
return NULL ;
memcpy ( copy , str , len + 1 ) ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
return copy ;
}
static int
run_failing_each_malloc ( int n_mallocs ,
TestMemoryFunction func ,
void * data )
{
n_mallocs + = 10 ; /* fudge factor to ensure reallocs etc. are covered */
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
while ( n_mallocs > = 0 )
2012-09-11 09:26:36 +04:00
{
2003-04-24 20:06:47 +04:00
set_fail_alloc_counter ( n_mallocs ) ;
if ( ! ( * func ) ( data ) )
return FALSE ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
n_mallocs - = 1 ;
}
set_fail_alloc_counter ( _TEST_INT_MAX ) ;
return TRUE ;
}
/**
2003-11-16 09:25:42 +03:00
* test_oom_handling :
* @ func : function to call
* @ data : data to pass to function
*
2003-04-24 20:06:47 +04:00
* Tests how well the given function responds to out - of - memory
* situations . Calls the function repeatedly , failing a different
* call to malloc ( ) each time . If the function ever returns # FALSE ,
* the test fails . The function should return # TRUE whenever something
* valid ( such as returning an error , or succeeding ) occurs , and # FALSE
* if it gets confused in some way .
*
2003-11-16 09:25:42 +03:00
* Returns # TRUE if the function never returns FALSE
2003-04-24 20:06:47 +04:00
*/
int
test_oom_handling ( TestMemoryFunction func ,
void * data )
{
int approx_mallocs ;
/* Run once to see about how many mallocs are involved */
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
set_fail_alloc_counter ( _TEST_INT_MAX ) ;
if ( ! ( * func ) ( data ) )
return FALSE ;
approx_mallocs = _TEST_INT_MAX - get_fail_alloc_counter ( ) ;
set_fail_alloc_failures ( 1 ) ;
if ( ! run_failing_each_malloc ( approx_mallocs , func , data ) )
return FALSE ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
set_fail_alloc_failures ( 2 ) ;
if ( ! run_failing_each_malloc ( approx_mallocs , func , data ) )
return FALSE ;
set_fail_alloc_failures ( 3 ) ;
if ( ! run_failing_each_malloc ( approx_mallocs , func , data ) )
return FALSE ;
set_fail_alloc_counter ( _TEST_INT_MAX ) ;
2012-09-11 09:26:36 +04:00
2003-04-24 20:06:47 +04:00
return TRUE ;
}