52RD上曾有朋友让我写一些调试技巧方面的文章.调试对于软件是十分重要的,但却不是一篇二篇文章能讲清楚的.有很多调试技巧都是零零碎碎的东西,用的时候能很容易使用,但要写出来时,却还是比较麻烦的.
MTK的调试一般来说可以分为仿真调试与手机调试.这两种调试对于研发新功能,修改BUG,研究算法都是十分重要的.当然,这两种调试也有差异,有时会出现在模拟器运行正常,在手机却运行失败,或者相反,这就突出了软件模拟环境与硬件手机环境的差别.原因可能是各种各样的,比如可能是有些硬件软件没有办法模拟,有些新功能对硬件依赖强,不能模拟,新软件的有些函数只能在手机上运行,没有写相应的模拟器代码.等等原因都会导致两者差异.这里不一一赘述,大家知道模拟器和手机有差异就行了.
模拟器调试具有直观,快速,追踪方便能优点,受到很多MMI开发者的喜欢.而有关模拟器的调试,其他也就是VC调试功能的使用.由于国内软件教育重编程,算法,轻调试,所以很少有系统的调试方书的书.在开发过程中,我也见过许多人压根就不使用模拟器,他们认为模拟器也就是在没有手机的时候使用.详细讲解模拟器的调试就放到以后,因为模拟器断点,内存,堆栈,变量各个方面的调试,详细写来都可以成一篇文章.这里先讲一个手机调试的TRACE使用.以前曾写过一篇DUMP调试的文章.通过出错的DUMP信息查找错误.有兴趣的朋友可以参考.Detail_RD.Blog_blogercn_19169.html
1.在MTK平台,我们最常使用的TRACE函数是kal_prompt_trace函数,这个函数是系统提供给我们的用于在catcher里调试错误的.在这个函数不能使用的场合,有时我们会使用函数system_print或者dbg_printf,这两个函数可以不使用catcher的情况,使用WIN自带的工具超级终端来调试程序.有时驱动的朋友会自己用函数PutUARTBytes写自已TRACE函数,这些函数可以使用超级终端调试,如下,就是别人写的一个TRACE函数.打印某一块数据的内容,常常TRACE内存数据,指定地址,指定大小
void perun_dump(void *buf, prn_int16 size){#ifdef PRN_TRACE_OPEN#ifdef MMI_ON_HARDWARE_P char str[2048]; char *ptr = (char*)str; char *ptr1 = buf; int i = 0; memset(str, 0, sizeof(str)); strcpy(ptr, "[Perun_dump]: "); ptr +=strlen("[Perun_dump]: "); while (i< size) { sprintf(ptr, " %02x", *ptr1); ptr += 3; ++ptr1; ++i; } sprintf(ptr, "\r\n"); PutUARTBytes(0, (kal_uint8 *)str, (kal_uint16)strlen(str));#endif#endif}
Void perun_trace(char *fmt, ...){#ifdef PRN_TRACE_OPEN#ifdef MMI_ON_HARDWARE_P va_listlist; char buf[2048]; char *ptr = (char*)buf; memset(buf, 0, sizeof(buf)); strcpy(ptr, "[Perun_trace]: "); ptr +=strlen("[Perun_trace]: "); va_start(list, fmt); vsprintf(ptr, fmt, list); va_end(list); strcat(buf, "\r\n"); buf[2047] = 0; PutUARTBytes(0, (kal_uint8 *)buf, strlen(buf));#endif#endif }
2.TRACE语句的编写是十分重要的.如何写出的TRACE既能在仿真下使用,也能在手机中使用,我一般会使用如下的格式:
#ifdef WIN32#define MYTRACE printf#else#define MYTRACE(...) kal_prompt_trace(MOD_WAP, __VA_ARGS__)#endif
经过如下的封装,MYTRACE就可以既能在手机上运行,也能在电脑中运行,并且我已经消除了MTK自带的函数与printf在调用上的不同.顺便说一下,模拟器调用函数MYTRACE时,会在控制制输出该函数的打印信息.手机调用MYTRACE时,会在filiter为MOD_WAP时输出信息.
3.有时为了便于观察,我会为我的TRACE语句添加一个前缀,比如我自己的TRACE前面添加十个>或者我自己的拼音名字,我会如下修改我的MYTRACE:
#ifdef WIN32#define MYTRACE printf#else#define STR(s) #s#define MYTRACE(...) kal_prompt_trace(MOD_WAP,STR(>>>>>>>>>>)##__VA_ARGS__)#endif
经过这样的改进,我的TRACE在输出信息时,信息头就是我的名字,我可以使用查找全部功能把我需要的TRACE全抓出来.如果你对#号的使用,有疑问,请自己查找相关资料
4.种种迹象和从理论上看来,TRACE和MMI_ASSERT是调试的好帮手,但在发布软件时,带上了这个会引来不必要的麻烦.MMI_ASSERT增加了系统重启的频率.TRACE增加了系统的ROM,RAM和CPU的开销.在工作中,我们曾经发现一款手机,由于ROM过于紧张,添加几条TRACE就会出现编译错误,去掉TRACE就编译通过了,导致出了BUG调试十分的麻烦.如何写一种使用时可以TRACE错误,不使用时又不占用系统资源的TRACE呢,我见许多人这样处理,因为NULL会被编译器优化点,后面括号变成一个表达式了.表达式对系统的开销自然小于函数了.
#ifdef DEBUG_KAL#define MYTRACE(...) kal_prompt_trace(MOD_WAP, __VA_ARGS__)#else#define MYTRACE NULL#endif
一般这样
#ifdef DEBUG_KAL#define MYTRACE(...) kal_prompt_trace(MOD_WAP, __VA_ARGS__)#else#define MYTRACE //#endif
我曾经认为这样写可以在不使用时,把宏函数变为注释符,但我的一个朋友认为这样写,并不能把我的函数变成注释符,他的道理是双斜线会被编译器外忽略掉,我认为是有道理的,后来我的朋友经过思考,写出如下的TRACE,通过一个反斜杠的连接符,哄编译器在展开宏时把两个斜杠连起来组成注释符,这个写法有些古怪,下面的单个斜杠必须顶格写,以保证经过连接合,两斜杠之间没有空格从而在被编译时与后面的代码组成注释行.但这样写的一个致命的缺陷是会让许多人看不明白.另一个缺陷时不美观
#ifdef DEBUG_KAL#define MYTRACE printf#else#define MYTRACE /\/#endif
5.有关C语言的古怪用法,据<C专家编程>的作者曾说,世界上有一个古怪的大赛叫”国际C语言混乱代码大赛”,每年举办一次.获奖的都功能齐全而代码惨不忍睹的典型.在MTK上,也曾经有一段费了我很大功夫的TRACE代码:
#define DBG_PRINTF(_x_) \do{ \printf("%s(%d)--:",__FILE__,__LINE__);\printf _x_; \}while(0);
当时怎么想都不明白为什么会有这么古怪的使用printf _x_;后来才发现使用时要加双层括号才能正常使用。
6.在手机与网络,手机与电脑的交互过程中,有时我们需要TRACE数据包的内容,特别在显示不正确或者不精确的情况下,需要研究数据包的数据是使用UTF编码,还UCS2编码,还是ASC编码,这时我们就需要TRACE数据的字节内容,从而便于分析。我一般使用下面这个自己随手写的函数来实现.
static U8 TraceUni(U8 *str, U8 len) {U8 *tempstr,*tempascstr;U8 i;tempstr = str;kal_prompt_trace(MOD_MMI,"XXXXXXXXXXXXXXXXXXXXXXXX Tracemmi_msg_handle_new_msg_ind isstart XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");if (*(tempstr + 1) == 0x00){UnicodeNToAnsii((S8*)tempascstr,(S8*)tempstr,len);kal_prompt_trace(MOD_MMI, "jone trace, thestring = %s", tempascstr);}else{for (i = 0; (*tempstr != NULL) || (*(tempstr+1)!= NULL) && (len-- != 0);i++){kal_prompt_trace(MOD_MMI,"jone trace, the string[%d] = %x", i, *(tempstr++));}}kal_prompt_trace(MOD_MMI,"XXXXXXXXXXXXXXXXXXXXXXXX Tracestring isend XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");return 0; }