mtk 调试方法总结

mtk开发基本调试方法大概就三种模拟器方式,trac方式,串口方式

普通的界面修改,功能行基本都可以通过模拟器完成,但是遇到死机或者与l4c相关的问题是就需要打trac配合cather进行调试了;如果还搞不顶,那么mtk会告诉你打开dump开关,通过trac方式抓包后上传到eservice后他们来分析问题的原因;串口方式主要是很多在mtk平台做第三方应用的会使用;


方法一:模拟器调试


前篇文章介绍了手机调试的TRACE技巧,MTK手机调试是比较简单的,除了打TRACE,找ASSERT,分析DUMP外,剩下的就是经验了,有经验的人看到现象就能猜到问题出在什么地方,简单看一下TRACE只是为了确认自己的猜测.初涉此道的朋友往往需要时间,慢慢积累经验.要达到熟练,就需要有空的时候多读多研究代码.熟练的掌握了代码,解决问题就会熟能生巧.

MTK的模拟器调试说没技巧也是对的,说有技巧也不错.因为模拟器的技术与其说是MTK经验,不如说是VC经验.

由于模拟器能在没有样机的情况做大量的UI方面的开发,同时模拟器具有快捷,所见即所改等优点,因此,受到很多开发老手的青睐.熟练掌握模拟器使用技巧,对于提高工作效率,节省资源具有很积极的意义.

工欲善其事,必先利其器.欲了解模拟器调试,就要先了解VC使用.VC调试过程中经常使用的快捷键如下:

vc++断点的使用:按F9(设置断点),F5调试,F10单步调试,shift+F5退出调试F10单语句执行,F11单指令执行,Ctrl+F10执行至光标处;F9按行设置/取消断点, Alt+F9可按行,按数据或按消息设置断点;调试时,按Alt+3打开跟踪窗口,Alt+4打开变量窗口,Alt+5打开寄存器窗口,Alt+6打开内存窗口,Alt+7打开堆栈窗口,Alt+8打开汇编指令窗口;

1.只要你有源代码,拥有一个模拟器是一件很容易的事.当然,有些情况下,这对于初入MTK的人来说,也是困难的,特别是在一些代码管理不善,公司人员众多,员工流动频繁的公司,你永远想象不出他们的代码会有多糟.不过一般情况下,我到一个新公司,都会试着去使用他们的代码编译得到模拟器,不管他们的代码多么糟,我总能快速的编出模拟器来.只要三个步骤:
a.在编译模拟器前请关掉所有SP的宏,这是很重要的,很多SP都不提供在模拟器下运行的库,所以一个项目SP越多,在模拟器下,他无法识别的函数也会越多
b.在编译过程中遇到错误,可以把所有与显示无关的代码全部使用#ifdef WIN32宏括起来,把所有不认识的类型使用typedef int xxx;重定义.把一些不能识别的宏定义使用WIN32括起来重定义一下
c.在生成MODIS时,会遇到大量不认识的函数,如果少于50个,全部建成void xxx(){}类型的空函数.如果多于一百个,尝试查找这些函数的来源,如果是相关SP的,就再次补充关掉相关SP.当然如果你不怕麻烦,也可以把这一百个函数建成空函数.一般来说,把所有第三方功能和与MTK无关的功能关掉的话,不能识别的函数一般不会超过十个.
经过这样三步,一般情况下你都能得一个差不多可以使用的模拟器.有了模拟器,你就可以进行下面的工作了.如果你仍然无法获得模拟器,建议咨询你的项目领导,如果你的领导也无法解决,说明这个软件组是一个组员变动很快,或者说缺乏条理性的部门,当然也有可能这个部门的程序员都是一些MTK的顶尖层高手,他们的程序从来不需要调试.总之这是会让人疯狂的部门.

2.如果你顺利到到一个模拟器,那么就可以开始调试了.首先说一下断点,VC中的断点分三大类,一类是本地断点,一类是数据断点(也称之为条件断点,有人称之为高级断点),还是一类是消息断点;这三类断点分别对应于快捷键"ALT+F9"打开的对话框中三个选项卡.由于消息断点是用于跟踪WIN的消息,所以在MTK中用不着.在模拟器中一般我们只使用本地断点与数据断点.本地断点和数据断点在BUG调试中举足轻重,熠熠生辉。熟练掌握断点技巧,可以使工作事倍功半。可能有些人会使用断点,但却不知道断点在模拟器中有什么妙用.断点有两个最常使用也的作用,一是跟踪代码执行情况,二是观察断点处局部变量值的变化.这两个功能应用最多.但他在MTK模拟器中还有两个隐含的用途,一就是快速定位文件,MTK本身的源代码大约有差不多上万个文件,而加入SP后,代码大约已经超过了万数.在这里面查找文章,查找函数,是一件很困难的事,这时就需要使用断点了.你在代码阅读工具SI中可以很轻松的利用"CTRL+O"打开文件,也可以使用"F7"快速定位到函数,但在VC中却没这么方便,不过你可以使用断点,比如想在VC中打开某个文件wgui_categories_CM.c,只要VC中使用"ALT+F9",在break at编辑框里输入{,E:\JMT_1120\plutommi\mmi\GUI\GUI_SRC\wgui_categories_CM.c,} .4693,按F5执行DEBUG后VC就能自己定位到这个文件wgui_categories_CM.c的4693行。这是文件断点,也可以打函数断点,比如文件wgui_categories_CM.c的4693行对应的是函数ShowCategory16Screen,也可以直接在break at编辑框里输入ShowCategory16Screen,使用F5键DEBUG时,函数运行到拨号界面时,就会停下来供你调试。本地断点就是这样使用的。那么本地断点有什么用呢?或者说有什么特殊用途呢?简单举几个例子吧,但断点可以应用于解决十分多的问题中,不要因为我的例子束缚了你的想象力.
a.如何使用断点快速定位到问题点呢?如果我们发现,拨号窗口显示出了问题,但我们对代码不熟,不知道拨号窗口的代码在哪个文件,哪一行,我们就可以在EntryNewScreen函数上打断点,当进入拨号时他就会停下来,这时我们能过堆栈窗口信息就可以很轻松的找到这个窗口的实现函数。详细研究代码,就可以找到解决方法.
b.断点可以用于快速解决窗口显示问题,比如我们的拨号窗口,有一个图片显示不正常,这时我们可以在绘图函数gdi_image_codec_draw上打上断点,进入拨号窗口中,每一次显示图片,都会在该函数上停下来,结合堆栈,我们可以很容易找到是哪个地方代码出来问题,从而找到解决方法.有关此类的函数太多,不一一举例.
C.断点可以用来研究全局变量被意外修改问题,我们打开断点对话框,选第二个选项卡,把需要跟踪的变量打进去,当每次变量变化时,VC都会停下来等我们调试.也可以设置条件,假设某全局变量U8 g_XXX, 其值等于5时会出错,但你不知道这个全局变量在什么地方被什么代码赋值为5,这时就可以设置数据断点,在第二选项卡上面的对话框里输入g_XXX == 5,当其值为5时就会停下来.
d.研究代码也可以使用断点,比如MTK代码里使用有很多函数指针封装,例如gui_print_text指针,你想研究他的实现过程,但由于是指针,你找不到他的函数体,这时你就可以在数据断点中,把指针gui_print_text输入进去,重启模拟器时,他就会定位到ui_print_text函数处.
兹举这四个例子吧,断点可以使用于你需要调试的任何场合,但过多的断点会影响你查找问题的速度,等熟练使用时,就可以有针对性的对某些变量和函数打断点以解决问题.这是一个积累的过程
3.堆栈调试,Alt+7打开堆栈窗口.该窗口中我们可以看到函数之间的调用关系,这是十分有用的,一般都是结合断点使用,定位BUG和研究代码十分有用.由于上面有例子,这里就不举了
4.变量窗口,Alt+4打开变量窗口,该窗口会自动显示断点代码处使用的变量及其值,阅读代码解决BUG时使用,单步执行时经常参考该窗口数据
5.WATCH窗口,按Alt+3打开,由于变量窗口自动显示的变量有时不是我们需要的,这时就可以把我们需要查看的变量拖放到该窗口研究,结合断点使用,并且这里也支持表达式取值,真是太棒了.
6.内存窗口,Alt+6打开内存窗口,内存窗口可以显示一块内存的内容,这是很有用的,比如我们要跟踪短信内容,有一个短信内容的指针,把该指针输入WATCH窗口,只能看到该指针指向的第一个值,要看其他的,会很麻烦,你只能输入表达式,但你把该指针输入内存窗口,就不必这样费事了
7.Alt+8打开汇编指令窗口,这个窗口用处不是很大,学习汇编的话,还是有用处的.一般情况下,如果第三方的库文件出了问题,也就只能使用这个窗口调试了.普通情况下,如果错误定位在C标准库文件的汇编代码上,只有一种可能,就是你的调用出错了.

 
 
调试占了研发的很大一部分时间,调试的基本技术就这样,一般情况下是综合运用,灵活掌握,以期快速解决问题,稳定代码.剩下就是经验积累的问题了.

 
注:

MTK Modis 使用简介
Modis PC仿真, 要进行如下操作:(假设工程路径为D:\X)
第一步:在如下目录中D:\X
执行  make Prj_Name GPRS gen_modis

第二步: 转到modis目录 D:\X\modis执行  createmodis.pl ..\make\Prj_Name_GPRS.mak

第三步:再切换到 D:\X
执行  make Prj_Name GPRS codegen_modis

第四步:最后转到modis目录,
执行   modiscodegen.pl ..\make\Prj_Name_GPRS.mak

第五步:开始使用Modis仿真--运行D:\X\modis\Modis.dsw,编译工程;
第六步:启动Modis->开机->Option->Luanch Catcher&NS,这样就可以进行模拟操作并从catcher中得到日志;
        注意:老版本的Catcher下出现NS不能控制的情况。

make WMD6225_GEMINI GPRS gen_modis
cd modis
createmodis.pl ..\make\WMD6225_GEMINI_GPRS.mak
cd ..
make WMD6225_GEMINI GPRS codegen_modis
cd modis
modiscodegen.pl ..\make\WMD6225_GEMINI_GPRS.mak





方法二:catcher 方式trac调试

曾有朋友让我写一些调试技巧方面的文章.调试对于软件是十分重要的,但却不是一篇二篇文章能讲清楚的.有很多调试技巧都是零零碎碎的东西,用的时候能很容易使用,但要写出来时,却还是比较麻烦的.
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
}
也可以写一个像MTK自带的一样的函数来TRACE,如下,该函数也是可以运行在终端中:
Void perun_trace(char *fmt, ...)
{
#ifdef PRN_TRACE_OPEN
#ifdef MMI_ON_HARDWARE_P
va_list list;
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会被编译器优化点,后面括号变成一个表达式了.表达式对系统的开销自然小于函数了.




MTK通过TRACE的栈信息寻找BUG原因与解决方法(2010-02-02 20:00:21)

前几天去一个公司帮他们解决BUG。BUG的描述是这样的,在使用在线QQ时,如果来电话,就会重启。
没有发现ASSERT信息,只有stack dump信息。起初听他们描述,感觉像是QQ或者通话的问题。
抓了TRACE之后,发现是MED模块的问题,由于MED主要是一些媒体文件的解码。
由于观察现象时发现,通话时,还没有来得及响铃,就开始重启,因此可以大概推知是来电振铃出了问题,
具体出在什么地方,需要查找TRACE信息。从别人那里获取的TRACE信息如下:

Trace 1745424 150706 MOD_NIL TRACE_ERROR [1] fatal error (4): Data_abort - MED
Trace 1745424 150706 MOD_NIL TRACE_ERROR Exception type: data abort
Trace 1745424 150706 MOD_NIL TRACE_ERROR software version:
E500_A.1.4
Trace 1745424 150706 MOD_NIL TRACE_ERROR boot mode: normal mode
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc sec = 16, rtc min = 33, rtc hour = 1
Trace 1745424 150706 MOD_NIL TRACE_ERROR rtc day = 1, rtc mon = 1, rtc wday = 1, rtc year = 9
Trace 1745424 150706 MOD_NIL TRACE_ERROR execution unit: MEDMED
Trace 1745424 150706 MOD_NIL TRACE_ERROR status: 0x00000000
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack pointer: 0x00169380
Trace 1745424 150706 MOD_NIL TRACE_ERROR stack dump:
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0xA0001BED
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x08480FD5
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0847CE59
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x084DE0EF
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0866D4D3
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0845E407
Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x0866D4A1
Trace 1745424 150706 MOD_NIL TRACE_ERROR number of messages in the external queue: 0
Trace 1745424 150706 MOD_NIL TRACE_ERROR messages in the external queue:
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE
Trace 1745424 150706 MOD_NIL TRACE_ERROR     MSG_ID_INVALID_TYPE


观察TRACE,结合BUG描述,可以大致推知是MED的振铃问题,具体位置需要看catcher打印的栈信息。
有些人遇到这个信息,就蒙了,感觉无从下手,我最早学习MTK时,也是无从下手,总觉得这样的信息没有ASSERT直观
,一看就知道哪个文件哪一行出错。但ASSERT并不总是金牌,有一次遇到一个MEMCPY的ASSERT,调用的地方太多,
照样需要查栈信息来寻找问题。当然写的代码,尽量在一些关键的地方加上ASSET,这会让人查找问题更轻松一些。
当然有了这个信息,我们也可以大概推测出BUG的地方和原因的。要查找这个error的信息,必须要找到该软件版本对应
的sym文件。该sym和BIN文件同步生成,都位于BUILD文件下。该文件储存了ARM编绎器为软件中所有函数生成的地址。
而出错时,MTK会保存当时栈中最近执行的10条指令的地址,我们通过对这些地址的研究,就可以找到出错的地方。
由于sym文件只保存了函数的地址,函数体里执行的代码地址没有列出,而一般打出的错误地址不会正好对应某个函数的
地址,一般会会偏移N个单位。当然也有例外,我曾遇到一次出错,有一个地址正好和某函数地址对应。对于出错信息
的地址与函数地址不对应的情况,我推测应该是函数体里的指令出错,所以应查找比该出错指令小且最近的函数。
比如上例行“Trace 1745424 150706 MOD_NIL TRACE_ERROR     0x085569C9”,我们在sym文件中不应该查找0x085569C9
对应的指令,因为一般会一个也找不到,我们要查找x085569C或者x085569对就的函数,这时大约可以查到许多函数,
找到与该出错地址最近且比该地址小的地址对应的函数,十个地址大约可以找到十个左右的函数,通过仔细排查,
确认以下函数:

med_int_left_size
med_int_sfreeaud_main
aud_tts_play_req_hdlr
med_int_smalloc
med_set_ext_memory_pool
TCC_Task_Shell

如此可以大致确定问题出处和原因了,请教项目负责人,知道aud_tts_play_req_hdlr
是语音王的播放函数,在呼入时,有来电报号,该功能和QQ都是从MED栈上动态分配内存,由于QQ占用了一部内存,有呼入电话时,语音王分配内存失败,导到语音解码故障。问题一下解决。

=====================================================================================
呵呵,通过map信息,来查找导致问题的函数入口,是最常用的调试手段之一。其实,
也完全可以通过现象分析来查找问题点。比如,这种完全能复现的问题,很容易找到固定路径的。
如来电提示改为震动,或者无声,会怎样,关闭来电报号,会怎样。一下问题点就出来了。共享内存导致的这个问题,
其实是设计时候的欠考虑,没有考虑到各种可能常见的边界。 Assert用得太多也未必好,作为一种调试手段,
如果都出厂了,还到处Assert或重启来处理一些问题,显得太那个了吧,太不健壮了。



三,串口调试
MTK串口调试设置 MTK 产品发布时AT和Debug复用仅有的一个串口。
一般AT指令和下载代码一般用串口1(APP uses uart_port1),
debug调试一般用串口2(TST uses uart_port2)。
代码中有可能把串口2和串口3设置为普通IO口来用,因此,在程序调试阶段,如果要想把AT调试和Debug调试同时打开,则需要从新设置uart_port2的GPIO设置。
设置方法:进入custom\drv\Drv_Tool,用DrvGen.exe 打开custom\drv\misc_drv\"项目名"\codegen.dws,对uart端口对应的GPIO进行相应设置即可。

如果只是想掉换TST和AT调试信息的串口,只需设置:
./custom/common/PLUTO_MMI/nvram_common_config.c中NVRAM_EF_PORT_SETTING_DEFAULT[]
0x00, 0x00,   /* TST uses uart_port2(value is 1) */    //longfeey modify 0x01, 0x00,
0x01, 0x00,   /* APP uses uart_port1 */               //longfeey modify 0x00, 0x00,
0x00,0x00 表示串口1,0x01,0x00表示串口2
根据自己需要设置即可。
 
 
 
 


你可能感兴趣的:(MTK开发与应用)