/** * @file cf_debug.c * @author myusgun * @version 0.1 */ #include "cf_debug.h" #include "cf_local.h" #include "cf_error.h" #include #include #include #include #ifdef _WIN32 # include #else # include #endif #define IS_READABLE_CHAR(__c) (' ' <= __c && __c <= '~') #define CHECK_INVALID_CTX(__ctx) \ if (__ctx == NULL) \ return CF_ERROR_DEBUG_INVALID_CTX #define GET_CTX_OSTREAM(__ctx) \ ((((CF_DEBUG_CTX *)__ctx)->fp == NULL) \ ? stderr \ : ((CF_DEBUG_CTX *)__ctx)->fp) typedef struct __cf_debug_callstack__ { char file[NAME_LENGTH + 1]; char func[NAME_LENGTH + 1]; int line; struct __cf_debug_callstack__ * caller; } S_CF_DEBUG_CALLSTACK, CF_DEBUG_CALLSTACK; typedef struct __cf_debug_ctx__ { char file[NAME_LENGTH + 1]; char func[NAME_LENGTH + 1]; int line; FILE * fp; CF_DEBUG_CALLSTACK callstack; } S_CF_DEBUG_CTX, CF_DEBUG_CTX; static int CF_Debug_Local_UpdateCtx (CF_Debug_Ctx ctx, const char * file, const char * func, const int line) { CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CHECK_INVALID_CTX (ctx); strncpy (context->file, file, strlen (file)); strncpy (context->func, func, strlen (func)); context->line = line; return CF_OK; } static int CF_Debug_Local_Print (FILE * fp, const char * file, const char * func, const int line, const char * fmt, va_list valist) { fprintf (fp, "[DEBUG][%s:%d][%s] ", file, line, func); vfprintf (fp, fmt, valist); return CF_OK; } static int CF_Debug_Local_PrintBin (FILE * fp, const char * file, const char * func, const int line, const unsigned char * bin, const int len) { int i, j; for (i = 0 ; i < len ; i += 16) { fprintf (fp, "[DEBUG][%s:%d][%s] ", file, line, func); fprintf (fp, "%06x : ", i); for (j = 0 ; j < 16 ; j++) { if (i+j < len) fprintf (fp, "%02x ", bin[i+j]); else fprintf (fp, " "); } fprintf (fp, " "); for (j = 0 ; j < 16 ; j++) { if (i+j < len) fprintf (fp, "%c", IS_READABLE_CHAR(bin[i+j]) ? bin[i+j] : '.'); } fprintf (fp, "\n"); } return CF_OK; } /** * 디버그 컨텍스트를 생성 * * @return 성공 시, CF_Debug_Ctx 형태의 컨텍스트; 실패 시, NULL * @see CF_DEBUG_CREATE_CTX */ CF_Debug_Ctx CF_Debug_CreateCtx (void) { CF_DEBUG_CTX * ctx = NULL; ctx = (CF_DEBUG_CTX *) calloc (sizeof (CF_DEBUG_CTX), 1); return (CF_Debug_Ctx) ctx; } /** * 디버그 컨텍스트를 해제 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 디버그 컨텍스트 * * @see CF_DEBUG_DESTROY_CTX */ int CF_Debug_DestroyCtx (CF_Debug_Ctx ctx) { CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CF_DEBUG_CALLSTACK * pop = NULL; CF_DEBUG_CALLSTACK * next = NULL; CHECK_INVALID_CTX (ctx); if (context->fp != NULL) fclose (context->fp); for (pop = next = context->callstack.caller ; pop ; pop = next) { next = next->caller; free (pop); } free (context); return CF_OK; } /** * 디버그 컨텍스트에 출력할 파일 디스크립터를 설정 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 디버그 컨텍스트 * @param fd 파일 디스크립터 * * @see CF_File_Open, CF_File_Create */ int CF_Debug_SetOutputFD (CF_Debug_Ctx ctx, int fd) { int result = 0; int dupfd = 0; CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; FILE * fp = NULL; CHECK_INVALID_CTX (ctx); TRY { dupfd = dup (fd); if (dupfd < 0) { result = -1; TRY_BREAK; } fp = fdopen (dupfd, "a"); if (fp == NULL) { close (dupfd); result = -2; TRY_BREAK; } context->fp = fp; } CATCH_IF (result < 0) { return CF_ERROR_DEBUG_SET_OUTPUT_FD; } return CF_OK; } /** * 디버그 메시지를 지정된 파일 포인터로 출력 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param fp 파일 포인터. 표준출력(stdout) 및 표준오류(stderr) 사용 가능 * @param file 파일 경로 * @param func 함수 이름 * @param line 라인 넘버 * @param fmt 포맷 스트링 * @param ... 가변 인자 * * @see CF_DEBUG_PRINT */ int CF_Debug_Print (FILE * fp, const char * file, const char * func, const int line, const char * fmt, ...) { va_list valist; va_start (valist, fmt); CF_Debug_Local_Print (fp, file, func, line, fmt, valist); va_end (valist); return CF_OK; } /** * 바이너리 데이터를 디버그 메시지와 함께 지정된 파일 포인터로 출력 * * @return CF_OK 반환 * * @param fp 파일 포인터. 표준출력(stdout) 및 표준오류(stderr) 사용 가능 * @param file 파일 경로 * @param func 함수 이름 * @param line 라인 넘버 * @param bin 라인 넘버 * @param len 바이너리 길이 * @param fmt 포맷 스트링 * @param ... 가변 인자 * * @see CF_DEBUG_PRINT_BIN */ int CF_Debug_PrintBin (FILE * fp, const char * file, const char * func, const int line, const unsigned char * bin, const int len, const char * fmt, ...) { va_list valist; va_start (valist, fmt); CF_Debug_Local_Print (fp, file, func, line, fmt, valist); va_end (valist); CF_Debug_Local_PrintBin (fp, file, func, line, bin, len); return CF_OK; } /** * 컨텍스트를 업데이트하고 디버그 메시지를 출력 * * @return CF_OK 반환 * * @param ctx 디버그 컨텍스트 * @param file 파일 경로 * @param func 함수 이름 * @param line 라인 넘버 * @param fmt 포맷 스트링 * @param ... 가변 인자 * * @see CF_Debug_Trace */ int CF_Debug_Trace (CF_Debug_Ctx ctx, const char * file, const char * func, const int line, const char * fmt, ...) { va_list valist; CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CHECK_INVALID_CTX (ctx); CF_Debug_Local_UpdateCtx (ctx, file, func, line); va_start (valist, fmt); CF_Debug_Local_Print (GET_CTX_OSTREAM (context), context->file, context->func, context->line, fmt, valist); va_end (valist); return CF_OK; } /** * 컨텍스트를 업데이트하고 바이너리 데이터를 디버그 메시지와 함께 출력 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 디버그 컨텍스트 * @param file 파일 경로 * @param func 함수 이름 * @param line 라인 넘버 * @param bin 바이너리 데이터 * @param len 바이너리 길이 * @param fmt 포맷 스트링 * @param ... 가변 인자 * * @see CF_DEBUG_TRACE_BIN */ int CF_Debug_TraceBin (CF_Debug_Ctx ctx, const char * file, const char * func, const int line, const unsigned char * bin, const int len, const char * fmt, ...) { va_list valist; CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CHECK_INVALID_CTX (ctx); CF_Debug_Local_UpdateCtx (ctx, file, func, line); va_start (valist, fmt); CF_Debug_Local_Print (GET_CTX_OSTREAM (context), context->file, context->func, context->line, fmt, valist); va_end (valist); CF_Debug_Local_PrintBin (GET_CTX_OSTREAM (context), context->file, context->func, context->line, bin, len); return CF_OK; } /** * 컨텍스트에 콜스택 푸시 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 디버그 컨텍스트 * @param file 파일 경로 * @param func 함수 이름 * @param line 라인 넘버 * * @see CF_DEBUG_CALLSTACK_PUSH */ int CF_Debug_CallStackPush (CF_Debug_Ctx ctx, const char * file, const char * func, const int line) { CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CF_DEBUG_CALLSTACK * push = NULL; CHECK_INVALID_CTX (ctx); push = (CF_DEBUG_CALLSTACK *) calloc (sizeof (CF_DEBUG_CALLSTACK), 1); if (push == NULL) return CF_ERROR_DEBUG_PUSH_CALLSTACK; /* push to callstack */ sprintf (push->file, "%s", file); sprintf (push->func, "%s", func); push->line = line; push->caller = context->callstack.caller; context->callstack.caller = push; CF_Debug_Local_UpdateCtx (ctx, file, func, line); return CF_OK; } /** * 컨텍스트에서 콜스택 팝 * * @return 성공 시, CF_OK; 실패 시, 오류 코드 * * @param ctx 디버그 컨텍스트 * @param callstack 콜스택 정보를 가져올 콜스택 데이터 구조체 포인터 * * @see CF_Debug_CallStackPop, CF_Debug_CallStack */ int CF_Debug_CallStackPop (CF_Debug_Ctx ctx, CF_Debug_CallStack * callstack) { CF_DEBUG_CTX * context = (CF_DEBUG_CTX *) ctx; CF_DEBUG_CALLSTACK * pop = NULL; CHECK_INVALID_CTX (ctx); pop = context->callstack.caller; if (pop == NULL) return CF_ERROR_DEBUG_POP_CALLSTACK; if (callstack != NULL) { sprintf (callstack->file , "%s", pop->file); sprintf (callstack->function, "%s", pop->func); callstack->line = pop->line; } memset (context->file, 0x00, sizeof (context->file)); memset (context->func, 0x00, sizeof (context->func)); context->line = 0; /* restore current context */ if (pop->caller != NULL) { CF_Debug_Local_UpdateCtx (ctx, pop->caller->file, pop->caller->func, pop->caller->line); } context->callstack.caller = pop->caller; free (pop); return CF_OK; }