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.
270 lines
5.2 KiB
270 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;
|
|
}
|