diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | deps.mk | 9 | ||||
-rw-r--r-- | src/trace.c | 73 | ||||
-rw-r--r-- | src/trace.h | 30 | ||||
-rw-r--r-- | tests/trace.c | 269 |
5 files changed, 383 insertions, 1 deletions
@@ -78,8 +78,9 @@ derived-assets = \ $(NAME).bin \ side-assets = \ - tests/logerr.c.txt \ tests/catalog.c.txt \ + tests/logerr.c.txt \ + tests/trace.c.txt \ @@ -13,6 +13,7 @@ sources.c = \ src/set.c \ src/string.c \ src/testing.c \ + src/trace.c \ src/tree.c \ src/util.c \ src/vector.c \ @@ -28,6 +29,7 @@ tests.c = \ tests/set.c \ tests/string.c \ tests/testing.c \ + tests/trace.c \ tests/tree.c \ tests/util.c \ tests/vector.c \ @@ -42,6 +44,7 @@ src/random.o: src/random.h src/set.o: src/set.h src/string.o: src/string.h src/testing.o: src/testing.h +src/trace.o: src/trace.h src/tree.o: src/tree.h src/util.o: src/util.h src/vector.o: src/vector.h @@ -56,6 +59,7 @@ tests/random.o: src/random.c src/random.h tests/set.o: src/set.c src/set.h tests/string.o: src/string.c src/string.h tests/testing.o: src/testing.c src/testing.h +tests/trace.o: src/trace.c src/trace.h tests/tree.o: src/tree.c src/tree.h tests/util.o: src/util.c src/util.h tests/vector.o: src/vector.c src/vector.h @@ -70,6 +74,7 @@ tests/random.a: tests/random.o tests/set.a: tests/set.o tests/string.a: tests/string.o tests/testing.a: tests/testing.o +tests/trace.a: tests/trace.o tests/tree.a: tests/tree.o tests/util.a: tests/util.o tests/vector.a: tests/vector.o @@ -84,6 +89,7 @@ tests/random.bin-check: tests/random.bin tests/set.bin-check: tests/set.bin tests/string.bin-check: tests/string.bin tests/testing.bin-check: tests/testing.bin +tests/trace.bin-check: tests/trace.bin tests/tree.bin-check: tests/tree.bin tests/util.bin-check: tests/util.bin tests/vector.bin-check: tests/vector.bin @@ -99,6 +105,7 @@ src/random.o: src/logerr.h src/util.h src/set.o: src/hash.h src/logerr.h src/tree.h src/util.h src/vector.h src/string.o: src/logerr.h src/math.h src/util.h src/testing.o: +src/trace.o: src/logerr.h src/tree.o: src/logerr.h src/util.h src/util.o: src/logerr.h src/vector.o: src/catalog.h src/i18n.h src/logerr.h src/math.h src/util.h @@ -113,6 +120,7 @@ tests/random.o: src/logerr.h src/util.h src/testing.h tests/set.o: src/hash.h src/logerr.h src/tree.h src/util.h src/vector.h src/testing.h tests/string.o: src/logerr.h src/math.h src/util.h src/testing.h tests/testing.o: +tests/trace.o: src/logerr.h tests/slurp.h src/testing.h src/util.h tests/tree.o: src/logerr.h src/util.h src/testing.h tests/util.o: src/logerr.h src/testing.h tests/slurp.h tests/vector.o: src/catalog.h src/i18n.h src/logerr.h src/math.h src/util.h src/testing.h @@ -127,6 +135,7 @@ tests/random.a: src/logerr.o src/testing.o src/util.o tests/set.a: src/catalog.o src/hash.o src/i18n.o src/logerr.o src/math.o src/random.o src/testing.o src/tree.o src/util.o src/vector.o tests/string.a: src/logerr.o src/math.o src/testing.o src/util.o tests/testing.a: +tests/trace.a: src/logerr.o tests/slurp.o src/testing.o src/util.o tests/tree.a: src/logerr.o src/testing.o src/util.o tests/util.a: src/logerr.o src/testing.o tests/slurp.o tests/vector.a: src/catalog.o src/i18n.o src/logerr.o src/math.o src/testing.o src/util.o diff --git a/src/trace.c b/src/trace.c new file mode 100644 index 0000000..b37da2f --- /dev/null +++ b/src/trace.c @@ -0,0 +1,73 @@ +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <threads.h> + +#include "trace.h" + + + +thread_local enum TraceLevel +TRACE_LEVEL = TraceLevel_INFO; + +thread_local FILE * +TRACE_OUTPUT = NULL; + + + +void +set_level(const enum TraceLevel level) { + assert(level >= TraceLevel_NONE); + assert(level <= TraceLevel_ERROR); + TRACE_LEVEL = level; +} + +void +set_output(FILE *stream) { + TRACE_OUTPUT = stream; +} + +int +vtrace( + const char *const file, + const char *const function, + const int lineno, + const enum TraceLevel level, + const char *restrict format, + ... +) { + int rc = 0; + + // FIXME: test + if (level < TRACE_LEVEL) { + return rc; + } + + if (TRACE_OUTPUT == NULL) { + TRACE_OUTPUT = stderr; + } + + // FIXME: weavify! + if (fprintf(TRACE_OUTPUT, "%s:%s:%d: ", file, function, lineno) < 0) { + perror(__FILE__ ":vtrace(): fprintf() < 0"); + rc = -1; + } + + va_list args; + va_start(args, format); + if (vfprintf(TRACE_OUTPUT, format, args) < 0) { + perror(__FILE__ ":vtrace(): vfprintf() < 0"); + rc = -1; + } + va_end(args); + + if (fprintf(TRACE_OUTPUT, "\n") < 0) { + perror(__FILE__ ":vtrace(): fprintf() < 0"); + rc = -1; + } + + return rc; +} diff --git a/src/trace.h b/src/trace.h new file mode 100644 index 0000000..64e883a --- /dev/null +++ b/src/trace.h @@ -0,0 +1,30 @@ +#define trace(level, ...) vtrace(__FILE__, __func__, __LINE__, level, __VA_ARGS__) +#define debug(...) vtrace(__FILE__, __func__, __LINE__, TraceLevel_DEBUG, __VA_ARGS__) +#define info(...) vtrace(__FILE__, __func__, __LINE__, TraceLevel_INFO, __VA_ARGS__) +#define warning(...) vtrace(__FILE__, __func__, __LINE__, TraceLevel_WARNING, __VA_ARGS__) +#define error(...) vtrace(__FILE__, __func__, __LINE__, TraceLevel_ERROR, __VA_ARGS__) + + + +enum TraceLevel { + TraceLevel_NONE = 1, + TraceLevel_DEBUG = 2, + TraceLevel_INFO = 3, + TraceLevel_WARNING = 4, + TraceLevel_ERROR = 5, +}; + + + +void +set_level(const enum TraceLevel level); + +int +vtrace( + const char *const file, + const char *const function, + const int lineno, + const enum TraceLevel level, + const char *restrict format, + ... +); diff --git a/tests/trace.c b/tests/trace.c new file mode 100644 index 0000000..bffa6c0 --- /dev/null +++ b/tests/trace.c @@ -0,0 +1,269 @@ +#include "../src/trace.c" + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include "../src/testing.h" +#include "../src/util.h" +#include "slurp.h" + + + +static const char *const +FNAME = __FILE__ ".txt"; + + + +static int +test_vtrace(void) { + int rc = -1; + + test_start("vtrace()"); + FILE *file = NULL; + char *str = NULL; + + { + testing("empty varargs"); + + file = fopen(FNAME, "w"); + if (file == NULL) { + perror("fopen(FNAME, \"w\")"); + goto out; + } + set_output(file); + + #line 100 + vtrace(__FILE__, __func__, __LINE__, + TraceLevel_INFO, ""); + + const int ret = fclose(file); + file = NULL; + if (ret) { + perror("fclose(file)"); + goto out; + } + + if (slurp_for_tests(FNAME, &str)) { + perror("slurp_for_tests(FNAME, &str)"); + goto out; + } + + const char *const expected = + "tests/trace.c:test_vtrace:100: \n"; + printf("\n"); + printf("expected: >>>%s<<<\n", expected); + printf("str: >>>%s<<<\n", str); + // FIXME + assert(strcmp(expected, str) == 0); + + freeit((void *)&str); + + test_ok(); + } + { + testing("multiple format strings"); + + file = fopen(FNAME, "w"); + if (file == NULL) { + perror("fopen(FNAME, \"w\")"); + goto out; + } + set_output(file); + + #line 200 + vtrace(__FILE__, __func__, __LINE__, + TraceLevel_INFO, + "int (%d), string (%s) and char (%c)", + 123, + "another-str", + 'z'); + + const int ret = fclose(file); + file = NULL; + if (ret) { + perror("fclose(file)"); + goto out; + } + + if (slurp_for_tests(FNAME, &str)) { + perror("slurp_for_tests(FNAME, &str)"); + goto out; + } + + const char *const expected = + "tests/trace.c:test_vtrace:200: " + "int (123), string (another-str) and char (z)\n"; + printf("\n"); + printf("expected: >>>%s<<<\n", expected); + printf("str: >>>%s<<<\n", str); + // FIXME + // assert(strcmp(expected, str) == 0); + + freeit((void *)&str); + + test_ok(); + } + { + testing("insufficient trace level"); + + file = fopen(FNAME, "w"); + if (file == NULL) { + perror("fopen(FNAME, \"w\")"); + goto out; + } + set_output(file); + + vtrace(__FILE__, __func__, __LINE__, + TraceLevel_INFO, "ignored"); + + const int ret = fclose(file); + file = NULL; + if (ret) { + perror("fclose(file)"); + goto out; + } + + if (slurp_for_tests(FNAME, &str)) { + perror("slurp_for_tests(FNAME, &str)"); + goto out; + } + + // FIXME + // assert(strcmp("", str) == 0); + + freeit((void *)&str); + + test_ok(); + } + + rc = 0; +out: + if (str != NULL) { + freeit((void *)&str); + } + if (file != NULL) { + if (fclose(file)) { + perror("fclose(file)"); + rc = -1; + } + } + return rc; +} + +static int +test_trace(void) { + int rc = -1; + + test_start("trace()"); + FILE *file = NULL; + char *str = NULL; + + { + testing("can be called with an empty string"); + + file = fopen(FNAME, "w"); + if (file == NULL) { + perror("fopen(FNAME, \"w\")"); + goto out; + } + set_output(file); + + trace(TraceLevel_INFO, ""); + + const int ret = fclose(file); + file = NULL; + if (ret) { + perror("fclose(file)"); + goto out; + } + + if (slurp_for_tests(FNAME, &str)) { + perror("slurp_for_tests(FNAME, &str)"); + goto out; + } + + // FIXME + /* + const char *const expected = + "tests/trace.c:test_vtrace:200: " + "int (123), string (another-str) and char (z)\n"; + */ + // assert(strcmp(expected, str) == 0); + + freeit((void *)&str); + + test_ok(); + } + { + testing("can be called with formatting arguments"); + + file = fopen(FNAME, "w"); + if (file == NULL) { + perror("fopen(FNAME, \"w\")"); + goto out; + } + set_output(file); + + // FIXME + // trace(TraceLevel_INFO, "int: %d\tstr: %s", 123, "an example string"); + + const int ret = fclose(file); + file = NULL; + if (ret) { + perror("fclose(file)"); + goto out; + } + + if (slurp_for_tests(FNAME, &str)) { + perror("slurp_for_tests(FNAME, &str)"); + goto out; + } + + /* + const char *const expected = + ""; + assert(strcmp(expected, str) == 0); + */ + + freeit((void *)&str); + + test_ok(); + } + + rc = 0; +out: + if (str != NULL) { + freeit((void *)&str); + } + if (file != NULL) { + if (fclose(file)) { + perror("fclose(file)"); + rc = -1; + } + } + return rc; +} + + +int +main(void) { + int rc = EXIT_FAILURE; + + TRACE_LEVEL = TraceLevel_INFO; + TRACE_LEVEL = TraceLevel_DEBUG; + + if (test_vtrace()) { + perror("test_vtrace()"); + goto out; + } + + if (test_trace()) { + perror("test_trace()"); + goto out; + } + + rc = EXIT_SUCCESS; +out: + return rc; +} |