版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
嵌入式软件调试、测试、问题定位
经验分享AEBELL陆灿2014-11-18内容:嵌入式软件开发概述常见问题及分析实例分析总结
嵌入式软件开发,是指针对嵌入式硬件平台带嵌入式操作系统的软件开发,硬件平台包括:单片机、ARM、PowerPC、DSP、MIPS等;主流嵌入式操作系统有:嵌入式linux、VxWorks、wince等。广义的嵌入式软件开发还包括单片机软件开发。
嵌入式软件开发根据不同的硬件平台、不同的操作系统、不同的开发环节有着多个的方向,如不带操作系统的前后台程序开发;带操作系统的可以有bootloader开发、内核开发、驱动开发、应用开发等。
相对于强大的PC,嵌入式硬件平台资源非常有限,低端的CPU其片内ram一般为K级至100K级,ROM为10K级至1M级,如单片机、ARM7、Cortex-M3。高端的CPU使用片外ram、rom,能达到G级,如A8、A9处理器。
由于嵌入式硬件资源的限制,嵌入式软件不能直接在板子上开发,而是以通用平台(PC)作为宿主机,板子作为目标机,在PC上进行代码编辑,用交叉编译器生成目标码,将目标码下载到板子中去才能运行。常见问题及分析错用运算符运算符优先级歧义大小端倒置内存越界内存泄露任务优先级问题常见问题:
错用运算符 1)条件判定“==”和赋值“=”: if(i=j) { … } 2)逻辑与&&、或||和位运算与&、或|。 if(a&b) { … }2.运算优先级歧义
如果代码行中运算符比较多,如:if(a|b&&a&c);看起来比较艰涩,搞不好容易出错。要熟记所有运算符优先级比较困难。为防止歧义并提高可读性,应当用括号确定表达式的操作顺序,如上式可写为:if((a|b)&&(a&c));
运算符的优先级与结合律3.大小端倒置
大端,是指数据的高位,保存在内存的低地址中,而数据的低
位,保存在内存的高地址中;小端与大端相反。
例如0x11223344,
在大端系统的内存中如下:
在小端系统中是相反顺序:
…11223344……44332211…内存增长方向系统与外界交互数据时通常会涉及到大小端问题,例如上位机(PC)将某歌曲信息传给下位机(设备),歌曲信息如下,规定传输高位在前,低位在后(大端方式)。
设备一般会定义相应结构体:typedefstruct{ chartype;//分类 shortrate;//采样率 intlen;//长度}SONG_INFO,*pSONG_INFO;inthandle_song_info(uchar*buf){chartype;shortrate;intlen;pSONG_INFOpsong_info;psong_info=(pSONG_INFO)buf;type=psong_info->type;rate=psong_info->rate;len=psong_info->len;……}buf是接收PC数据的缓冲区,假设接收到数据在buf中如下:在大端的CPU得到正确结果:rate:0x2233,len:0x44556677在小端的CPU得到的是:rate:0x3322,len:0x77665544显然与协议规定的不一致,要做如下大小端转换:rate=htons(psong_info->rate);len=htonl(psong_info->len);单字节不用考虑大小端问题,如例中的type变量。11223344556677常见问题:4.内存越界
内存越界难以被发现,往往会导致离奇古怪的问题,如一些变量值无故被修改、程序跑飞、系统重启等。
内存分配有3种:栈内存、堆内存和静态内存(全局变量、静态变量),相应内存越界有3种情况:栈内存越界、堆内存越界、静态内存越界。
栈内存堆内存静态内存 1)栈内存越界
深入了解栈,对提高程序的效率、程序调试、问题的定位都有很大的帮助。栈实际就是一块连续内存,用于开辟局部变量、传递参数、进入子程序前保存现场,入栈是从高地址向低地址增长。CPU内部有一个寄存器作为栈指针SP,指向当前栈地址。开始时SP指向栈顶,也就是栈空间的最高地址处。每当入栈n字节数据,硬件自动将SP减n,出栈时SP自动加n,栈后进先出。…………SP栈空间高地址低地址2023/2/3Socket模型介绍CPU内部有一组寄存器R0~R15,其中:R13:就是栈指针SP。R15:为程序计数寄存器PC,指向当前执行指令的地址。R14:为连接寄存器LR,在调用子程序时,由R14保存返回地址。R0~R12:为通用寄存器,用于数据操作。内存中的数据不能在内存中作运算,只能先加载到通用寄存器中,在寄存器里完成运算,结果再存回内存。例如:buf[10]++;要先将buf[10]的值加载到寄存器,寄存器作自加,结果再存回buf[10]。虽然只有一个语句,但并非原子操作,需要执行多个指令。这就是在临界区有时一个语句都需要加锁的原因。栈内存越界分析:intfun1(void){ … fun2(a,b,c,d,e,f); …}intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; memset(buf2,0,100); … return0;}分析一下函数fun1调用子函数fun2后会产生什么后果intfun1(void){ … fun2(a,b,c,d,e,f); … R0R1R2R3}intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; … memset(buf2,0,100); … return0;}栈空间高地址低地址……fePCRxbuf1buf2buf3……前4个参数通过R0~R4传递超过4个参数通过栈传递进入子函数PC先入栈调用前SPbuf2执行清空操作越界了,谁先遭殃,有什么后果???intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; … memset(buf2,0,100); … return0;}不要以为buf2越界是向buf3越界,看栈空间布局,buf2后面(高地址)紧跟的是buf1,执行memset(buf2,0,100);把buf2地址起后面的100字节栈空间都清0,包括buf1和入栈的Rx、PC值,当子函数返回时从栈中弹出返回地址给PC,这时PC值为0,程序跳到绝对地址为0处执行,程序跑飞!高地址低地址……fePCRxbuf1buf2buf3……SP高地址100字节清02)堆内存越界:
堆空间实际也是一块连续内存,内存分配函数以链表方式将分配的内存块手拉手链接起来,因而每块内存都有个链表节点(如下图)。每次调用内存分配函数,遍历链表,找到空闲的空间足够的内存块则返回给调用程序。如果某块内存越界就会把下一块内存的链表节点给冲掉,链表指针“指飞”,堆空间崩溃。prenextprenextprenext…堆内存结构示意图3)静态内存越界:charbuf1[4];charbuf2[4];inta;intb;intmain(void){ … memset(buf2,0,100); …}memset(buf2,0,100);操作使buf2越界,从右边内存布局图可以看到,变量a、b被冲掉。当测试发现一些全局变量值莫名变掉,首先查看它附近是否有内存越界。高地址低地址……babuf2buf1……高地址0x2000000c0x200000080x200000040x20000000内存越界总结:
前面例子是为了说明问题明显制造的内存越界,实际编程中不会这么明显,很多时候是子函数对传入的指针操作引起。intfun(char*buf){ inti; charstr[100]; … … strcpy(buf,str); sprintf(buf,“%s%d”,str,i); …}在调试、测试中如出现变量无故被修改、设备无故重启、程序跑分现象,第一反应应该是内存越界。5.内存泄露
内存泄露就是申请的内存在不使用后没有释放掉。如果系统在运行过程中不断的申请内存,用完又没释放,势必造成内存耗尽而无法工作。指针被修改:intfun(void){ charstr[10]; char*p;
p=(char*)malloc(128); …
p=str; … free(p);}漏掉释放:intfun(void){ … char*p; p=(char*)malloc(128); … if(xx){
return0; } … free(p);}6.任务优先级问题
对于抢占式操作系统,任务的优先级不同,相同的操作有不同的表现,例如uc_os系统,每个任务的优先级都不同,高优先级的任务就绪后就抢占低优先级任务获得运行。如下假设系统中有两个任务task1和task2,在创建任务时指定的优先级不同,得到的执行结果就不一样。OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);printf(“a=%d\r\n”,a);OSTimeDly(1000); }}void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);a=10;OSTimeDly(500); }}OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);
printf(“a=%d\r\n”,a);
OSTimeDly(1000); … }}从上面执行轨迹可以看出,执行结果为:a=0void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);
a=10;
OSTimeDly(500); }}如果task1优先级比task2高,执行流程如下:task1休眠,主动放弃CPU,task2将获得CPU运行task1抛出信号,OSSemPost系统调用发现task2在等待该信号但优先级低,不切换任务,继续执行task1。OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);
printf(“a=%d\r\n”,a);
OSTimeDly(1000); … }}从上面执行轨迹可以看出,a被task2赋值10,所以执行结果为:a=10void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);
a=10;
OSTimeDly(500); }}如果task2优先级比task1高,执行流程如下:task2得到信号资源抢占CPU获得运行task1抛出信号,OSSemPost系统调用发现task2在等待该信号并且优先级高,切换到task2运行task2休眠,主动放弃CPU,task1将获得CPU运行OSSemPost(pevent)系统调用流程至此任务暂停运行软中断中实现任务切换主要操作是:1.保存当前任务的环境(R0~R15、xPSR)到该任务的栈;2.将前面找到的新任务OSPrioHighRdy赋给当前任务OSPrioCur变量,新任务成为当前任务;3.从新任务的栈中弹出其之前的运行环境。至此,任务环境切换完毕,中断子程序退出后新任务就得以运行,实现了任务切换。……PCR0~R3R4~R11SP……PCR0~R3R4~R11SP……当前任务环境入栈新任务环境出栈OSPrioCur=OSPrioHighRdy当前任务栈空间新任务栈空间123实例分析丢包问题死机问题爆音问题
1.丢包问题现象:IP广播设备连续播放10分钟到半小时内出现不可自动回复的丢包,大约每5隔5秒丢100包,造成音频播放严重卡顿。解决过程:首先问题定位以缩小查找范围,将问题出处设定在:服务器、设备端应用层代码、tcp/ip协议栈。观察设备端应用层代码和telnet打印信息,发现应用层从协议接收到的数据包都没有丢失,基本排除应用层;服务器日志显示确实有数据包发不出,范围再次缩小:是服务器问题导致发不出还是设备端接收有问题。怀疑可能设备端协议栈分配不到内存导致接收不了数据,调试发现确实出现分配内存失败。后来开启协议栈的打印信息,发现协议栈的内存池分配失败。查看协议栈代码得知分配内存有2种方式:内存池和内存堆,原来默认的是内存池方式。后来尝试改成内存堆方式,不再丢包。tcp/ip协议栈底层接收数据时申请缓冲内存,程序片段:if(len>0){
//p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL); p=pbuf_alloc(PBUF_RAW,len,PBUF_RAM); /*Copyreceivedframefromethernetdriverbuffertostackbuffer*/ if(p!=NULL) { for(q=p;q!=NULL;q=q->next) { memcpy((u8_t*)q->payload,(u8_t*)&buffer[l],q->len); l=l+q->len; } }}
原来的内存池方式改后的内存堆方式2.死机问题现象:IP广播设备连续播放4~10小时不等,基本出现与服务器失联,CPU指示灯不闪,telnet不通,ping不通。解决过程:这种问题一般是程序进入了某个死循环出不来,非常难定位,即使把全部代码都仔细去看一遍也难以看出来,况且开源的tcp/ip协议栈代码不大可能去通读。这时最好的方法就是硬件调试,让调试器一直开着,直到出现问题,暂停运行看程序停在哪个代码段,那里一般就是死循环的地方。while(pcb!=NULL){ if(pcb->last_timer!=tcp_timer_ctr){ structtcp_pcb*next; pcb->last_timer=tcp_timer_ctr; /*senddelayedACKs*/ if(pcb->flags&TF_ACK_DELAY){ LWIP_DEBUGF(TCP_DEBUG,("delayedACK\n")); tcp_ack_now(pcb); tcp_output(pcb); pcb->flags&=~(TF_ACK_DELAY|TF_ACK_NOW); } next=pcb->next; …… pcb=next; } else{ pcb=pcb->next; }}当这个条件不成立时,这段程序就死循环了修正,if条件不成立时使pcb指向链表下一节点3.爆音问题现象:IP广播设备在播放,突然音量自动变得非常大,出现很随机。解决过程:首先查看所有修改音量的代码,没发现异常。怀疑是不是服务器发指令修改音量,修改音量telnet会打印相关信息,爆音时telnet没有打印修改音量信息,排除掉服务器引起。偶尔观察到爆音时打印:“接收数据xx,有效数据xxx”。搜索代码出现在接收音频数据的代码段如下:
AvaDataSize=GetAudioDataAvaSpaD
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年生物技术领域专业技术服务合同范本版
- 二手挖掘机买卖合同
- 酒水采购合同样本
- 解除合同协议样本
- 鹅绒采购协议
- 技术服务合同的技术开发收费标准制定
- 采购合同制作关键点讲解
- 集中采购合同的跨国运输合同管理
- 红枣购销供应合同
- 简装衣物购销合同
- 2024年秋季新人教版道德与法治七年级上册全册教案
- 传感技术智慧树知到期末考试答案章节答案2024年哈尔滨工业大学
- JBT 11699-2013 高处作业吊篮安装、拆卸、使用技术规程
- 24春国家开放大学《离散数学》大作业参考答案
- 国际发展援助概论智慧树知到期末考试答案2024年
- 浙江大学实验报告(流体力学)
- 国开电大本科《管理英语3》机考真题(第一套)
- 2023年大学生《思想道德与法治》考试题库附答案(712题)
- DB32T 4353-2022 房屋建筑和市政基础设施工程档案资料管理规程
- 学前教育-幼儿园环境创设对幼儿创造力和审美性影响研究
- 《数据库系统概论》课程设计实验报告书
评论
0/150
提交评论