Commit 6f83a7813310766325ae14f6ac7591dc56341fc5

Vicent Martí 2013-03-07T11:14:03

Merge pull request #1403 from ethomson/tracing Optional tracing back to consumers

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5a0043f..7f2e293 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 0000000..7409b03
--- /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 0000000..159ac91
--- /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 0000000..f4bdff8
--- /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 <stdarg.h>
+
+#include <git2/trace.h>
+#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 0000000..712fe62
--- /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
+}
+