zoukankan      html  css  js  c++  java
  • 软件调试实战入门(2)

    根源的查找--源码调试及演示:

    一)、代码的构建,以下代码实现非负整数的阶乘为例。

    1//filename:factorial.c
    2 #include <stdio.h>
    3 #include <stdlib.h>
    4  
    5 int factorial(int n){
    6     int result =1l;
    7     if(n ==0){
    8         return result;
    9     }else{
    10         result =  n * factorial(n -1);
    11         return result;
    12     }
    13 }
    14  
    15 int main(int argc,char **argv)
    16 {
    17     int n;
    18     int result;
    19     if(argc !=2){
    20         fprintf(stderr,"usage: factorial n, n >= 0 ");
    21         return1;
    22     }
    23     n = atoi(argv[1]);
    24     result = factorial(n);
    25     printf("factorial%d =%d ", n, result);
    26  
    27     return0;
    28 }
    该代码纯粹是为了演示之用:没有考虑到n的传入参数为负数时的特殊情况,也没有考虑int型数值的范围。

    为了使程序与调试器一起运行,须要将编译器中的调试信息存放到程序代码中,这些调试信息又被称为调试符号或符号信息,它们之中包含着函数和变量的名称、CPU指令、源文件、甚至行号等源码间的关系。大多数编译器默认模式下未启用调试,代码中的调试信息会使程序变大,许多编译器优化方式在调试模式下是禁用的,程序运行会很慢。需要在编译时,使用-g将调试信息添加到程序代码中。

    直接在vim中编译:":!gcc -g % -o %<"
    直接在vim中调试:":!sh"转到shell提示符下,通过exit命令返回vim中

    二)、使用GDB调试程序
    (1)正确的参数:
    gdb factorial
    GNU gdb (GDB) 7.9
    Copyright (C) 2015 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-unknown-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from factorial...done.

    以上灰色背景的都是gdb的提示信息
    (gdb) r(run) 1
    Starting program: /home/munication/factorial 1
    factorial 1 = 1
    [Inferior 1 (process 9122) exited normally]

    (2)错误的参数:
    (gdb) r -1
    Starting program: /home/munication/factorial -1

    Program received signal SIGSEGV, Segmentation fault.
    0x00000000004005de in factorial (
       n=<error reading variable: Cannot access memory at address 0x7fffff7feffc>)
       at factorial.c:5
    warning: Source file is more recent than executable.
    5       int factorial(int n){

    很明显出现了段错误,原因是传入的参数不正确,跟踪栈帧:
    (gdb) bt
    #0  0x00000000004005de in factorial (
       n=<error reading variable: Cannot access memory at address 0x7fffff7feffc>)
       at factorial.c:5
    #1  0x0000000000400602 in factorial (n=-174662) at factorial.c:10
    #2  0x0000000000400602 in factorial (n=-174661) at factorial.c:10
    #3  0x0000000000400602 in factorial (n=-174660) at factorial.c:10
    #4  0x0000000000400602 in factorial (n=-174659) at factorial.c:10
    #5  0x0000000000400602 in factorial (n=-174658) at factorial.c:10
    #6  0x0000000000400602 in factorial (n=-174657) at factorial.c:10
    #7  0x0000000000400602 in factorial (n=-174656) at factorial.c:10
    #8  0x0000000000400602 in factorial (n=-174655) at factorial.c:10
    #9  0x0000000000400602 in factorial (n=-174654) at factorial.c:10
    #10 0x0000000000400602 in factorial (n=-174653) at factorial.c:10
    #11 0x0000000000400602 in factorial (n=-174652) at factorial.c:10
    #12 0x0000000000400602 in factorial (n=-174651) at factorial.c:10
    #13 0x0000000000400602 in factorial (n=-174650) at factorial.c:10
    #14 0x0000000000400602 in factorial (n=-174649) at factorial.c:10
    #15 0x0000000000400602 in factorial (n=-174648) at factorial.c:10
    #16 0x0000000000400602 in factorial (n=-174647) at factorial.c:10
    #17 0x0000000000400602 in factorial (n=-174646) at factorial.c:10
    #18 0x0000000000400602 in factorial (n=-174645) at factorial.c:10
    #19 0x0000000000400602 in factorial (n=-174644) at factorial.c:10
    #20 0x0000000000400602 in factorial (n=-174643) at factorial.c:10
    #21 0x0000000000400602 in factorial (n=-174642) at factorial.c:10
    #22 0x0000000000400602 in factorial (n=-174641) at factorial.c:10
    #23 0x0000000000400602 in factorial (n=-174640) at factorial.c:10
    #24 0x0000000000400602 in factorial (n=-174639) at factorial.c:10
    #25 0x0000000000400602 in factorial (n=-174638) at factorial.c:10
    #26 0x0000000000400602 in factorial (n=-174637) at factorial.c:10
    #27 0x0000000000400602 in factorial (n=-174636) at factorial.c:10
    ---Type <return> to continue, or q <return> to quit---
    ………………………………………………………………………………
    #174649 0x0000000000400602 in factorial (n=-14) at factorial.c:10
    #174650 0x0000000000400602 in factorial (n=-13) at factorial.c:10
    #174651 0x0000000000400602 in factorial (n=-12) at factorial.c:10
    #174652 0x0000000000400602 in factorial (n=-11) at factorial.c:10
    #174653 0x0000000000400602 in factorial (n=-10) at factorial.c:10
    #174654 0x0000000000400602 in factorial (n=-9) at factorial.c:10
    #174655 0x0000000000400602 in factorial (n=-8) at factorial.c:10
    #174656 0x0000000000400602 in factorial (n=-7) at factorial.c:10
    #174657 0x0000000000400602 in factorial (n=-6) at factorial.c:10
    ---Type <return> to continue, or q <return> to quit---
    #174658 0x0000000000400602 in factorial (n=-5) at factorial.c:10
    #174659 0x0000000000400602 in factorial (n=-4) at factorial.c:10
    #174660 0x0000000000400602 in factorial (n=-3) at factorial.c:10
    #174661 0x0000000000400602 in factorial (n=-2) at factorial.c:10
    #174662 0x0000000000400602 in factorial (n=-1) at factorial.c:10
    #174663 0x000000000040066c in main (argc=2, argv=0x7fffffffde48) at factorial.c:24

    不停的按回车键即可,一直到n= -1;

    (3)不合适的参数:
    (gdb) r 13
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/munication/factorial 13
    factorial 13 = 1932053504
    [Inferior 1 (process 9215) exited normally]

    实际结果是:6,227,020,800
    显示结果是:1,932,053,504
    两结果插值:4,294,967,296 = 2^32

    (4)使用断点控制代码的执行,查看变量和表达式的值
    1)在main函数设置断点:
    (gdb) b main
    Breakpoint 1 at 0x400621: file factorial.c, line 19.
    2)跟据行号设置断点:
    (gdb) b 8
    Breakpoint 2 at 0x4005ef: file factorial.c, line 8.
    3)根据函数和行号设置断点:
    (gdb) b factorial:11
    Breakpoint 3 at 0x4005e1: file factorial.c, line 6.
    (gdb) b 11
    Breakpoint 3 at 0x4005e1: file factorial.c, line 11.
    4)程序中导航并查看变量和表达式的值
    设置参数13运行程序:
    (gdb) start 13
    Temporary breakpoint 5 at 0x400621: file factorial.c, line 19.
    Starting program: /home/munication/factorial 13

    Breakpoint 1, main (argc=2, argv=0x7fffffffde48) at factorial.c:19
    19          if(argc != 2){
    你就会发现程序在设置的第一个断点处停下来了。

    (gdb) n
    23          n = atoi(argv[1]);
    输入n或next,程序以块为执行单位执行,将
    if(argc != 2){
    fprintf(stderr, "usage: factorial n, n >= 0 ");
    return 1;
        }
    代码块作为一条语句执行。如果继续使用n执行,将24行代码:result = factorial(n);中的函数调用直接调用完成,为了查看阶乘的计算过程,不能使用next命令。

    (gdb) s
    24          result = factorial(n);

    (gdb) s

    Breakpoint 3, factorial (n=13) at factorial.c:6
    6           int result = 1;

    此时可以看到已经进入到factorial函数中的第一条语句:int result = 1;

    为了观察函数在参数为0时是不是能够实现返回1的能力,在第8行设置一个断点,为了观察函数参数大于0时的计算结果,在第11行设置一个断点。不但可以在开始设置断点,也可以随时设置断点。
    使用命令c或continue是程序能够稍微快速执行:

    (gdb) c
    Continuing.

    Breakpoint 3, factorial (n=12) at factorial.c:6
    6           int result = 1l;

    多执行几下c命令后:查看n变量的值,查看result的值
    (gdb) p n
    $13 = 11
    (gdb) p result
    $14 =
    (gdb) p result
    $6 =
    39916800

    (gdb) n

    Breakpoint 4, factorial (n=12) at factorial.c:11
    11              return result;

    (gdb) p result
    $15 = 479001600

    此时可以看到当n为12时,执行的结果还是正确的,当n为13时

    Breakpoint 4, factorial (n=13) at factorial.c:11
    11              return result;
    (gdb) p n
    $16 = 13
    (gdb) p result
    $17 = 1932053504
    此时是由于32位的整型数据的最大值为2^32 = 4,294,967,296,数据溢出后,剩余的数值,到此为止调试结束。
    下表为gdb产用的调试命令:

    命令名称调试命令 其他说明
    运行程序 run [args]开始调试
    启动程序 start [args]逐步调试
    暂停 Ctrl-c 全部中断
    继续运行 cont(c) 继续运行

    step-over next(n) 逐过程,将函数作为一条语句,直接返回值即可
    step-into step(s)逐语句,进入调用的函数,逐条语句执行
    step-out finish(f) 跳出执行

    断点 break(b) file:lineno按照文件行号设置断点,也可以以函数名设置
    跟踪点 watch file: expr观察函数中的某一行
    观察点 watch expr观察表达式的值

    栈跟踪 bt, where堆栈跟踪
    输出表达式 print expr输出表达式
    显示表达式 display expr显示表达式

    设置变量 set var var = expr设置变量值
    设置环境变量 set env var[=val]设置属性、调试环境等

    显示机器代码 Disassemble反汇编代码
    在机器代码中执行step-over nexti逐过程
    在机器代码中执行step-into stepi逐语句

    条件断点 condition bnum根据条件设置断点
    时间断点 handle, signal根据信号设置断点
    异常断点 catch, throw测试异常
    函数断点 break, function在函数所在处中断
    临时断点 tbreak临时中断函数
    列出所有断点 info breakpoints查看所有断点

    将命令连接到断点 commands bnum将命令输出链接到断点
    输出到命令行 printf将断点输出到命令行

    查找函数 info functions expr查找函数断点
    调用函数 call expr调用函数
    修改函数返回值 return expr修改函数的返回数值

    输出类型 What is arg转到声明
    输出类型描述 ptype arg转到定义
    输出内存内容 x arg临时输出内存的内容
    选择栈帧 frame arg
    输出栈帧描述 info frame调用堆栈


  • 相关阅读:
    centos7上以RPM方式安装MySQL5.6
    区别和详解:jQuery extend()和jQuery.fn.extend()
    jQuery笔记总结
    CSS Hack的一些知识
    12种不宜使用的javascript的语法
    64位Win7系统下vs2010调试无法连接oracle解决办法
    HashCode()与equals()深入理解
    Java ArrayList自动扩容机制
    Java基础知识
    MySQL的MVCC机制
  • 原文地址:https://www.cnblogs.com/guochaoxxl/p/6823184.html
Copyright © 2011-2022 走看看