gdb调试启动调试程序

http://blog.csdn.net/yangzhongxuan/article/details/6892221


gdb调试(一)启动调试程序
一、启动
>>gdb启动
      gdb 调试之前加载调试符号,即编译时候加 –g选项,如 gcc file.c –g –o target
      启用gdb的方法种有3种,一种是启动core,还有是attach一个已经运行的进程。
   1. gdb
   2. gdb core
      用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
   3. gdb
     如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。 program应该在PATH环境变量中搜索得到。
 
>>运行调试程序
  1. run argv1 argv2
     gdb带参数运行
  2. run
     不带参数运行
  3. set args argv_a  argv_b
     gdb启动程序执行后可重新设置参数
  4. run > ./output

     gdb启动程序时进行重定向
     
     
     
     
http://sourceware.org/gdb/current/onlinedocs/gdb

二、断点设置

gdb断点分类:

以设置断点的命令分类:

breakpoint

可以根据行号、函数、条件生成断点。

watchpoint

监测变量或者表达式的值发生变化时产生断点。

catchpoint

监测信号的产生。例如c++的throw,或者加载库的时候。

gdb中的变量从1开始标号,不同的断点采用变量标号同一管理,可以 用enable、disable等命令管理,同时支持断点范围的操作,比如有些命令接受断点范围作为参数。

例如:disable 5-8

 

1、break及break变种详解:

相关命令有break,tbreak,rbreak,hbreak,thbreak,后两种是基于硬件的,先不介绍。

>>break 与 tbeak

    break,tbreak可以根据行号、函数、条件生成断点。tbreak设置方法与break相同,只不过tbreak只在断点停一次,过后会自动将断点删除,break需要手动控制断点的删除和使能。

    break 可带如下参数:

    linenum                 本地行号,即list命令可见的行号

    filename:linenum  制定个文件的行号

    function                函数,可以是自定义函数也可是库函数,如open

    filename:function  制定文件中的函数

    condtion                条件

     

    *address      地址,可是函数,变量的地址,此地址可以通过info add命令得到。

    例如:

    break 10    

    break test.c:10

    break main

    break test.c:main

    break system

    break open

    如果想在指定的地址设置断点,比如在main函数的地址出设断点。

    可用info add main 获得main的地址如0x80484624,然后用break *0x80484624.

    条件断点就是在如上述指定断点的同时指定进入断点的条件。

    例如:(假如有int 类型变量 index)

    break 10 if index == 3

    tbreak 12 if index == 5

>>rbreak

    rbreak 可以跟一个规则表达式。rbreak + 表达式的用法与grep + 表达式相似。即在所有与表达式匹配的函数入口都设置断点。

     

    rbreak list_* 即在所有以 list_ 为开头字符的函数地方都设置断点。

    rbreak ^list_ 功能与上同。

>>查看断点信息

    info break [break num ]

    info break 可列出所有断点信息,info break 后也可设置要查看的break num如:

    info break 1 列出断点号是1的断点信息

     Num     Type           Disp Enb  Address    What

     1       breakpoint     keep y   

             stop only if i==1

             breakpoint already hit 1 time

     1.1                         y    0x080486a2 in void foo() at t.cc:8

     1.2                         y    0x080486ca in void foo() at t.cc:8
     
     



2、watch

     watch [-l|-location] expr [thread threadnum] [mask maskvalue]

     -l 与 mask没有仔细研究,thread threadnum 是在多线程的程序中限定只有被线程号是threadnum的线程修改值后进入断点。

     经常用到的如下命令:

     watch

     为表达式(变量)expr设置一个观察点。变量量表达式值有变化时,马上停住程序。

 

     表达式可以是一个变量

     例如:watch value_a

 

     表达式可以是一个地址:

     例如:watch *(int *)0x12345678 可以检测4个字节的内存是否变化。

 

     表达式可以是一个复杂的语句表达式:

     例如:watch a*b + c/d

 

     watch 在有些操作系统支持硬件观测点,硬件观测点的运行速度比软件观测点的快。如果系统支持硬件观测的话,当设置观测点是会打印如下信息:

     Hardware watchpoint num: expr

    如果不想用硬件观测点的话可如下设置:

    set can-use-hw-watchpoints

 

    watch两个变种 rwatch,awatch,这两个命令只支持硬件观测点如果系统不支持硬件观测点会答应出不支持这两个命令的信息:,

 

    rwatch

    当表达式(变量)expr被读时,停住程序。

       

    awatch

    当表达式(变量)的值被读或被写时,停住程序。

   

    info watchpoints

    列出当前所设置了的所有观察点。

 

     watch 所设置的断点也可以用控制断点的命令来控制。如 disable、enable、delete等。

 

     可以为停止点设定运行命令

 

     commands [bnum]

    ... command-list ...

    end

    为断点号bnum指写一个命令列表。当程序被该断点停住时,gdb会依次运行命令列表中的命令。

    例如:

 

        break foo if x>0

        commands

        printf "x is %d/n",x

        continue

        end

    断点设置在函数foo中,断点条件是x>0,如果程序被断住后,也就是,一旦x的值在foo函数中大于0,GDB会自动打印出x的值,并继续运行程序。

   注意:watch 设置也是断点,如果调试的时候设置的断点(任何种类的断点)过多的时候,watch断点会被忽略,有时候没有任何提示,

            这是我在测试的时候发现的,只有把多余的断点删除后才可用。


在调试的时候通常用catchpoints来捕获事件,如c++的异常等。捕获点的设置通过catch与tcatch两个命令。
    tcatch所设置的断点停止一次后自动删除,设置断点的方法与catch相同。
    用法:catch event
    这些event事件如下:
    throw
        The throwing of a C++ exception.
    catch
        The catching of a C++ exception.
    exception
    
    exception unhandled
        An exception that was raised but is not handled by the program.
    assert
        Ada 语言 assert断言失败时,断点被踩到。
    exec
        调用exec时断点被踩到。
    syscall
    syscall [name | number] ...
        通过系统函数的名称和系统号,来设置捕获点,当所设定的系统调用时,断点被踩到。
         因为经常在linux用c语言,所以主要用到的event是最后四个,其他的没有仔细研究。
     例如:
     catch syscall open
     catch syscall 5
     这两个捕获断点一样。
     
            断点的删除与断点的设置同样的重要。删除断点的命令有两个:
            delete
            用法:delete [breakpoints num] [range...]
            delete可删除单个断点,也可删除一个断点的集合,这个集合用连续的断点号来描述。
            例如:
            delete 5
            delete 1-10
            
            clear
            用法:clear
                删除所在行的多有断点。
                clear location
            clear 删除所选定的环境中所有的断点
            clear location location描述具体的断点。
            例如:
            clear list_insert         //删除函数的所有断点
            clear list.c:list_delet   //删除文件:函数的所有断点
            clear 12                  //删除行号的所有断点
            clear list.c:12           //删除文件:行号的所有断点
            
            clear 删除断点是基于行的,不是把所有的断点都删除。
            
                 

gdb断点(六)condition 与ignore

如果为一个断点设置一个条件,每当程序到达这个断点的时候都会去判断是否为TRUE,
只有条件为TRUE时才会在断点出停下。断点条件的调试方式与断点断言的调试方式逻辑是想反的,assert是当条件为FALSE时停止,
所一如果在条件调中用断言的话应该是:condition !assert(exp)。
断点的条件调试优越性在同个地方设置多个断点的时候体现的更完美。这样我们就可以控制在
同一个断点,因不同的条件(不同的地方调用)而停止程序,同时你也可以自定义命令行,来

打印所需要的信息。


设置断点的条件方式如下:


1、设置断点的时候加入条件
      break foo if value_a > value_b

2、用condition命令

      condition bnum expression


      例如: condition 6 if value_a == 10
      如果你设置的断点条件,无效会提示:(这于断点的上下文有关,关于断点的上下文会子专门章节阐述)
      No symbol "foo" in current context

3、取消断点条件

     condition bnum


4、断点条件特殊用法
      断点条件的一个特殊用法是,程序只有在到达断点一定次数之后才会停止。这用一个特殊的命令可以实现。
      ignore bnum count

      ignore 设置的触发条件在重新加载程序之后自动删除。

      ignore 2 10  //触发断点10次后,才会停止,每次触发断点count自减1
      如果一个断点及设置了条件,又设置了触发次数,在触发次数count为0之前,是不会判断断点的条件。
      ignore 命令对breakpoint watchpoint catchpoint都有效。
      

gdb断点(七)为断点设定指令集Breakpoint Command Lists

如果想在程序停止在断点的时候,打印信息,或给一些有价值的变量赋值以便定位bug,或者是激活其他断点,可以为这个断点设置一些指令集,完成这些操作。gdb的commands指令帮你实现这个功能能。

用法:

commands [range...]

... command-list ...

end

 

例1:在设置断点的时候设定命令集

每个指令以行的形式设置,每行输入一个gdb指令,结束的时候一end结束。

     break foo if x>0

     commands       //指令集设置命令

     silent         //断点触发时不打印断点信息

     printf "x is %d\n",x

     cont

     end            //指令集设置结束时必须用end结束

 

例2:为某个指定的断点设置指令集

     commands 403    

     silent

     set x = y + 4

     cont

     end

      
三、gdb运行指令

 

continue [ignore-count]

       继续运行程序,直到结束或者触发下个断点。

step

       单步执行,可进入函数内部。

step count

 

next [count]

       以文件行为参考,运行下一行指令。遇到函数调用,跳过函数内部。

 

finish

       运行至函数返回。

 

until

       跳出循环。

 

advance location

       运行至指定的地方,如函数,指定行,指定地址。

例如:

advance list_display    //运行至list_display 函数出

advance 23           //运行至23行

 

stepi

    单步执行一条机器指令

 

nexti

    运行一条机器指令
    
什么是函数调用栈

 

    程序每调用一次函数,关于这个函数的信息就会产生。这些信息包括,调用函数的地方、函数的参数、被调用函数变量等。这些信息存储在一个叫做函数调用信息帧的内存中,这些函数信息帧就组成了函数调用栈。

    gdb提供了一些指令可以查看这些帧中的信息。当查询函数变量的信息时,gdb就是从这个被选中的帧内获取信息,但是查看被选中帧外的变量信息是非法的。当程序运行停止的时候,gdb会自动选择当前被调用的函数帧,并且打印简单帧信息。



gdb调试(四)函数调用栈之--frame
函数调用栈由连续的栈帧组成。每个栈帧记录一个函数调用的信息,这些信息包括函数参数,函数变量,函数运行地址。

    当程序启动后,栈中只有一个帧,这个帧就是main函数的帧。我们把这个帧叫做初始化帧或者叫做最外层帧。每当一

个函数被调用,一个新帧将被建立,每当一个函数返回时,函数帧将被剔除。如果函数是个递归函数,栈中将有很多帧是
记录同一个函数的。但前执行的函数的帧被称作最深帧,这个帧是现存栈中最近被创建的帧。

    在程序内部,函数栈帧用函数的地址来标记。一个帧由一定字节的内存组成,每个字节都有自己的地址 。每种类型的计
算机有个约定,用一个特殊字节的地址存放函数帧的地址。通常函数帧的地址存放在一个称作帧指针的寄存器中--$fp.

    gdb 为所有存活的栈帧分配一个数字编号,最深帧的编号是0,被它调用的内个帧的编号就是1。这些编号子程序中是不
存在的,只不过时调试的时候被gdb用的。

    关于函数帧的两个指令:

    frame args
    移动到args指定的栈帧中去,并打印选中的栈的信息。args可以时帧编号或者时帧的地址。如果没有args,则打印当前帧的信息。

    select-frame args
    移动到指定的帧中去,不打印信息。
    

gdb调试(四)函数调用栈之Backtraces

通过产看栈信息,我们可以了解栈内帧的编号或地址,通过选择帧我们可以移动到指定的帧内去产看信息。



1、查看栈信息


产看函数调用栈的几个函数

bt
    显示所有的函数调用栈帧的信息,每个帧一行。
bt n
    显示栈定的n个帧信息。
bt -n
    显示栈底的n个帧信息。

bt full
    显示栈中所有帧的完全信息如:函数参数,本地变量
bt full n
    用发同上。
bt full -n

例如:

(gdb) bt

#0 get_net_ipaddr (s=5, ifr=0xbfffe7ec, ipaddr=0xbfffe88c "192.168.1.200") at netif.c:67

#1 0x080491e2 in netinf_get (dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,

netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001") at netif.c:296

#2 0x08048813 in main () at netif.c:34

(gdb) bt full

#0 get_net_ipaddr (s=5, ifr=0xbfffe7ec, ipaddr=0xbfffe88c "192.168.1.200") at netif.c:67

err = 0

#1 0x080491e2 in netinf_get (dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,

netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001") at netif.c:296

s = 5

err = 0

ifr = {ifr_ifrn = {ifrn_name = "eth0", '\000' }, ifr_ifru = {ifru_addr = {sa_family = 2,

sa_data = "\000\000\300\250\001\310\000\000\000\000\000\000\000"}, ifru_dstaddr = {sa_family = 2,

sa_data = "\000\000\300\250\001\310\000\000\000\000\000\000\000"}, ifru_broadaddr = {sa_family = 2,

ifru_newname = "\002\000\000\000\300\250\001\310\000\000\000\000\000\000\000",

ifru_data = 0x2

}}

#2 0x08048813 in main () at netif.c:34

err = 1

isup = 0

ipaddr = "192.168.1.200\000\004\b\270\350\377\277\245\324\025\000\060\340\021\000\273\231\004\b"



2、选择帧


只有查看所选择帧内的信息是合法的,如果要查看指定帧内的信息,首先要移动到指定帧。

frame n
f n
    通过帧编号来选择帧,帧编号可以通过bt来查看。

f addr
    通过帧地址来选择帧,帧编号可以通过bt来查看。

up n
    在栈中向上移动n个帧。即向着最外层移动n个帧。

down n
    与 up 反方向移动n个帧。

up-silently n
down-silently n
    在栈中移动n个帧,但是不打印信息。



3、查看帧内信息

frame
f
    打印帧内函数的信息。

info frame
info f
    打印帧的信息。

例如:

(gdb) info frame

Stack level 1, frame at 0xbfffe820: //被选帧的地址

eip = 0x80491e2 in netinf_get (netif.c:296); saved eip 0x8048813

called by frame at 0xbfffe8c0, caller of frame at 0xbfffe7b0 //调用帧和被调用帧的地址

source language c.

Arglist at 0xbfffe818, args: dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,

netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001" //变量地址

Locals at 0xbfffe818, Previous frame's sp is 0xbfffe820

Saved registers:

ebp at 0xbfffe818, eip at 0xbfffe81c //保存的寄存器值地址

info frame addr
info f addr

打印通过addr指定帧的信息。
例如: info f 0x08048813

info args
    打印函数变量的值。

info locals
    打印本地变量的信息。

info catch


gdb 调试(五)检查文件之编辑源文件。    
gdb 调试的时候可以编辑源文件。

edit location

location可以使行号,函数名,文件中指定的位置。

例:

edit 32         编辑当前文件的32行

edit main       编辑当前文件的main函数

edit main.c:init  .编辑main.c中的init函数

edit mian.c:32   编辑main.c中的32行

 

指定编辑器

 

在sh shell中指定编辑器:

     EDITOR=/usr/bin/vi

     export EDITOR

     gdb ...

 

在csh shell中指定编辑器:

     setenv EDITOR /usr/bin/vi

     gdb ...     
     
 gdb 调试(五)检查文件之list        
 gdb根据记录的调试信息知道我们要调试的文件信息。所以通过gdb我们能查看源码。

同时gdb也提供修改源码文件的指令。

 

1、  查看源码

gdb中通过list命令查看源码。list每次显示的函数可以指定,下文中假定指定显示的行数为10行。

 

list linenum

    以linenum指定的行号为中心,显示10行

 

list function

    以指定的函数为中心,显示10行

 

list

    重复上一次的list指令,也可以直接按回车键,重复上次指令。

 

set listsize count

    设置每次显示的行数。

 

show listsize

    显示已设置的显示行数。

list linespec

    设定显示的文件,list指令都是以指定的文件为域来显示源文件,这个指令可以指定要显示的原文件。

    例:list list_test.c

    以后的list指令都是显示,list_test.c 的源文件。

 

list first,last

    显示指定起始行到结束结束行的源文件。

    例:list 10,100

 

list ,last

    显示以指定的last为结束行,显示10行。

    例:list ,30  显示21~30行,共显示10行

 

list first,

    以first为第一行,显示10行。

 

list +

    以上次显示的结束行为起始行显示后10行

 

list –

    以上次显示的起始行为结束行,显示前10行
 
 
gdb 调试(五)检查文件之搜索和查看汇编指令
1、  源码文件查找

gdb调试可以通过以下三个命令查找源文件。

 

forward-search regexp

从源文件首行还是搜索 关键字regexp,并将搜索到的行号记录。这个记录可以被下一个gdb命令引用。

 

例:for void

搜索结果:38 void

接下来运行命令:list 则显示以38行为中心的10行内容。(要显示的行数可以指定)

 

search regexp

从当前行向下搜索。

 

reverse-search regexp

从最后一行向上搜索。

 

2、  查看机器指令(汇编)

 

disassemble

disassemble /m  十六进制显示汇编指令

disassemble /r  

disassemble start,end  指定起始,结束地址

disassemble start,+length 指定起始地址和显示长度

 

获取地址可如下面的列子:

     (gdb) info line main

     Line 895 of "builtin.c" starts at pc 0x634c and ends at 0x6350.

 

     (gdb) info line *0x63ff

     Line 926 of "builtin.c" starts at pc 0x63e4 and ends at 0x6404.

 

     (gdb) disas 0x634c, 0x6404

 

     Dump of assembler code from 0x32c4 to 0x32e4:

        0x32c4 :      addil 0,dp

        0x32c8 :      ldw 0x22c(sr0,r1),r26

        0x32cc :      ldil 0x3000,r31

        0x32d0 :      ble 0x3f8(sr4,r31)

        0x32d4 :      ldo 0(r31),rp

        0x32d8 :      addil -0x800,dp

        0x32dc :      ldo 0x588(r1),r26

        0x32e0 :      ldil 0x3000,r31

     End of assembler dump.  


你可能感兴趣的:(linux)