From b5ec5430a8cad68c1c534568a5a7047e42f75580 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 4 Mar 2013 23:52:30 -0600 Subject: [PATCH] optional tracing --- CMakeLists.txt | 6 +++ include/git2/trace.h | 68 +++++++++++++++++++++++++++++++ src/trace.c | 39 ++++++++++++++++++ src/trace.h | 56 ++++++++++++++++++++++++++ tests-clar/trace/trace.c | 86 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 include/git2/trace.h create mode 100644 src/trace.c create mode 100644 src/trace.h create mode 100644 tests-clar/trace/trace.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a0043f95..7f2e293d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ OPTION( BUILD_CLAR "Build Tests using the Clar suite" ON ) OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF ) OPTION( TAGS "Generate tags" OFF ) OPTION( PROFILE "Generate profiling information" OFF ) +OPTION( ENABLE_TRACE "Enables tracing support" OFF ) IF(MSVC) # This option is only availalbe when building with MSVC. By default, # libgit2 is build using the stdcall calling convention, as that's what @@ -105,6 +106,11 @@ ELSE() FILE(GLOB SRC_SHA1 src/hash/hash_generic.c) ENDIF() +# Enable tracing +IF (ENABLE_TRACE STREQUAL "ON") + ADD_DEFINITIONS(-DGIT_TRACE) +ENDIF() + # Include POSIX regex when it is required IF(WIN32 OR AMIGA) INCLUDE_DIRECTORIES(deps/regex) diff --git a/include/git2/trace.h b/include/git2/trace.h new file mode 100644 index 000000000..7409b032d --- /dev/null +++ b/include/git2/trace.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_trace_h__ +#define INCLUDE_git_trace_h__ + +#include "common.h" +#include "types.h" + +/** + * @file git2/trace.h + * @brief Git tracing configuration routines + * @defgroup git_trace Git tracing configuration routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Available tracing levels. When tracing is set to a particular level, + * callers will be provided tracing at the given level and all lower levels. + */ +typedef enum { + /** No tracing will be performed. */ + GIT_TRACE_NONE = 0, + + /** Severe errors that may impact the program's execution */ + GIT_TRACE_FATAL = 1, + + /** Errors that do not impact the program's execution */ + GIT_TRACE_ERROR = 2, + + /** Warnings that suggest abnormal data */ + GIT_TRACE_WARN = 3, + + /** Informational messages about program execution */ + GIT_TRACE_INFO = 4, + + /** Detailed data that allows for debugging */ + GIT_TRACE_DEBUG = 5, + + /** Exceptionally detailed debugging data */ + GIT_TRACE_TRACE = 6 +} git_trace_level_t; + +/** + * An instance for a tracing function + */ +typedef void (*git_trace_callback)(git_trace_level_t level, const char *msg); + +/** + * Sets the system tracing configuration to the specified level with the + * specified callback. When system events occur at a level equal to, or + * lower than, the given level they will be reported to the given callback. + * + * @param level Level to set tracing to + * @param cb Function to call with trace data + * @return 0 or an error code + */ +GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_callback cb); + +/** @} */ +GIT_END_DECL +#endif + diff --git a/src/trace.c b/src/trace.c new file mode 100644 index 000000000..159ac91cc --- /dev/null +++ b/src/trace.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * 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 "buffer.h" +#include "common.h" +#include "global.h" +#include "trace.h" +#include "git2/trace.h" + +#ifdef GIT_TRACE + +struct git_trace_data git_trace__data = {0}; + +#endif + +int git_trace_set(git_trace_level_t level, git_trace_callback callback) +{ +#ifdef GIT_TRACE + assert(level == 0 || callback != NULL); + + git_trace__data.level = level; + git_trace__data.callback = callback; + GIT_MEMORY_BARRIER; + + return 0; +#else + GIT_UNUSED(level); + GIT_UNUSED(callback); + + giterr_set(GITERR_INVALID, + "This version of libgit2 was not built with tracing."); + return -1; +#endif +} + diff --git a/src/trace.h b/src/trace.h new file mode 100644 index 000000000..f4bdff88a --- /dev/null +++ b/src/trace.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_trace_h__ +#define INCLUDE_trace_h__ + +#include + +#include +#include "buffer.h" + +#ifdef GIT_TRACE + +struct git_trace_data { + git_trace_level_t level; + git_trace_callback callback; +}; + +extern struct git_trace_data git_trace__data; + +GIT_INLINE(void) git_trace__write_fmt( + git_trace_level_t level, + const char *fmt, ...) +{ + git_trace_callback callback = git_trace__data.callback; + git_buf message = GIT_BUF_INIT; + va_list ap; + + va_start(ap, fmt); + git_buf_vprintf(&message, fmt, ap); + va_end(ap); + + callback(level, git_buf_cstr(&message)); + + git_buf_free(&message); +} + +#define git_trace_level() (git_trace__data.level) +#define git_trace(l, ...) { \ + if (git_trace__data.level >= l && \ + git_trace__data.callback != NULL) { \ + git_trace__write_fmt(l, __VA_ARGS__); \ + } \ + } + +#else + +#define git_trace_level() ((void)0) +#define git_trace(lvl, ...) ((void)0) + +#endif + +#endif diff --git a/tests-clar/trace/trace.c b/tests-clar/trace/trace.c new file mode 100644 index 000000000..712fe62c6 --- /dev/null +++ b/tests-clar/trace/trace.c @@ -0,0 +1,86 @@ +#include "clar_libgit2.h" +#include "trace.h" + +static int written = 0; + +static void trace_callback(git_trace_level_t level, const char *message) +{ + cl_assert(strcmp(message, "Hello world!") == 0); + + written = 1; +} + +void test_trace_trace__initialize(void) +{ + git_trace_set(GIT_TRACE_INFO, trace_callback); + written = 0; +} + +void test_trace_trace__cleanup(void) +{ + git_trace_set(GIT_TRACE_NONE, NULL); +} + +void test_trace_trace__sets(void) +{ +#ifdef GIT_TRACE + cl_assert(git_trace_level() == GIT_TRACE_INFO); +#endif +} + +void test_trace_trace__can_reset(void) +{ +#ifdef GIT_TRACE + cl_assert(git_trace_level() == GIT_TRACE_INFO); + cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback)); + + cl_assert(written == 0); + git_trace(GIT_TRACE_INFO, "Hello %s!", "world"); + cl_assert(written == 0); + + git_trace(GIT_TRACE_ERROR, "Hello %s!", "world"); + cl_assert(written == 1); +#endif +} + +void test_trace_trace__can_unset(void) +{ +#ifdef GIT_TRACE + cl_assert(git_trace_level() == GIT_TRACE_INFO); + cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL)); + + cl_assert(git_trace_level() == GIT_TRACE_NONE); + + cl_assert(written == 0); + git_trace(GIT_TRACE_FATAL, "Hello %s!", "world"); + cl_assert(written == 0); +#endif +} + +void test_trace_trace__skips_higher_level(void) +{ +#ifdef GIT_TRACE + cl_assert(written == 0); + git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world"); + cl_assert(written == 0); +#endif +} + +void test_trace_trace__writes(void) +{ +#ifdef GIT_TRACE + cl_assert(written == 0); + git_trace(GIT_TRACE_INFO, "Hello %s!", "world"); + cl_assert(written == 1); +#endif +} + +void test_trace_trace__writes_lower_level(void) +{ +#ifdef GIT_TRACE + cl_assert(written == 0); + git_trace(GIT_TRACE_ERROR, "Hello %s!", "world"); + cl_assert(written == 1); +#endif +} +