You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
5.2 KiB
269 lines
5.2 KiB
/* |
|
* 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; |
|
|
|
/** |
|
* set_fail_alloc_counter: |
|
* @until_next_fail: number of successful allocs before one fails |
|
* |
|
* 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. |
|
* Set to _TEST_INT_MAX to not fail anything. |
|
*/ |
|
static void |
|
set_fail_alloc_counter (int until_next_fail) |
|
{ |
|
fail_alloc_counter = until_next_fail; |
|
} |
|
|
|
/** |
|
* get_fail_alloc_counter: |
|
* |
|
* Returns the number of successful allocs until we'll simulate |
|
* a failed alloc. |
|
*/ |
|
static int |
|
get_fail_alloc_counter (void) |
|
{ |
|
return fail_alloc_counter; |
|
} |
|
|
|
/** |
|
* set_fail_alloc_failures: |
|
* @failures_per_failure: number to fail |
|
* |
|
* 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; |
|
} |
|
|
|
/** |
|
* decrement_fail_alloc_counter: |
|
* |
|
* 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. |
|
* |
|
* returns #TRUE if this alloc should fail |
|
*/ |
|
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; |
|
} |
|
|
|
return TRUE; |
|
} |
|
else |
|
{ |
|
fail_alloc_counter -= 1; |
|
return FALSE; |
|
} |
|
} |
|
|
|
/** |
|
* test_get_malloc_blocks_outstanding: |
|
* |
|
* Get the number of outstanding malloc()'d blocks. |
|
* |
|
* Returns number of blocks |
|
*/ |
|
int |
|
test_get_malloc_blocks_outstanding (void) |
|
{ |
|
return n_blocks_outstanding; |
|
} |
|
|
|
void* |
|
test_malloc (size_t bytes) |
|
{ |
|
if (decrement_fail_alloc_counter ()) |
|
{ |
|
/* FAIL the malloc */ |
|
return NULL; |
|
} |
|
|
|
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; |
|
} |
|
|
|
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; |
|
|
|
if (str == NULL) |
|
return NULL; |
|
|
|
len = strlen (str); |
|
|
|
copy = test_malloc (len + 1); |
|
if (copy == NULL) |
|
return NULL; |
|
|
|
memcpy (copy, str, len + 1); |
|
|
|
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 */ |
|
|
|
while (n_mallocs >= 0) |
|
{ |
|
set_fail_alloc_counter (n_mallocs); |
|
|
|
if (!(* func) (data)) |
|
return FALSE; |
|
|
|
n_mallocs -= 1; |
|
} |
|
|
|
set_fail_alloc_counter (_TEST_INT_MAX); |
|
|
|
return TRUE; |
|
} |
|
|
|
/** |
|
* test_oom_handling: |
|
* @func: function to call |
|
* @data: data to pass to function |
|
* |
|
* 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. |
|
* |
|
* Returns #TRUE if the function never returns FALSE |
|
*/ |
|
int |
|
test_oom_handling (TestMemoryFunction func, |
|
void *data) |
|
{ |
|
int approx_mallocs; |
|
|
|
/* Run once to see about how many mallocs are involved */ |
|
|
|
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; |
|
|
|
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); |
|
|
|
return TRUE; |
|
}
|
|
|