来自 http://www.oschina.net/question/249672_59411的文章
我是不喜欢用类似VC下的F5,F10。曾经很喜欢用。被代码逻辑逼的没招了。所以不喜欢用了。
比如,错误是根据动态数据,产生的行为错误,无论是该写的未写,还是不该写的写了。指针跑飞什么等等,无非就是上述两个错导致。但要找到具体原因,F5,F10根本不够。所以索引不用了。
这里介绍一下我现在的方法。不过注明这不是LOG。
#ifndef _debug_H_
#define _debug_H_
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#ifndef UNDEBUG_FILE
#define DEBUG_FLAG 1
#else
#define DEBUG_FLAG 0
#endif
#if (DEBUG_FLAG == 1)
static
unsigned
int
__sdebug_time = 0;
#define __debug_Msg_Size 1024
static
char
__pdebug_Msg[__debug_Msg_Size];
//注意下面的__attribute__不是C标准。。。。
static
void
__debug_info(
const
char
*prefix,
const
char
*fmt, ...) __attribute__((format (
printf
, 2, 3)));
#define __NEXT_DBG_TIME() do{sdebug_time++;}while(0)
#define __PRINT_POS() do {fprintf(stderr,"%s(%d){%s}",__FILE__,__LINE__,__func__);}while(0)
#define __FUNC_LOG() do{__PRINT_POS();fprintf(stderr,": t = %d ",__sdebug_time++);} while(0)
#define __FUNC_LOGn() do{__FUNC_LOG();fprintf(stderr,"\n");} while(0)
#define __PRINT_POS_exp(exp) do{__PRINT_POS();fprintf(stderr,"| (%s):",#exp);}while(0)
#define __PRINT_POS_P_exp(prefix,exp) do{__PRINT_POS();fprintf(stderr,"|<%s> (%s):",prefix,#exp);}while(0)
#define __PRINT_POS_expn(exp) do{__PRINT_POS_exp(exp);fprintf(stderr,"\n");}while(0)
#define __PRINT_POS_P_expn(prefix,exp) do{__PRINT_POS_P_exp(prefix,exp);fprintf(stderr,"\n");}while(0)
#define __ASSERT(exp) do{if (exp){}else{__PRINT_POS_P_expn("ASSERT ERROR",exp);}}while (0)
#define __ASSERT_EXIT(exp) do{if (exp){}else{__PRINT_POS_P_expn("ASSERT ERROR",exp);exit(1);}}while (0)
#define __debug_info_LOG(exp,PREFIX,fmt,...) do{if (exp){__PRINT_POS_P_exp(PREFIX,exp);__debug_info(PREFIX,fmt,__VA_ARGS__);}}while (0)
#define __ASSERT_LOG(exp,fmt,...) __debug_info_LOG(exp,"ASSERT!",fmt,__VA_ARGS__)
#define __ERROR_LOG(exp,fmt,...) __debug_info_LOG(exp,"ERROR!",fmt,__VA_ARGS__)
define __BEFORE_LOG(N,fmt,...)
do
{__debug_info_LOG((N) < __sdebug_time,
"BEFORE!"
,fmt,__VA_ARGS__);__NEXT_DBG_TIME()}
while
(0)
#define __AFTER_LOG(N,fmt,...) do {__debug_info_LOG((N) >= __sdebug_time,"AFTER!",fmt,__VA_ARGS__); __NEXT_DBG_TIME();}while(0)
static
void
__debug_info(
const
char
*prefix,
const
char
*fmt, ...) {
va_list
params;
va_start
(params, fmt);
__ASSERT_EXIT((__pdebug_Msg) && (__pdebug_Msg_Size ));
vsnprintf(__pdebug_Msg, __pdebug_Msg_Size, fmt, params);
if
(prefix){
fprintf
(stderr,
" %s %s\n"
, prefix, __pdebug_Msg);
}
else
{
fprintf
(stderr,
" %s\n"
, __pdebug_Msg);
}
va_end
(params);
}
#else
#define __NOP do{}while(0)
#define __NEXT_DEBUG_TIME() __NOP
#define __FUNC_LOGn() __NOP
#define __FUNC_LOG() __NOP
#define __PRINT_POS_Sn(exp) __NOP
#define __PRINT_POS_S(exp) __NOP
#define __ASSERT(exp) __NOP
#define __ASSERT_EXIT(exp) __NOP
#define __debug_info_LOG(exp,PREFIX,fmt,...) __NOP
#define __ASSERT_LOG(exp,fmt,...) __NOP
#define __ERROR_LOG(exp,fmt,...) __NOP
#define __BEFORE_LOG(N,fmt,...) __NOP
#define __AFTER_LOG(N,fmt,...) __NOP
#endif
#endif
上面
|
1
|
UNDEBUG_FILE
|
是如下用法,如果你觉得加了一对debug信息太乱,那么整体C文件(模块)暂时没错可以如下
|
1
2
3
|
...
#define UNDEBUG_FILE
#include "debug.h"
|
|
1
|
__FUNC_LOGn();
|
可以写在每个函数的入口。这对跟踪函数之间的调用很有帮助(注意不是LOG,LOG打印这些就是没事找事了)
|
1
|
__PRINT_POS_Sn
|
主要是,例如指针跑飞,可以在各个地方加插该内容,以判断是否经过该地方。其和
|
1
|
__FUNC_LOGn();
|
输出是一样,但前者不影响sdebug_time。可以给出文件路径,行号,函数名和位置。一些输出情况如下:
|
1
2
3
4
5
6
|
src/graph.c(90){create_graph_abs_by_ID}: t = 0
src/graph_abstract.c(40){refresh_graph_abs}: t = 0
src/graph_abstract.c(41){refresh_graph_abs}: t = 1
src/graph_abstract.c(41){refresh_graph_abs}|<ASSERT!> (1): N = 82
src/graph_abstract.c(42){refresh_graph_abs}: t = 2
src/graph.c(91){create_graph_abs_by_ID}: t = 1
|
上面的内容就是上述头文件被#include到graph.c ,graph_abstract.c中,对应调用
|
1
2
3
|
__FUNC_LOGn();
以及
__ASSERT_LOG(1,
"N = %d"
,N);
|
的结果。需要注意,__ASSERT_LOG,我和传统的__ASSERT是反过来的。此处表示,断言成立下,才LOG信息到stderr中。
|
1
|
src/graph_abstract.c(42){refresh_graph_abs}: t = 2
|
|
1
|
__sdebug_time
|
|
1
|
__sdebug_time
|
运行了6000次后才出现某个情况出错,则可以使用
|
1
|
__AFTER_LOG
|
__BEFORE_LOG则是反过来。
|
1
|
__ASSERT_LOG
|
本文介绍了一种通过宏定义实现的调试方法,该方法能够帮助开发者更好地定位和解决程序中的错误,尤其适用于指针错误等问题的排查。相较于传统的断点调试,此方法更加灵活高效。
1021

被折叠的 条评论
为什么被折叠?



