blob: be904b32f97fd1461635334f86679db4a60b4fde [file] [log] [blame]
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001/*
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
31static int fail_alloc_counter = _TEST_INT_MAX;
32static int n_failures_per_failure = 1;
33static int n_failures_this_failure = 0;
34static 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 */
44static void
45set_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 */
56static int
57get_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 */
68static void
69set_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 */
82static int
83decrement_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 */
109int
110test_get_malloc_blocks_outstanding (void)
111{
112 return n_blocks_outstanding;
113}
114
115void*
116test_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
138void*
139test_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
165void
166test_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
176char*
177test_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
196static int
197run_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 */
230int
231test_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}