diff options
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | LICENSE | 15 | ||||
-rw-r--r-- | Makefile | 75 | ||||
-rw-r--r-- | config.mk | 6 | ||||
-rw-r--r-- | copy_error.c | 31 | ||||
-rw-r--r-- | free_error.c | 15 | ||||
-rw-r--r-- | get_error.c | 11 | ||||
-rw-r--r-- | internal.c | 6 | ||||
-rw-r--r-- | internal.h | 34 | ||||
-rw-r--r-- | liberror.h | 179 | ||||
-rw-r--r-- | linux.mk | 5 | ||||
-rw-r--r-- | macos.mk | 5 | ||||
-rw-r--r-- | print_backtrace.c | 11 | ||||
-rw-r--r-- | print_error.c | 68 | ||||
-rw-r--r-- | reset_error.c | 10 | ||||
-rw-r--r-- | save_backtrace.c | 10 | ||||
-rw-r--r-- | set_error.c | 33 | ||||
-rw-r--r-- | set_error_errno.c | 9 |
18 files changed, 533 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..983233b --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*\#* +*~ +*.o +*.a +*.lo +*.so +*.so.* +*.su +*.dll +*.dylib @@ -0,0 +1,15 @@ +ISC License + +© 2019 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f2157ad --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# linux = Linux +# macos = Mac OS +include $(OS).mk + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) + + +HDR =\ + liberror.h\ + internal.h + +OBJ =\ + copy_error.o\ + free_error.o\ + get_error.o\ + internal.o\ + print_backtrace.o\ + print_error.o\ + reset_error.o\ + save_backtrace.o\ + set_error.o\ + set_error_errno.o + +LOBJ = $(OBJ:.o=.lo) + + +all: liberror.a liberror.$(LIBEXT) +$(OBJ): $(@:.o=.c) $(HDR) +$(LOBJ): $(@:.lo=.c) $(HDR) + +liberror.a: $(OBJ) + -rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) s $@ + +liberror.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -c -o $@ $< -fPIC $(CFLAGS) $(CPPFLAGS) + +install: liberror.a liberror.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + mkdir -p -- "$(DESTDIR)$(PREFIX)/licenses/liberror" + cp -- liberror.a "$(DESTDIR)$(PREFIX)/lib" + cp -- liberror.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBMINOREXT)" + ln -sf -- liberror.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBMAJOREXT)" + ln -sf -- liberror.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBEXT)" + cp -- liberror.h "$(DESTDIR)$(PREFIX)/include" + cp -- LICENSE "$(DESTDIR)$(PREFIX)/licenses/liberror" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/liberror."* + -rm -f -- "$(DESTDIR)$(PREFIX)/include/liberror.h" + -rm -rf -- "$(DESTDIR)$(PREFIX)/licenses/liberror" + +clean: + -rm -f -- *.o *.lo *.a *.so *.so.* *.su *.test + +.SUFFIXES: +.SUFFIXES: .c .o .lo .a + +.PHONY: all install uninstall clean diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..fdfd458 --- /dev/null +++ b/config.mk @@ -0,0 +1,6 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS = -std=c11 -Wall -pedantic +LDFLAGS = -s diff --git a/copy_error.c b/copy_error.c new file mode 100644 index 0000000..8d93131 --- /dev/null +++ b/copy_error.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +struct liberror_error * +liberror_copy_error(struct liberror_error *src) +{ + struct liberror_error *dest; + int saved_errno; + if (!src) + return NULL; + saved_errno = errno; + dest = malloc(sizeof(*dest)); + if (dest) { + memcpy(dest, src, sizeof(*src)); + if (dest->backtrace) + dest->backtrace->refcount += 1; + dest->dynamically_allocated = 1; + if (src->cause) { + dest->cause = liberror_copy_error(src->cause); + if (!dest->cause) { + if (dest->backtrace) + dest->backtrace->refcount -= 1; + free(dest); + dest = NULL; + } + } + } + errno = saved_errno; + return dest; +} diff --git a/free_error.c b/free_error.c new file mode 100644 index 0000000..a15d36c --- /dev/null +++ b/free_error.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +void +liberror_free_error(struct liberror_error *error) +{ + if (!error) + return; + if (error->backtrace && !--error->backtrace->refcount) + free(error->backtrace); + liberror_free_error(error->cause); + if (error->dynamically_allocated) + free(error); +} diff --git a/get_error.c b/get_error.c new file mode 100644 index 0000000..4ee3dbc --- /dev/null +++ b/get_error.c @@ -0,0 +1,11 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +struct liberror_error * +liberror_get_error(void) +{ + if (liberror_have_error_) + return &liberror_error_; + return NULL; +} diff --git a/internal.c b/internal.c new file mode 100644 index 0000000..034a2db --- /dev/null +++ b/internal.c @@ -0,0 +1,6 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +_Thread_local struct liberror_error liberror_error_; +_Thread_local int liberror_have_error_; diff --git a/internal.h b/internal.h new file mode 100644 index 0000000..a77aa43 --- /dev/null +++ b/internal.h @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include "liberror.h" + +#include <alloca.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +#if defined(__GNUC__) +# define GCC_ATTRIBUTES(...) __attribute__((__VA_ARGS__)) +#else +# define GCC_ATTRIBUTES(...) +#endif + +#define HIDDEN GCC_ATTRIBUTES(__visibility__("hidden")) +#define WEAK GCC_ATTRIBUTES(__weak__) + + +struct liberror_backtrace { + size_t refcount; + size_t n; + uintptr_t rips[]; +}; + + +HIDDEN extern _Thread_local struct liberror_error liberror_error_; +HIDDEN extern _Thread_local int liberror_have_error_; + + +int liberror_save_backtrace_(struct liberror_error *); diff --git a/liberror.h b/liberror.h new file mode 100644 index 0000000..27c700d --- /dev/null +++ b/liberror.h @@ -0,0 +1,179 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBERROR_H +#define LIBERROR_H + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + + +/** + * Opaque backtrace structure + */ +struct liberror_backtrace; + +/** + * Error structure + */ +struct liberror_error { + /** + * Backtrace for the error, `NULL` if the it could + * not be allocated or if the program is not linked + * with `-lerror-backtrace` + */ + struct liberror_backtrace *backtrace; + + /** + * Description of the error + */ + char description[256]; + + /** + * The function that failed + */ + char source[64]; + + /** + * Name of error code group, for normal `errno` + * errors this is "error", for getaddrinfo(3) errors + * this is "addrinfo", for custom errors it is the + * name of the library or application + */ + char code_group[64]; + + /** + * The error code + */ + long long int code; + + /** + * The error that caused this error, `NULL` if + * none or it could not be allocated (if and only + * if so, `.failed_to_allocate_cause` will be set + * to a non-zero value, specifically 1) + */ + struct liberror_error *cause; + + /** + * Whether allocation of `.cause` failed + */ + int failed_to_allocate_cause; + + /** + * Whether the error is physically allocated + */ + int dynamically_allocated; +}; + + +/** + * Get the current error for the thread + * + * @return The current error, `NULL` if none + */ +struct liberror_error *liberror_get_error(void); + +/** + * Create a copy of an error + * + * This function will only fail of enough memory + * cannot be allocated, however `errno` will not + * be changed + * + * @param error The error to copy + * @return Copy of the error, `NULL` on failure + */ +struct liberror_error *liberror_copy_error(struct liberror_error *); + +/** + * Deallocate and error and the error stored as + * its cause (recursively) + * + * @param error The error to deallocate + */ +void liberror_free_error(struct liberror_error *); + +/** + * Deallocate the current error for the thread + * and reset the error for the thread + * + * This function shall be called after handling + * the error + */ +void liberror_reset_error(void); + +/** + * Print the backtrace of an error + * + * If the backtrace could not be allocated, + * nothing will be printed + * + * This function will never change `errno` + * + * Note: this library does not actually save + * a backtrace, `-lerror-backtrace` is needed + * for that functionallity (it will replace + * some things in this library, so no other + * action is required) + * + * @param error The error + * @param fp The file to print the backtrace to + * @param indent Text to print at the beginning of each line + */ +void liberror_print_backtrace(struct liberror_error *, FILE *, const char *); + +/** + * Set the current error for the thread + * + * If the thread already has an error saved, + * it will be stored as the cause of the new + * error + * + * @param description Description of the error, empty for default description + * @param source The function that failed + * @param code_group Name of error code group, for normal `errno` errors + * this shall be "error", for getaddrinfo(3) errors + * this shall be "addrinfo", for custom errors it shall + * be the name of the library or application + * @param code The error code + */ +void liberror_set_error(const char[256], const char[64], const char[64], long long int); + +/** + * Set the current error for the thread + * + * This function can be used as an alternative + * to `liberror_set_error` for `errno` errors + * + * If the thread already has an error saved, + * it will be stored as the cause of the new + * error + * + * @param description Description of the error, empty for default description + * @param source The function that failed + * @param code The `errno` value + */ +void liberror_set_error_errno(const char[256], const char[64], int); + +/** + * The an error, its backtrace, and its + * cause (recursively) + * + * If `error` is `NULL` and the thread does + * not have any error set, this function + * will not do anything + * + * @param error The error, the thread's current error if `NULL` + * @param fp Output stream, standard error if `NULL` + * @param reset Whether `error` shall be deallocated, `error` + * is null and `reset` is non-zero, the thread's + * error will be reset + * @param prefix Prefix for each printed line, ": " will be + * appended to this prefix; if `NULL` or empty, + * no prefix is used; this should normally be + * `argv[0]` from the main() function + */ +void liberror_print_error(struct liberror_error *, FILE *, int, const char *); + + +#endif diff --git a/linux.mk b/linux.mk new file mode 100644 index 0000000..c9f74a0 --- /dev/null +++ b/linux.mk @@ -0,0 +1,5 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,libkeccak.$(LIBEXT).$(LIB_MAJOR) + +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) diff --git a/macos.mk b/macos.mk new file mode 100644 index 0000000..b475197 --- /dev/null +++ b/macos.mk @@ -0,0 +1,5 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib + +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) diff --git a/print_backtrace.c b/print_backtrace.c new file mode 100644 index 0000000..d69dafd --- /dev/null +++ b/print_backtrace.c @@ -0,0 +1,11 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +WEAK void +liberror_print_backtrace(struct liberror_error *error, FILE *fp, const char *indent) +{ + (void) error; + (void) fp; + (void) indent; +} diff --git a/print_error.c b/print_error.c new file mode 100644 index 0000000..4675bab --- /dev/null +++ b/print_error.c @@ -0,0 +1,68 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +static void +print_error_description(struct liberror_error *error, FILE *fp, const char *prefix) +{ + if (*error->description) { + if (*error->source) + fprintf(fp, "%sError in function %s: %s\n", prefix, error->source, error->description); + else + fprintf(fp, "%sError: %s\n", prefix, error->description); + } else if (*error->source) { + fprintf(fp, "%sError in function %s: %s error %lli\n", prefix, error->source, error->code_group, error->code); + } else { + fprintf(fp, "%sError: %s error %lli\n", prefix, error->code_group, error->code); + } +} + + +void +liberror_print_error(struct liberror_error *error, FILE *fp, int reset, const char *prefix_) +{ + struct liberror_error *err = error; + char *prefix = (char []){" "}; + char *p, *q; + + if (!err) { + err = liberror_get_error(); + if (!err) + return; + } + + if (prefix_ && *prefix_) { + prefix = alloca(strlen(prefix) + sizeof(": ")); + stpcpy(q = stpcpy(p = stpcpy(stpcpy(prefix, prefix_), ": "), " "), " "); + } else { + p = &prefix[0]; + q = &prefix[2]; + } + + if (!fp) + fp = stderr; + + *p = *q = '\0'; + print_error_description(err, fp, prefix); + + *p = ' '; + liberror_print_backtrace(err, fp, prefix); + + while ((err = err->cause)) { + *p = *q = '\0'; + fprintf(fp, "%sCaused by:\n", prefix); + + *p = ' '; + print_error_description(err, fp, prefix); + + *q = ' '; + liberror_print_backtrace(err, fp, prefix); + } + + if (reset) { + if (error) + liberror_free_error(error); + else + liberror_reset_error(); + } +} diff --git a/reset_error.c b/reset_error.c new file mode 100644 index 0000000..502d149 --- /dev/null +++ b/reset_error.c @@ -0,0 +1,10 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +void +liberror_reset_error(void) +{ + liberror_free_error(liberror_get_error()); + liberror_have_error_ = 0; +} diff --git a/save_backtrace.c b/save_backtrace.c new file mode 100644 index 0000000..7df2fb5 --- /dev/null +++ b/save_backtrace.c @@ -0,0 +1,10 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +WEAK int +liberror_save_backtrace(struct liberror_error *error) +{ + (void) error; + return 0; +} diff --git a/set_error.c b/set_error.c new file mode 100644 index 0000000..5ee54ce --- /dev/null +++ b/set_error.c @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +void +liberror_set_error(const char description[256], const char source[64], const char code_group[64], long long int code) +{ + struct liberror_error *cause; + struct liberror_error *error; + int have_cause; + + cause = liberror_get_error(); + have_cause = !!cause; + cause = liberror_copy_error(cause); + + error = &liberror_error_; + liberror_have_error_ = 1; + + memset(error, 0, sizeof(*error)); + + if (*description) { + stpcpy(error->description, description); + } else if (!strcmp(code_group, "errno")) { + if (code >= (long long int)INT_MIN && code <= (long long int)INT_MAX) + strerror_r((int)code, error->description, sizeof(error->description)); + } + stpcpy(error->source, source); + stpcpy(error->code_group, code_group); + error->code = code; + liberror_save_backtrace_(error); + error->cause = cause; + error->failed_to_allocate_cause = have_cause && !cause; +} diff --git a/set_error_errno.c b/set_error_errno.c new file mode 100644 index 0000000..1b8347a --- /dev/null +++ b/set_error_errno.c @@ -0,0 +1,9 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + + +void +liberror_set_error_errno(const char description[256], const char source[64], int code) +{ + liberror_set_error(description, source, "errno", (long long int)code); +} |