Skip to content

Commit ea12edb

Browse files
committed
Core: Add a memory allocator stress testing suite
1 parent 54d4af2 commit ea12edb

File tree

5 files changed

+322
-2
lines changed

5 files changed

+322
-2
lines changed

Makefile.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ensure_test_defs:
88

99
unit_tests: ensure_test_defs $(NAME)
1010
-@echo " ============= Start Unit Tests ============="
11-
./$(NAME) -T -w .
11+
./$(NAME) -T -w . -m1024 -M1024 -a HP_MALLOC
1212
-@echo " ================================================"
1313
-@echo " ======== Passed All Tests! ᕦ(ò_óˇ)ᕤ ========"
1414
-@echo " ================================================"

mem/test/test_malloc.c

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
* Copyright (C) 2020 OpenSIPS Solutions
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips 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+
* opensips 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
19+
*/
20+
21+
#include <tap.h>
22+
23+
#include "../../str.h"
24+
#include "../../ut.h"
25+
#include "../../pt.h"
26+
#include "../../lib/list.h"
27+
#include "../../mem/mem_funcs.h"
28+
#include "../../lib/osips_malloc.h"
29+
30+
#include "test_malloc.h"
31+
32+
static osips_malloc_t MALLOC;
33+
static osips_realloc_t REALLOC;
34+
static osips_free_t FREE;
35+
36+
/* keep in sync with Makefile.test "-m" option! */
37+
#define HPT_SHM (1 * 1024L * 1024 * 1024)
38+
39+
#define HPT_MAX_PROC_USAGE .8
40+
41+
#define MY_MAX_USED ((long)((double)HPT_SHM / TEST_MALLOC_PROCS * HPT_MAX_PROC_USAGE))
42+
#define check_limit() (hpt_my_used < MY_MAX_USED)
43+
44+
#define HPT_MAX_ALLOC_SZ 65536
45+
#define HPT_FAC 65536
46+
#define HPT_OPS 4000000
47+
48+
static long hpt_my_used = 0;
49+
static long mallocs, reallocs, frees;
50+
static long should_grow = 1;
51+
52+
OSIPS_LIST_HEAD(hpt_frags);
53+
static long fragments;
54+
55+
static stat_var *workers;
56+
57+
struct hpt_frag {
58+
void *chunk;
59+
ssize_t size;
60+
struct list_head list;
61+
};
62+
63+
static void _hpt_malloc(void)
64+
{
65+
struct hpt_frag *ret;
66+
ssize_t size;
67+
68+
if (!check_limit()) {
69+
should_grow = 0;
70+
return;
71+
}
72+
73+
size = (rand() % HPT_FAC) * (HPT_MAX_ALLOC_SZ/HPT_FAC) + 1;
74+
75+
ret = MALLOC(sizeof *ret + size);
76+
if (!ret) {
77+
LM_ERR("oom\n");
78+
should_grow = 0;
79+
return;
80+
}
81+
memset(ret, -1, sizeof *ret + size);
82+
83+
ret->chunk = (void *)(ret + 1);
84+
ret->size = size;
85+
86+
list_add(&ret->list, &hpt_frags);
87+
88+
hpt_my_used += size + sizeof *ret + HP_FRAG_OVERHEAD;
89+
mallocs++;
90+
fragments++;
91+
}
92+
93+
static void _hpt_realloc(void)
94+
{
95+
struct hpt_frag *f, *ret;
96+
ssize_t size;
97+
98+
if (list_empty(&hpt_frags))
99+
return _hpt_malloc();
100+
101+
f = list_entry(hpt_frags.prev, struct hpt_frag, list);
102+
list_del(&f->list);
103+
104+
hpt_my_used -= f->size;
105+
106+
if (should_grow) {
107+
if (!check_limit() || f->size >= MY_MAX_USED) {
108+
should_grow = 0;
109+
goto out;
110+
}
111+
112+
size = f->size + (rand() % HPT_FAC) * (HPT_MAX_ALLOC_SZ/HPT_FAC) + 1;
113+
} else {
114+
size = rand() % f->size + 1;
115+
}
116+
117+
ret = REALLOC(f, sizeof *ret + size);
118+
if (!ret) {
119+
LM_ERR("oom\n");
120+
should_grow = 0;
121+
goto out;
122+
}
123+
memset(ret, -1, sizeof *ret + size);
124+
125+
ret->chunk = (void *)(ret + 1);
126+
ret->size = size;
127+
hpt_my_used += size;
128+
129+
list_add(&ret->list, &hpt_frags);
130+
131+
reallocs++;
132+
return;
133+
134+
out:
135+
FREE(f);
136+
frees++;
137+
fragments--;
138+
}
139+
140+
#define hpt_malloc() (rand() & 1 ? _hpt_malloc() : _hpt_realloc())
141+
142+
static void hpt_free(void)
143+
{
144+
struct hpt_frag *f;
145+
146+
if (list_empty(&hpt_frags)) {
147+
should_grow = 1;
148+
return;
149+
}
150+
151+
f = list_entry(hpt_frags.prev, struct hpt_frag, list);
152+
153+
hpt_my_used -= (f->size + sizeof *f + HP_FRAG_OVERHEAD);
154+
155+
list_del(&f->list);
156+
157+
FREE(f);
158+
frees++;
159+
fragments--;
160+
}
161+
162+
static void _test_malloc(int procs)
163+
{
164+
int i;
165+
int my_pid = 0;
166+
167+
update_stat(workers, +1);
168+
169+
for (i = 1; i < procs; i++) {
170+
update_stat(workers, +1);
171+
if (internal_fork("malloc test", OSS_PROC_NO_IPC|OSS_PROC_NO_LOAD, TYPE_NONE) == 0) {
172+
my_pid = i;
173+
printf("forked extra test worker #%d!\n", i);
174+
break;
175+
}
176+
}
177+
178+
srand(getpid());
179+
180+
for (i = HPT_OPS; i; i--) {
181+
if (i % 100000 == 0)
182+
LM_INFO("ops left: %d, F: %ld, M: %ld, R: %ld, F: %ld, usage: %ld/%ld, frags: %lu\n",
183+
i, fragments, mallocs, reallocs, frees,
184+
hpt_my_used, MY_MAX_USED, shm_frags ? get_stat_val(shm_frags) : -1);
185+
186+
if (should_grow) {
187+
if (rand() % 10 >= 1)
188+
hpt_malloc();
189+
else
190+
hpt_free();
191+
} else {
192+
if (rand() % 10 < 1)
193+
hpt_malloc();
194+
else
195+
hpt_free();
196+
}
197+
}
198+
199+
for (i = 0; !list_empty(&hpt_frags); i++)
200+
hpt_free();
201+
202+
LM_INFO("Worker %d ended, freed up remaining %d chunks.\n", my_pid, i);
203+
update_stat(workers, -1);
204+
205+
if (my_pid != 0) {
206+
exit(0);
207+
} else {
208+
while (get_stat_val(workers) > 0) {
209+
LM_INFO("waiting for everyone to finish...\n");
210+
sleep(1);
211+
}
212+
}
213+
}
214+
215+
static inline void test_pkg_malloc(void)
216+
{
217+
MALLOC = osips_pkg_malloc;
218+
REALLOC = osips_pkg_realloc;
219+
FREE = osips_pkg_free;
220+
221+
LM_INFO("Starting PKG stress test...\n");
222+
LM_INFO("================================\n");
223+
224+
_test_malloc(1);
225+
}
226+
227+
static inline void test_shm_malloc(void)
228+
{
229+
unsigned long used, rused, new_used, new_rused;
230+
231+
MALLOC = osips_shm_malloc;
232+
REALLOC = osips_shm_realloc;
233+
FREE = osips_shm_free;
234+
235+
used = get_stat_val(get_stat(_str("used_size")));
236+
rused = get_stat_val(get_stat(_str("real_used_size")));
237+
238+
LM_INFO("Starting SHM stress test...\n");
239+
LM_INFO("================================\n");
240+
LM_INFO("used: %ld\n", used);
241+
LM_INFO("real_used: %ld\n", rused);
242+
LM_INFO("max_real_used: %ld\n", get_stat_val(get_stat(_str("max_used_size"))));
243+
LM_INFO("fragments: %ld\n", get_stat_val(get_stat(_str("fragments"))));
244+
LM_INFO("================================\n");
245+
246+
_test_malloc(TEST_MALLOC_PROCS);
247+
248+
new_used = get_stat_val(get_stat(_str("used_size")));
249+
new_rused = get_stat_val(get_stat(_str("real_used_size")));
250+
251+
LM_INFO("SHM test complete. Final stats:\n");
252+
LM_INFO("================================\n");
253+
LM_INFO("used: %ld\n", new_used);
254+
LM_INFO("real_used: %ld\n", new_rused);
255+
LM_INFO("max_real_used: %ld\n", get_stat_val(get_stat(_str("max_used_size"))));
256+
LM_INFO("fragments: %ld\n", get_stat_val(get_stat(_str("fragments"))));
257+
LM_INFO("================================\n");
258+
259+
ok(new_used == used, "check stats: shm_used");
260+
ok(new_rused == rused, "check stats: shm_rused");
261+
}
262+
263+
void test_malloc(void)
264+
{
265+
test_pkg_malloc();
266+
test_shm_malloc();
267+
}
268+
269+
void init_malloc_tests(void)
270+
{
271+
if (load_module("mi_fifo.so") != 0) {
272+
printf("failed to load mi_fifo\n");
273+
exit(-1);
274+
}
275+
276+
if (register_stat("test_malloc", "test-workers", &workers, 0) != 0) {
277+
LM_ERR("failed to register stat\n");
278+
return;
279+
}
280+
}

mem/test/test_malloc.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) 2020 OpenSIPS Solutions
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips 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+
* opensips 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 Street, Fifth Floor, Boston, MA 02110-1301,USA
19+
*/
20+
21+
#ifndef __TEST_MALLOC_H__
22+
#define __TEST_MALLOC_H__
23+
24+
/* attendant + 7 more */
25+
#define TEST_MALLOC_PROCS 8
26+
27+
/* stress-test the PKG and/or SHM allocator implementations */
28+
void init_malloc_tests(void);
29+
void test_malloc(void);
30+
31+
#endif /* __TEST_MALLOC_H__ */

pt.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ int init_multi_proc_support(void)
6666
* all the other modules already adjusted their extra numbers */
6767
counted_max_processes = count_child_processes();
6868

69+
#ifdef UNIT_TESTS
70+
#include "mem/test/test_malloc.h"
71+
counted_max_processes += TEST_MALLOC_PROCS - 1;
72+
#endif
73+
6974
/* allocate the PID table to accomodate the maximum possible number of
7075
* process we may have during runtime (covering extra procs created
7176
* due auto-scaling) */

test/unit_tests.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "../cachedb/test/test_backends.h"
2626
#include "../lib/test/test_csv.h"
2727
#include "../parser/test/test_parse_qop.h"
28+
#include "../mem/test/test_malloc.h"
2829

2930
#include "../lib/list.h"
3031
#include "../dprint.h"
@@ -33,11 +34,14 @@
3334
void init_unit_tests(void) {
3435
set_mpath("modules/");
3536
//init_cachedb_tests();
37+
//init_malloc_tests();
3638
}
3739

3840
int run_unit_tests(void) {
39-
//test_cachedb_backends();
4041
test_lib_csv();
4142
test_parse_qop_val();
43+
44+
//test_cachedb_backends();
45+
//test_malloc();
4246
done_testing();
4347
}

0 commit comments

Comments
 (0)