1 /*
2  * Boilerplate code used for testing extent hooks via interception and
3  * passthrough.
4  */
5 
6 static void	*extent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr,
7     size_t size, size_t alignment, bool *zero, bool *commit,
8     unsigned arena_ind);
9 static bool	extent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr,
10     size_t size, bool committed, unsigned arena_ind);
11 static bool	extent_commit_hook(extent_hooks_t *extent_hooks, void *addr,
12     size_t size, size_t offset, size_t length, unsigned arena_ind);
13 static bool	extent_decommit_hook(extent_hooks_t *extent_hooks, void *addr,
14     size_t size, size_t offset, size_t length, unsigned arena_ind);
15 static bool	extent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr,
16     size_t size, size_t offset, size_t length, unsigned arena_ind);
17 static bool	extent_purge_forced_hook(extent_hooks_t *extent_hooks,
18     void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);
19 static bool	extent_split_hook(extent_hooks_t *extent_hooks, void *addr,
20     size_t size, size_t size_a, size_t size_b, bool committed,
21     unsigned arena_ind);
22 static bool	extent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a,
23     size_t size_a, void *addr_b, size_t size_b, bool committed,
24     unsigned arena_ind);
25 
26 static extent_hooks_t *default_hooks;
27 static extent_hooks_t hooks = {
28 	extent_alloc_hook,
29 	extent_dalloc_hook,
30 	extent_commit_hook,
31 	extent_decommit_hook,
32 	extent_purge_lazy_hook,
33 	extent_purge_forced_hook,
34 	extent_split_hook,
35 	extent_merge_hook
36 };
37 
38 /* Control whether hook functions pass calls through to default hooks. */
39 static bool try_alloc = true;
40 static bool try_dalloc = true;
41 static bool try_commit = true;
42 static bool try_decommit = true;
43 static bool try_purge_lazy = true;
44 static bool try_purge_forced = true;
45 static bool try_split = true;
46 static bool try_merge = true;
47 
48 /* Set to false prior to operations, then introspect after operations. */
49 static bool called_alloc;
50 static bool called_dalloc;
51 static bool called_commit;
52 static bool called_decommit;
53 static bool called_purge_lazy;
54 static bool called_purge_forced;
55 static bool called_split;
56 static bool called_merge;
57 
58 /* Set to false prior to operations, then introspect after operations. */
59 static bool did_alloc;
60 static bool did_dalloc;
61 static bool did_commit;
62 static bool did_decommit;
63 static bool did_purge_lazy;
64 static bool did_purge_forced;
65 static bool did_split;
66 static bool did_merge;
67 
68 #if 0
69 #  define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__)
70 #else
71 #  define TRACE_HOOK(fmt, ...)
72 #endif
73 
74 static void *
extent_alloc_hook(extent_hooks_t * extent_hooks,void * new_addr,size_t size,size_t alignment,bool * zero,bool * commit,unsigned arena_ind)75 extent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
76     size_t alignment, bool *zero, bool *commit, unsigned arena_ind)
77 {
78 	void *ret;
79 
80 	TRACE_HOOK("%s(extent_hooks=%p, new_addr=%p, size=%zu, alignment=%zu, "
81 	    "*zero=%s, *commit=%s, arena_ind=%u)\n", __func__, extent_hooks,
82 	    new_addr, size, alignment, *zero ?  "true" : "false", *commit ?
83 	    "true" : "false", arena_ind);
84 	assert_ptr_eq(extent_hooks, &hooks,
85 	    "extent_hooks should be same as pointer used to set hooks");
86 	assert_ptr_eq(extent_hooks->alloc, extent_alloc_hook,
87 	    "Wrong hook function");
88 	called_alloc = true;
89 	if (!try_alloc)
90 		return (NULL);
91 	ret = default_hooks->alloc(default_hooks, new_addr, size, alignment,
92 	    zero, commit, 0);
93 	did_alloc = (ret != NULL);
94 	return (ret);
95 }
96 
97 static bool
extent_dalloc_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,bool committed,unsigned arena_ind)98 extent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
99     bool committed, unsigned arena_ind)
100 {
101 	bool err;
102 
103 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
104 	    "arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
105 	    "true" : "false", arena_ind);
106 	assert_ptr_eq(extent_hooks, &hooks,
107 	    "extent_hooks should be same as pointer used to set hooks");
108 	assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_hook,
109 	    "Wrong hook function");
110 	called_dalloc = true;
111 	if (!try_dalloc)
112 		return (true);
113 	err = default_hooks->dalloc(default_hooks, addr, size, committed, 0);
114 	did_dalloc = !err;
115 	return (err);
116 }
117 
118 static bool
extent_commit_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,size_t offset,size_t length,unsigned arena_ind)119 extent_commit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
120     size_t offset, size_t length, unsigned arena_ind)
121 {
122 	bool err;
123 
124 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
125 	    "length=%zu, arena_ind=%u)\n", __func__, extent_hooks, addr, size,
126 	    offset, length, arena_ind);
127 	assert_ptr_eq(extent_hooks, &hooks,
128 	    "extent_hooks should be same as pointer used to set hooks");
129 	assert_ptr_eq(extent_hooks->commit, extent_commit_hook,
130 	    "Wrong hook function");
131 	called_commit = true;
132 	if (!try_commit)
133 		return (true);
134 	err = default_hooks->commit(default_hooks, addr, size, offset, length,
135 	    0);
136 	did_commit = !err;
137 	return (err);
138 }
139 
140 static bool
extent_decommit_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,size_t offset,size_t length,unsigned arena_ind)141 extent_decommit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
142     size_t offset, size_t length, unsigned arena_ind)
143 {
144 	bool err;
145 
146 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
147 	    "length=%zu, arena_ind=%u)\n", __func__, extent_hooks, addr, size,
148 	    offset, length, arena_ind);
149 	assert_ptr_eq(extent_hooks, &hooks,
150 	    "extent_hooks should be same as pointer used to set hooks");
151 	assert_ptr_eq(extent_hooks->decommit, extent_decommit_hook,
152 	    "Wrong hook function");
153 	called_decommit = true;
154 	if (!try_decommit)
155 		return (true);
156 	err = default_hooks->decommit(default_hooks, addr, size, offset, length,
157 	    0);
158 	did_decommit = !err;
159 	return (err);
160 }
161 
162 static bool
extent_purge_lazy_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,size_t offset,size_t length,unsigned arena_ind)163 extent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
164     size_t offset, size_t length, unsigned arena_ind)
165 {
166 	bool err;
167 
168 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
169 	    "length=%zu arena_ind=%u)\n", __func__, extent_hooks, addr, size,
170 	    offset, length, arena_ind);
171 	assert_ptr_eq(extent_hooks, &hooks,
172 	    "extent_hooks should be same as pointer used to set hooks");
173 	assert_ptr_eq(extent_hooks->purge_lazy, extent_purge_lazy_hook,
174 	    "Wrong hook function");
175 	called_purge_lazy = true;
176 	if (!try_purge_lazy)
177 		return (true);
178 	err = default_hooks->purge_lazy == NULL ||
179 	    default_hooks->purge_lazy(default_hooks, addr, size, offset, length,
180 	    0);
181 	did_purge_lazy = !err;
182 	return (err);
183 }
184 
185 static bool
extent_purge_forced_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,size_t offset,size_t length,unsigned arena_ind)186 extent_purge_forced_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
187     size_t offset, size_t length, unsigned arena_ind)
188 {
189 	bool err;
190 
191 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
192 	    "length=%zu arena_ind=%u)\n", __func__, extent_hooks, addr, size,
193 	    offset, length, arena_ind);
194 	assert_ptr_eq(extent_hooks, &hooks,
195 	    "extent_hooks should be same as pointer used to set hooks");
196 	assert_ptr_eq(extent_hooks->purge_forced, extent_purge_forced_hook,
197 	    "Wrong hook function");
198 	called_purge_forced = true;
199 	if (!try_purge_forced)
200 		return (true);
201 	err = default_hooks->purge_forced == NULL ||
202 	    default_hooks->purge_forced(default_hooks, addr, size, offset,
203 	    length, 0);
204 	did_purge_forced = !err;
205 	return (err);
206 }
207 
208 static bool
extent_split_hook(extent_hooks_t * extent_hooks,void * addr,size_t size,size_t size_a,size_t size_b,bool committed,unsigned arena_ind)209 extent_split_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
210     size_t size_a, size_t size_b, bool committed, unsigned arena_ind)
211 {
212 	bool err;
213 
214 	TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, size_a=%zu, "
215 	    "size_b=%zu, committed=%s, arena_ind=%u)\n", __func__, extent_hooks,
216 	    addr, size, size_a, size_b, committed ? "true" : "false",
217 	    arena_ind);
218 	assert_ptr_eq(extent_hooks, &hooks,
219 	    "extent_hooks should be same as pointer used to set hooks");
220 	assert_ptr_eq(extent_hooks->split, extent_split_hook,
221 	    "Wrong hook function");
222 	called_split = true;
223 	if (!try_split)
224 		return (true);
225 	err = (default_hooks->split == NULL ||
226 	    default_hooks->split(default_hooks, addr, size, size_a, size_b,
227 	    committed, 0));
228 	did_split = !err;
229 	return (err);
230 }
231 
232 static bool
extent_merge_hook(extent_hooks_t * extent_hooks,void * addr_a,size_t size_a,void * addr_b,size_t size_b,bool committed,unsigned arena_ind)233 extent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
234     void *addr_b, size_t size_b, bool committed, unsigned arena_ind)
235 {
236 	bool err;
237 
238 	TRACE_HOOK("%s(extent_hooks=%p, addr_a=%p, size_a=%zu, addr_b=%p "
239 	    "size_b=%zu, committed=%s, arena_ind=%u)\n", __func__, extent_hooks,
240 	    addr_a, size_a, addr_b, size_b, committed ? "true" : "false",
241 	    arena_ind);
242 	assert_ptr_eq(extent_hooks, &hooks,
243 	    "extent_hooks should be same as pointer used to set hooks");
244 	assert_ptr_eq(extent_hooks->merge, extent_merge_hook,
245 	    "Wrong hook function");
246 	called_merge = true;
247 	if (!try_merge)
248 		return (true);
249 	err = (default_hooks->merge == NULL ||
250 	    default_hooks->merge(default_hooks, addr_a, size_a, addr_b, size_b,
251 	    committed, 0));
252 	did_merge = !err;
253 	return (err);
254 }
255 
256 static void
extent_hooks_prep(void)257 extent_hooks_prep(void)
258 {
259 	size_t sz;
260 
261 	sz = sizeof(default_hooks);
262 	assert_d_eq(mallctl("arena.0.extent_hooks", (void *)&default_hooks, &sz,
263 	    NULL, 0), 0, "Unexpected mallctl() error");
264 }
265