source: libcf/trunk/src/cf_debug.c@ 63

Last change on this file since 63 was 63, checked in by cheese, 11 years ago

#1 add interface to print callstack

File size: 9.8 KB
Line 
1/**
2 * @file cf_debug.c
3 * @author myusgun <myusgun@gmail.com>
4 * @version 0.1
5 */
6#include "cf_debug.h"
7#include "cf_local.h"
8#include "cf_error.h"
9#include "cf_thread.h"
10
11#include <stdlib.h>
12#include <ctype.h>
13#include <stdarg.h>
14#include <string.h>
15
16#if defined(_WIN32) || defined(_WIN64)
17# include <io.h>
18#else
19# include <unistd.h>
20#endif
21
22#define IS_READABLE_CHAR(__c) (' ' <= __c && __c <= '~')
23
24#define ASSERT_CTX(__ctx) \
25 if (__ctx == NULL) \
26 return CF_ERROR_DEBUG_INVALID_CTX
27
28/**
29 * 디버그 컨텍스트
30 *
31 * @remark change from public to private
32 */
33typedef void * CF_Debug_Ctx;
34
35/* *
36 * 콜스택 데이터
37 *
38 * @remark change from public to private
39 */
40typedef struct cf_debug_callStack {
41 char file[NAME_LENGTH + 1]; /* *< 파일 이름 */
42 char function[NAME_LENGTH + 1]; /* *< 함수 이름 */
43 int line; /* *< 라인 넘버 */
44} CF_Debug_CallStack;
45
46typedef struct __cf_debug_callstack__
47{
48 char file[NAME_LENGTH + 1];
49 char func[NAME_LENGTH + 1];
50 int line;
51
52 int frameIndex;
53
54 struct __cf_debug_callstack__ * caller;
55} S_CF_DEBUG_CALLSTACK, CF_DEBUG_CALLSTACK;
56
57typedef struct __cf_debug_ctx__
58{
59 char file[NAME_LENGTH + 1];
60 char func[NAME_LENGTH + 1];
61 int line;
62
63 CF_Mutex mutex;
64
65 CF_DEBUG_CALLSTACK callstack;
66} S_CF_DEBUG_CTX, CF_DEBUG_CTX;
67
68CF_Debug_Ctx gDebugSingleCtx = NULL;
69
70static int
71CF_Debug_Local_UpdateCtx (CF_Debug_Ctx ctx,
72 const char * file,
73 const char * func,
74 const int line)
75{
76 CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx;
77
78 ASSERT_CTX (ctx);
79
80 strncpy (context->file, file, strlen (file));
81 strncpy (context->func, func, strlen (func));
82 context->line = line;
83
84 return CF_OK;
85}
86
87static int
88CF_Debug_Local_Print (FILE * fp,
89 const char * file,
90 const char * func,
91 const int line,
92 const char * fmt,
93 va_list valist)
94{
95 fprintf (fp, "[DEBUG][%s:%d][%s] ", file, line, func);
96 vfprintf (fp, fmt, valist);
97
98 return CF_OK;
99}
100
101static int
102CF_Debug_Local_PrintBin (FILE * fp,
103 const char * file,
104 const char * func,
105 const int line,
106 const unsigned char * bin,
107 const int len)
108{
109 int i, j;
110
111 for (i = 0 ; i < len ; i += 16)
112 {
113 fprintf (fp, "[DEBUG][%s:%d][%s] ", file, line, func);
114 fprintf (fp, "%06x : ", i);
115
116 for (j = 0 ; j < 16 ; j++)
117 {
118 if (i+j < len)
119 fprintf (fp, "%02x ", bin[i+j]);
120 else
121 fprintf (fp, " ");
122 }
123 fprintf (fp, " ");
124
125 for (j = 0 ; j < 16 ; j++)
126 {
127 if (i+j < len)
128 fprintf (fp, "%c", IS_READABLE_CHAR(bin[i+j]) ? bin[i+j] : '.');
129 }
130 fprintf (fp, "\n");
131 }
132
133 return CF_OK;
134}
135
136/**
137 * 디버그 메시지를 지정된 파일 포인터로 출력
138 *
139 * @return 성공 시, CF_OK; 실패 시, 오류 코드
140 *
141 * @param fp 파일 포인터. 표준출력(stdout) 및 표준오류(stderr) 사용 가능
142 * @param file 파일 경로
143 * @param func 함수 이름
144 * @param line 라인 넘버
145 * @param fmt 포맷 스트링
146 * @param ... 가변 인자
147 *
148 * @see CF_DEBUG_PRINT
149 */
150int
151CF_Debug_Print (FILE * fp,
152 const char * file,
153 const char * func,
154 const int line,
155 const char * fmt, ...)
156{
157 va_list valist;
158
159 va_start (valist, fmt);
160 CF_Debug_Local_Print (fp, file, func, line, fmt, valist);
161 va_end (valist);
162
163 return CF_OK;
164}
165
166/**
167 * 바이너리 데이터를 디버그 메시지와 함께 지정된 파일 포인터로 출력
168 *
169 * @return CF_OK 반환
170 *
171 * @param fp 파일 포인터. 표준출력(stdout) 및 표준오류(stderr) 사용 가능
172 * @param file 파일 경로
173 * @param func 함수 이름
174 * @param line 라인 넘버
175 * @param bin 라인 넘버
176 * @param len 바이너리 길이
177 * @param fmt 포맷 스트링
178 * @param ... 가변 인자
179 *
180 * @see CF_DEBUG_PRINT_BIN
181 */
182int
183CF_Debug_PrintBin (FILE * fp,
184 const char * file,
185 const char * func,
186 const int line,
187 const unsigned char * bin,
188 const int len,
189 const char * fmt, ...)
190{
191 va_list valist;
192
193 va_start (valist, fmt);
194 CF_Debug_Local_Print (fp, file, func, line, fmt, valist);
195 va_end (valist);
196
197 CF_Debug_Local_PrintBin (fp, file, func, line, bin, len);
198
199 return CF_OK;
200}
201
202/**
203 * 컨텍스트에 콜스택 푸시
204 *
205 * @return 성공 시, CF_OK; 실패 시, 오류 코드
206 *
207 * @param ctx 디버그 컨텍스트
208 * @param file 파일 경로
209 * @param func 함수 이름
210 * @param line 라인 넘버
211 */
212static int
213CF_Debug_CallStackPush (CF_Debug_Ctx ctx,
214 const char * file,
215 const char * func,
216 const int line)
217{
218 CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx;
219 CF_DEBUG_CALLSTACK * push = NULL;
220
221 ASSERT_CTX (ctx);
222
223 push = (CF_DEBUG_CALLSTACK *) calloc (sizeof (CF_DEBUG_CALLSTACK), 1);
224 if (push == NULL)
225 return CF_ERROR_DEBUG_PUSH_CALLSTACK;
226
227 /* push to callstack */
228 sprintf (push->file, "%s", file);
229 sprintf (push->func, "%s", func);
230 push->line = line;
231 push->caller = context->callstack.caller;
232 push->frameIndex = push->caller == NULL
233 ? 0
234 : push->caller->frameIndex + 1;
235 context->callstack.caller = push;
236
237 CF_Debug_Local_UpdateCtx (ctx, file, func, line);
238
239 return CF_OK;
240}
241
242/**
243 * 컨텍스트에서 콜스택에서 TOP을 제거하지 않고 가져옴
244 *
245 * @return 성공 시, CF_OK; 실패 시, 오류 코드
246 *
247 * @param ctx 디버그 컨텍스트
248 * @param callstack 콜스택 정보를 가져올 콜스택 데이터 구조체 포인터
249 *
250 * @see CF_Debug_CallStack
251 */
252static int
253CF_Debug_CallStackPeek (CF_Debug_Ctx ctx,
254 CF_Debug_CallStack * callstack)
255{
256 CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx;
257 CF_DEBUG_CALLSTACK * pop = NULL;
258
259 pop = context->callstack.caller;
260 if (pop == NULL)
261 return CF_ERROR_DEBUG_PEEK_CALLSTACK;
262
263 if (callstack != NULL)
264 {
265 sprintf (callstack->file , "%s", pop->file);
266 sprintf (callstack->function, "%s", pop->func);
267 callstack->line = pop->line;
268 }
269
270 return CF_OK;
271}
272
273/**
274 * 컨텍스트에서 콜스택 팝
275 *
276 * @return 성공 시, CF_OK; 실패 시, 오류 코드
277 *
278 * @param ctx 디버그 컨텍스트
279 * @param callstack 콜스택 정보를 가져올 콜스택 데이터 구조체 포인터
280 *
281 * @see CF_Debug_CallStack
282 */
283static int
284CF_Debug_CallStackPop (CF_Debug_Ctx ctx,
285 CF_Debug_CallStack * callstack)
286{
287 CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx;
288 CF_DEBUG_CALLSTACK * pop = NULL;
289
290 ASSERT_CTX (ctx);
291
292 pop = context->callstack.caller;
293 if (pop == NULL)
294 return CF_ERROR_DEBUG_POP_CALLSTACK;
295
296 if (CF_Debug_CallStackPeek (ctx, callstack) < 0)
297 return CF_ERROR_DEBUG_PEEK_CALLSTACK;
298
299 memset (context->file, 0x00, sizeof (context->file));
300 memset (context->func, 0x00, sizeof (context->func));
301 context->line = 0;
302
303 /* restore current context */
304 if (pop->caller != NULL)
305 {
306 CF_Debug_Local_UpdateCtx (ctx,
307 pop->caller->file,
308 pop->caller->func,
309 pop->caller->line);
310 }
311
312 context->callstack.caller = pop->caller;
313 free (pop);
314
315 return CF_OK;
316}
317
318/**
319 * 디버그 컨텍스트를 해제
320 *
321 * @return 성공 시, CF_OK; 실패 시, 오류 코드
322 *
323 * @param ctx 디버그 컨텍스트
324 *
325 * @see CF_DEBUG_DESTROY_CTX
326 */
327static int
328CF_Debug_DestroyCtx (CF_Debug_Ctx ctx)
329{
330 CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx;
331
332 ASSERT_CTX (ctx);
333
334 while (context->callstack.caller)
335 CF_Debug_CallStackPop (ctx, NULL);
336
337 if (context->mutex)
338 CF_Mutex_Destory (&context->mutex);
339
340 free (context);
341
342 return CF_OK;
343}
344
345/**
346 * 디버그 컨텍스트를 생성
347 *
348 * @return 성공 시, CF_Debug_Ctx 형태의 컨텍스트; 실패 시, NULL
349 * @see CF_DEBUG_CREATE_CTX
350 */
351static int
352CF_Debug_CreateCtx (CF_Debug_Ctx * ctx)
353{
354 int result = 0;
355 CF_DEBUG_CTX * context = NULL;
356
357 TRY
358 {
359 context = (CF_DEBUG_CTX *) calloc (sizeof (CF_DEBUG_CTX), 1);
360 if (context == NULL)
361 {
362 result = CF_ERROR_DEBUG_ALLOCATE_CTX;
363 TRY_BREAK;
364 }
365
366 result = CF_Mutex_Create (&context->mutex);
367 if (result < 0)
368 {
369 TRY_BREAK;
370 }
371
372 *ctx = (CF_Debug_Ctx) context;
373 }
374 CATCH_IF (result < 0)
375 {
376 CF_Debug_DestroyCtx (context);
377 }
378
379 return CF_OK;
380}
381
382/**
383 * 콜스택 매니저 초기화 (글로벌 컨텍스트)
384 *
385 * @return 성공 시, CF_OK; 실패 시, 오류 코드
386 *
387 * @see CF_Debug_Finalize
388 */
389int
390CF_Debug_Initialize (void)
391{
392 int result = 0;
393
394 if (gDebugSingleCtx == NULL)
395 {
396 result = CF_Debug_CreateCtx (&gDebugSingleCtx);
397 if (result != CF_OK)
398 return result;
399 }
400
401 return CF_OK;
402}
403
404/**
405 * 콜스택 매니저 해제 (글로벌 컨텍스트)
406 *
407 * @return 성공 시, CF_OK; 실패 시, 오류 코드
408 */
409int
410CF_Debug_Finalize (void)
411{
412 return CF_Debug_DestroyCtx (gDebugSingleCtx);
413}
414
415/**
416 * 함수 진입을 명시 (글로벌 컨텍스트)
417 *
418 * @return 성공 시, CF_OK; 실패 시, 오류 코드
419 *
420 * @param file 파일 경로
421 * @param func 함수 이름
422 * @param line 라인 넘버
423 *
424 * @see CF_Debug_LeaveFunction
425 */
426int
427CF_Debug_EnterFunction (const char * file,
428 const char * func,
429 const int line)
430{
431 CF_DEBUG_CTX * ctx = (CF_DEBUG_CTX *)gDebugSingleCtx;
432
433 ASSERT_CTX (ctx);
434
435 CF_Mutex_Lock (&ctx->mutex);
436 CF_Debug_CallStackPush (gDebugSingleCtx, file, func, line);
437 CF_Mutex_Unlock (&ctx->mutex);
438
439 return CF_OK;
440}
441
442/**
443 * 함수 종료를 명시 (글로벌 컨텍스트)
444 *
445 * @return 성공 시, CF_OK; 실패 시, 오류 코드
446 *
447 * @see CF_Debug_EnterFunction
448 */
449int
450CF_Debug_LeaveFunction (void)
451{
452 CF_DEBUG_CTX * ctx = (CF_DEBUG_CTX *)gDebugSingleCtx;
453
454 ASSERT_CTX (ctx);
455
456 CF_Mutex_Lock (&ctx->mutex);
457 CF_Debug_CallStackPop (gDebugSingleCtx, NULL);
458 CF_Mutex_Unlock (&ctx->mutex);
459
460 return CF_OK;
461}
462
463/**
464 * 현재 콜스택을 출력 (글로벌 컨텍스트)
465 *
466 * @return 성공 시, CF_OK; 실패 시, 오류 코드
467 *
468 * @param fp 출력 할 파일 포인터
469 */
470int
471CF_Debug_PrintCallStack (FILE * fp)
472{
473 int iter = 0;
474
475 CF_DEBUG_CTX * ctx = gDebugSingleCtx;
476 CF_DEBUG_CALLSTACK * callstack = NULL;
477
478 ASSERT_CTX (ctx);
479
480 for ( callstack = ctx->callstack.caller
481 ; callstack
482 ; callstack = callstack->caller)
483 {
484 fprintf (fp == NULL ? stderr : fp,
485 "#%-4d %s <%s:%d>\n",
486 iter++,
487 callstack->func,
488 callstack->file,
489 callstack->line);
490 }
491
492 return CF_OK;
493}
Note: See TracBrowser for help on using the repository browser.