【Linux调试技术1】初步基础

简介: 作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ 1.调试技术的几个准则 惊喜准则:找到错误是一种惊喜,心理上不要畏惧而是要怀着感恩的心去面对。

作者gnuhpc
出处http://www.cnblogs.com/gnuhpc/

1.调试技术的几个准则

  • 惊喜准则找到错误是一种惊喜心理上不要畏惧而是要怀着感恩的心去面对。
  • 从小处开始准则刚开始测试的使用从小处着手暂时不涉及边界数据虽然这样可能会掩盖一些Bug但是这样或许能查到最主要的Bug例如你的程序包含了一个巨大的循环体最容易发现的Bug在第一个循环或第二次循环执行的时候。
  • 自顶向下准则优先选择step over而不是step into以节省时间。
  • Segmentation Fault准则出现段错误时第一个想到的不应该是printf而是Debugger因为在调试器中你能看到你的哪一行代码导致了错误更重要的是你可以通过backtrace等工具得到更多有用的信息。
  • 折半查找准则在寻找bug时可以充分利用编辑器等工具来进行折半查找具体在后边有例子说明。

    2.Linux下代码调试工具

    主要使用的GDB以及基于GDB的图形化工具如DDDeclipse选择上看个人习惯了。

    命令行式的GDB启动较快可以在ssh终端下使用操作简洁并且在调试GUI程序时不会崩溃但较之图形化则在单步调试或设置断点时非常不方便。

    当然你可以使用Vim等编辑器的插件或者补丁clewn or vimGDB来弥补这一缺憾并且在GDB6.1以上的版本你可以使用GDB -tui这个模式或者在GDB的命令行模式下按CTRL-x-a打开一个类似于图形界面的文本界面模式在这个界面中你可以使用上下键查看源代码CTRL-P CTRL-N完成输入过的命令的查看.

     

  • 或者你还可以使用cGDB这个工具很庆幸这个项目在停止了三年后又有人开始维护了这个工具是将GDB用curses包装了一下提供了一些很好用的featureEsc和i键在代码和命令框间切换在代码框中支持vim型的操作在命令框中支持tab键补全命令在移动到想加入断点的行行号为高亮白色直接用空格键设定好后行号会变红。另外在调试C-S程序时推荐使用eclipse。

    在本文中重点介绍ddd的操作因为这个工具即结合了GDB命令行和图形界面的操作。其余请参阅各个工具的手册。

    3.GDB命令行最基本操作

    程序启动
    A.冷启动
    gdb program              e.g., gdb ./cs
    gdb –p pid                 e.g., gdb –p `pidof cs`
    gdb program core      e.g., gdb ./cs core.xxx
    B.热启动
    (gdb) attach pid        e.g., (gdb) attach 2313
    C.传入命令行参数
    gdb program --args arglist
    (gdb) set args arglist
    (gdb) run arglist
    使用shell命令shell command
    Makemake make-args(=shell make make-args) 
    设置断点b LineNumber
    运行程序r args1 args2 ...
    彻底终止程序kill
    单步执行nTIPs1可以按回车重复上一次操作在单步调试时这个feature很有用。
    单步进入s
    继续执行c
    设置临时断点tb LineNumber 可以理解为一次性断点与断点不同临时断点只在第一次执行时起作用。
    查看变量p
    设置观察点
    w Expression当Expression是一个变量名时这个变量变化时会停止执行你也可以使用条件来限定比如w (z>28)当z大于28时程序停止。注意观察点一般使用在更大范围上的变量而不是本地变量因为在局部变量上设置的观察点在局部结束时比如该变量所在的函数执行结束时就被取消了。
    当然这并不包含main的情况因为main函数执行结束后程序就结束了。
    查看栈帧
    栈帧指的是在一个函数调用时该函数调用的运行信息包含本地变量、参数以及函数被调用的位置存储的地方。每当一个函数被调用时一个新的帧就被系统压入一个由系统维护的帧在这个栈的顶端是现在正在运行的函数信息当该函数调用结束时被弹出并析构。
    在GDB中frame 0为当前帧frame 1为当前帧的父帧frame 2为父帧的父帧等等用down命令则是反向的。这是一个很有用的信息因为在早期的一些帧中的信息可能会给你一些提示。
    backtrace(bt/ where)查看整个帧栈
    注意在帧中来回并不影响程序的执行。
    实例插入排序算法调试

    用伪代码描述这个过程如下

     

  • 拟调试代码如下

  • // 
    
    
    
    // insertion sort, 
    
    
    
    // 
    
    
    
    // usage: insert_sort num1 num2 num3 ..., where the numi are the numbers to 
    
    
    
    // be sorted 
    
    
    
    int x[10], // input array 
    
    
    
    y[10], // workspace array 
    
    
    
    num_inputs, // length of input array 
    
    
    
    num_y = 0; // current number of elements in y 
    
    
    
    void get_args(int ac, char **av) 
    
    
    
    { int i; 
    
    
    
    num_inputs = ac - 1; 
    
    
    
    for (i = 0; i < num_inputs; i++) 
    
    
    
    x[i] = atoi(av[i+1]); 
    
    
    
    } 
    
    
    
    void scoot_over(int jj) 
    
    
    
    { int k; 
    
    
    
    for (k = num_y-1; k > jj; k++) 
    
    
    
    y[k] = y[k-1]; 
    
    
    
    } 
    
    
    
    void insert(int new_y) 
    
    
    
    { int j; 
    
    
    
    if (num_y = 0) { // y empty so far, easy case 
    
    
    
    y[0] = new_y; 
    
    
    
    return; 
    
    
    
    } 
    
    
    
    // need to insert just before the first y 
    
    
    
    // element that new_y is less than 
    
    
    
    for (j = 0; j < num_y; j++) { 
    
    
    
    if (new_y < y[j]) { 
    
    
    
    // shift y[j], y[j+1],... rightward 
    
    
    
    // before inserting new_y 
    
    
    
    scoot_over(j); 
    
    
    
    y[j] = new_y; 
    
    
    
    return; 
    
    
    
    } 
    
    
    
    } 
    
    
    
    } 
    
    
    
    void process_data() 
    
    
    
    { 
    
    
    
    for (num_y = 0; num_y < num_inputs; num_y++) 
    
    
    
    // insert new y in the proper place 
    
    
    
    // among y[0],...,y[num_y-1] 
    
    
    
    insert(x[num_y]); 
    
    
    
    } 
    
    
    
    void print_results() 
    
    
    
    { int i; 
    
    
    
    for (i = 0; i < num_inputs; i++) 
    
    
    
    printf("%d/n",y[i]); 
    
    
    
    } 
    
    
    
    int main(int argc, char ** argv) 
    
    
    
    { get_args(argc,argv); 
    
    
    
    process_data(); 
    
    
    
    print_results(); 
    
    
    
    } 
    
    

    我们编译一下

    gcc -g -Wall -o insert_sort ins.c

    注意我们要使用-g选项告诉编译器在可执行文件中保存符号表——我们程序中变量和代码对应的内存地址。

    现在我们开始运行一下我们使用"从小处开始准则"首先使用两个数进行测试

    ./insert_sort 12 5

    我们发现该程序没有退出貌似进入了一个死循环。我们开始使用ddd调试这个程序

    ddd insert_sort

    运行程序传入两个参数

    r 12 5

    此时程序一直运行不退出按Ctrl+C暂停程序的执行

    (GDB) r 12 5
    ^C
    Program received signal SIGINT, Interrupt.
    0x080484ff in insert (new_y=3) at insert_sort.c:45
    /home/gnuhpc/MyCode/Debug/Chapter_01/insert_sort/pg_019/insert_sort.c:45:939:beg:0x80484ff
    (GDB)

    我们可以看到程序停止在第49行。我们看一下num_y现在的值

    (GDB) p num_y
    $1 = 1

    这里的$1指的是你要GDB告诉你的第一个变量。找到了这个地方后我们看看在num_y=1时都发生了什么我们在insert函数第27行设置断点你也可以直接使用break insert在这个函数的入口设置断点并且设置GDB在断点1处你可以通过info break命令查看断点只当num_y==1时才停止

    (GDB) b 27
    Breakpoint 1 at 0x80484a1: file insert_sort.c, line 27.
    (GDB) condition 1 num_y==1
    (GDB)

    上述命令也可以使用break if合一

    (GDB) break 27 if num_y==1

    然后再运行程序随后用n单步调试发现我们跳到了该函数的出口处

    此时我们看看num_y的值以便查看到底这个for循环执行的情况。

    (GDB) p num_y
    $2 = 0

    此时的情况是我们进入这个函数时num_y1但是现在num_y0在中间这个变量被改变了。现在你知道Bug就在30-36行间。同时通过单步调试你发现31-33行被跳过了3435行为注释那么Bug就在第30或第36行间了。

    我们现在仔细看这两行就能得出结论了30行有个典型的if判断条件写成赋值的错误致命的是这个变量是全局变量直接导致49行的for循环变量一直被重置。我们修改后重新编译可以另开一个编辑器不用退出ddd然后再运行

    (GDB) r 12 5
    5
    0

    虽然没有了死循环但是结果还是不对的。

    请注意初始的时候数组y是空的在#49进行第一次循环时y[0]应该为12在第二个循环中程序应该挪动125腾出位置插入但是此时这个结果看上去是5取代了12

    此时单步调试进入for循环#37y[0]的值的确是12。我们执行到scoot_over函数时根据自顶向下准则我们单步跳过继续执行到#41看看结果对错再决定是不是要单步进入scoot_over函数

      

    我们发现12根本就没有被移动说明scoot_over函数有问题我们去掉insert函数入口的断点在scoot_over入口处设置断点当num_y=1的时候终止b scoot_over if num_y==1。进一步单步调试后发现这个#23for循环就没有执行。

    (GDB) p jj
    $12 = 0
    (GDB) p k
    $13 = 0

    我们看到是因为没有满足for循环条件而不能进入循环。在这里12应该从y[0]移动到y[1]那么我们确定是循环的初始化错误应该为k = num_y将这个地方修改后编译运行程序出现段错误。我们清空所有的断点然后在

    (GDB) r

    Program received signal SIGSEGV, Segmentation fault.
    0x08048483 in scoot_over (jj=0) at insert_sort.c:24
    (GDB)

    这里指出在24行出现seg fault那么要么k超过了数组界限要么k-1为负的。打印一下k的值我们就发现

    (GDB) p k
    $14 = 992
    (GDB)

    远远超过k应该有的值。查看num_y 1说明在处理第二个要排序的数时出错再打印jj值发现为0就发现我们的for循环k++应该改为k—

    编译运行发现ok。但是运行多个数据就又出错了

    (GDB) r 12 5 19 22 6 1
    1
    5
    6
    12
    0
    0

    Program exited with code 06.
    (GDB)

    我们看到结果中从19开始的排序都有问题我们在for (j = 0; j < num_y; j++)  这一行行设置断点条件为new_y==19的时候

    (GDB) break 36 if new_y==19
    Breakpoint 10 at 0x80484b1: file insert_sort.c, line 36.
    (GDB)

    单步调试就发现我们没有对当要插入的元素大于所有元素时进行处理。在#44后加入y[num_y] = new_y;重新编译运行程序正确至此我们通过一个简单的例子演示了一下如何使用GDB进行调试。

      

    参考文献
    Art of Debugging
    Linux® Debugging and Performance Tuning: Tips and Techniques

作者gnuhpc
出处http://www.cnblogs.com/gnuhpc/


               作者gnuhpc
               出处http://www.cnblogs.com/gnuhpc/
               除非另有声明本网站采用知识共享“署名 2.5 中国大陆”许可协议授权。


分享到

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
28天前
|
缓存 NoSQL Linux
Linux调试
本文介绍了Linux调试、性能分析和追踪的培训资料,涵盖调试、性能分析和追踪的基础知识及常用工具。
221 6
Linux调试
|
5月前
|
Ubuntu Linux vr&ar
IM跨平台技术学习(十二):万字长文详解QQ Linux端实时音视频背后的跨平台实践
本文详细记录了新版QQ音视频通话在 Linux 平台适配开发过程中的技术方案与实现细节,希望能帮助大家理解在 Linux 平台从 0 到 1 实现音视频通话能力的过程。
180 2
|
2月前
|
Linux 虚拟化
Vmware 傻瓜式安装(不可不知道的Linux基础知识和技术 01)
本文介绍了VMware虚拟机的下载与安装步骤。首先,通过提供的网盘链接下载VMware安装包。接着,详细描述了安装流程,包括接受协议、选择安装路径(建议避免系统C盘)、取消更新选项等。最后,输入许可证密钥完成安装,并展示了打开虚拟机后的主界面。整个过程简单易懂,适合新手操作。
150 1
|
3月前
|
安全 Linux Android开发
Linux CFI (Control-flow integrity)技术相关资料汇总
Linux CFI (Control-flow integrity)技术相关资料汇总
|
4月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
67 10
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
113 3
|
4月前
|
Ubuntu Linux
内核实验(四):Qemu调试Linux内核,实现NFS挂载
本文介绍了在Qemu虚拟机中配置NFS挂载的过程,包括服务端的NFS服务器安装、配置和启动,客户端的DHCP脚本添加和开机脚本修改,以及在Qemu中挂载NFS、测试连通性和解决挂载失败的方法。
236 0
内核实验(四):Qemu调试Linux内核,实现NFS挂载
|
4月前
|
存储 监控 Linux
在Linux中,如何进行虚拟化技术的应用?
在Linux中,如何进行虚拟化技术的应用?
|
4月前
|
安全 Linux 图形学
Linux平台Unity下RTMP|RTSP低延迟播放器技术实现
本文介绍了在国产操作系统及Linux平台上,利用Unity实现RTMP/RTSP直播播放的方法。通过设置原生播放模块的回调函数,可将解码后的YUV数据传递给Unity进行渲染,实现低延迟播放。此外,还提供了播放器启动、参数配置及停止的相关代码示例,并概述了如何在Unity中更新纹理以显示视频帧。随着国产操作系统的发展,此类跨平台直播解决方案的需求日益增长,为开发者提供了灵活高效的开发方式。
|
4月前
|
Linux 数据安全/隐私保护 Perl
解锁Linux高手秘籍:文件操作+命令解析大揭秘,面试场上让你光芒万丈,技术实力惊艳四座!
【8月更文挑战第5天】Linux作为服务器与嵌入式系统的基石,其文件管理和命令行操作是技术人员必备技能。本文从文件操作和基础命令两大方面,深入浅出地解析Linux核心要义,助你在面试中脱颖而出。首先探索文件系统的树状结构及操作,包括使用`ls -la`浏览文件详情、`touch`创建文件、`rm -r`慎删目录、`cp`与`mv`复制移动文件、以及利用`find`搜索文件。接着掌握命令行技巧,如用`cat`、`more`和`less`查看文件内容;借助`grep`、`sed`与`awk`处理文本;运用`ps`、`top`和`kill`管理进程;并通过`chmod`和`chown`管理文件权限。
83 8