1 调试初步

来源:互联网 发布:python列表推导式 编辑:程序博客网 时间:2024/06/07 01:50

—–《软件调试的艺术》

  1. gcc -glevel sourcefile …
    一般,为方便调试器对程序的调试,在用gcc编译程序时可添加-glevel选项:
    -g
    该选项可以利用操作系统的“原生格式(native format)”生成调试信息。GDB 可以直接利用这个信息,其它调试器也可以使用这个调试信息;
    -g2
    这是默认的级别,此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息 ;
    -g3
    包含级别2中的所有调试信息,以及源代码中定义的宏;
    -g1
    级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段。

  2. 编译完成后即可启动GDB对程序进行调试,进入GDB调试会话:
          gdb executablefile -tui
    -tui可使调试是源程序可视化(快捷键Ctrl+A+X);退出GDB调试会话命令:quit.
    在GDB调试会话中,获取帮助说明:
    (gdb)help gdbcommand (help list)
    这里写图片描述

  3. 开始调试(start args, run args)
    进入gdb会话后,通过run,start启动调试程序:run [args], start [args];
    start [args],相当于先执行:
    (gdb) tbreak main
    (gdb) run args

  4. 暂停机制
    有3种方式可以通知GDB暂停程序的执行。
    断点:通知GDB在程序的特定位置执行;break
    监视点:通知GDB当特定内存位置的值(或涉及一个或多个位置的表达式)发生变化时暂停执行;watch
    捕获点:通知GDB当特定事件发生时执。catch
    (*GDB使用机器语言指令工作)
    (*GDB使用多个断点(如中断条件不同)中断一行代码时,它每次只会中断一次,即从断点恢复时会忽略其他同行断点)
    (*?退出调试器时,可以将断点放在源代码所在目录的.gdbinit启动文件中,以保存断点)
    (*gdb不会在不具有调试信息的代码内停止,比如常用库函数printf)

    (1)设置断点:break, tbreak,rbreak
    (gdb) help break
    这里写图片描述
    设置断点后,程序将执行到断点所在行的开始,下一步执行断点所在行。
    (gdb)info breakpoints (i b)  //查询已设置的断点
    (gdb) break 25  //linenum
    (gdb) break main //function
    (gdb) break sourcebed.c:35 //function:linenum
    (gdb) break *address //address
       //在虚拟内存地址处设置断点,对于程序没有调试信息的部分非常有用(比如源代码不可用时,或对于共享库)
    tbreak: 仅在断点处停止一次,首次到达后删除,伺候查询不到对应断点。
    rbreak:采用grep风格的正则表达式工作,并在匹配该正则表达式的任何函
        数入口处设置断点

    (2)删除断点:clear, delete
    不带参数时,二者均删除全部断点;
    删除具体断点时,可先通过(gdb)i b 查看断点信息;
    clear的用法可break用法对应,通过行号,函数名,地址等确定目标;
    delete则通过断点序号删除断点,如(gdb)delete 1 //删除断点1除断

    (3)禁用,启用断点:disable breakpoint-list, enable breakpoint-list

    (4)条件断点: break break-args if (confition)
    (gdb)break main if argc>1
    (gdb)break test.c:34 if (x&y)==1
    break-args用以指定断点位置;
    conditon定义布尔表达式,圆括号可选;
    告诉调试器只有当符合某种条件时才在断点处停止;
      condition中使用的表达式除常用的逻辑算数表达式,还可包括
    自己编写的函数,只要它们被链接到程序中,如:
    (gdb) break test.c:myfunc if ! check_variable_sanity(i)
    库函数,只要该库被链接到代码中,如:
    (gdb) break 44 if strlen(mystring)==0
      也可以对正常断点设置条件以将它们转变为条件断点:condition(cond)
    (gdb) cond 3 i==3
    如果以后要删除条件,但保留该断点,只要键入:
    (gdb) cond 3  
      如果在GDB中使用库函数,而该库不是用调试符号编译的,那么唯一能在断点条件中使用的返回值类型为int。换言之,如果没有调试信息,GDB会假设函数的返回值为int类型。

    有一种方式可以在GDB中使用不反回int型的函数,技巧在于使用指向函数的恰当的数据类型定义GDB方便变量:
    (gdb) set $p=(double(*)(double))cos
    (gdb) ptype $p
    type = double (*)()
    (gdb) p cos(3.14159265)
    $2=14368
    (gdb) p $p(3.14159265)
    $4=-1

    (5)断点命令列表
    让GDB每次到达断点时,自动执行一组命令:command
    这里写图片描述

    使用GDB的define命令创建宏:

    (gdb) define print_and_go
    > printf $arg0, $arg1
    > continue
    > end
    (gdb)command 1
    > silent
    > print_and_go “fibonacci() was passed %d\n” n
    > end

    (6)监视点:watch
    监视点没有“住在某一行代码中”,而是指示GDB当某个表达式改变了值就暂停执行的指令。

    (gdb) watch i
    (gdb) watch (i | j>12) && i>24&&strlen(name)>6
       断点与监视点的一个重要区别是:断点与源代码中的一个位置相关联,只要代码没有改变,就不存在某行代码“超出作用域”的风险;
      因为C语言有严格的作用域规则,左移只能监视存在且在作用域中的变量,一旦不存在于调用栈的任何帧中,GDB会自动删除监视点;
      GDB只能监视单个线程中的变量。

  5. 恢复执行:continue, next, step,finish, until
    (1)next, step ,可采用一个可选的数值参数,如(gdb) step 2,表执行step两次,或执行step一次,然后按下Enter键一次;
    (2)continue,可执行到下一个断点,若没有则执行完,
    continue num, GDB恢复执行并忽略接下来的num个断点;
    (3)finish,恢复执行,直到恰好在当前帧完成之后,即执行完当前函数,step over;
    在递归函数中,finish只会使程序进入到递归的上一层,如果要在递归层次较高时完全退出递归函数,使用tbreak &continue更合适;
    这里写图片描述
    (4) until, 让GDB在循环(for,while)后面的第一行代码处暂停;等价于在循环后的第一行代码处设置临时断点tbreak,然后运行continue跳出循环;until与finish执行前应注意禁用内部断点。
    这里写图片描述

0 0