diff options
-rw-r--r-- | include/git2/errors.h | 13 | ||||
-rw-r--r-- | include/git2/types.h | 9 | ||||
-rw-r--r-- | src/common.h | 9 | ||||
-rw-r--r-- | src/errors.c | 112 | ||||
-rw-r--r-- | src/errors.h | 48 | ||||
-rw-r--r-- | src/global.h | 6 | ||||
-rw-r--r-- | src/remote.c | 1 | ||||
-rw-r--r-- | tests-clay/object/tree/frompath.c | 3 | ||||
-rw-r--r-- | tests-clay/status/worktree.c | 1 | ||||
-rw-r--r-- | tests/t18-status.c | 2 | ||||
-rwxr-xr-x | tests/test_lib.c | 16 | ||||
-rwxr-xr-x | tests/test_lib.h | 1 |
12 files changed, 182 insertions, 39 deletions
diff --git a/include/git2/errors.h b/include/git2/errors.h index 5ac0d5b27..db71fc2ef 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -8,6 +8,7 @@ #define INCLUDE_git_errors_h__ #include "common.h" +#include "types.h" /** * @file git2/errors.h @@ -113,7 +114,7 @@ typedef enum { /** The buffer is too short to satisfy the request */ GIT_ESHORTBUFFER = -32, -} git_error; +} git_error_code; /** * Return a detailed error string with the latest error @@ -139,6 +140,16 @@ GIT_EXTERN(const char *) git_strerror(int num); */ GIT_EXTERN(void) git_clearerror(void); +GIT_EXTERN(void) git_error_free(git_error *err); + +/** + * Print a stack trace to stderr + * + * A bog standard stack trace. You can use it if you don't want to do + * anything more complex in your UI. + */ +GIT_EXTERN(void) git_error_print_stack(git_error *error_in); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/types.h b/include/git2/types.h index 1df18974a..03b5fcf3b 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -131,6 +131,15 @@ typedef struct git_reflog_entry git_reflog_entry; /** Representation of a reference log */ typedef struct git_reflog git_reflog; +/** Represensation of a git_error */ +typedef struct git_error { + int code; + char *msg; + struct git_error *child; + const char *file; + unsigned int line; +} git_error; + /** Time in a signature */ typedef struct git_time { git_time_t time; /** time in seconds from epoch */ diff --git a/src/common.h b/src/common.h index 727a08e77..f4dcc1ccd 100644 --- a/src/common.h +++ b/src/common.h @@ -50,14 +50,7 @@ typedef SSIZE_T ssize_t; #include "thread-utils.h" #include "bswap.h" -extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2); -#define git__throw(error, ...) \ - (git___throw(__VA_ARGS__), error) - -extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2); -#define git__rethrow(error, ...) \ - (git___rethrow(__VA_ARGS__), error) - +#include "errors.h" #include "util.h" #endif /* INCLUDE_common_h__ */ diff --git a/src/errors.c b/src/errors.c index 81770e786..22b8ae7bf 100644 --- a/src/errors.c +++ b/src/errors.c @@ -4,7 +4,10 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ + #include "common.h" +#include "errors.h" +#include "posix.h" #include "global.h" #include <stdarg.h> @@ -55,50 +58,109 @@ const char *git_strerror(int num) return "Unknown error"; } -#define ERROR_MAX_LEN 1024 +static git_error git_error_OOM = { + GIT_ENOMEM, + "out of memory", + NULL, + NULL, + -1 +}; + +git_error * git_error_oom(void) +{ + /* + * Throw an out-of-memory error: + * what we return is actually a static pointer, because on + * oom situations we cannot afford to allocate a new error + * object. + * + * The `git_error_free` function will take care of not + * freeing this special type of error. + * + */ + return &git_error_OOM; +} -void git___rethrow(const char *msg, ...) +git_error * git_error_createf(const char *file, unsigned int line, int code, + const char *fmt, ...) { - char new_error[ERROR_MAX_LEN]; - char *last_error; - char *old_error = NULL; + git_error *err; + va_list ap; + size_t size; - va_list va; + err = git__malloc(sizeof(git_error)); + if (err == NULL) + return git_error_oom(); - last_error = GIT_GLOBAL->error.last; + memset(err, 0x0, sizeof(git_error)); - va_start(va, msg); - vsnprintf(new_error, ERROR_MAX_LEN, msg, va); - va_end(va); + va_start(ap, fmt); + size = p_vsnprintf(err->msg, 0, fmt, ap); + va_end(ap); - old_error = git__strdup(last_error); + size++; - snprintf(last_error, ERROR_MAX_LEN, "%s \n - %s", new_error, old_error); + err->msg = git__malloc(size); + if (err->msg == NULL) { + free(err); + return git_error_oom(); + } - git__free(old_error); + va_start(ap, fmt); + size = p_vsnprintf(err->msg, size, fmt, ap); + va_end(ap); + + err->code = code; + err->child = GIT_GLOBAL->git_errno; + err->file = file; + err->line = line; + + GIT_GLOBAL->git_errno = err; + + return err; } -void git___throw(const char *msg, ...) +git_error * git_error__quick_wrap(const char *file, int line, + git_error_code error, const char *msg) { - va_list va; + if (error == GIT_SUCCESS) + return GIT_SUCCESS; - va_start(va, msg); - vsnprintf(GIT_GLOBAL->error.last, ERROR_MAX_LEN, msg, va); - va_end(va); + return git_error_createf(file, line, error, "%s", msg); } -const char *git_lasterror(void) +void git_error_free(git_error *err) { - char *last_error = GIT_GLOBAL->error.last; + if (err == NULL) + return; + + if (err->child) + git_error_free(err->child); - if (!last_error[0]) - return NULL; + if (err->msg) + free(err->msg); - return last_error; + free(err); } void git_clearerror(void) { - char *last_error = GIT_GLOBAL->error.last; - last_error[0] = '\0'; + git_error_free(GIT_GLOBAL->git_errno); + GIT_GLOBAL->git_errno = NULL; +} + +const char *git_lasterror(void) +{ + return GIT_GLOBAL->git_errno == NULL ? NULL : GIT_GLOBAL->git_errno->msg; +} + +void git_error_print_stack(git_error *error_in) +{ + git_error *error; + + if (error_in == NULL) + error_in = GIT_GLOBAL->git_errno; + + for (error = error_in; error; error = error->child) + fprintf(stderr, "%s:%u %s\n", error->file, error->line, error->msg); } diff --git a/src/errors.h b/src/errors.h new file mode 100644 index 000000000..525cb0f09 --- /dev/null +++ b/src/errors.h @@ -0,0 +1,48 @@ +#ifndef INCLUDE_errors_h__ +#define INCLUDE_errors_h__ + +#include "git2/common.h" + +/* Deprecated - please use the more advanced functions below. */ +#define git__throw(error, ...) \ + (git_error_createf(__FILE__, __LINE__, error, __VA_ARGS__), error) + +#define git__rethrow(error, ...) \ + (git_error_createf(__FILE__, __LINE__, error, __VA_ARGS__), error) + +/* + * This implementation is loosely based on subversion's error + * handling. + */ + +git_error * git_error_createf(const char *file, unsigned int line, int code, + const char *msg, ...) GIT_FORMAT_PRINTF(4, 5); + +git_error * git_error__quick_wrap(const char *file, int line, + git_error_code error, const char *msg); + +/* + * Wrap an error with a message. All git_error values are assigned with + * child's fields. + */ +#define git_error_quick_wrap(error, message) \ + git_error__quick_wrap(__FILE__, __LINE__, error, message) + +/* + * Use this function to wrap functions like + * + * git_error * foo(void) + * { + * return git_error_trace(bar()); + * } + * + * Otherwise the call of foo() wouldn't be visible in the trace. + * + */ +#define git_error_trace(error) \ + git_error_quick_wrap(error, "traced error"); + +/* Throw an out-of-memory error */ +extern git_error * git_error_oom(void); + +#endif /* INCLUDE_errors_h__ */ diff --git a/src/global.h b/src/global.h index 641f47cbc..6a15a8d02 100644 --- a/src/global.h +++ b/src/global.h @@ -8,12 +8,10 @@ #define INCLUDE_global_h__ #include "mwindow.h" +#include "git2/types.h" typedef struct { - struct { - char last[1024]; - } error; - + git_error *git_errno; git_mwindow_ctl mem_ctl; } git_global_st; diff --git a/src/remote.c b/src/remote.c index 3ff08a21e..51e77e584 100644 --- a/src/remote.c +++ b/src/remote.c @@ -14,6 +14,7 @@ #include "remote.h" #include "fetch.h" #include "refs.h" +#include "error.h" static int refspec_parse(git_refspec *refspec, const char *str) { diff --git a/tests-clay/object/tree/frompath.c b/tests-clay/object/tree/frompath.c index 1effcb1db..651a86d86 100644 --- a/tests-clay/object/tree/frompath.c +++ b/tests-clay/object/tree/frompath.c @@ -23,9 +23,10 @@ void test_object_tree_frompath__cleanup(void) { git_tree_close(tree); git_repository_free(repo); + git_clearerror(); } -static void assert_tree_from_path(git_tree *root, const char *path, git_error expected_result, const char *expected_raw_oid) +static void assert_tree_from_path(git_tree *root, const char *path, int expected_result, const char *expected_raw_oid) { git_tree *containing_tree = NULL; diff --git a/tests-clay/status/worktree.c b/tests-clay/status/worktree.c index 1e8a5ddbc..7d120ecb5 100644 --- a/tests-clay/status/worktree.c +++ b/tests-clay/status/worktree.c @@ -92,6 +92,7 @@ void test_status_worktree__cleanup(void) _repository = NULL; cl_fixture_cleanup("status"); + git_clearerror(); } /** diff --git a/tests/t18-status.c b/tests/t18-status.c index 73e328c2c..3b75472ad 100644 --- a/tests/t18-status.c +++ b/tests/t18-status.c @@ -430,6 +430,8 @@ BEGIN_TEST(singlestatus4, "can't determine the status for a folder") git_repository_free(repo); git_futils_rmdir_r(TEMP_REPO_FOLDER, 1); + + git_clearerror(); END_TEST BEGIN_SUITE(status) diff --git a/tests/test_lib.c b/tests/test_lib.c index a4c39dfde..9d3cba1a6 100755 --- a/tests/test_lib.c +++ b/tests/test_lib.c @@ -10,12 +10,15 @@ #define DO_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) #define GIT_MAX_TEST_CASES 64 +extern git_error *git_errno; + struct git_test { char *name; char *message; char *failed_pos; char *description; char *error_message; + git_error *error_stack; git_testfunc function; unsigned failed:1, ran:1; @@ -36,6 +39,7 @@ static void test_free(git_test *t) free(t->failed_pos); free(t->message); free(t->error_message); + git_error_free(t->error_stack); free(t); } } @@ -84,6 +88,8 @@ static void fail_test(git_test *tc, const char *file, int line, const char *mess tc->failed = 1; tc->message = strdup(message); tc->failed_pos = strdup(buf); + tc->error_stack = GIT_GLOBAL->git_errno; + GIT_GLOBAL->git_errno = NULL; if (last_error) tc->error_message = strdup(last_error); @@ -146,6 +152,14 @@ void git_testsuite_add(git_testsuite *ts, git_testfunc test) ts->list[ts->count++] = create_test(test); } +static void print_trace(git_error *error) +{ + git_error *err; + + for (err = error; err; err = err->child) + printf("\t%s:%u %s\n", err->file, err->line, err->msg); +} + static void print_details(git_testsuite *ts) { int i; @@ -165,6 +179,8 @@ static void print_details(git_testsuite *ts) failCount, tc->description, tc->name, tc->failed_pos, tc->message); if (tc->error_message) printf("\tError: %s\n", tc->error_message); + fprintf(stderr, "\tError stack trace:\n"); + print_trace(tc->error_stack); } } } diff --git a/tests/test_lib.h b/tests/test_lib.h index 9d90e4847..7552ebf12 100755 --- a/tests/test_lib.h +++ b/tests/test_lib.h @@ -7,6 +7,7 @@ #include <string.h> #include "common.h" +#include "global.h" #include <git2.h> #define DECLARE_SUITE(SNAME) extern git_testsuite *libgit2_suite_##SNAME(void) |