爱气象,爱气象家园! 

气象家园

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

搜索
查看: 4621|回复: 6

[参考资料] FAQ之 Debug单步调试(转)

[复制链接]

新浪微博达人勋

发表于 2015-1-7 21:39:07 | 显示全部楼层 |阅读模式

登录后查看更多精彩内容~

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
本帖最后由 lysx 于 2015-1-7 21:47 编辑

FAQ之 Debug单步调试
本文介绍了 IVF+VS 和 Compaq Visual Fortran 编译器的单步调试操作方法,其他编译器可以类比参考。适合初学者阅读。
第一,Debug 调试是什么?

据传,世界上最早一批程序员中的一位,葛丽丝·霍普,在调试程序时出现故障,拆开继电器后,发现有只飞蛾被夹扁在触点中间,从而“卡”住了机器的运行。于是,霍波诙谐的把程序故障统称为“臭虫(BUG)”,把排除程序故障叫DeBug。后来,Debug 就成为了一个专业的计算机术语。

Debug 是一个程序员的基本功。它有很多层含义,比如源码级Debug,汇编级Debug,系统级Debug等等。Fortran 程序员,通常只会接触到源码级Debug。本文也只描述源码级Debug,如您对汇编和反汇编有一定的了解,还可尝试汇编级Debug。

源码级 Debug,意思是,让程序逐行逐行的运行,运行在中途时,可暂停运行,并将此时的若干状态呈现给程序员查看,以便程序员分析,此时各变量各过程及程序流程,是否符合自己的预期。同时,调试者可随时改变这些状态,例如变量的值,然后继续运行,以便测试在不同情况下程序的反应。

目前的计算机运行速度已经很快了,往往很长的代码运行完毕也就一眨眼的功夫。如果运行过程中出现错误,或者计算结果不正确。往往程序员不知道问题发生在哪里。而 Debug 则可以让程序在可疑的程序段暂停下来,程序员可以查看此时是否符合自己的预期?从而为查找问题提供更多的依据。

源码级 Debug,可便于程序员查找运行时错误(Run-time Error),计算结果不正确,这两种问题。(编译链接错误不能通过Debug查找

第二,Debug 的前提

Debug 实际上是一种运行程序的方式。所以,首要前提是,程序已经可以正常链接,获得可执行文件。对于不单独运行的程序,例如 DLL,LIB 等,需要额外的 Loader 来加载它,并进行调试(本文暂不涉及,如您有疑问,可于本站论坛提出)。如果您的代码编译链接还有错误,那么请先解决编译链接问题。

其次,Debug 需要调试器(Debugger),这是一种软件。一般商业编译器都会附带调试器。免费开源编译器也会有附属的开源调试器。如果你的编译器产品需要选择组件安装,请确保自己勾选了相应的调试器并进行了安装。

最后,很多编译器允许两种编译链接方式:Debug模式 Release模式
这两种链接方式的区别主要是:

1.Debug 模式:程序几乎不进行优化。产生的可执行程序具有调试信息,执行效率低,文件尺寸大。
2.Release 模式:程序进行合理优化。产生的可执行程序不具有调试信息,执行效率高,文件尺寸小。
(实际上,Debug模式 和 Release模式只是编译器预设的两种方式,我们可以通过调节编译链接参数来获得更自由的搭配,产生介于Debug和Release之间的编译方式)

想要进行 Debug 调试,我们需要程序中存在调试信息,需采用 Debug 模式 编译链接程序才可以。

第三,Debug 的操作

调试器的操作,因不同调试器而不同,这里以集成在 Visual Studio 中的 Intel 调试器为例。Intel Visual Fortran 会默认安装这个调试器。

其他的调试器,如果也是集成在IDE中的,则操作方式大同小异。如果是单独的命令行程序,则需要通过命令来进行调试(本文暂不涉及)

首先我们来看一个示范代码:
Program www_fcode_cn
!// 本程序用于演示 Intel 调试器的使用  
Implicit None  
Integer , parameter :: N = 10  
Real :: a( N ) , b( N )
integer :: i  
Do i = 1 , N   
   a(i) = i   
   b(i) = 100 - i  
End Do
!a(5) = 0.0  
Do i = 1 , N  
  write( * , * ) b(i) / a(i)
End Do
End Program www_fcode_cn
程序第11行,是故意将a(5) 设定为 0.0 的。如果不执行它,则程序应该输出:

   99.00000
   49.00000
   32.33333
   24.00000
   19.00000
   15.66667
   13.28571
   11.50000
   10.11111
   9.000000

如果执行了11行,则可能会输出 Infinity,也可能会出现运行时错误forrtl: error (73): floating divide by zero
(根据不同编译器或编译参数中,浮点数的设置而不同)

假设我们程序执行时,遇到了错误,分母为零了。我们来看看如何通过 Debug 发现这个错误。

首先第一步,我们要在可疑的位置插入断点(Insert breakpoint),调试器会让程序运行到断点的位置并暂停下来。如果可疑的位置有多处,可以分别插入断点。
集成开发环境里,在编辑代码时,右键既可插入断点。(在某些环境下,直接点击某行最前方也可插入断点)

请注意,如果你修改了代码,则需要保存,然后编译链接,才能够插入断点。另外,断点不可位于注释语句上,比如上例,想在11行插入断点,就需要取消11行的注释感叹号(然后保存重新编译链接)


                               
登录/注册后可看大图


需要注意的是,断点需要执行到它的位置,只能位于执行语句(定义语句并不执行)。


                               
登录/注册后可看大图


如上图,我们插入了两个断点。存在断点的行,一般会有红色的实心圆。

第二步,启动调试程序。可通过菜单栏,工具栏上的按钮进行。在不同的环境下,具体菜单栏和按钮位置不同。你或许需要自定义一下工具栏。


                               
登录/注册后可看大图


之后,我们可以看到,程序执行到它遇到的第一个断点处。


                               
登录/注册后可看大图


此时,黄色的箭头指向的行,即为当前程序执行到的位置。(一般来说,一开始都在断点上,所以是红色圆内一个黄色箭头)

此时,程序就在一开始的位置等待我们。我们可以查看到各种状态,例如变量的值。在局部变量窗口我们会看到 I B A 三个变量(或数组),他们的值很乱。因为此时还没有对他们进行初始化。


                               
登录/注册后可看大图


如果你无法看到局部变量窗口,可能是被隐藏起来了,你或许需要在角落里找一下它,或者通过某些菜单把他“召唤”出来。(由于VS版本的不同,具体位置和图标外观,菜单名称可能稍有不同)


                               
登录/注册后可看大图


在局部变量里,我们已经可以修改 i 或者 a b 数组的值了,但是目前修改,还没有太大的意义。

我们按下逐语句按钮:

                               
登录/注册后可看大图


黄色箭头就会下移到下一条语句,这表示程序又向前执行了一行。


                               
登录/注册后可看大图


同时,局部变量里,i 的值变成了 1 ,这表示循环变量 i 有了值,并且当前是 1。红色,表示此时与上一步相比值发生了改变。相比而言,B 和 A 数组未改变,因此不是红色。

再次点击逐语句按钮(或快捷键F11),黄色箭头继续下移,且 A(1) 变为红色。这表示数组 a 的第一个元素发生了改变。

多次点击逐语句按钮。会发现,黄色箭头开始在循环体内来回移动,i 的值从1开始变大,a 数组慢慢变为 1-10 之间的数。这是完全符合我们的代码预期的。


                               
登录/注册后可看大图


如果循环较多,比如1000次循环,点击逐语句就需要点击几千次。此时,我们可以在循环外插入第二个断点,例如在本例的第11行。然后点击继续按钮(表示直接运行到下一个断点处,而不是一步一步执行了)。

                               
登录/注册后可看大图


此时,可发现黄色箭头已到了下一个断点的位置,而且 A 数组和 B 数组已经赋值完毕。均符合我们的预期。

(假设我们此时无法确定第11行导致了 a(5) = 0.0)

再次点击“继续”按钮。编译器一直运行,直到发生了错误为止,会弹出错误:


                               
登录/注册后可看大图


我们点击“中断”,会发现程序虽然没有碰到断点,但依然暂停了。黄色箭头停留在第13行。我们把鼠标移动到 i 变量上,看到他的值是 5,说明第5次循环出现了错误。


                               
登录/注册后可看大图


分别移动鼠标到 b 和 a 上,查看 b(5) 和 a(5) 的值。会发现 a(5) 的值为 0.000,这就是导致错误的原因


                               
登录/注册后可看大图


在某些情况下,某些编译器,或者因为设置原因,并不会抛出错误,也无法中断,编译器会让 b(5) / 0.0 = Infinity。
此时,就需要更细致的断点,跟踪,检查各变量的值是否符合预期,然后再来确定原因了。(当然也可以通过设置让编译器抛出浮点数错误),例如 IVF 如此设置:


                               
登录/注册后可看大图



关于调试,还有很多问题。下面简单陈述,具体效果请读者自行测试,很容易理解。

上述的逐语句按钮,在遇到函数调用时,会进入函数内部。如果不希望进入函数内部,可使用逐过程按钮(或跟踪步过),如果已进入函数内,可使用跳出当前函数执行到返回。


                               
登录/注册后可看大图


读者可以自行拿出自己以前的100行以内有函数调用的代码来测试一下,点击这些按钮,观察黄色箭头的位置,这些概念并不难理解。

最后,有一个比较常用的东西,叫做“条件断点”,它会在满足某些条件时才会触发这个断点。例如,一个10000次的循环,循环到 1234 次出现了错误。我们不能点1234次逐语句吧?此时,我们可设置断点的触发条件为 i>=1234。
或者说,循环到不知道多少次的时候,某个数 ff 变为 0 了,我们可设置断点的触发条件为 ff < 0.00001。

首先插入常规断点,然后在断点处右键:

                               
登录/注册后可看大图



                               
登录/注册后可看大图



调试还有很多可利用的工具,例如调用堆栈可查看程序调用函数的全部路线,对某些变量数组添加监视,查看内存中的数据,寄存器的值,windows返回的错误,调试字符串等等。

请读者朋友们根据现有的提示自行琢磨。祝大家的代码都调通!



评分

参与人数 2金钱 +30 贡献 +5 体力 +160 收起 理由
言深深 + 20 + 5 + 160 很给力!
Mid_Farmer + 10 赞一个!

查看全部评分

密码修改失败请联系微信:mofangbao

新浪微博达人勋

发表于 2015-1-7 22:10:54 | 显示全部楼层
赞!!!
密码修改失败请联系微信:mofangbao
回复

使用道具 举报

新浪微博达人勋

 成长值: 0
发表于 2015-1-8 09:47:39 | 显示全部楼层
很不错,也欢迎楼主多多发原创性的工作。
密码修改失败请联系微信:mofangbao

新浪微博达人勋

 楼主| 发表于 2015-1-8 17:07:30 | 显示全部楼层
言深深 发表于 2015-1-8 09:47
很不错,也欢迎楼主多多发原创性的工作。

之前在fortran、grads下有一些原创作品发表,但现在临近毕业,太忙了,只能看到好东西了给大家转来
密码修改失败请联系微信:mofangbao

新浪微博达人勋

 成长值: 0
发表于 2015-1-8 17:11:39 | 显示全部楼层
lysx 发表于 2015-1-8 17:07
之前在fortran、grads下有一些原创作品发表,但现在临近毕业,太忙了,只能看到好东西了给大家转来

也是极好的
密码修改失败请联系微信:mofangbao

新浪微博达人勋

发表于 2015-1-9 09:31:11 | 显示全部楼层
感谢楼主!
密码修改失败请联系微信:mofangbao
回复

使用道具 举报

新浪微博达人勋

发表于 2016-10-25 11:05:04 | 显示全部楼层
请问楼主,每次IVF一debug就闪退是什么原因
密码修改失败请联系微信:mofangbao
您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

快速回复 返回顶部 返回列表