diff options
| author | peter <peter@pmikkelsen.com> | 2026-09-04 19:45:05 +0000 |
|---|---|---|
| committer | peter <peter@pmikkelsen.com> | 2026-09-04 19:45:05 +0000 |
| commit | 4d1f519e1bcca659100a2593bd7ca7f2f4983ffe (patch) | |
| tree | df2b96f6bb871c8a52c8eda2ac2e2d87387d91bc | |
| parent | cde3778df1f5086052d01bf770910b2e05d0a1e1 (diff) | |
Add a test which runs all the scripts in tests/scripts/good to meaure how many memory allocations they perform, and then rerun them with a mode that causes the memory allocator to fail after N allocations. The scripts are run for all values of N from 0 to the number of allocations they need to succeed.
FossilOrigin-Name: ed1953c203d3493d9c1adbcb9256d7d380bc1fdcfc7856f03f9d801291f478cd
| -rw-r--r-- | CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/bin/aplwc_repl.h | 1 | ||||
| -rw-r--r-- | src/bin/main.c | 61 | ||||
| -rw-r--r-- | src/bin/util.c | 16 | ||||
| -rw-r--r-- | src/include/aplwc.h | 1 | ||||
| -rw-r--r-- | src/lib/main.c | 4 | ||||
| -rw-r--r-- | src/lib/memory.c | 11 | ||||
| -rwxr-xr-x | tests/scripts/memfail.sh | 13 |
8 files changed, 101 insertions, 16 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index efa3cc1..9631df4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ target_compile_options(common INTERFACE $<$<CONFIG:Coverage>: -O0 + -g --coverage -fcondition-coverage -DAPLWC_BUILD_TYPE_COVERAGE @@ -111,6 +112,11 @@ foreach(script ${TEST_SCRIPTS_GOOD}) set_tests_properties("aplwc-repl ${script}" PROPERTIES ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>" ) + + add_test(NAME "memfail ${script}" COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/memfail.sh" "${script}") + set_tests_properties("memfail ${script}" PROPERTIES + ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>" + ) endforeach() file(GLOB TEST_SCRIPTS_BAD CONFIGURE_DEPENDS "tests/scripts/bad/*.aplwc") @@ -128,6 +134,10 @@ foreach(script ${TEST_SCRIPTS_GOOD}) set_tests_properties("${script}" PROPERTIES ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>" ) + add_test(NAME "memfail ${script}" COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/memfail.sh" "${script}") + set_tests_properties("memfail ${script}" PROPERTIES + ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>" + ) endforeach() file(GLOB TEST_SCRIPTS_BAD CONFIGURE_DEPENDS "tests/scripts/bad/*.sh") diff --git a/src/bin/aplwc_repl.h b/src/bin/aplwc_repl.h index 7de1c59..31a0516 100644 --- a/src/bin/aplwc_repl.h +++ b/src/bin/aplwc_repl.h @@ -32,6 +32,7 @@ /* util.c */ unsigned long getenv_ulong(const char *, unsigned long); +long getenv_long(const char *, long); /* wrappers.c */ void *wrapped_malloc(size_t); diff --git a/src/bin/main.c b/src/bin/main.c index 02b3d0a..481874c 100644 --- a/src/bin/main.c +++ b/src/bin/main.c @@ -23,6 +23,9 @@ #include <errno.h> #include "aplwc_repl.h" +static uint64_t alloc_count; +static long memdbg; + static void * my_alloc(size_t min, size_t max, size_t *actual) { @@ -61,6 +64,16 @@ my_dealloc(void *p) free(p); } +static int +hook_alloc(void) +{ + alloc_count++; + if((memdbg >= 0) && (alloc_count > ((uint64_t)memdbg))) + return 0; + else + return 1; +} + int main(int argc, char *argv[]) { @@ -84,9 +97,12 @@ main(int argc, char *argv[]) exit_code = 0; line_number = 0; + memdbg = getenv_long("APLWC_ALLOC_FAIL_AFTER", -2); + memset(&fns, 0, sizeof(fns)); fns.alloc = my_alloc; fns.dealloc = my_dealloc; + fns.hook_alloc = hook_alloc; while((opt = getopt(argc, argv, "x")) != -1){ switch(opt){ @@ -96,7 +112,8 @@ main(int argc, char *argv[]) default: usage: fprintf(stderr, "Usage: %s [-x] [file]\n", argv[0]); - return 1; + exit_code = 1; + goto end; } } @@ -105,7 +122,8 @@ usage: f = fopen(argv[optind], "r"); if(f == NULL){ fprintf(stderr, "Could not open file: %s: %s\n", argv[optind], strerror(errno)); - return 1; + exit_code = 1; + goto end; } }else if(optind == argc) f = stdin; @@ -115,14 +133,16 @@ usage: aplwc = aplwc_new(fns, &ret); if(ret){ fprintf(stderr, "Couldn't create new aplwc instance: %s\n", aplwc_errstr_rc(ret)); - return 1; + exit_code = 1; + goto end; } if(input_file){ if(fseek(f, 0, SEEK_END)){ file_size: fprintf(stderr, "Failed to determine file size\n"); - return 1; + exit_code = 1; + goto end; } tmp = ftell(f); if(tmp < 0) @@ -132,11 +152,13 @@ file_size: input = malloc(input_size); if(input == NULL){ fprintf(stderr, "Failed to allocate space for file contents\n"); - return 1; + exit_code = 1; + goto end; } if(fread(input, 1, input_size, f) < input_size){ fprintf(stderr, "Failed to read file contents\n"); - return 1; + exit_code = 1; + goto end; } fclose(f); @@ -156,14 +178,15 @@ file_size: ret = aplwc_load_module(aplwc, "Main", src, src_size); if(ret){ fprintf(stderr, "Couldn't load Main module from file %s: %s\n", argv[optind], aplwc_errstr_rc(ret)); - return 1; + exit_code = 1; + goto end; } free(input); input = NULL; } if(xflag) - goto exit; + goto end; for(;;){ if(input) @@ -183,10 +206,11 @@ file_size: aplwc_read(aplwc, input, &line); if(!aplwc_valid_line(&line)){ line_error: + ret = aplwc_last_error(aplwc); fprintf(stderr, "error on line %d: %s: %s\n", line_number, aplwc_errstr(aplwc), input); if(input_file){ exit_code = 1; - goto exit; + goto end; } continue; } @@ -197,14 +221,29 @@ line_error: } } -exit: +end: if(input) free(input); if(aplwc_valid_line(&line)) aplwc_free_line(&line); if(aplwc_valid_value(&value)) aplwc_free_value(&value); + if(aplwc) + aplwc_destroy(aplwc); + + if(memdbg == -1){ + f = fdopen(3, "w"); + if(f == NULL){ + fprintf(stderr, "Failed to open fd 3\n"); + exit_code = 1; + }else{ + fprintf(f, "%ld\n", alloc_count); + fclose(f); + } + } + + if((exit_code != 0) && (memdbg >= 0) && (ret == APLWC_OUT_OF_MEMORY)) + exit_code = 0; - aplwc_destroy(aplwc); return exit_code; } diff --git a/src/bin/util.c b/src/bin/util.c index b887447..c609e48 100644 --- a/src/bin/util.c +++ b/src/bin/util.c @@ -34,3 +34,19 @@ getenv_ulong(const char *name, unsigned long def) } return ret; } + +long +getenv_long(const char *name, long def) +{ + long ret, parsed; + char *env, *p; + + ret = def; + env = getenv(name); + if((env != NULL) && (strlen(env) > 0)){ + parsed = strtol(env, &p, 10); + if((*p) == 0) + ret = parsed; + } + return ret; +} diff --git a/src/include/aplwc.h b/src/include/aplwc.h index cb4fe84..f07cd10 100644 --- a/src/include/aplwc.h +++ b/src/include/aplwc.h @@ -47,6 +47,7 @@ typedef struct { struct aplwc_ref ref; } AplwcValue; struct aplwc_fns { void *(*alloc)(size_t, size_t, size_t *); void (*dealloc)(void *); + int (*hook_alloc)(void); }; struct aplwc *aplwc_new(struct aplwc_fns, enum aplwc_rc *); diff --git a/src/lib/main.c b/src/lib/main.c index d2c353f..1bafa27 100644 --- a/src/lib/main.c +++ b/src/lib/main.c @@ -44,8 +44,10 @@ aplwc_new(struct aplwc_fns fns, enum aplwc_rc *rcp) rc = aplwc_load_module(aplwc, "Boot", (const char *)boot_src, boot_src_len); end: - if(rc != APLWC_OK) + if(rc != APLWC_OK){ aplwc_destroy(aplwc); + aplwc = NULL; + } assert(rcp); *rcp = rc; return aplwc; diff --git a/src/lib/memory.c b/src/lib/memory.c index b5bb82a..583b2ab 100644 --- a/src/lib/memory.c +++ b/src/lib/memory.c @@ -53,7 +53,7 @@ memory_init(struct aplwc *aplwc) void memory_destroy(struct aplwc *aplwc) { - if(aplwc->fns.dealloc) + if(aplwc->fns.dealloc && aplwc->unaligned_data) aplwc->fns.dealloc(aplwc->unaligned_data); } @@ -173,8 +173,7 @@ grow(struct aplwc *aplwc, size_t extra) if(aplwc->data){ memmove(aligned_data, aplwc->data, aplwc->size); fixrefs(aplwc, aligned_data, aplwc->data); - if(aplwc->fns.dealloc) - aplwc->fns.dealloc(aplwc->unaligned_data); + memory_destroy(aplwc); for(free = head; free; free = next(free)) if(next(free)) assert((free)->tag != CHUNK_TAG_FREE); @@ -380,7 +379,9 @@ alloc(struct aplwc *aplwc, size_t size, enum chunk_tag tag) if(pad) size += ALIGNMENT - pad; - + if(aplwc->fns.hook_alloc && !aplwc->fns.hook_alloc()) + goto fail; + attempt = 0; retry: DEBUG(check_mem(aplwc)); @@ -398,6 +399,8 @@ retry: goto retry; /* fall-through */ default: +fail: + aplwc->last_error = APLWC_OUT_OF_MEMORY; return NULL; } } diff --git a/tests/scripts/memfail.sh b/tests/scripts/memfail.sh new file mode 100755 index 0000000..3909ab8 --- /dev/null +++ b/tests/scripts/memfail.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +export APLWC_ALLOC_FAIL_AFTER=-1 +N=$($1 3>&1 1>/dev/null 2>/dev/null) + +for n in $(seq 0 $N) +do + echo $n out of $N + export APLWC_ALLOC_FAIL_AFTER=$n + $1 1>/dev/null 2>/dev/null +done |