嵌入式Linux调试技术课件_第1页
嵌入式Linux调试技术课件_第2页
嵌入式Linux调试技术课件_第3页
嵌入式Linux调试技术课件_第4页
嵌入式Linux调试技术课件_第5页
已阅读5页,还剩34页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

张林软件研发部嵌入式Linux调试技术主要内容嵌入式调试也是一门艺术GDBCoreDump在嵌入式软件开发过程中,一般来说,花在测试和花在编码的时间比为3:1(实际上可能更多)。这个比例随着你的编程和测试水平的提高而不断下降,但不论怎样,软件测试对一般人来讲很重要。很多年前,一位开发人员为了在对嵌入式有更深层次的理解,向Oracle询问了这样的一个问题:我怎么才能知道并懂得我的系统到底在干些什么呢?

Oracle面对这个问题有些吃惊,因为在当时没有人这么问过,而同时代的嵌入式开发人员问的最多的大都围绕“我怎么才能使程序跑的更快”、“什么编译器最好”等肤浅的问题。所以,面对这个不同寻常却异乎成熟的问题,Oracle感到欣喜并认真回复了他:你的问题很有深度很成熟,因为只有不断地去深入理解才有可能不断地提高水平。并且Oracle为了鼓励这位执着的程序员,把10条关于嵌入式软件开发测试的秘诀告诉了他:

一个小故事1.懂得使用工具2.尽早发现内存问题3.深入理解代码优化4.不要让自己大海捞针5.重现并隔离问题6.以退为进7.确定测试的完整性8.提高代码质量意味着节省时间9.发现它,分析它,解决它10.利用初学者的思维10条秘诀就象修车需要工具一样,好的程序员应该能够熟练运用各种软件工具。不同的工具,有不同的使用范围,有不同的功能。使用这些工具,你可以看到你的系统在干些什么,它又占用什么资源,它到底和哪些外界的东西打交道。让你郁闷好几天的问题可能通过某个工具就能轻松搞定,可惜你就是不知道。那么为什么那么多的人总是在折腾个半死之后才想到要用测试工具呢?原因很多,主要有两个。一个是害怕,另一个是惰性。害怕是因为加入测试用具或测试模块到代码需要技巧同时有可能引入新的错误,所以他们总喜欢寄希望于通过不断地修改重编译代码来消除bug,结果却无济于事。懒惰是因为他们习惯了使用printf之类的简单测试手段懂得使用工具常用调试工具1.源代码级调试器(Source-levelDebugger)--gdb2.简单实用的打印显示工具[printf]3.ICE或JTAG调试器[In-circuitEmulator]–BDI20004.ROM监视器[ROMMonitor]5.Data监视器[DataMonitor]6.OS监视器[OperatingSystemMonitor]7.性能分析工具[Profiler]8.内存分析工具[MemoryTeseter]–mtrace、boundschecker9.运行跟踪器[ExecutionTracer]--strace10.覆盖工具[CoverageTester]11.自制工具[Home-madetester]12.GUI测试工具[GUITester]–robot、LoadRunner

主要有三种类型:内存泄露、内存碎片和内存崩溃对于内存问题态度必须要明确,那就是早发现早“治疗”。尽早发现内存问题深入理解代码优化讲到系统稳定性,人们更多地会想到实时性和速度,因为代码效率对嵌入式系统来说太重要了。知道怎么优化代码是每个嵌入式软件开发人员必须具备的技能。就象女孩子减肥一样,起码知道她哪个地方最需要减,才能去购买减肥药或器材来减掉它。可见,代码优化的前提是找到真正需要优化的地方,然后对症下药,优化相应部分的代码。

不要让自己大海捞针大海捞针只是对调试的一种生动比喻。

经常听到组里有人对自己正在调试的代码抱怨!可以理解,因为代码不是他写的,他有足够的理由去抱怨bug百出的代码,只要他自己不要写出这种代码,否则有一天同组的其它人可能同样会抱怨他写的代码。为何会有大海捞针呢?肯定是有人把针掉到海里咯;那针为何会掉在海里呢?肯定是有人不小心或草率呗。所以当你在抱怨针那么难找的时候,你是否想过是你自己草率地丢掉的。同样,当你调试个半死的时候,你是否想过你要好好反省一下当初为了寻求捷径可能没有严格地遵守好的编码设计规范、没有检测一些假设条件或算法的正确性、没有将一些可能存在问题的代码打上记号呢?已所不欲,勿施于人重现并隔离问题如果你不是把针掉在大海了,而是掉在草堆里,那要好办写。因为至少我们可以把草堆分成很多块,一块一块的找。对于模块独立的大型项目,使用隔离方法往往是对付那些隐藏极深bug的最后方法。如果问题的出现是间歇性的,我们有必要设法去重现它并记录使其重现的整个过程以备在下一次可以利用这些条件去重现问题。如果你确信可以使用记录的那些条件去重现问题,那么我们就可以着手去隔离问题。怎么隔离呢?我们可以用#ifdef把一些可能和问题无关的代码关闭,把系统最小化到仍能够重现问题的地步。如果还是无法定位问题所在,那么有必要打开“工具箱”了。可以试着用ICE或数据监视器去查看某个可疑变量的变化;可以使用跟踪工具获得函数调用的情况包括参数的传递;检查内存是否崩溃以及堆栈溢出的问题。以退为进猎人为了不使自己在森林里迷路,他常常会在树木上流下一些标记,以备自己将来有一天迷路时可以根据这些标记找到出路。对过去代码的修改进行跟踪记录对将来出现问题之后的调试很有帮助。假如有一天,你最近一次修改的程序跑了很久之后忽然死掉了,那么你这时的第一反映就是我到底改动了些什么呢,因为上次修改之前是好的。那么如何检测这次相对于上次的修改呢?没错,代码控制系统SCS或称版本控制系统VCS(ConcurrentVersionControl,CVS是VCS的演化版本)。将上个版本checkin下来后和当前测试版本比较。比较的工具可以是SCS/VCS/CVS自带的diff工具或其它功能更强的比较工具,比如BeyondCompare和ExamDiff。通过比较,记录所有改动的代码,分析所有可能导致问题的可疑代码。确定测试的完整性

你怎么知道你的测试有多全面呢?覆盖测试(coveragetesting)可以回答这个问题。覆盖测试工具可以告诉你CPU到底执行了那些代码。好的覆盖工具通常可以告诉你大概20%到40%代码没有问题,而其余的可能存在bug。覆盖工具有不同的测试级别,用户可以根据自己的需要选择某个级别。即使你很确信你的单元测试已经很全面并且没有deadcode,覆盖工具还是可以为你指出一些潜在的问题,看下面的代码:

if(i>=0&&(almostAlwaysZero==0||(last=i)))

如果almostAlwaysZero为非0,那么last=i赋值语句就被跳过,这可能不是你所期望的。这种问题通过覆盖工具的条件测试功能可以轻松的被发现。

总之,覆盖测试对于提高代码质量很有帮助。

提高代码质量意味节约时间有研究表明软件开发的时间超过80%被用在下面几个方面:调试自己的代码(单元测试)调试自己和其他相关的代码(模块间测试)调试整个系统(系统测试)

更糟糕的是你可能需要花费10-200倍的时间来找一个bug,而这个bug在开始的时候可能很容易就能找到。一个小bug可能让你付出巨大的代价,即使这个bug对整个系统的性能没有太大的影响,但很可能会影响让那些你可以看得到的部分。所以我们必须要养成良好的编码和测试手段以求更高的代码质量,以便缩短调试的代码。这世界没有万能的膏药。profile再强大也有力不从心的时候;内存监视器再好,也有无法发现的时候;覆盖工具再好用,也有不能覆盖的地方。一些隐藏很深的问题即使用尽所有工具也有可能无法查到其根源,这时我们能做的就是通过这些问题所表现出来的外在现象或一些数据输出来发现其中的规律或异常。一旦发现任何异常,一定要深入地理解并回溯其根源,直到解决为止。发现、分析、解决利用初学者的思维有人这样说过:“有些事情在初学者的脑子里可能有各种各样的情况,可在专家的头脑里可能就很单一”。有时候,有些简单的问题会被想的很复杂,有些简单的系统别设计的很复杂,就是由于你的“专家思维”。当你被问题难住时,关掉电脑,出去走走,把你的问题和你的朋友甚至你的小狗说说,或许他们可以给你意想不到的启发。嵌入式调试也是一门艺术。就像其它的艺术一样,如果你想取得成功,你必须具备智慧、经验并懂得使用工具。只要我们能够很好地领悟Oracle这十条秘诀,我相信我们在嵌入式测试方面就能够取得成功。总结gdb名称

gdb-GNU调试器语法

gdb

[-help][-nx][-q][-batch][-cd=dir][-f][-bbps][-tty=dev][-ssymfile][-eprog][-seprog][-c

core][-xcmds][-ddir][prog[core|procID]]描述

调试器(如GDB)的目的是允许你在程序运行时进入到某个程序内部去看看该程序在做什么,或者在该程序崩溃时它在做什么。GDB主要可以做4大类事(加上一些其他的辅助工作),以帮助用户在程序运行过程中发现bug。

o

启动您的程序,并列出可能会影响它运行的一些信息

o

使您的程序在特定条件下停止下来

o

当程序停下来的时候,检查发生了什么

o

对程序做出相应的调整,这样您就能尝试纠正一个错误并继续发现其它错误gdbgdb调试的基本步骤编译带调试信息的可执行文件gcc–g……

运行gdb程序

GDB通过在命令行方式下输入gdb来执行。启动过后,GDB会从终端读取命令,直到您输入GDB命令quit使GDB退出。您能通过GDB命令help获取在线帮助。

您能以无参数无选项的形式运行GDB,不过通常的情况是以一到两个参数运行GDB,以待调试的可执行程序名为参数

gdb程序名

您能用两个参数来运行GDB,可执行程序名与core文件(译注:不知道怎么翻译好,就不翻译了)。

gdb程序名core

您可以以进程ID作为第二个参数,以调式一个正在运行的进程

gdb程序名1234

将会把gdb附在进程1234之上(除非您正好有个文件叫1234,gdb总是先查找core文件)gdb执行gdb启动界面gdb基本命令filequitruninfolistbreakwatchprintsetstepnextcontinuefile[filename]

装入想要调试的可执行文件break[file:]function

在(file文件的)function函数中设置一个断点clear

除一个断点,这个命令需要指定代码行或者函数名作为参数gdb基本命令

run[arglist]

运行程序(如果指定了arglist,则将arglist作为参数运行程序)

bt

Backtrace:显示程序堆栈信息

printexpr

打印表达式的值

continue

继续运行您的程序(在停止之后,比如在一个断点之后gdb基本命令

list

列出产生执行文件的源代码的一部分

next

单步执行(在停止之后);跳过函数调用

nexti

执行下一行的源代码中的一条汇编指令

set

设置变量的值。例如:setnval=54将把54保存到nval变量中gdb基本命令

step

单步执行(在停止之后);进入函数调用

stepi

继续执行程序下一行源代码中的汇编指令。如果是函数调用,这个命令将进入函数的内部,单步执行函数中的汇编代码

watch

使你能监视一个变量的值而不管它何时被改变gdb基本命令display

在断点的停止的地方,显示指定的表达式的值。(显示变量)

undisplay

删除一个display设置的变量显示。这个命令需要将displaylist中的索引做参数quit

退出gdb.gdb基本命令gdb远程调试模型gdb远程调试环境模型图建立gdb和gdbserver之间的连接在目标板上运行gdbserver[root@vm/root]#./gdbserver192.168.1.1:2345helloProcesshellocreated;pid=1000Listeningonport2345将hello程序复制到主机的相应目录,执行arm-linux-gdb:./arm-linux-gdbhello连接到开发板(gdb)targetremote192.168.1.1:2345何谓core?在使用半导体作为内存的材料前,人类是利用线圈当作内存的材料(发明者为王安),线圈就叫作core,用线圈做的内存就叫作corememory。如今,半导体工业澎勃发展,已经没有人用corememory了,不过,在许多情况下,人们还是把记忆体叫作core。何谓coredump?我们在开发(或使用)一个程序时,最怕的就是程序莫明其妙地当掉。虽然系统没事,但我们下次仍可能遇到相同的问题。于是这时操作系统就会把程序当掉时的内存内容dump出来(现在通常是写在一个叫core的file里面),让我们或是debugger做为参考。这个动作就叫作coredump。core-dump为何会发生coredump?前面说过,在程序当掉时出错。在C/C++语言中,最常发生错误的地方就是指针有问题。您可以利用core文件和debugger把错误找出来(要怎麽在debugger中使用core文件?man一下gdb吧!)。要怎麽才不会让core文件出现?如果用的是bash的话,在/etc/profile里加上(或者修改)一条:

ulimit-c0core-dump如何让操作系统生产core文件?再看看默认的一些参数,注意corefilesize是个0,程序出错时不会产生core文件了。$ulimit-a

corefilesize(blocks,-c)0

datasegsize(kbytes,-d)unlimited

filesize(blocks,-f)unlimited

maxlockedmemory(kbytes,-l)4

maxmemorysize(kbytes,-m)unlimited

openfiles(-n)2048

pipesize(512bytes,-p)8

stacksize(kbytes,-s)10240

cputime(seconds,-t)unlimited

maxuserprocesses(-u)7168

virtualmemory(kbytes,-v)unlimitedcore-dump$ulimit-c1024$ulimit–cunlimited(使用-cunlimited不限制core文件大小)core文件命名core.$PID

例如:core.234、core.9618等core-dumpcore-dump:Example$morefoo.c#include<stdio.h>staticvoidsub(void);intmain(void)

{

sub();

return0;

}staticvoidsub(void)

{

int*p=NULL;

/*derefernceanullpointer,expectcoredump.*/

printf("%d",*p);

}$gcc-Wall-gfoo.c

$./a.out

Segmentationfault(coredumped)

$ls-lcore.*

-rw-------1uniwareuniware53248Jun3017:10core.9128注意看上述的输出信息,多了个(coredumped)。确实产生了一个core文件,9128是该进程的PID。我们用GDB来看看这个core。core-dump:Example$gdb--core=core.9128

GNUgdbAsianux(6.0post-0.20040223.17.1AX)

Copyright2004FreeSoftwareFoundation,Inc.

GDBi

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论