Daniel Veillard | a76fe5c | 2003-04-24 16:06:47 +0000 | [diff] [blame] | 1 | /* |
| 2 | * testOOM.c: Test out-of-memory handling |
| 3 | * |
| 4 | * See Copyright for the status of this software. |
| 5 | * |
| 6 | * Copyright 2003 Red Hat, Inc. |
| 7 | * Written by: hp@redhat.com |
| 8 | */ |
| 9 | |
| 10 | #include "testOOMlib.h" |
| 11 | |
| 12 | #ifdef HAVE_STDLIB_H |
| 13 | #include <stdlib.h> |
| 14 | #endif |
| 15 | |
| 16 | #include <string.h> |
| 17 | |
| 18 | #define _TEST_INT_MAX 2147483647 |
| 19 | #ifndef TRUE |
| 20 | #define TRUE (1) |
| 21 | #endif |
| 22 | #ifndef FALSE |
| 23 | #define FALSE (0) |
| 24 | #endif |
| 25 | #ifndef NULL |
| 26 | #define NULL ((void*)0) |
| 27 | #endif |
| 28 | |
| 29 | #include <libxml/xmlmemory.h> |
| 30 | |
| 31 | static int fail_alloc_counter = _TEST_INT_MAX; |
| 32 | static int n_failures_per_failure = 1; |
| 33 | static int n_failures_this_failure = 0; |
| 34 | static int n_blocks_outstanding = 0; |
| 35 | |
| 36 | /** |
| 37 | * Sets the number of allocations until we simulate a failed |
| 38 | * allocation. If set to 0, the next allocation to run |
| 39 | * fails; if set to 1, one succeeds then the next fails; etc. |
| 40 | * Set to _TEST_INT_MAX to not fail anything. |
| 41 | * |
| 42 | * @param until_next_fail number of successful allocs before one fails |
| 43 | */ |
| 44 | static void |
| 45 | set_fail_alloc_counter (int until_next_fail) |
| 46 | { |
| 47 | fail_alloc_counter = until_next_fail; |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Gets the number of successful allocs until we'll simulate |
| 52 | * a failed alloc. |
| 53 | * |
| 54 | * @returns current counter value |
| 55 | */ |
| 56 | static int |
| 57 | get_fail_alloc_counter (void) |
| 58 | { |
| 59 | return fail_alloc_counter; |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Sets how many mallocs to fail when the fail alloc counter reaches |
| 64 | * 0. |
| 65 | * |
| 66 | * @param number to fail |
| 67 | */ |
| 68 | static void |
| 69 | set_fail_alloc_failures (int failures_per_failure) |
| 70 | { |
| 71 | n_failures_per_failure = failures_per_failure; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Called when about to alloc some memory; if |
| 76 | * it returns #TRUE, then the allocation should |
| 77 | * fail. If it returns #FALSE, then the allocation |
| 78 | * should not fail. |
| 79 | * |
| 80 | * @returns #TRUE if this alloc should fail |
| 81 | */ |
| 82 | static int |
| 83 | decrement_fail_alloc_counter (void) |
| 84 | { |
| 85 | if (fail_alloc_counter <= 0) |
| 86 | { |
| 87 | n_failures_this_failure += 1; |
| 88 | if (n_failures_this_failure >= n_failures_per_failure) |
| 89 | { |
| 90 | fail_alloc_counter = _TEST_INT_MAX; |
| 91 | |
| 92 | n_failures_this_failure = 0; |
| 93 | } |
| 94 | |
| 95 | return TRUE; |
| 96 | } |
| 97 | else |
| 98 | { |
| 99 | fail_alloc_counter -= 1; |
| 100 | return FALSE; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Get the number of outstanding malloc()'d blocks. |
| 106 | * |
| 107 | * @returns number of blocks |
| 108 | */ |
| 109 | int |
| 110 | test_get_malloc_blocks_outstanding (void) |
| 111 | { |
| 112 | return n_blocks_outstanding; |
| 113 | } |
| 114 | |
| 115 | void* |
| 116 | test_malloc (size_t bytes) |
| 117 | { |
| 118 | if (decrement_fail_alloc_counter ()) |
| 119 | { |
| 120 | /* FAIL the malloc */ |
| 121 | return NULL; |
| 122 | } |
| 123 | |
| 124 | if (bytes == 0) /* some system mallocs handle this, some don't */ |
| 125 | return NULL; |
| 126 | else |
| 127 | { |
| 128 | void *mem; |
| 129 | mem = xmlMemMalloc (bytes); |
| 130 | |
| 131 | if (mem) |
| 132 | n_blocks_outstanding += 1; |
| 133 | |
| 134 | return mem; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | void* |
| 139 | test_realloc (void *memory, |
| 140 | size_t bytes) |
| 141 | { |
| 142 | if (decrement_fail_alloc_counter ()) |
| 143 | { |
| 144 | /* FAIL */ |
| 145 | return NULL; |
| 146 | } |
| 147 | |
| 148 | if (bytes == 0) /* guarantee this is safe */ |
| 149 | { |
| 150 | test_free (memory); |
| 151 | return NULL; |
| 152 | } |
| 153 | else |
| 154 | { |
| 155 | void *mem; |
| 156 | mem = xmlMemRealloc (memory, bytes); |
| 157 | |
| 158 | if (memory == NULL && mem != NULL) |
| 159 | n_blocks_outstanding += 1; |
| 160 | |
| 161 | return mem; |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | void |
| 166 | test_free (void *memory) |
| 167 | { |
| 168 | if (memory) /* we guarantee it's safe to free (NULL) */ |
| 169 | { |
| 170 | n_blocks_outstanding -= 1; |
| 171 | |
| 172 | xmlMemFree (memory); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | char* |
| 177 | test_strdup (const char *str) |
| 178 | { |
| 179 | int len; |
| 180 | char *copy; |
| 181 | |
| 182 | if (str == NULL) |
| 183 | return NULL; |
| 184 | |
| 185 | len = strlen (str); |
| 186 | |
| 187 | copy = test_malloc (len + 1); |
| 188 | if (copy == NULL) |
| 189 | return NULL; |
| 190 | |
| 191 | memcpy (copy, str, len + 1); |
| 192 | |
| 193 | return copy; |
| 194 | } |
| 195 | |
| 196 | static int |
| 197 | run_failing_each_malloc (int n_mallocs, |
| 198 | TestMemoryFunction func, |
| 199 | void *data) |
| 200 | { |
| 201 | n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */ |
| 202 | |
| 203 | while (n_mallocs >= 0) |
| 204 | { |
| 205 | set_fail_alloc_counter (n_mallocs); |
| 206 | |
| 207 | if (!(* func) (data)) |
| 208 | return FALSE; |
| 209 | |
| 210 | n_mallocs -= 1; |
| 211 | } |
| 212 | |
| 213 | set_fail_alloc_counter (_TEST_INT_MAX); |
| 214 | |
| 215 | return TRUE; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Tests how well the given function responds to out-of-memory |
| 220 | * situations. Calls the function repeatedly, failing a different |
| 221 | * call to malloc() each time. If the function ever returns #FALSE, |
| 222 | * the test fails. The function should return #TRUE whenever something |
| 223 | * valid (such as returning an error, or succeeding) occurs, and #FALSE |
| 224 | * if it gets confused in some way. |
| 225 | * |
| 226 | * @param func function to call |
| 227 | * @param data data to pass to function |
| 228 | * @returns #TRUE if the function never returns FALSE |
| 229 | */ |
| 230 | int |
| 231 | test_oom_handling (TestMemoryFunction func, |
| 232 | void *data) |
| 233 | { |
| 234 | int approx_mallocs; |
| 235 | |
| 236 | /* Run once to see about how many mallocs are involved */ |
| 237 | |
| 238 | set_fail_alloc_counter (_TEST_INT_MAX); |
| 239 | |
| 240 | if (!(* func) (data)) |
| 241 | return FALSE; |
| 242 | |
| 243 | approx_mallocs = _TEST_INT_MAX - get_fail_alloc_counter (); |
| 244 | |
| 245 | set_fail_alloc_failures (1); |
| 246 | if (!run_failing_each_malloc (approx_mallocs, func, data)) |
| 247 | return FALSE; |
| 248 | |
| 249 | set_fail_alloc_failures (2); |
| 250 | if (!run_failing_each_malloc (approx_mallocs, func, data)) |
| 251 | return FALSE; |
| 252 | |
| 253 | set_fail_alloc_failures (3); |
| 254 | if (!run_failing_each_malloc (approx_mallocs, func, data)) |
| 255 | return FALSE; |
| 256 | |
| 257 | set_fail_alloc_counter (_TEST_INT_MAX); |
| 258 | |
| 259 | return TRUE; |
| 260 | } |