using GDB/DDD/Eclipse for other languages

对其他语言使用GDB/DDD/Eclipse

       人们一般都知道GDBDDDC/C++程序的调试器,但是他们也可以用于其他语言的开发。Eclipse最初是为Java开发设计的。

       不管是CC++JavaPythonPerl还是其他可以使用这些工具的语言或调试器,如果能够使用相同的调试界面,那将是相当棒的。DDD就适用于所有这次语言

       这些工具的多语言功能是如何实现的:

l  虽然最初GDB是为了C/c++的调试器创建的,但是后来使用GNU的人也提供了一款Java的编译器GCJ

l  DDD本身不是调试器,而是GUI可以通过它来想底层调试器发布命令,对于C/c++,该底层调试器通常是GDB,然后,DDD经常可用来作为其他语言特有的调试器的前端;

l  Eclipse也只是前端,各种语言的插件赋予了它管理用那些语言编写代码的开发与调试能力。

 

DDD可以直接与Java Development KitJDB调试器结合起来使用,例如:

$ddd –jdb test.java

       Perl有它自己的内置调试器,可以通过-d选项调用:

$perl –d test.pl

       Python的基本调试器时PDB,这是一个基于文本的工具,它的有用性通过使用DDD作为GUI前端而得到大大增强。也可以通过ddd –pydb来使用。

调试SWIG代码

       SWIGSimplified Wrapper and Interface Generator)是一种流行的开源工具,用来将JavaPerlPython和若干其他解释语言与C/C++结合。大部分Linux分布式系统都包括SWIG,它允许使用解释语言编写应用程序的大部分代码,并与程序员用C/C++编写的特定部分结合,从而增强性能

汇编语言

       GDBDDD在调试汇编语言代码时也机器有用。

程序崩溃处理

程序崩溃处理

       有人说C语言是低级语言,这有一部分原因是因为应用程序的内存管理大部分需要由程序员来实现。虽然这种方法非常有用,但是也给程序员添加了很多的麻烦。

       也有人说,C语言是相对较小且容易学习的语言,然而,只有不考虑标准C语言库的典型实现时,C语言才比较小,这个库相当庞大,很多程序员认为C语言是易用语言,那是因为他们还没有遇到指针。

       一般而言,程序错误会导致下面两件事情的发生:

l  导致程序做一些程序员没有打算做的事情;

l  导致程序崩溃

 

相信很多调试过程序的兄弟都碰到过段错误即segmentation fault,则合格主要原因是试图在未经允许的情况下访问一个内存单元。硬件会感知这件事并执行对操作系统的跳转。

堆区域

       调用malloc函数分配的内存;

栈区域

       用来动态分配数据的空间,函数调用的数据(包括参数、局部变量和返回地址)都存储在栈上。

查看程序在Linux上的精确内存布局情况

       可以通过使用info proc mappings来详细查看该程序在Linux上的精确内存布局情况,例如:

clip_image002

此时我们还可以看到这个进程号为14455,所以我们还可以通过文件/proc/14455/maps来查看该信息。通过这些信息,我们有可能看到文本和数据区域,以及堆和栈。

分配页策略

       操作系统不会将不完整的页分配给程序,例如,如果要运行的程序总共大约有10000字节,如果完全加载,会占用3个内存页(一个页占4096个字节),它不会仅占用2.5个页,因为页是虚拟内存系统能够操作的最小内存单元,这是调试时要着重了解的情况,这也导致了程序的一些错误内存访问不会触发段错误,换言之,在调试会话期间,没有引起段错误并不能直接说明代码是没有问题的。

页的角色细节

       当程序执行时,它会连续访问程序中的各个区域,导致硬件按照以下几种情况所示处理页表:

l  每次程序使用全局变量时,需要具有对数据区域的读写访问权限;

l  每次程序访问局部变量时,程序会访问栈,需要对栈区域具有读写访问权限;

l  每次程序进入或离开函数时,对该栈进行一次或多次访问,需要对栈区具有读写访问权限;

l  每次程序访问通过调用malloc或者new创建的存储器时,都会发生堆访问,也需要读写访问权限;

l  程序执行的每个机器指令是从文本区域取出的,因此需要具有读和执行文件;

信号

       这里需要注意的是,进程抛出的信号,实际上没有任何内容发送给进程。所发生的事情只不过是操作系统将信号记录到进程表中,以便下次进程接收信号时得到CPU上的时间片,执行恰当的信号处理程序。

自定义信号的复杂性

       使用GDB/DDD/Eclipse调试时,自定义信号处理程序可能会使程序变得复杂,无论是直接使用还是通过DDD GUI,每当发出任何信号时,GDB都会停止进程,所以,有可能意味着GDB会因为与调试无关的工作而频繁的停止,此时可以使用handle命令告诉GDB在某些信号发生时不要停止。

总线错误的原因

l  访问不存在的物理地址;

l  在很多架构上,要求访问32位量的机器指令要求字对齐,而导致视图在奇数号地址上访问具有4字节的数的指针错误可能引起总线错误。

总线错误是处理器层的异常,导致在Unix系统上发出SIGBUS信号,默认情况下,SIGBUS会导致转储内存并终止。

核心文件

       有些信号表示让某个进程继续是不妥当的,甚至是不可能的,在这些情况中,默认动作是提前终止进程,并编写一个名为核心文件core file文件,俗称转储核心

       核心文件包含程序崩溃时对程序状态的详细描述:栈的内容、CPU寄存器的内容、程序的静态分配变量的值。

       我们可以通过file命令来查看文件的详细信息。

为什么需要核心文件

l  只有在运行了一段长时间后才发生段错误,所以在调试器中无法重新创建该错误;

l  程序的行为取决于随机的环境事件,因此再次运行程序可能不会再现段错误;

l  当新手用户运行程序时发生的段错误,需要发送核心文件给开发人员。

重载功能

       GDB注意到重新编译了程序后,它会自动加载新的可执行文件,因此不需要退出和重启GDB

调试设计的内容

l  确认原则;

l  使用核心文件进行崩溃进程的“死后”分析;

l  纠正、编译并重新运行程序后,甚至不需要退出GDB

l  Printf()风格调试的不足之处;

l  利用你的智慧,这是无可替代的;

l  如果你过去使用printf风格调试的,就会发现使用printf跟踪这些程序错误中的部分错误原来有多难,虽然在调试中使用printf诊断代码有一定的好处,但是作为一种通用目的的工具,它远远不足以用来跟踪实际代码中发生的大部分程序错误。

 

检查和设置变量

检查和设置变量

GDB中检查

l  可以直接使用print a来输出整个结构的内容,其中a为结构名;

l  也可以使用更方便的display命令来输出;

l  可以使用GDBcommands命令来写个小程序;

l  可以使用GDBcall命令来测试一个程序;

单独的窗口ddd

       对于调试比较复杂的结构,我们可以通过ddd –separate file来创建独立的源代码窗口、控制台窗口和数据窗口,防止ddd出现混乱的情况。

检查动态数组

       对于静态数组int a[20],我们可以通过print x来打印出数组a20个数据,而对于类似int *p的动态数组的解决方法为:

       print *pointer@number_of_elements

显示局部变量

       我们可以使用info locals命令得到当前栈帧中所有局部变量的值列表。

printdisplay的高级选项

使用print/x y会以十六进制格式显示变量。

停下来环顾程序

停下来环顾程序

调试器不仅能够运行程序,还可以通知它暂停程序的运行,暂停以后,调试器提供了检查变量、跟踪执行路径的机会。

暂停机制

3中方式可以通过GDB暂停程序的执行:

l  断点:通知GDB在程序中的特定位置暂停执行;

l  监视点:通知GDB当特定内存位置(或者设计一个或多个位置的表达式)的值发生变化时暂停执行;

l  捕获点:通知GDB当特定事件发生时暂停执行;

GDB中删除断点

l  delete breakpoint_list

l  delete

l  clear

l  clear functionclear filenamefunctionclear line_numberclear filenameline_number

2.7.2 GDB中禁用断点 51

       在调试会话期间,会遇到大量断点,对于经常重复的循环结构或函数,这种情况使得调试极不方便。如果要保留断点以便以后使用,暂时又不希望GDB停止执行,可以禁用它们,在以后需要时再启用。此时我们可以使用disable/enable breakpoint_list来禁用和启用断点。

DDD

l  可以直接拖拽断点,很方便;

l  还有一个优秀的功能Undo/Redo

GDB中恢复执行的方法

l  使用stepnext单步调试程序;

l  使用continue使GDB无条件地恢复程序的执行,知道它遇到另一个断点或者程序结束;

l  finishuntil命令恢复。

nextstep的区别

       next执行函数,不会在其中暂停,然后在调用之后的第一条语句处暂停;

       step在函数中的第一个语句处暂停;

step的单步进入函数

clip_image002

next单步越过函数

clip_image004

使用continue恢复程序执行

       continue与执行一行代码的stepnext相反,这个命令使GDB恢复程序的执行,直到触发断点或者程序结束。

       continue命令可以接受一个可选的整数参数n,这个数字要求GDB忽略下面n个断点。

使用finish恢复程序执行

       命令finish指示GDB恢复执行,直到恰好在当前栈帧完成之后位置,这意味着如果你在一个不是main的函数中,finish命令会导致GDB恢复执行,直到恰好在函数返回之后为止,例如:

clip_image006

       如果在一个递归函数中,finish只会将你带到递归的上一层,这是因为每次调用都被看做在它自己权限内的函数调用,因为每个函数都有自己的栈帧,如果要在递归层次较高时完全退出递归函数,那么更适合使用临时断点及continue,或者使用until命令

使用until恢复程序执行

       命令until执行程序,直到到达当前循环体外的下一行源代码。

clip_image008

设置条件断点的方法

       break break-args if (condition)

 

监视点

       监视点是一种特殊类型的断点,它类似于正常断点,是要求GDB暂停程序执行的指令。区别在于监视点是没有住在某一行源码中,取而代之的是,监视点是指示GDB每当某个表达式改变了值就暂停执行的指令。

 

预备知识

正确使用恰当的调试工具可以提高发现和改正错误的效率,GDB用于逐行跟踪程序、设置断点、检查变量以及查看特定时间程序的执行情况,DDD是流行的GDBGUI前端,而eclipse提供完整的集成开发环境。

预备知识

gdb

GNU调试器(GNU Debugger,缩写:GDB),是GNU软件系统中的标准调试器,此外GDB也是个具有移携性的调试器,经过移携需求的调修与重新编译,如今许多的类UNIX操作系统上都可以使用GDB,而现有GDB所能支持除错的编程语言有CC++Pascal以及FORTRAN

DDD

GNU DDD(Data Display Debugger)是命令行调试程序,如GDBDBXWDBLadebugJDBXDBPerl DebuggerPython Debugger的可视化图形前端。它特有的图形数据显示功能(Graphical Data Display)可以把数据结构按照图形的方式显示出来。

DDD最初源于1990Andreas Zeller编写的VSL结构化语言,后来经过一些程序员的努力,演化成今天的模样。DDD的功能非常强大,可以调试用C\C++ AdaFortranPascalModula-2Modula-3编写的程序;可以超文本方式浏览源代码;能够进行断点设置、回溯调试和历史纪录编辑;具有程序在终端运行的仿真窗口,并在远程主机上进行调试的能力;图形数据显示功能(Graphical Data Display)是创建该调试器的初衷之一,能够显示各种数据结构之间的关系,并由此将数据结构以图形化形式显示;具有GDB/DBX/XDB的命令行界面,包括完全的文本编辑、历史纪录、搜寻引擎。

Eclipse

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development KitJDK)。

为什么要用调试工具,使用printf或者cout不是很好嘛

       对于打印输出要求我们有策略地持续添加跟踪代码,重新编译程序,运行程序并分析跟踪代码的输出,在修正程序错误之后删除跟踪代码,并且针对发现的每个新的程序错误重复上述这些步骤,很费事,很费力,很容易将我们的注意力转移到排查错误的过程上而不是实际的任务上。

       相反,使用调试工具,比如ddd或者eclipseGUI,我们只需要使用鼠标指针就可以检查变量的值,并显示该变量的当前值,and调试器还可以指出程序错误所在的大概位置。例如,段错误即内存访问错误,调试器会立即指出段错误所在的位置,and调试器还可以设置监视点watchpoint,持续监视某个变量的值。

调试原则:

l  从简单工作开始调试;

l  使用自顶向下的方法;

l  使用调试工具确定段错误的位置;

l  通过发出中断确定无限循环的位置;

l  使用二分搜索;

命令行调试 vs GUI调试

Gdb

clip_image002

ddd

clip_image004

eclipse

clip_image006

GUI的优点

       GUI界面比GDB提供的GUI界面的外观更加形象,使用起来更加方便;

GDB的优点

l  GDB的启动速度比DDD快很多;

l  在某些情况下,通过来自于公共中断的SSH连接远程执行调试,如果没有安装X11,就完全不能使用GUI了,即使有X11GUI的屏幕刷新操作也会非常缓慢;

l  当调试彼此之间协同操作的多个程序时,就需要针对每个程序的独立调试窗口,对于GUI窗口操作就比较麻烦;