The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
ring_buffer_test.c
Go to the documentation of this file.
1/*
2 * ring_buffer_test.c Tests for ring buffers
3 *
4 * Version: $Id: e9a43a3cb0a734553445d54c0042d5b742f94393 $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * @copyright 2016 Alan DeKok (aland@freeradius.org)
21 */
22
23RCSID("$Id: e9a43a3cb0a734553445d54c0042d5b742f94393 $")
24
25#include <freeradius-devel/io/ring_buffer.h>
26#include <freeradius-devel/util/debug.h>
27#include <freeradius-devel/util/hash.h>
28#include <freeradius-devel/util/syserror.h>
29
30#ifdef HAVE_GETOPT_H
31# include <getopt.h>
32#endif
33
34#define ALLOC_SIZE (8)
35#define ARRAY_SIZE (4 * ALLOC_SIZE)
36
37static size_t used = 0;
38static size_t array[ARRAY_SIZE];
40
41static int debug_lvl = 0;
42
43static char const *seed_string = "foo";
44static size_t seed_string_len = 3;
45
46/**********************************************************************/
47
48static void alloc_blocks(fr_ring_buffer_t *rb, uint32_t *seed, UNUSED int *start, int *end)
49{
50 int i;
52
53 for (i = 0; i < ALLOC_SIZE; i++) {
54 int index;
55 uint8_t *p;
56
57 index = (*end + i) & (ARRAY_SIZE - 1);
58
60 *seed = hash;
61
62 hash &= 0x3ff;
63 hash += 16; /* can't have it zero... */
64
65 array[index] = hash;
66 p = fr_ring_buffer_reserve(rb, 2048);
67
68 if (!fr_cond_assert(p != NULL)) fr_exit_now(EXIT_FAILURE);
69
70 data[index] = fr_ring_buffer_alloc(rb, hash);
71 if (!fr_cond_assert(data[index] == p)) fr_exit_now(EXIT_FAILURE);
72
73 if (debug_lvl > 1) printf("%08x\t", hash);
74
75 used += hash;
77 }
78
79 *end += ALLOC_SIZE;
80}
81
82static void free_blocks(fr_ring_buffer_t *rb, UNUSED uint32_t *seed, int *start, int *end)
83{
84 int i;
85
86 for (i = 0; i < ALLOC_SIZE; i++) {
87 int index;
88 int rcode;
89
90 index = (*start + i) & (ARRAY_SIZE - 1);
91
92 rcode = fr_ring_buffer_free(rb, array[index]);
93 if (!fr_cond_assert(rcode == 0)) fr_exit_now(EXIT_FAILURE);
94
95 used -= array[index];
97
98 array[index] = 0;
99 data[index] = NULL;
100 }
101
102 *start += ALLOC_SIZE;
103 if (*start >= ARRAY_SIZE) {
104 *start -= ARRAY_SIZE;
105 *end -= ARRAY_SIZE;
106 }
107}
108
109/*
110 * @todo - mover to acutest framework.
111 */
112static void verify_start(fr_ring_buffer_t *rb, int start_idx)
113{
114 uint8_t *p_start;
115 size_t p_size;
116 int idx = start_idx & (ARRAY_SIZE - 1);
117
118 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
119
120 if (used == 0) {
121 if (!fr_cond_assert(p_size == 0)) fr_exit_now(EXIT_FAILURE);
122 return;
123 }
124
125 /*
126 * The contiguous block at the start can never exceed
127 * the total used.
128 */
129 if (!fr_cond_assert(p_size > 0)) fr_exit_now(EXIT_FAILURE);
130 if (!fr_cond_assert(p_size <= fr_ring_buffer_used(rb))) fr_exit_now(EXIT_FAILURE);
131
132 /*
133 * The start pointer must match the first un-freed
134 * block's data pointer.
135 */
136 if (data[idx] && !fr_cond_assert(p_start == data[idx])) fr_exit_now(EXIT_FAILURE);
137}
138
139static void test_start_basic(TALLOC_CTX *ctx)
140{
142 uint8_t *p, *p2, *p_start;
143 size_t p_size;
144
145 rb = fr_ring_buffer_create(ctx, 1024);
146 if (!fr_cond_assert(rb != NULL)) fr_exit_now(EXIT_FAILURE);
147
148 /*
149 * Empty buffer: size must be 0.
150 */
151 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
152 if (!fr_cond_assert(p_size == 0)) fr_exit_now(EXIT_FAILURE);
153
154 /*
155 * Single allocation: start points to it, size matches.
156 */
157 p = fr_ring_buffer_reserve(rb, 100);
158 if (!fr_cond_assert(p != NULL)) fr_exit_now(EXIT_FAILURE);
159 p = fr_ring_buffer_alloc(rb, 100);
160 if (!fr_cond_assert(p != NULL)) fr_exit_now(EXIT_FAILURE);
161
162 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
163 if (!fr_cond_assert(p_start == p)) fr_exit_now(EXIT_FAILURE);
164 if (!fr_cond_assert(p_size == 100)) fr_exit_now(EXIT_FAILURE);
165
166 /*
167 * Second allocation: start still points to first block,
168 * size covers both contiguous blocks.
169 */
170 p2 = fr_ring_buffer_reserve(rb, 50);
171 if (!fr_cond_assert(p2 != NULL)) fr_exit_now(EXIT_FAILURE);
172 p2 = fr_ring_buffer_alloc(rb, 50);
173 if (!fr_cond_assert(p2 != NULL)) fr_exit_now(EXIT_FAILURE);
174
175 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
176 if (!fr_cond_assert(p_start == p)) fr_exit_now(EXIT_FAILURE);
177 if (!fr_cond_assert(p_size == 150)) fr_exit_now(EXIT_FAILURE);
178
179 /*
180 * Free the first block: start advances to second block.
181 */
182 if (!fr_cond_assert(fr_ring_buffer_free(rb, 100) == 0)) fr_exit_now(EXIT_FAILURE);
183
184 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
185 if (!fr_cond_assert(p_start == p2)) fr_exit_now(EXIT_FAILURE);
186 if (!fr_cond_assert(p_size == 50)) fr_exit_now(EXIT_FAILURE);
187
188 /*
189 * Free everything: size must return to 0.
190 */
191 if (!fr_cond_assert(fr_ring_buffer_free(rb, 50) == 0)) fr_exit_now(EXIT_FAILURE);
192
193 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
194 if (!fr_cond_assert(p_size == 0)) fr_exit_now(EXIT_FAILURE);
195
196 talloc_free(rb);
197
198 if (debug_lvl) printf("test_start_basic: OK\n");
199}
200
201static void test_start_wrapped(TALLOC_CTX *ctx)
202{
204 uint8_t *first, *wrapped, *p_start;
205 size_t p_size, rb_size;
206 size_t tail_size, total_used;
207
208 rb = fr_ring_buffer_create(ctx, 1024);
209 if (!fr_cond_assert(rb != NULL)) fr_exit_now(EXIT_FAILURE);
210
211 rb_size = fr_ring_buffer_size(rb);
212
213 /*
214 * Allocate most of the buffer, leaving a small gap
215 * at the end.
216 */
217 first = fr_ring_buffer_reserve(rb, rb_size - 64);
218 if (!fr_cond_assert(first != NULL)) fr_exit_now(EXIT_FAILURE);
219 first = fr_ring_buffer_alloc(rb, rb_size - 64);
220 if (!fr_cond_assert(first != NULL)) fr_exit_now(EXIT_FAILURE);
221
222 /*
223 * State: |S*************WE.......|
224 * data_start=0, data_end=rb_size-64, write_offset=rb_size-64
225 */
226
227 /*
228 * Free the first half, advancing data_start.
229 */
230 if (!fr_cond_assert(fr_ring_buffer_free(rb, (rb_size - 64) / 2) == 0)) fr_exit_now(EXIT_FAILURE);
231
232 /*
233 * State: |.....S********WE.......|
234 * data_start=(rb_size-64)/2, data_end=rb_size-64, write_offset=rb_size-64
235 */
236
237 /*
238 * Allocate 128 bytes. This is larger than the 64-byte gap at the end, so it wraps to the start
239 * of the buffer.
240 */
241 wrapped = fr_ring_buffer_reserve(rb, 128);
242 if (!fr_cond_assert(wrapped != NULL)) fr_exit_now(EXIT_FAILURE);
243 wrapped = fr_ring_buffer_alloc(rb, 128);
244 if (!fr_cond_assert(wrapped != NULL)) fr_exit_now(EXIT_FAILURE);
245
246 /*
247 * State: |***W.....S****E........|
248 * write_offset=128, data_start=(rb_size-64)/2, data_end=rb_size-64
249 * Buffer is wrapped.
250 */
251 tail_size = (rb_size - 64) - (rb_size - 64) / 2;
252 total_used = tail_size + 128;
253
254 if (!fr_cond_assert(fr_ring_buffer_used(rb) == total_used)) fr_exit_now(EXIT_FAILURE);
255
256 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
257
258 /*
259 * Start points to the tail block (from data_start to data_end), NOT the wrapped block at offset
260 * 0.
261 */
262 if (!fr_cond_assert(p_start == first + (rb_size - 64) / 2)) fr_exit_now(EXIT_FAILURE);
263 if (!fr_cond_assert(p_size == tail_size)) fr_exit_now(EXIT_FAILURE);
264
265 /*
266 * The contiguous block at start is strictly less than total_used when wrapped.
267 */
268 if (!fr_cond_assert(p_size < total_used)) fr_exit_now(EXIT_FAILURE);
269
270 /*
271 * Free the tail block. The buffer unwraps:
272 * data_start=0, data_end=128, write_offset=128
273 *
274 * Now start should point to the wrapped block at offset 0.
275 */
276 tail_size = (rb_size - 64) - (rb_size - 64) / 2;
277 if (!fr_cond_assert(fr_ring_buffer_free(rb, tail_size) == 0)) fr_exit_now(EXIT_FAILURE);
278
279 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
280 if (!fr_cond_assert(p_start == wrapped)) fr_exit_now(EXIT_FAILURE);
281 if (!fr_cond_assert(p_size == 128)) fr_exit_now(EXIT_FAILURE);
282
283 /*
284 * Free the last block: empty.
285 */
286 if (!fr_cond_assert(fr_ring_buffer_free(rb, 128) == 0)) fr_exit_now(EXIT_FAILURE);
287
288 if (!fr_cond_assert(fr_ring_buffer_start(rb, &p_start, &p_size) == 0)) fr_exit_now(EXIT_FAILURE);
289 if (!fr_cond_assert(p_size == 0)) fr_exit_now(EXIT_FAILURE);
290
291 talloc_free(rb);
292
293 if (debug_lvl) printf("test_start_wrapped: OK\n");
294}
295
296static NEVER_RETURNS void usage(void)
297{
298 fprintf(stderr, "usage: ring_buffer_test [OPTS]\n");
299 fprintf(stderr, " -x Debugging mode.\n");
300 fprintf(stderr, " -s <string> Set random seed to <string>.\n");
301 fprintf(stderr, " -l <length> Set the iteration number to <length>.\n");
302
303 fr_exit_now(EXIT_SUCCESS);
304}
305
306int main(int argc, char *argv[])
307{
308 int c;
309
310 int i, start, end, length = 1000;
312 uint32_t seed;
313
314 TALLOC_CTX *autofree = talloc_autofree_context();
315
316 while ((c = getopt(argc, argv, "hl:s:x")) != -1) switch (c) {
317 case 'l':
318 length = strtol(optarg, NULL, 10);
319 break;
320 case 's':
321 seed_string = optarg;
322 seed_string_len = strlen(optarg);
323 break;
324
325 case 'x':
326 debug_lvl++;
327 break;
328
329 case 'h':
330 default:
331 usage();
332 }
333#if 0
334 argc -= (optind - 1);
335 argv += (optind - 1);
336#endif
337
338 /*
339 * Run targeted fr_ring_buffer_start() tests first.
340 */
343
345 if (!rb) {
346 fprintf(stderr, "Failed creating ring buffer\n");
347 fr_exit_now(EXIT_FAILURE);
348 }
349
350 seed = 0xabcdef;
351 start = 0;
352 end = 0;
353
354 /*
355 * Allocate the first set of blocks.
356 */
357 alloc_blocks(rb, &seed, &start, &end);
358 verify_start(rb, start);
359
360 /*
361 * Do 1000 rounds of alloc / free.
362 */
363 for (i = 0; i < length; i++) {
364 if (debug_lvl) printf("Loop %d (used %zu) \n", i, used);
365 alloc_blocks(rb, &seed, &start, &end);
366 verify_start(rb, start);
367
368 free_blocks(rb, &seed, &start, &end);
369 verify_start(rb, start);
370 }
371
372 free_blocks(rb, &seed, &start, &end);
373 verify_start(rb, start);
374
375 fr_assert(used == 0);
377
378 fr_exit_now(EXIT_SUCCESS);
379}
static TALLOC_CTX * autofree
Definition fuzzer.c:44
#define RCSID(id)
Definition build.h:506
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:334
#define UNUSED
Definition build.h:336
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:236
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
Definition hash.c:881
talloc_free(hp)
unsigned int uint32_t
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition ring_buffer.c:63
uint8_t * fr_ring_buffer_alloc(fr_ring_buffer_t *rb, size_t size)
Mark data as allocated.
uint8_t * fr_ring_buffer_reserve(fr_ring_buffer_t *rb, size_t size)
Reserve room in the ring buffer.
int fr_ring_buffer_free(fr_ring_buffer_t *rb, size_t size_to_free)
Mark data as free,.
size_t fr_ring_buffer_used(fr_ring_buffer_t *rb)
Get the amount of data used in a ring buffer.
size_t fr_ring_buffer_size(fr_ring_buffer_t *rb)
Get the size of the ring buffer.
int fr_ring_buffer_start(fr_ring_buffer_t *rb, uint8_t **p_start, size_t *p_size)
Get a pointer to the data at the start of the ring buffer.
static void test_start_basic(TALLOC_CTX *ctx)
int main(int argc, char *argv[])
static uint8_t * data[ARRAY_SIZE]
static void free_blocks(fr_ring_buffer_t *rb, UNUSED uint32_t *seed, int *start, int *end)
#define ARRAY_SIZE
static size_t array[ARRAY_SIZE]
static void verify_start(fr_ring_buffer_t *rb, int start_idx)
static size_t seed_string_len
static void test_start_wrapped(TALLOC_CTX *ctx)
static char const * seed_string
static size_t used
static NEVER_RETURNS void usage(void)
#define ALLOC_SIZE
static void alloc_blocks(fr_ring_buffer_t *rb, uint32_t *seed, UNUSED int *start, int *end)
static int debug_lvl
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:48