mkernel 0.0.2
Micro-kernel framework, everything as a module
oom.c
Go to the documentation of this file.
1
17#include "oom.h" /* OOM simulation */
18
19#ifndef __GNUC__
20# ifndef __asm__
21# define __asm__ asm
22# endif
23# ifndef __sync_synchronize
24# define __sync_synchronize void
25# endif
26#endif
27
28#include "debug/assert.h" /* libdebug's Assertions */
29#include <sys/resource.h> /* setrlimit */
30#include <errno.h> /* errno */
31#include <string.h> /* strerror() */
32#include <stdio.h> /* printf */
33#include <malloc.h> /* malloc_trim() */
34#include <stdlib.h> /* abort() */
35#include <alloca.h> /* alloca() */
36
37#ifndef _GNU_SOURCE
38#define _GNU_SOURCE
39#endif
40#include <unistd.h> /* sysconf() */
41
47#define RAMBLOCKS_MAX 1000
48
52static void* _oomblocks[RAMBLOCKS_MAX] = {0};
53
55static int checked_getrlimit(int resource, struct rlimit *rlim)
56{
57 /* Get current limit values */
58 if (getrlimit(resource, rlim) != 0) {
59 /* Can occur, thus not ignored, but impossible to trigger for gcov/lcov */
60 fprintf (stderr,"%s:%d getrlimit() failed with errno=%d %s\n",
61 __FILE__,__LINE__, errno,strerror(errno));
62 abort();
63 }
64 return 0;
65}
66
75static size_t oomtest_fill_preinit(const size_t minHeap, const size_t minStack)
76{
77 (void)minHeap;
78 (void)minStack;
79 ASSERT(NULL==_oomblocks[0]);
80 return 0;
81}
82
83/* Documented in header file */
84size_t (*oomtest_fill)(const size_t minHeap, const size_t minStack)=oomtest_fill_preinit;
85
94static void oomtest_free_preinit()
95{
96 ASSERT(NULL==_oomblocks[0]);
97}
98
99/* Documented in header file */
100void (*oomtest_free)()=oomtest_free_preinit;
101
110static size_t oomtest_enable_preinit(const size_t softlimit)
111{
112 (void)softlimit;
113
114 /* Should not be called without configuration */
115 fprintf(stderr,"%s:%d oomtest_enable called without oomtest_config before\n",__FILE__,__LINE__);
116 abort();
117}
118
119/* Documented in header file */
120size_t (*oomtest_enable)(const size_t softlimit)=oomtest_enable_preinit;
121
130static size_t oomtest_disable_preinit()
131{
132 /* Should not be called before configuration */
133 fprintf(stderr,"%s:%d oomtest_disable called without oomtest_config before\n",__FILE__,__LINE__);
134 abort();
135}
136
137/* Documented in header file */
138size_t (*oomtest_disable)()=oomtest_disable_preinit;
139
151static size_t oomtest_getbiggestblock(void** p_ramblock)
152{
153 /* This function could receive an optimized "max" value and return the final
154 * "max" value to the caller. So, it could be used as the next invocation
155 * starting "max" value. This would save few loop iterations, at the cost of
156 * extra stack usage, I chose to not pass this value as a parameter to avoid
157 * consuming stack.
158 * The function starts to search between 0..rlim_cur which has very little
159 * impact given the Olog2 complexity. It could be optimized and start to
160 * search between 0..sysconf(AVPHYSPAGE*PAGESIZE), assuming that sysconf is
161 * faster than few loop iterations. */
162
163 /* Use static to avoid stack allocation/free */
164 static size_t max,cur;
165 static struct rlimit limit;
166
167 ASSERT(NULL!=p_ramblock);
168 ASSERT(NULL==*p_ramblock);
169
170 /* Get the current limits */
171 checked_getrlimit(RLIMIT_AS, &limit);
172 max = limit.rlim_cur;
173
174 /* Restart the whole process if cur can not be allocated at the end */
175 while ((max>0)&&(NULL==*p_ramblock)) {
176 static size_t min;
177 /* Iterate quickly (Olog2) to converge to the biggest available RAM block */
178 min = 0;
179 while (max>min) {
180 cur = min+(max-min)/2; /* To avoid overflow */
181 if (NULL==(*p_ramblock = malloc(cur))) {
182 max = cur;
183 } else {
184 min = cur+1;
185 free(*p_ramblock);
186 }
187 }
188 cur -= 1;
189 *p_ramblock=malloc(cur);
190 }
191
192 return cur;
193}
194
202static size_t oomtest_fill_postinit(const size_t minHeap, const size_t minStack)
203{
204 unsigned int l_numblock;
205 size_t l_sum;
206 void* volatile l_reservedheap;
207 void* l_reservedstack;
208
209 /* Probably a mistake in the test code, do not accept despite we could */
210 if (NULL!=_oomblocks[0]) {
211 /* Dirty hack to free some RAM and allow abort to SIGABRT */
212 free(_oomblocks[0]);
213 fprintf (stderr,"%s:%d oomblocks are already allocated\n", __FILE__,__LINE__);
214 abort();
215 }
216
217 /* Reserve/Protect stack bytes which will be auto freed at return */
218 /* A failure means a stackoverflow, which is not recoverable and will abort
219 * the process anyway. */
220 if (0<minStack)
221 l_reservedstack=alloca(minStack);
222 (void)l_reservedstack;
223
224 /* Reserve/Protect heap bytes which will be released before return */
225 l_reservedheap=NULL;
226 if (0<minHeap)
227 if (NULL==(l_reservedheap=malloc(minHeap))) {
228 /* This will fail if minHeap is higher than rlimit */
229 fprintf(stderr,"%s:%d Failed to reserve minheap bytes\n",__FILE__,__LINE__);
230 abort();
231 }
232
233 /* Find and allocate the biggest available RAM blocks until no mre RAM
234 * available or all _oomblocks are allocated */
235 l_numblock=0;
236 l_sum = 0;
237 l_sum += oomtest_getbiggestblock(&(_oomblocks[l_numblock++]));
238 while ((NULL!=_oomblocks[l_numblock-1])&&((RAMBLOCKS_MAX-1)>l_numblock))
239 l_sum += oomtest_getbiggestblock(&(_oomblocks[l_numblock++]));
240 /* Either already NULL, which stopped the while loop or reached the last
241 * slot in the table, which stopped the while loop and the slot has to be
242 * set to NULL */
243 _oomblocks[l_numblock]=NULL;
244
245 /* There can be less than 4 bytes available at this point !!! */
246
247 /* Make the protected heap bytes available again */
248 if (NULL!=l_reservedheap)
249 free(l_reservedheap);
250
251 return l_sum;
252}
253
261static void oomtest_free_postinit()
262{
263 unsigned int l_i;
264
265 /* Despite we could manage, abort to help detecting mistakes in test code */
266 if (NULL==_oomblocks[0]) {
267 fprintf(stderr,"%s:%d no blocks to free in oomtest_free.\n",__FILE__,__LINE__);
268 abort();
269 }
270
271 /* Actually free allocated blocks and set their pointer to NULL */
272 l_i = 0;
273 while ((l_i<(RAMBLOCKS_MAX-1))&&(_oomblocks[l_i])) {
274 free(_oomblocks[l_i]);
275 _oomblocks[l_i] = NULL;
276 l_i+=1;
277 }
278 /* These barrier protections are probably useless */
279 malloc_trim(0);
280 /* SW barrier (compiler only) */
281 __asm__ volatile("": : :"memory");
282 /* HW barrier (CPU instruction) */
284}
285
293static size_t oomtest_enable_postinit(const size_t softlimit)
294{
295 struct rlimit limit;
296 size_t l_softlimit = softlimit;
297
298 /* Probably a bug in oomtest */
299 ASSERT(NULL==_oomblocks[0]);
300
301 /* Get current limit values */
302 checked_getrlimit(RLIMIT_AS, &limit);
303
304 /* Check the requested value */
305 if (0==l_softlimit) {
306 /* Defaults to available physical RAM if requested 0 */
307 l_softlimit = limit.rlim_max;
308 };
309
310 /* Soft limit available RAM */
311 limit.rlim_cur = l_softlimit;
312
313 /* Abort if setrlimit fails to avoid RAM bombing */
314 if (setrlimit(RLIMIT_AS, &limit) != 0) {
315 fprintf (stderr,"%s:%d setrlimit(cur=%lu, max=%lu) with errno=%d %s\n",
316 __FILE__,__LINE__,
317 (unsigned long)limit.rlim_cur, (unsigned long)limit.rlim_max,
318 errno,strerror(errno));
319 abort();
320 }
321
322 /* Activate fill/free functions */
323 oomtest_fill = oomtest_fill_postinit;
324 oomtest_free = oomtest_free_postinit;
325
326 /* Return the actual current soft limit */
327 return limit.rlim_cur;
328}
329
337static size_t oomtest_disable_postinit()
338{
339 struct rlimit limit;
340
341 if (NULL!=_oomblocks[0]) {
342 fprintf(stderr,"%s:%d RAM still allocated while calling oomtest_disable\n",__FILE__,__LINE__);
343 abort();
344 }
345
346 /* Do not allow calling disable if not enabled to detect test mistakes */
347 if ((oomtest_fill!=oomtest_fill_postinit)||(oomtest_free!=oomtest_free_postinit)) {
348 fprintf(stderr,"%s:%d Impossible to disable oomtest if not previously enabled\n",__FILE__,__LINE__);
349 abort();
350 }
351
352 /* Reset the soft limit to hard limit */
353 oomtest_enable_postinit(0);
354
355 /* Get current limit values */
356 checked_getrlimit(RLIMIT_AS, &limit);
357
358 /* Restore disabled functors */
359 oomtest_fill = oomtest_fill_preinit;
360 oomtest_free = oomtest_free_preinit;
361
362 /* Return the actual current soft limit, which is equal to hard limit */
363 return limit.rlim_cur;
364}
365
366/* Documented in header file */
367size_t oomtest_config(const size_t hardlimit)
368{
369 struct rlimit limit;
370 size_t l_avail;
371 size_t l_hardlimit = hardlimit;
372
373 /* Probably a test implementation mistake */
374 if (NULL!=_oomblocks[0]) {
375 fprintf(stderr,"%s:%d Calling oomtest_config with allocated RAM blocks is not allowed.\n",
376 __FILE__,__LINE__);
377 abort();
378 }
379
380 /* Find *installed* physical RAM, 0 in case of failure */
381 l_avail = (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE));
382
383 /* Get current limit values */
384 checked_getrlimit(RLIMIT_AS, &limit);
385 if (0==l_hardlimit) {
386 /* Defaults to available physical RAM or already set rlimit
387 * if 0 requested */
388 l_hardlimit=(limit.rlim_max<l_avail?limit.rlim_max:l_avail);
389 } else if (l_hardlimit>l_avail) {
390 /* Fails if request is over available physical RAM to avoir swapping */
391 fprintf(stderr,"%s:%d Requesing a limit %lu bigger than *installed* RAM %lu is not allowed.\n",
392 __FILE__,__LINE__,
393 (unsigned long)l_hardlimit,(unsigned long)l_avail);
394 abort();
395 }
396
397 /* Hard limit available RAM to hardlimit globally with no way back */
398 limit.rlim_cur = l_hardlimit;
399 limit.rlim_max = l_hardlimit;
400
401 /* Abort if setrlimit fails to avoid RAM bombing */
402 if (setrlimit(RLIMIT_AS, &limit) != 0) {
403 fprintf (stderr,"%s:%d setrlimit(cur=%lu, max=%lu) with errno=%d %s\n",
404 __FILE__,__LINE__, (unsigned long)limit.rlim_cur,
405 (unsigned long)limit.rlim_max, errno,strerror(errno));
406 /* Get current limit values */
407 checked_getrlimit(RLIMIT_AS, &limit);
408 fprintf (stderr,"%s:%d getrlimit() is cur=%lu, max=%lu\n",
409 __FILE__,__LINE__, (unsigned long)limit.rlim_cur,
410 (unsigned long)limit.rlim_max);
411 abort();
412 }
413
414 /* Activate enable/disable functions */
415 oomtest_enable = oomtest_enable_postinit;
416 oomtest_disable = oomtest_disable_postinit;
417
418 /* Return the configured limit hard=soft */
419 return limit.rlim_max;
420}
421
422unsigned char oomtest_enabled()
423{
424 return (oomtest_fill==oomtest_fill_postinit?1:0);
425}
Debugging macros.
#define ASSERT(condition)
Assertion check macro.
Definition: assert.h:104
#define malloc(size)
Same syntaxt and same behavior than regular malloc function, with memory leaks tracking.
Definition: memory.h:32
#define free(ptr)
Same syntaxt and same behavior than regular free function, with memory leaks tracking.
Definition: memory.h:41
#define NULL
Definition: mkernel-opt.c:64
void(* oomtest_free)()
Ends a single simple OOM test.
Definition: oom.c:100
size_t(* oomtest_disable)()
Stops the current oomtest.
Definition: oom.c:138
#define RAMBLOCKS_MAX
Maximum number of fragmented blocks to allocate.
Definition: oom.c:47
#define __sync_synchronize
Definition: oom.c:24
#define __asm__
Definition: oom.c:21
size_t(* oomtest_fill)(const size_t minHeap, const size_t minStack)
Starts an almost OOM single and simple test.
Definition: oom.c:84
size_t(* oomtest_enable)(const size_t softlimit)
Starts a new oomtest environment or reconfigure the soft limit.
Definition: oom.c:120
unsigned char oomtest_enabled()
Definition: oom.c:422
size_t oomtest_config(const size_t hardlimit)
Sets the oomtest helpers hard rlimit and enables the oomtest helper features.
Definition: oom.c:367