首页 > 其他分享 >随想录(软件调试)

随想录(软件调试)

时间:2022-11-23 11:39:05浏览次数:61  
标签:int 随想录 ebp 堆栈 软件 断点 调试


    对于很多程序员朋友来说,编写代码要比调试代码快乐的多。似乎创造软件比维护软件更能给人带来成就感。然而,在企业里面维护前人留下的代码也是工作中不可缺少的一项内容。所以,如何调试软件,更快更好地寻找软件中的bug,就成了我们必须学习的一门功课。当然,有人查找故障很快,而有的人却要慢一点,这中间的原因很多,比如说对业务的熟悉程度,对调试工具的使用程度。这也从一方面说明了,掌握软件调试的技巧是十分重要的。这里讨论的内容,不是指怎么用visual studio或者是gdb、kgdb、systemtap调试,而是说说调试软件的基本原理是什么。说到底,调试软件也是软件,它需要芯片、操作系统、编译软件、堆栈格式的支持。


(1)芯片的支持

    很多朋友都喜欢在软件运行的过程中设置断点,比如说在代码中插入一个__asm__ ("int $3" ::)就可以达到这样的效果。关键是为什么插入这个代码就会有这样的效果。原来在x86芯片中,上面的int 3会被翻译成0xCC。当cpu遇到这样一行指令的时候,自身就会产生异常,进而会查找相应的异常函数进行处理。在x86中是存在专门的调试指令的,但是在某些cpu中这样的指令却未必存在,比如说powerpc,那这个时候怎么办呢?其实也简单,只需要把对应的指令替换成cpu不认识的指令即可,这样同样可以产生异常的效果。至于当前异常是不是调试异常,那么就需要操作系统来判断了。


(2)操作系统的支持

    芯片本身只是负责产生异常,寻找到异常处理函数,至于这样的函数还要做些什么,那就是操作系统要负责的事情了。比如说,现在我们要调试的程序已经运行到断点了,那么操作系统就要通知gdb或者visual studio当前的程序已经运行到节点了,接下来要做什么。在一般的软件调试中,功能其实都大同小异,比如说查看寄存器、查看内存、设置断点、取消断点、查看线程号、继续运行等等。这些都需要操作系统本身的支持,否则作为用户侧的gdb怎么知道当前的调试程序运行到什么地方了,它的基本信息在哪里等等。毕竟,gdb本身也是一个软件,它关于调试程序的具体信息都是别人告诉它的,它又不是神。


(3)编译软件的支持

有了芯片和操作系统的支持,其实就可以调试软件了,比如说wingdb就是这么干的。但是,我们还不是很满足,为什么?因为有的时候,我们还需要知道函数的参数值、全局变量是多少,有没有发生改变,C语言代码有没有对应的汇编代码,能否实现汇编级的调试等等。当然,这些信息对于执行文件本身的运行其实是无关紧要的,只是我们为了调试软件的时候使用的。所以,在visual studio编译的软件版本当中有Debug版本,有release版本之分;有普通的软件版本,有优化的软件版本。在linux上,人们为了调试的需要,也会在gcc调试的选项中添加-g选项,获得额外的调试信息。


(4)堆栈的支持

    在调试软件中,有一项非常棒的内容,那就是函数堆栈查看功能。堆栈会根据临时变量的添加、减少进行浮动处理。可以说掌握了堆栈就掌握了cpu、掌握了编程、掌握了软件调试。在x86中,ebp是比较神奇的寄存器。在堆栈中,ebp[0]保存了上一个ebp的地址,ebp[1]保存了返回函数的地址。通过迭代,就可以的得到所有的函数指针了。当然,通过编译器可以生成软件的systemp map文件,也就记录所有函数的空间地址。把返回地址和system map联系在一起,我们就可以知道当前代码的函数堆栈了。除此之外,我们还可以利用堆栈中的返回地址设置断点,这样可以在当前函数运行到结束的时候断住,使用起来也是十分方便。

void print_function_addr(int ebp, int level)
{
int* start;
int index;
if(0 == ebp || 0 == level)
return;

start = (int*)ebp;
index = 0;
while(index < level){
printf("[%d] 0x%08x\n", index, start[1]);
index ++;
start = (int*)start[0];
}
}


(5)日志和计数器

    依靠系统本身的调试软件当然是不够的,所以为了看清数据的运行流程,我们还需要获得一些额外的调试信息了。所以,对于业务而言,我们需要按照告警、错误、数据格式分别进行日志保存。当然,有时候我们还要对业务性能、频率进行衡量和比较,所以很多时候计数器也是必不可少的。这些信息都是正常代码之外的额外信息,所以处理好他们和普通代码之间的关系也是一门大学问。同时,日志有时还要受到多线程、效率、异常等因素的影响,所以思考和执行的时候一定要顾虑周全。


(6)调试原则

    这些调试原则只是我个人的一些经验总结,谈不上真知灼见,仅供大家参考。1)先排除硬件,后软件。特别高频信号要注意时序和信号完整性;2)软件设计大于调试;3)软件早调试早得益;4)复杂的功能调试可以在仿真软件上进行,比如说全局地址越界等,使用gdb的条件断点调试就十分方便,在嵌入式设备上查找却十分困难;5)尽量自己编写调试函数,不断改进和优化处理,用得也顺手;6)日志模块要健壮,尤其要适合多线程处理;7)定位故障一定要寻找到根本原因,否则极易生成新的故障。




标签:int,随想录,ebp,堆栈,软件,断点,调试
From: https://blog.51cto.com/feixiaoxing/5880690

相关文章

  • 西门子1200,1500,300,400PLC编程软件,西门子博途博图TIA 13 V14 V15 V16 V17安装包。
     西门子博途博图TIA13V14V15V16V17安装包,西门子1200,1500,300,400PLC编程软件,永久使用。 下载安装包......
  • 随想录(写给自己的C++编程规范)
       对于我这样一个C语言的程序员来说,编写C++的机会其实不太多。但是我还是比较喜欢写C++语言,原因主要有几个方面:(1)自己学C++语言的时间比较长了,也比较了解,如果从大一的时......
  • 随想录(png的读取和显示)
       之前在阅读FTK代码的时候,发现工程本身用到了PNGLIB的代码。虽然网上关于pnglib的描述文件很多,但是真正好用、可以用的却没有多少。所以,为了学习的方便,我自己做了一个......
  • 随想录(公司程序员的九层楼)
        就IT公司而言,都希望自己的程序员在单位时间内生产出效率最高的代码。但是,不同的人有不同的开发效率。至于说效率之间的差别究竟有多少,还真不得而知。这里写了几个......
  • 随想录(软件中的bug)
       软件由于其特殊性,始终和bug紧密地联系在一起。没有bug的软件是不存在的。为什么这么说呢?我们知道,软件是由很多人完成的,不同的人完成代码的水平是不一样的,一旦沟通不......
  • 随想录(锁的来由和使用)
       对于开发系统级别软件的朋友来说,无论你是主动的还是被动的,锁的应用都是少不了的。很多人用锁,可是却未必知道锁的前世今生,什么时候用锁,什么时候不用锁?该用什么样的锁?......
  • 随想录(编写用户侧定时器)
       定时器是我们在平时开发中经常使用到的工具,特别是在协议的编写上更是必不可少的组成部分。虽然系统本身给我们提供了定时器,但是有的时候,我们也想自己编写一个粗粒度......
  • 随想录(为什么循环队列具有先天的并行性)
       循环队列是很多人喜欢用的一种数据结构。本着先来先服务的特性,循环队列是一种十分简单、健壮的数据结构。不像链表、二叉树,如果使用不慎,就会造成很大的麻烦,但是在循......
  • 随想录(linux下的pv操作)
         关于pv操作部分的内容,其实算不上什么新的东西。但是它对于我们理解信号量、消息处理部分的工作还是有很大帮助的。之前我们给出了一个win32的处理方案,但是实现的......
  • 随想录(写给那些学校不是985、211的同学们)
       每年的6、7月份都是一年一度的毕业季。按照某些新闻机构的统计数字来说,现在每一年毕业的人数达到了600万之多。然而随着社会经济的放缓、贫富差距的拉开,找工作变得越......