]> git.proxmox.com Git - libgit2.git/blob - tests/clar_libgit2_trace.c
8eb6d4e8dd96d41c9addf6f67bca3009c8df0a07
[libgit2.git] / tests / clar_libgit2_trace.c
1 #include "clar_libgit2_trace.h"
2 #include "clar_libgit2.h"
3 #include "clar_libgit2_timer.h"
4 #include "trace.h"
5
6 struct method {
7 const char *name;
8 void (*git_trace_cb)(git_trace_level_t level, const char *msg);
9 void (*close)(void);
10 };
11
12 static const char *message_prefix(git_trace_level_t level)
13 {
14 switch (level) {
15 case GIT_TRACE_NONE:
16 return "[NONE]: ";
17 case GIT_TRACE_FATAL:
18 return "[FATAL]: ";
19 case GIT_TRACE_ERROR:
20 return "[ERROR]: ";
21 case GIT_TRACE_WARN:
22 return "[WARN]: ";
23 case GIT_TRACE_INFO:
24 return "[INFO]: ";
25 case GIT_TRACE_DEBUG:
26 return "[DEBUG]: ";
27 case GIT_TRACE_TRACE:
28 return "[TRACE]: ";
29 default:
30 return "[?????]: ";
31 }
32 }
33
34 static void _git_trace_cb__printf(git_trace_level_t level, const char *msg)
35 {
36 printf("%s%s\n", message_prefix(level), msg);
37 }
38
39 #if defined(GIT_WIN32)
40 static void _git_trace_cb__debug(git_trace_level_t level, const char *msg)
41 {
42 OutputDebugString(message_prefix(level));
43 OutputDebugString(msg);
44 OutputDebugString("\n");
45
46 printf("%s%s\n", message_prefix(level), msg);
47 }
48 #else
49 #define _git_trace_cb__debug _git_trace_cb__printf
50 #endif
51
52
53 static void _trace_printf_close(void)
54 {
55 fflush(stdout);
56 }
57
58 #define _trace_debug_close _trace_printf_close
59
60
61 static struct method s_methods[] = {
62 { "printf", _git_trace_cb__printf, _trace_printf_close },
63 { "debug", _git_trace_cb__debug, _trace_debug_close },
64 /* TODO add file method */
65 {0},
66 };
67
68
69 static int s_trace_loaded = 0;
70 static int s_trace_level = GIT_TRACE_NONE;
71 static struct method *s_trace_method = NULL;
72 static int s_trace_tests = 0;
73
74 static int set_method(const char *name)
75 {
76 int k;
77
78 if (!name || !*name)
79 name = "printf";
80
81 for (k=0; (s_methods[k].name); k++) {
82 if (strcmp(name, s_methods[k].name) == 0) {
83 s_trace_method = &s_methods[k];
84 return 0;
85 }
86 }
87 fprintf(stderr, "Unknown CLAR_TRACE_METHOD: '%s'\n", name);
88 return -1;
89 }
90
91
92 /**
93 * Lookup CLAR_TRACE_LEVEL and CLAR_TRACE_METHOD from
94 * the environment and set the above s_trace_* fields.
95 *
96 * If CLAR_TRACE_LEVEL is not set, we disable tracing.
97 *
98 * TODO If set, we assume GIT_TRACE_TRACE level, which
99 * logs everything. Later, we may want to parse the
100 * value of the environment variable and set a specific
101 * level.
102 *
103 * We assume the "printf" method. This can be changed
104 * with the CLAR_TRACE_METHOD environment variable.
105 * Currently, this is only needed on Windows for a "debug"
106 * version which also writes to the debug output window
107 * in Visual Studio.
108 *
109 * TODO add a "file" method that would open and write
110 * to a well-known file. This would help keep trace
111 * output and clar output separate.
112 *
113 */
114 static void _load_trace_params(void)
115 {
116 char *sz_level;
117 char *sz_method;
118 char *sz_tests;
119
120 s_trace_loaded = 1;
121
122 sz_level = cl_getenv("CLAR_TRACE_LEVEL");
123 if (!sz_level || !*sz_level) {
124 s_trace_level = GIT_TRACE_NONE;
125 s_trace_method = NULL;
126 return;
127 }
128
129 /* TODO Parse sz_level and set s_trace_level. */
130 s_trace_level = GIT_TRACE_TRACE;
131
132 sz_method = cl_getenv("CLAR_TRACE_METHOD");
133 if (set_method(sz_method) < 0)
134 set_method(NULL);
135
136 sz_tests = cl_getenv("CLAR_TRACE_TESTS");
137 if (sz_tests != NULL)
138 s_trace_tests = 1;
139 }
140
141 #define HR "================================================================"
142
143 /**
144 * Timer to report the take spend in a test's run() method.
145 */
146 static cl_perf_timer s_timer_run = CL_PERF_TIMER_INIT;
147
148 /**
149 * Timer to report total time in a test (init, run, cleanup).
150 */
151 static cl_perf_timer s_timer_test = CL_PERF_TIMER_INIT;
152
153 void _cl_trace_cb__event_handler(
154 cl_trace_event ev,
155 const char *suite_name,
156 const char *test_name,
157 void *payload)
158 {
159 GIT_UNUSED(payload);
160
161 if (!s_trace_tests)
162 return;
163
164 switch (ev) {
165 case CL_TRACE__SUITE_BEGIN:
166 git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
167 #if 0 && defined(GIT_WIN32_LEAKCHECK)
168 git_win32__crtdbg_stacktrace__dump(
169 GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
170 suite_name);
171 #endif
172 break;
173
174 case CL_TRACE__SUITE_END:
175 #if 0 && defined(GIT_WIN32_LEAKCHECK)
176 /* As an example of checkpointing, dump leaks within this suite.
177 * This may generate false positives for things like the global
178 * TLS error state and maybe the odb cache since they aren't
179 * freed until the global shutdown and outside the scope of this
180 * set of tests.
181 *
182 * This may under-report if the test itself uses a checkpoint.
183 * See tests/trace/windows/stacktrace.c
184 */
185 git_win32__crtdbg_stacktrace__dump(
186 GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
187 suite_name);
188 #endif
189 git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
190 break;
191
192 case CL_TRACE__TEST__BEGIN:
193 git_trace(GIT_TRACE_TRACE, "\n%s::%s: Begin Test", suite_name, test_name);
194 cl_perf_timer__init(&s_timer_test);
195 cl_perf_timer__start(&s_timer_test);
196 break;
197
198 case CL_TRACE__TEST__END:
199 cl_perf_timer__stop(&s_timer_test);
200 git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%.3f %.3f)", suite_name, test_name,
201 cl_perf_timer__last(&s_timer_run),
202 cl_perf_timer__last(&s_timer_test));
203 break;
204
205 case CL_TRACE__TEST__RUN_BEGIN:
206 git_trace(GIT_TRACE_TRACE, "%s::%s: Begin Run", suite_name, test_name);
207 cl_perf_timer__init(&s_timer_run);
208 cl_perf_timer__start(&s_timer_run);
209 break;
210
211 case CL_TRACE__TEST__RUN_END:
212 cl_perf_timer__stop(&s_timer_run);
213 git_trace(GIT_TRACE_TRACE, "%s::%s: End Run", suite_name, test_name);
214 break;
215
216 case CL_TRACE__TEST__LONGJMP:
217 cl_perf_timer__stop(&s_timer_run);
218 git_trace(GIT_TRACE_TRACE, "%s::%s: Aborted", suite_name, test_name);
219 break;
220
221 default:
222 break;
223 }
224 }
225
226 /**
227 * Setup/Enable git_trace() based upon settings user's environment.
228 */
229 void cl_global_trace_register(void)
230 {
231 if (!s_trace_loaded)
232 _load_trace_params();
233
234 if (s_trace_level == GIT_TRACE_NONE)
235 return;
236 if (s_trace_method == NULL)
237 return;
238 if (s_trace_method->git_trace_cb == NULL)
239 return;
240
241 git_trace_set(s_trace_level, s_trace_method->git_trace_cb);
242 cl_trace_register(_cl_trace_cb__event_handler, NULL);
243 }
244
245 /**
246 * If we turned on git_trace() earlier, turn it off.
247 *
248 * This is intended to let us close/flush any buffered
249 * IO if necessary.
250 *
251 */
252 void cl_global_trace_disable(void)
253 {
254 cl_trace_register(NULL, NULL);
255 git_trace_set(GIT_TRACE_NONE, NULL);
256 if (s_trace_method && s_trace_method->close)
257 s_trace_method->close();
258
259 /* Leave s_trace_ vars set so they can restart tracing
260 * since we only want to hit the environment variables
261 * once.
262 */
263 }