From 07da7f3238a980f1aa9654c2d084a24e00e48e90 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Sun, 26 Nov 2017 22:29:59 +0100 Subject: [PATCH] save --- lib/ecoli_init.h | 3 +- lib/ecoli_keyval.c | 12 ++- lib/ecoli_malloc.c | 38 +++++--- lib/ecoli_malloc.h | 206 +++++++++++++++++++++++++++++++++++++------- lib/main-readline.c | 2 +- lib/main.c | 13 ++- lib/test.sh | 2 +- 7 files changed, 222 insertions(+), 54 deletions(-) diff --git a/lib/ecoli_init.h b/lib/ecoli_init.h index 9166cdc..b70e4dc 100644 --- a/lib/ecoli_init.h +++ b/lib/ecoli_init.h @@ -68,7 +68,8 @@ void ec_init_register(struct ec_init *test); /** * Initialize ecoli library * - * Must be called before any other function. + * Must be called before any other function from libecoli, except + * ec_malloc_register(). * * @return * 0 on success, -1 on error (errno is set). diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c index 9baba5f..dbc5522 100644 --- a/lib/ecoli_keyval.c +++ b/lib/ecoli_keyval.c @@ -323,7 +323,7 @@ static int ec_keyval_testcase(void) EC_TEST_ASSERT(ec_keyval_len(keyval) == 0); ret = ec_keyval_set(keyval, "key1", "val1", NULL); EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); - ret = ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2); + ret = ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free_func); EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); EC_TEST_ASSERT(ec_keyval_len(keyval) == 2); @@ -336,7 +336,8 @@ static int ec_keyval_testcase(void) ret = ec_keyval_set(keyval, "key1", "another_val1", NULL); EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); - ret = ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2); + ret = ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), + ec_free_func); EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); EC_TEST_ASSERT(ec_keyval_len(keyval) == 2); @@ -359,6 +360,13 @@ static int ec_keyval_testcase(void) ret = ec_keyval_set(keyval, buf, "val", NULL); EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); } + + /* einval */ + ret = ec_keyval_set(keyval, NULL, "val1", NULL); + EC_TEST_ASSERT(ret == -1); + val = ec_keyval_get(keyval, NULL); + EC_TEST_ASSERT(val == NULL); + ec_keyval_free(keyval); diff --git a/lib/ecoli_malloc.c b/lib/ecoli_malloc.c index 7c6f489..3113f0e 100644 --- a/lib/ecoli_malloc.c +++ b/lib/ecoli_malloc.c @@ -25,20 +25,29 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#include #include #include #include #include +static int init_done = 0; + struct ec_malloc_handler ec_malloc_handler; int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, ec_realloc_t usr_realloc) { - if (usr_malloc == NULL || usr_free == NULL || usr_realloc == NULL) + if (usr_malloc == NULL || usr_free == NULL || usr_realloc == NULL) { + errno = EINVAL; + return -1; + } + + if (init_done) { + errno = EBUSY; return -1; + } ec_malloc_handler.malloc = usr_malloc; ec_malloc_handler.free = usr_free; @@ -47,20 +56,12 @@ int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, return 0; } -void ec_malloc_unregister(void) -{ - /* XXX handlers must set errno on error */ - ec_malloc_handler.malloc = NULL; - ec_malloc_handler.free = NULL; - ec_malloc_handler.realloc = NULL; -} - void *__ec_malloc(size_t size, const char *file, unsigned int line) { return ec_malloc_handler.malloc(size, file, line); } -void *ec_malloc2(size_t size) +void *ec_malloc_func(size_t size) { return __ec_malloc(size, __FILE__, __LINE__); } @@ -70,7 +71,7 @@ void __ec_free(void *ptr, const char *file, unsigned int line) ec_malloc_handler.free(ptr, file, line); } -void ec_free2(void *ptr) +void ec_free_func(void *ptr) { __ec_free(ptr, __FILE__, __LINE__); } @@ -129,3 +130,16 @@ char *__ec_strndup(const char *s, size_t n, const char *file, unsigned int line) return s2; } + +static int ec_malloc_init_func(void) +{ + init_done = 1; + return 0; +} + +static struct ec_init ec_malloc_init = { + .init = ec_malloc_init_func, + .priority = 50, +}; + +EC_INIT_REGISTER(ec_malloc_init); diff --git a/lib/ecoli_malloc.h b/lib/ecoli_malloc.h index 76b63e0..748601a 100644 --- a/lib/ecoli_malloc.h +++ b/lib/ecoli_malloc.h @@ -25,6 +25,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * Interface to configure the allocator used by libecoli. + * By default, the standard allocation functions from libc are used. + */ + #ifndef ECOLI_MALLOC_ #define ECOLI_MALLOC_ @@ -32,11 +37,81 @@ #include #include +/** + * Function type of malloc, passed to ec_malloc_register(). + * + * The API is the same than malloc(), excepted the file and line + * arguments. + * + * @param size + * The size of the memory area to allocate. + * @param file + * The path to the file that invoked the malloc. + * @param line + * The line in the file that invoked the malloc. + * @return + * A pointer to the allocated memory area, or NULL on error (errno + * is set). + */ typedef void *(*ec_malloc_t)(size_t size, const char *file, unsigned int line); + +/** + * Function type of free, passed to ec_malloc_register(). + * + * The API is the same than free(), excepted the file and line + * arguments. + * + * @param ptr + * The pointer to the memory area to be freed. + * @param file + * The path to the file that invoked the malloc. + * @param line + * The line in the file that invoked the malloc. + */ typedef void (*ec_free_t)(void *ptr, const char *file, unsigned int line); + +/** + * Function type of realloc, passed to ec_malloc_register(). + * + * The API is the same than realloc(), excepted the file and line + * arguments. + * + * @param ptr + * The pointer to the memory area to be reallocated. + * @param file + * The path to the file that invoked the malloc. + * @param line + * The line in the file that invoked the malloc. + * @return + * A pointer to the allocated memory area, or NULL on error (errno + * is set). + */ typedef void *(*ec_realloc_t)(void *ptr, size_t size, const char *file, unsigned int line); +/** + * Register allocation functions. + * + * This function can be use to register another allocator + * to be used by libecoli. By default, ec_malloc(), ec_free() and + * ec_realloc() use the standard libc allocator. Another handler + * can be used for debug purposes or when running in a specific + * environment. + * + * This function must be called before ec_init(). + * + * @param usr_malloc + * A user-defined malloc function. + * @param usr_free + * A user-defined free function. + * @param usr_realloc + * A user-defined realloc function. + * @return + * 0 on success, or -1 on error (errno is set). + */ +int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, + ec_realloc_t usr_realloc); + struct ec_malloc_handler { ec_malloc_t malloc; ec_free_t free; @@ -45,36 +120,44 @@ struct ec_malloc_handler { extern struct ec_malloc_handler ec_malloc_handler; -int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, - ec_realloc_t usr_realloc); -void ec_malloc_unregister(void); - -/* internal */ -void *__ec_malloc(size_t size, const char *file, unsigned int line); -void __ec_free(void *ptr, const char *file, unsigned int line); -void *__ec_calloc(size_t nmemb, size_t size, const char *file, - unsigned int line); -void *__ec_realloc(void *ptr, size_t size, const char *file, unsigned int line); -char *__ec_strdup(const char *s, const char *file, unsigned int line); -char *__ec_strndup(const char *s, size_t n, const char *file, - unsigned int line); - -/* XXX rename into ec_malloc, and change macro to uppercase */ -void *ec_malloc2(size_t size); -void ec_free2(void *ptr); - -/* we use macros here to ensure the file/line stay correct when - * debugging the standard malloc with valgrind */ - -#define ec_malloc(sz) ({ \ +/** + * Allocate a memory area. + * + * Like malloc(), ec_malloc() allocates size bytes and returns a pointer + * to the allocated memory. The memory is not initialized. The memory is + * freed with ec_free(). + * + * @param size + * The size of the area to allocate in bytes. + * @return + * The pointer to the allocated memory, or NULL on error (errno is set). + */ +#define ec_malloc(size) ({ \ void *ret_; \ if (ec_malloc_handler.malloc == NULL) \ - ret_ = malloc(sz); \ + ret_ = malloc(size); \ else \ - ret_ = __ec_malloc(sz, __FILE__, __LINE__); \ + ret_ = __ec_malloc(size, __FILE__, __LINE__); \ ret_; \ }) +/** + * Ecoli malloc function. + * + * On use this function when the macro ec_malloc() cannot be used. + */ +void *ec_malloc_func(size_t size); + +/** + * Free a memory area. + * + * Like free(), ec_free() frees the area pointed by ptr, which must have + * been returned by a previous call to ec_malloc() or any other + * allocation function of this file. + * + * @param ptr + * The pointer to the memory area. + */ #define ec_free(ptr) ({ \ if (ec_malloc_handler.free == NULL) \ free(ptr); \ @@ -82,24 +165,63 @@ void ec_free2(void *ptr); __ec_free(ptr, __FILE__, __LINE__); \ }) -#define ec_realloc(ptr, sz) ({ \ +/** + * Ecoli free function. + * + * On use this function when the macro ec_free() cannot be used. + */ +void ec_free_func(void *ptr); + +/** + * Resize an allocated memory area. + * + * @param ptr + * The pointer to the previously allocated memory area, or NULL. + * @param size + * The new size of the memory area. + * @return + * A pointer to the newly allocated memory, or NULL if the request + * fails. In that case, the original area is left untouched. + */ +#define ec_realloc(ptr, size) ({ \ void *ret_; \ if (ec_malloc_handler.realloc == NULL) \ - ret_ = realloc(ptr, sz); \ + ret_ = realloc(ptr, size); \ else \ - ret_ = __ec_realloc(ptr, sz, __FILE__, __LINE__); \ + ret_ = __ec_realloc(ptr, size, __FILE__, __LINE__); \ ret_; \ }) -#define ec_calloc(n, sz) ({ \ +/** + * Allocate and initialize an array of elements. + * + * @param n + * The number of elements. + * @param size + * The size of each element. + * @return + * The pointer to the allocated memory, or NULL on error (errno is set). + */ +#define ec_calloc(n, size) ({ \ void *ret_; \ if (ec_malloc_handler.malloc == NULL) \ - ret_ = calloc(n, sz); \ + ret_ = calloc(n, size); \ else \ - ret_ = __ec_calloc(n, sz, __FILE__, __LINE__); \ + ret_ = __ec_calloc(n, size, __FILE__, __LINE__); \ ret_; \ }) +/** + * Duplicate a string. + * + * Memory for the new string is obtained with ec_malloc(), and can be + * freed with ec_free(). + * + * @param s + * The string to be duplicated. + * @return + * The pointer to the duplicated string, or NULL on error (errno is set). + */ #define ec_strdup(s) ({ \ void *ret_; \ if (ec_malloc_handler.malloc == NULL) \ @@ -109,6 +231,20 @@ void ec_free2(void *ptr); ret_; \ }) +/** + * Duplicate at most n bytes of a string. + * + * This function is similar to ec_strdup(), except that it copies at + * most n bytes. If s is longer than n, only n bytes are copied, and a + * terminating null byte ('\0') is added. + * + * @param s + * The string to be duplicated. + * @param n + * The maximum length of the new string. + * @return + * The pointer to the duplicated string, or NULL on error (errno is set). + */ #define ec_strndup(s, n) ({ \ void *ret_; \ if (ec_malloc_handler.malloc == NULL) \ @@ -118,5 +254,15 @@ void ec_free2(void *ptr); ret_; \ }) +/* internal */ +void *__ec_malloc(size_t size, const char *file, unsigned int line); +void __ec_free(void *ptr, const char *file, unsigned int line); +void *__ec_calloc(size_t nmemb, size_t size, const char *file, + unsigned int line); +void *__ec_realloc(void *ptr, size_t size, const char *file, unsigned int line); +char *__ec_strdup(const char *s, const char *file, unsigned int line); +char *__ec_strndup(const char *s, size_t n, const char *file, + unsigned int line); + #endif diff --git a/lib/main-readline.c b/lib/main-readline.c index 80ef88d..87653f7 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -260,7 +260,7 @@ static int create_commands(void) ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the person to greet", NULL); ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "count")), "help", - "how many times to greet", NULL); + "how many times to greet (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; diff --git a/lib/main.c b/lib/main.c index b44a47d..4a48d1e 100644 --- a/lib/main.c +++ b/lib/main.c @@ -386,12 +386,6 @@ int main(int argc, char **argv) srandom(seed); - if (ec_init() < 0) { - fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); - return 1; - } - ec_log_fct_register(debug_log, NULL); - /* register a new malloc to track memleaks */ TAILQ_INIT(&debug_alloc_hdr_list); if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) { @@ -399,6 +393,12 @@ int main(int argc, char **argv) return 1; } + if (ec_init() < 0) { + fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); + return 1; + } + ec_log_fct_register(debug_log, NULL); + ret = 0; if (argc <= 1) { ret = ec_test_all(); @@ -407,7 +407,6 @@ int main(int argc, char **argv) ret |= ec_test_one(argv[i]); } - ec_malloc_unregister(); leaks = debug_alloc_dump_leaks(); if (alloc_fail_proba == 0 && ret != 0) { diff --git a/lib/test.sh b/lib/test.sh index 3d97e77..9d78a68 100755 --- a/lib/test.sh +++ b/lib/test.sh @@ -4,7 +4,7 @@ set -e SEED=100 while [ ${SEED} -gt 0 ]; do - CMD="./build/test --random-alloc-fail=1 --seed=${SEED}" + CMD="./build/test --random-alloc-fail=1 --seed=${SEED} $*" ${CMD} --log-level=0 || ( echo "=== test failed, replay seed=${SEED} with logs ===" && ${CMD} --log-level=6 || -- 2.20.1