1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 #include <config.h>
13 
14 #if HAVE_CMOCKA
15 
16 #include <stdarg.h>
17 #include <stddef.h>
18 #include <setjmp.h>
19 
20 #include <fcntl.h>
21 #include <sched.h> /* IWYU pragma: keep */
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 #define UNIT_TESTING
26 #include <cmocka.h>
27 
28 #include <isc/mem.h>
29 #include <isc/print.h>
30 #include <isc/result.h>
31 #include <isc/stdio.h>
32 #include <isc/util.h>
33 
34 #include "isctest.h"
35 
36 static int
_setup(void ** state)37 _setup(void **state) {
38 	isc_result_t result;
39 
40 	UNUSED(state);
41 
42 	result = isc_test_begin(NULL, true, 0);
43 	assert_int_equal(result, ISC_R_SUCCESS);
44 
45 	return (0);
46 }
47 
48 static int
_teardown(void ** state)49 _teardown(void **state) {
50 	UNUSED(state);
51 
52 	isc_test_end();
53 
54 	return (0);
55 }
56 
57 static void *
default_memalloc(void * arg,size_t size)58 default_memalloc(void *arg, size_t size) {
59 	UNUSED(arg);
60 	if (size == 0U) {
61 		size = 1;
62 	}
63 	/* cppcheck-suppress leakNoVarFunctionCall */
64 	return (malloc(size));
65 }
66 
67 static void
default_memfree(void * arg,void * ptr)68 default_memfree(void *arg, void *ptr) {
69 	UNUSED(arg);
70 	free(ptr);
71 }
72 
73 #define	MP1_FREEMAX	10
74 #define	MP1_FILLCNT	10
75 #define	MP1_MAXALLOC	30
76 
77 #define	MP2_FREEMAX	25
78 #define	MP2_FILLCNT	25
79 
80 /* general memory system tests */
81 static void
isc_mem_test(void ** state)82 isc_mem_test(void **state) {
83 	isc_result_t result;
84 	void *items1[50];
85 	void *items2[50];
86 	void *tmp;
87 	isc_mem_t *localmctx = NULL;
88 	isc_mempool_t *mp1 = NULL, *mp2 = NULL;
89 	unsigned int i, j;
90 	int rval;
91 
92 	UNUSED(state);
93 
94 	result = isc_mem_create(0, 0, &localmctx);
95 	assert_int_equal(result, ISC_R_SUCCESS);
96 
97 	result = isc_mempool_create(localmctx, 24, &mp1);
98 	assert_int_equal(result, ISC_R_SUCCESS);
99 
100 	result = isc_mempool_create(localmctx, 31, &mp2);
101 	assert_int_equal(result, ISC_R_SUCCESS);
102 
103 	isc_mempool_setfreemax(mp1, MP1_FREEMAX);
104 	isc_mempool_setfillcount(mp1, MP1_FILLCNT);
105 	isc_mempool_setmaxalloc(mp1, MP1_MAXALLOC);
106 
107 	/*
108 	 * Allocate MP1_MAXALLOC items from the pool.  This is our max.
109 	 */
110 	for (i = 0; i < MP1_MAXALLOC; i++) {
111 		items1[i] = isc_mempool_get(mp1);
112 		assert_non_null(items1[i]);
113 	}
114 
115 	/*
116 	 * Try to allocate one more.  This should fail.
117 	 */
118 	tmp = isc_mempool_get(mp1);
119 	assert_null(tmp);
120 
121 	/*
122 	 * Free the first 11 items.  Verify that there are 10 free items on
123 	 * the free list (which is our max).
124 	 */
125 	for (i = 0; i < 11; i++) {
126 		isc_mempool_put(mp1, items1[i]);
127 		items1[i] = NULL;
128 	}
129 
130 	rval = isc_mempool_getfreecount(mp1);
131 	assert_int_equal(rval, 10);
132 
133 	rval = isc_mempool_getallocated(mp1);
134 	assert_int_equal(rval, 19);
135 
136 	/*
137 	 * Now, beat up on mp2 for a while.  Allocate 50 items, then free
138 	 * them, then allocate 50 more, etc.
139 	 */
140 
141 	isc_mempool_setfreemax(mp2, 25);
142 	isc_mempool_setfillcount(mp2, 25);
143 
144 	for (j = 0; j < 500000; j++) {
145 		for (i = 0; i < 50; i++) {
146 			items2[i] = isc_mempool_get(mp2);
147 			assert_non_null(items2[i]);
148 		}
149 		for (i = 0; i < 50; i++) {
150 			isc_mempool_put(mp2, items2[i]);
151 			items2[i] = NULL;
152 		}
153 	}
154 
155 	/*
156 	 * Free all the other items and blow away this pool.
157 	 */
158 	for (i = 11; i < MP1_MAXALLOC; i++) {
159 		isc_mempool_put(mp1, items1[i]);
160 		items1[i] = NULL;
161 	}
162 
163 	isc_mempool_destroy(&mp1);
164 	isc_mempool_destroy(&mp2);
165 
166 	isc_mem_destroy(&localmctx);
167 
168 	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
169 				  NULL, &localmctx, ISC_MEMFLAG_INTERNAL);
170 	assert_int_equal(result, ISC_R_SUCCESS);
171 
172 	result = isc_mempool_create(localmctx, 2, &mp1);
173 	assert_int_equal(result, ISC_R_SUCCESS);
174 
175 	tmp = isc_mempool_get(mp1);
176 	assert_non_null(tmp);
177 
178 	isc_mempool_put(mp1, tmp);
179 
180 	isc_mempool_destroy(&mp1);
181 
182 	isc_mem_destroy(&localmctx);
183 
184 }
185 
186 /* test TotalUse calculation */
187 static void
isc_mem_total_test(void ** state)188 isc_mem_total_test(void **state) {
189 	isc_result_t result;
190 	isc_mem_t *mctx2 = NULL;
191 	size_t before, after;
192 	ssize_t diff;
193 	int i;
194 
195 	UNUSED(state);
196 
197 	/* Local alloc, free */
198 	mctx2 = NULL;
199 	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
200 				  NULL, &mctx2, 0);
201 	if (result != ISC_R_SUCCESS) {
202 		goto out;
203 	}
204 
205 	before = isc_mem_total(mctx2);
206 
207 	for (i = 0; i < 100000; i++) {
208 		void *ptr;
209 
210 		ptr = isc_mem_allocate(mctx2, 2048);
211 		isc_mem_free(mctx2, ptr);
212 	}
213 
214 	after = isc_mem_total(mctx2);
215 	diff = after - before;
216 
217 	/* 2048 +8 bytes extra for size_info */
218 	assert_int_equal(diff, (2048 + 8) * 100000);
219 
220 	/* ISC_MEMFLAG_INTERNAL */
221 
222 	before = isc_mem_total(mctx);
223 
224 	for (i = 0; i < 100000; i++) {
225 		void *ptr;
226 
227 		ptr = isc_mem_allocate(mctx, 2048);
228 		isc_mem_free(mctx, ptr);
229 	}
230 
231 	after = isc_mem_total(mctx);
232 	diff = after - before;
233 
234 	/* 2048 +8 bytes extra for size_info */
235 	assert_int_equal(diff, (2048 + 8) * 100000);
236 
237  out:
238 	if (mctx2 != NULL) {
239 		isc_mem_destroy(&mctx2);
240 	}
241 
242 }
243 
244 /* test InUse calculation */
245 static void
isc_mem_inuse_test(void ** state)246 isc_mem_inuse_test(void **state) {
247 	isc_result_t result;
248 	isc_mem_t *mctx2 = NULL;
249 	size_t before, after;
250 	ssize_t diff;
251 	void *ptr;
252 
253 	UNUSED(state);
254 
255 	mctx2 = NULL;
256 	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
257 				  NULL, &mctx2, 0);
258 	if (result != ISC_R_SUCCESS) {
259 		goto out;
260 	}
261 
262 	before = isc_mem_inuse(mctx2);
263 	ptr = isc_mem_allocate(mctx2, 1024000);
264 	isc_mem_free(mctx2, ptr);
265 	after = isc_mem_inuse(mctx2);
266 
267 	diff = after - before;
268 
269 	assert_int_equal(diff, 0);
270 
271  out:
272 	if (mctx2 != NULL) {
273 		isc_mem_destroy(&mctx2);
274 	}
275 
276 }
277 
278 /*
279  * Main
280  */
281 
282 int
main(void)283 main(void) {
284 	const struct CMUnitTest tests[] = {
285 		cmocka_unit_test_setup_teardown(isc_mem_test,
286 				_setup, _teardown),
287 		cmocka_unit_test_setup_teardown(isc_mem_total_test,
288 				_setup, _teardown),
289 		cmocka_unit_test_setup_teardown(isc_mem_inuse_test,
290 				_setup, _teardown),
291 	};
292 
293 	return (cmocka_run_group_tests(tests, NULL, NULL));
294 }
295 
296 #else /* HAVE_CMOCKA */
297 
298 #include <stdio.h>
299 
300 int
main(void)301 main(void) {
302 		printf("1..0 # Skipped: cmocka not available\n");
303 			return (0);
304 
305 }
306 
307 #endif
308