zoukankan      html  css  js  c++  java
  • gdb调试工具常用命令 && kdb

    编译程序时需要加上-g,之后才能用gdb进行调试:gcc -g main.c -o main

    gdb中命令:

    回车键:重复上一命令

    (gdb)help:查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h

    (gdb)run:重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件),简写r

    (gdb)start:单步执行,运行程序,停在第一执行语句

    (gdb)list:查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数),简写l

    (gdb)set:设置变量的值

    (gdb)next:单步调试(逐过程,函数直接执行),简写n

    (gdb)step:单步调试(逐语句:跳入自定义函数内部执行),简写s

    (gdb)backtrace:查看函数的调用的栈帧和层级关系,简写bt

    (gdb)frame:切换函数的栈帧,简写f

    (gdb)info:查看函数内部局部变量的数值,简写i

    (gdb)finish:结束当前函数,返回到函数调用点

    (gdb)continue:继续运行,简写c

    (gdb)print:打印值及地址,简写p

    (gdb)quit:退出gdb,简写q

    (gdb)break+num:在第num行设置断点,简写b

    (gdb)info breakpoints:查看当前设置的所有断点

    (gdb)delete breakpoints num:删除第num个断点,简写d

    (gdb)display:追踪查看具体变量值

    (gdb)undisplay:取消追踪观察变量

    (gdb)watch:被设置观察点的变量发生修改时,打印显示

    (gdb)i watch:显示观察点

    (gdb)enable breakpoints:启用断点

    (gdb)disable breakpoints:禁用断点

    (gdb)x:查看内存x/20xw 显示20个单元,16进制,4字节每单元

    (gdb)run argv[1] argv[2]:调试时命令行传参

    (gdb)set follow-fork-mode child#Makefile项目管理:选择跟踪父子进程(fork())

       core文件:先用$ ulimit -c 1024 开启core,当程序出错会自动生成core文件。调试时 gdb a.out core

    ctrl+c:退出输入

    首先介绍下GDB常用命令

    流程
    start <args>或run <args>:bin程序开始执行
    c(continue):从断点开始继续执行,直到下一断点或结束
    n(next):执行下一行(跳过函数)
    s(step):执行下一行(进入函数)
    finish:运行到当前函数结束退出
    运行状态下ctrl-c:暂停
    q(quit),或暂停状态下ctrl-c:退出

    断点
    b <func-name>:在函数上打断点
    b * <address>:在代码段地址上打断点
    i b:查看断点
    d b <N>:禁用指定编号的断点
    e b <N>:启用指定编号的断点
    b ... if <expr>:条件断点:若<expr>为真(或非NULL),在断点处暂停

    临时进入shell模式
    shell(进入)、exit(退出)
    帮助:help或?

    查看信息
    bt(backtrace):查看调用栈
    i r:查看寄存器信息
    x/Ns:以字符串形式查看指定变量/地址
    x/Nd:以十进制数字形式查看指定变量/地址
    x/Nx:以十六进制形式查看指定变量/地址
    x/Ni:查看指定函数/地址的汇编指令
    p <expr>:查看<expr>的值

    x/b &str:查看全局变量的字符串,str是一个全局字符串

    其它
    暂停状态下回车:执行上一条命令
    支持上翻、下翻

    设置打印字符的个数为不限制数量,设置为unlimited或者zero。

    (gdb) set print elements 0
    (gdb) show print elements
    Limit on string chars or arry elemtns to print is unlimited

    CPU寄存器信息

    mips

    zero:永远为0
    v0/v1:临时寄存器;函数返回时存返回值
    a0 ~ a3:临时寄存器;调用函数时存参数
    t0 ~ t9:临时寄存器。t9常用于临时存要调用的子函数入口地址
    s0 ~ s8:非易失性寄存器
    gp:全局指针
    sp:栈顶指针
    ra:当前函数返回地址
    pc:当前指令

    x86

    1) %rax 作为函数返回值使用。
    2) %rsp 栈指针寄存器,指向栈顶。
    3) %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数等。
    4) %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改。
    5) %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值。
     
     
    栈结构信息
    X86_64的栈结构:
     
    如上图所示:%rsp寄存器存储的地址指向当前函数的栈顶,%rbp寄存器是我们推栈的关键,它指向的位置保存了两个字,前一个函数栈的%rbp寄存器值和函数返回地址。
     
    (三)推栈过程:
     
        下面开始介绍推栈过程,可以发现,推栈的过程就是跟踪查找%rbp寄存器的内容,而且我们无需像MIPS体系那样关注某个函数的堆栈的大小。
     
     1) 设备死机时会进KDB,通过KDB的rd命令可查看当前挂死环境所有寄存器的内容:
    [12]kdb> rd
         r15 = 0x0000000000000007      r14 = 0x0000000000000000 
         r13 = 0x0000000000000000      r12 = 0xffffc9005dd6b2b0 
          bp = 0xffff8800462375e8       bx = 0xffff8800463780a0 (这里的bp寄存器和上面提到的%rbp寄存器是等价的)
         r11 = 0x0000000000000000      r10 = 0xffffc9005dd735b0 
          r9 = 0x0000000000000002       r8 = 0xffffc9005dd67130 
          ax = 0x0000000004230423       cx = 0xffffc90061f9b6d3 
          dx = 0x0000000000000095       si = 0x0000000000000000 
          di = 0xffffc9005dd67008  orig_ax = 0xffffffffffffffff 
          ip = 0xffffffffc224156c       cs = 0x0000000000000010  
       flags = 0x0000000000010246       sp = 0xffff880046237570 
          ss = 0x0000000000000018 &regs = 0xffff8800462374d8

    2) SP寄存器指向当前调用栈的栈顶,通过md命令可查看调用栈的内容,而IP寄存器里存储的是正在执行的函数地址
    [12]kdb> md 0xffff880046237570    
    fa_traverse_dfa+0x1ec             
    0xffff880046237570 ffff880046237580 0000000000000018   .u#F............
    0xffff880046237580 ffff880046237696 ffff880046378355   .v#F....U.7F....
    0xffff880046237590 0000000000000000 ffffc9005dd7772e   .........w.]....
    0xffff8800462375a0 ffff8800462376e4 ffff880046378356   .v#F....V.7F....
    0xffff8800462375b0 ffff880046378010 0000000042dd1653   ..7F....S..B....
    0xffff8800462375c0 0000000000000001 0000000000000000   ................
    0xffff8800462375d0 0000000000000000 0000000000000346   ........F.......
    0xffff8800462375e0 0000000000000000                                     // 3)BP寄存器指向的地址(0xffff8800462375e8),存储了前一个函数栈的BP和返回地址。ffff880046237638 是前一个函数栈的BP值, ffffffffc22427a9是函数返回地址。
    ffff880046237638   ........8v#F....
    0xffff8800462375f0 ffffffffc22427a9                                   

    fa_traverse+0x69        // 4)通过函数返回地址得到前一个调用函数名称fa_traverse。
    0000000000000000   .'$.............
    0xffff880046237600 ffff880046237688 0000000000000000   .v#F............
    0xffff880046237610 0000000000000000 0000000000000000   ................
    0xffff880046237620 0000000000000000 ffffffffc31ff918   ................
    0xffff880046237630 ffffc9005dd67000                                 // 5)同时,我们得到了fa_traverse函数的BP寄存器的值,它指向的地址(ffff880046237638)同样存储了前一个函数的BP和返回地址。
    ffff8800462376c8   .p.].....v#F....
    0xffff880046237640 ffffffffc22428ed                                 // 6)以此类推, 接着向下推栈即可。

    regex_traverse+0x7d
    0000000000000000   .($.............
    0xffff880046237650 ffff880046237680 ffff880046237690   .v#F.....v#F....
    0xffff880046237660 ffff8800462376e4 0000000000000000   .v#F............
    [12]kdb> 
    0xffff880046237670 ffff880046237688 0000000000000000   .v#F............
    0xffff880046237680 0000000000000000 0000000000000001   ................
    0xffff880046237690 0000000000000000 0000000000000000   ................
    0xffff8800462376a0 ffffffffc31ff660 ffff880029099c00   `..........).... 
    0xffff8800462376b0 0000000000000000 0000000000000010   ................
    0xffff8800462376c0 0000000000000000 
    ffff8800462376f8   .........v#F....
    0xffff8800462376d0 ffffffffc223f533

    pcre_engine_regex_check+0x43
    ffffffffc210f000   3.#.............
    0xffff8800462376e0 00003a98c31ff660 ffff880029099c00   `....:.....)....
    0xffff8800462376f0 ffffffffc31ff660 
    ffff880046237778   `.......xw#F....
    0xffff880046237700 ffffffffc237dea1 

    dcdm_pcre_new_single_rule_proc+0xf1
    0000020000000200   ..7.............
    0xffff880046237710 0000000000000000 ffff880029099c30   ........0..)....
    0xffff880046237720 ffffe90142dd16f0 ffffe90142dd16d8   ...B.......B....
    0xffff880046237730 ffffffffc31ff918 0000000000000000   ................
    0xffff880046237740 ffff880029099c1c ffff8800462377a8   ...).....w#F....
    0xffff880046237750 0000000000000000 ffff880046370000   ..........7F....
    0xffff880046237760 ffffffffc31ff660 0000000000000000   `...............
    0xffff880046237770 ffff880029099c00 
    ffff8800462377d8   ...).....w#F....
    0xffff880046237780 ffffffffc237e34b 

    dcdm_pcre_new_flow_proc+0x1eb
    ffff880065a04880   K.7......H.e....
    0xffff880046237790 0000000165a1e0f6 ffffe90142dd16c0   ...e.......B....
    0xffff8800462377a0 ffff880065a04880 ffff8800462377d8   .H.e.....w#F....
    0xffff8800462377b0 ffff880065a04880 ffff880046370000   .H.e......7F....
    0xffff8800462377c0 ffffe90142dd16a8 000000000002d2a0   ...B............
    0xffff8800462377d0 ffffe90142dd1518 ffff8800462377f8   ...B.....w#F....
    0xffff8800462377e0 ffffffffc237e3cb ffff880065a04880   ..7......H.e....
    0xffff8800462377f0 ffff880046370000 ffff880046237898   ..7F.....x#F....
    0xffff880046237800 ffffffffc237d311 0000000000000000   ..7.............
    0xffff880046237810 0000000000000000 ffff880046237868   ........hx#F....
    0xffff880046237820 ffffffffc21eb9ff 471d0201ba190101   ...............G
    0xffff880046237830 000000061606db27 ffff880046237858   '.......Xx#F....
    0xffff880046237840 ffff880065a04880 ffffffffc5ed6860   .H.e....`h......
    0xffff880046237850 42000004c2380ca3 ffff880046237898   ..8....B.x#F....
    0xffff880046237860 00000001c23785f4 ffff8800462378b8   ..7......x#F....

    调试进程

    使用方法
    gdb -p <pid>
    适用场景
    某进程运行过程中会出故障,需要跟踪执行过程
    例:页面下发配置时发生500错误,需分析web_main代码流程
    某进程已经出故障但未崩溃,需要查看当前正在哪个流程中
    例:串口卡住,但远程诊断能用,需要析vtysh卡在何处
    操作流程:
    进入gdb模式;
    打断点;
    c;
    进行页面/命令行等操作,使进程运行至断点处;
    分析寄存器、内存信息。
    反复进行3~5步操作,直到分析明白。
    q;

    调试bin

    使用方法
    gdb <bin-file-path>
    适用场景
    某程序执行时不会转变成后台进程,运行过程中会出故障,需要跟踪执行过程
    例:配置恢复有启动项失败,需要从startup程序跟踪其执行流程
    某程序执行时会转变成后台进程,但进入自身循环之前(相当于进程启动阶段)会出故障,需要分析该过程执行流程
    例:eventd进程进入死循环前出故障,需要分析该执行过程
    set follow-fork-mode (parent|child)命令用法
    用于指定跟随子进程还是父进程(默认为parent)
    当前进程切换成后台进程时,执行set follow-fork-mode child
    调用生成子进程的函数(如system、popen、exec系列函数等)前,执行set follow-fork-mode parent
    操作流程:
    与后台进程类似,但在1~3之间执行set follow-fork-mode child,确定已切换到后台后set follow-fork-mode parent切回来。

    调试启动项:

    先找到要调用的启动项所在文件startup_N_*、所在行M
    对/usr/bin/startup挂gdb
    先打断点、执行set follow-fork-mode child
    运行:r -p N M

    KDB

    kdb提供丰富的命令实现运行控制、内存操纵、寄存器操纵、断点设置、堆栈跟踪等许多功能,总共有33条命令,下面分别进行介绍。

      运行控制类

      包括go、ss和ssb三个命令,提供对程序执行的控制。具体用法如下:

      go:继续程序执行

      格式:go

      该命令使内核继续执行,直到遇到一个断点才停止。如果没有设置断点,该命令将离开kdb调试器,系统回到正常运行状态。Caps和Scroll指示灯恢复到原来的状态。

      ss:单步执行程序

      格式:ss

      该命令仅仅执行下一条指令,执行完后停止。这在进行跟踪时是必不可少的。

      ssb:执行到分支或者函数调用时停止

      格式:ssb

      该命令与ss的区别是,ss只执行一条语句,而ssb执行一组语句,它使指令继续执行,在遇到一个分支语句,或者遇到一个函数调用语句时停止。

      断点类

      kdb提供强大的断点功能,包括设置断点、清除断点、激活断点、使断点失效,kdb也可以设置硬件断点。断点指令包括bp、bl、bpa、bph、bpha、bc、be和bd。

      bp:设置或者显示断点

      格式:bp [<vaddr>]

      该命令设置一个新的断点,其中vaddr是要设置的断点的地址。如果不带参数,运行bp将显示当前设置的所有断点。

      bl:设置或者显示断点

      格式:bl [<vaddr>]

      该命令的操作与bp命令相同。

      bpa:设置或者显示全局断点

      格式:bpa [<vaddr>]

      该命令设置一个全局断点,或者显示所有全局断点,用法同上。

      bph:设置硬件断点或者显示所有断点

      格式:bph [vaddr [datar|dataw|io [length]]]

      如果不带参数,则显示所有断点。如果带参数,那么设置断点。其中vaddr为要设置硬件断点的地址,datar表示对该内存区进行读操作,dataw表示写操作,io表示对该内存区进行io输入输出操作。length指明读写io操作的数据长度。

      bpha:设置硬件断点或者显示所有断点

      格式和用法同bph。

      bc:清除断点

      格式:bc <bpnum>

      清除标号为bpnum的断点。如果断点号为“*”,将清除所有断点。

      bd:使断点无效

      格式:bd <bpnum>

      使标号为bpnum的断点无效,如果标号为“*”,表示使所有断点无效。

      be:激活断点

      格式:be <bpnum>

      激活标号为bpnum的断点。如果标号为“*”,将激活所有无效的断点。

      内存操作类

      内存操作类命令包括对内存进行显示和修改的md、mdr、mds、mm四条命令。

      md:显示内存内容

      格式1:md [vaddr [line-count [output-radix]] ]

      显示地址为vaddr的内存的内容。line-count为要显示的内存的行数,output-radix指定以8进制、10进制或者16进制显示。如果省略line-count和output-radix,那么将以设置的环境变量MDCOUNT和RADIX方式显示。如果不带任何参数,md命令将接着上次md命令的后续地址显示内存内容

          格式2:mdWcn

      在缺省情况下,md以当前环境变量BYTESPERWORD的值读取数据,在读取硬件寄存器的时候,需要指定数据的宽度。这是可以使用mdWcn来进行读取,W是读取的宽度,单位是字节,cn为要读取的数目。

      mdr:显示原始内存的内容

      格式:mdr <vaddr> <count>

      从指定地址vaddr开始显示count长度的内存,它打印一连串的内存数据。这个命令是留给外部的调试器使用的,一般很少使用。

      mds:以符号的方式显示内存的内容

      格式:mds [vaddr [line-count [output-radix]]]

      从指定地址vaddr开始显示内存的内容,与md的区别是每行仅显示一个字,并且它试图将该地址与符号表进行匹配,如果找到,那么它将显示相应的符号名以及偏移值。如果不带参数,它将从上次mds的末尾开始显示。

      mm:修改内存内容

      格式1:mm <vaddr> <new content>

      将指定地址vaddr开始的数据修改为新的数据。修改的数据的长度为一个机器字。

      格式2:mmW <vaddr> <new content>

      意义同上,区别在于它改变W字节的内容。

      堆栈跟踪类

      该类指令实现对堆栈的跟踪,包括bt、btp和bta三条命令。

      bt:显示调用堆栈

      格式:bt [<stack-frame addr>]

      如果不指定参数,它根据当前寄存器的内容显示堆栈,提供当前活动线程的完整的堆栈跟踪。如果指定stack-frame addr参数,它将从该地址开始跟踪。

      btp:显示进程的堆栈

      格式:btp <pid>

      显示由pid指定的进程的堆栈。

      bta:显示所有进程的堆栈

      格式:bta

      寄存器类

      寄存器类命令包括对寄存器内容进行显示和修改的rd和rm指令,以及异常帧显示指令ef。

      rd:显示寄存器内容

      格式:rd [c|d|u]

      如果不带任何参数,rd显示所有进入kdb调试器时该点所设置的所有通用寄存器的值。如果带c参数,它将显示控制寄存器cr0、cr1、cr2、cr4 寄存器的内容。如果带d参数,它显示调试寄存器的内容。如果带u参数,它显示当进入kdb调试器时当前任务的所有寄存器。

      rm:修改寄存器的内容

      格式:rm <register-name> <register-content>

      该命令修改register-name指定的寄存器的内容为register-content。其中register-name 为%eax、%ebx、%ecx、%edx、%esi、%edi、%esp、%eip或%ebp。如果参数为%%,由rd u指定的寄存器将被修改。当前rm命令不允许修改控制寄存器,也不允许显示和修改Pentium和Pentium Pro系列的特定寄存器。

      ef:显示异常帧

      格式: ef <vaddr>

      显示vaddr地址处的异常帧。

      环境变量类

      这类指令对kdb调试器环境变量进行显示和设置。包括set和env命令。

      set:设置环境变量

      格式:set <env-var=value>

      将环境变量env-var的值设置为value。最多有33个环境变量,每个环境变量最大512字节。kdb的主要环境变量有:

      PROMPT:kdb调试器提示符,缺省为kdb。

      MOREPROMPT:在一屏显示不下的情况下,系统的提示符,缺省为more。

      RADIX:显示数据时所使用的数制,缺省为16进制。

      LINES:kdb调试器显示行数。缺省为24行。

      COLUMNS:kdb调试器显示的列数。缺省为80列。

      MDCOUNT:执行md指令时显示的内存行数,缺省为8行。

      BTARGS:执行bt跟踪时,指定任一函数在打印时所使用参数最大个数。

      SSCOUNT:该环境变量规定在执行ssb命令时,如果显示超过此数,执行将停止。缺省为20。

      IDMODE:反汇编时所使用的指令格式。缺省为x86。

      BYTESPERWORD:指定字的长度,缺省为4个字节。

      IDCOUNT:反汇编时,一次反汇编的指令长度,缺省为16条指令。

      env:显示环境变量

      格式:env

      显示所有环境变量的值。

    杂项

      id:指令反汇编

      格式:id <vaddr>

      从vaddr开始的地址反汇编指令。

      cpu:切换到另一个CPU

      格式:cpu <cpunum>

      这条命令仅仅在SMP结构下有用,它切换到由cpunum指定的CPU。

      ps:显示所有活动的进程

      格式:ps

      显示当前的活动的进程。包括pid、父进程pid、CPU号、当前状态,以及对应的线程。

      reboot:重新启动机器

      格式:reboot

      在某些情况下,内核无法返回到正常工作状态,这时可以利用reboot重新启动机器。注意在重启机器前,它不进行任何状态保存的工作。

      sections:列出内核中所有已知的段的信息

      格式:sections

      列出模块和内核的所有已知的段的信息。首先是模块信息,最后是内核信息。包括模块名和一个或者多个段的信息。段信息包括段名、段起始地址、段结束地址和段标识。本命令仅仅是为外部调试器而设立的。

      sr:激活SysRq代码,也就是调用MAGIC_SYSRQ函数

      格式:sr <sysrq key>

      将sysrq key字符作为参数传递给SysRq函数进行处理,就像你已经键入了SysRq键和该字符一样。如果要使用这个命令,需要在配置内核时,选择Magic SysRq Key。然后在新内核启动后,使用如下命令激活SysRq功能。

      #echo “1” > /proc/sys/kernel/sysrq

      这是一个功能强大的命令,它使得在kdb中可以使用操作系统提供的SysRq处理函数。

      lsmod:列出内核中加载的所有模块

      格式:lsmod

      显示所有模块的信息。包括模块名、模块大小、模块结构地址、引用计数,以及被哪个模块所引用。

      rmmod:卸载一个模块

      格式:rmmod <modname>

      将由modname指定的模块从内核中卸载。

      ll:对链表中的每个元素重复执行命令

      格式:ll <addr> <link-offset> <cmd>

      它对以地址addr开头的链表的头link-offset个元素,重复执行cmd命令。

      help和?:显示帮助信息。

      格式:help 或者?

      显示kdb的命令以及简单的用法。

    常用的一些内核模块操作

    查看加载模块用lsmod或者cat /proc/modules(这个还可以查看模块加载地址)

    卸载模块用rmmod,有时加上-f强行卸载

    加载模块用modprobe或者insmod,区别转载如下:

    "

    insmod 与 modprobe 都是载入 kernel module,不过一般差别于 modprobe 能够处理 module 载入的相依问题。

    比方你要载入 a module,但是 a module 要求系统先载入 b module 时,直接用 insmod 挂入通常都会出现错误讯息,不过 modprobe 倒是能够知道先载入 b module  后才载入 a module,如此相依性就会满足。

    不过 modprobe 并不是大神,不会厉害到知道 module 之间的相依性为何,该程式是读取 /lib/modules/2.6.xx/modules.dep 档案得知相依性的。而该档案是透过 depmod 程式所建立。

    modprobe:

    modprobe可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的相依关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块。

      -a或--all  载入全部的模块。 

      -c或--show-conf  显示所有模块的设置信息。 

      -d或--debug  使用排错模式。 

      -l或--list  显示可用的模块。 

      -r, --remove //若在命令指定模块,则删除指定模块,否则,指定"自动清除"模式  

      -t或--type  指定模块类型。 

      -v或--verbose  执行时显示详细的信息。 

      -V或--version  显示版本信息。 

      -help  显示帮助。

      -C, --config configfile //指定配置文件.默认使用/etc/modules.conf文件为配置文件

    modprobe -r ip_vs  # 删除ip_vs模块

    modprobe -l | grep ip_vs  # 查看ip_vs模块是否编译进内核

    lsmod -l | grep ip_vs  # 查看ip_vs模块是否载入进内核

                    kdb               gdb
    显示断点        bl                info break
    清除断点        bc xxx            del xxx
    打断点          bp                break(b)
    使能断点        be xxx            enable xxx
    去使能断点      bd xxx            disable xxx
    继续执行        go                continue(c)--reverse-continue
    单步执行                          next(n)--reverse-next
    显示寄存器      rd                info register
                    
    修改内存        mm sym/addr v     set symbol/addr=value
    显示内存        md                print(p)
    显示函数栈      bt                bt
                                      step--reverse-step
                                      finish--reverse-finish
                                       
  • 相关阅读:
    【CodeForces】835D Palindromic characteristics
    【BZOJ】2006: [NOI2010]超级钢琴
    【比赛】STSRM 09
    【比赛】洛谷夏令营NOIP模拟赛
    【BZOJ】4147: [AMPPZ2014]Euclidean Nim
    【BZOJ】3895: 取石子
    【胡策08】解题报告
    【codevs】3196 黄金宝藏
    【BZOJ】1443: [JSOI2009]游戏Game
    【BZOJ】3105: [cqoi2013]新Nim游戏
  • 原文地址:https://www.cnblogs.com/yipianchuyun/p/12307745.html
Copyright © 2011-2022 走看看