• GDB调试Breakpoints之Catchpoints


    关键词:breakpoint、catchpoint、catch、throw、assert、load/unload、fork/vfork/exec、syscall、signal等等。

    Breakpoints能让程序执行到后暂停流程,包括Breakpoints、Watchpoints、Catchpoints。

    Catchpoints是一种特殊的Breakpoints,当某种特殊的事件产生后停止程序执行。

    除了设置Catchpoints,其他对Catchpoints的管理方式类似于Breakpoints。

    测试环境:Ubuntu 16.04, GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1。

    1. 设置Catchpoints

    Breakpoints, Watchpoints, and Catchpoints》、《Setting catchpoints》、《Signals》、《Stopping and Starting Multi-thread Programs》。

     在gdb中通过help catch可以看出支持哪些种类的events:

    catch assert -- Catch failed Ada assertions
    catch catch -- Catch an exception
    catch exception -- Catch Ada exceptions
    catch exec -- Catch calls to exec
    catch fork -- Catch calls to fork
    catch load -- Catch loads of shared libraries
    catch rethrow -- Catch an exception
    catch signal -- Catch signals by their names and/or numbers
    catch syscall -- Catch system calls by their names and/or numbers
    catch throw -- Catch an exception
    catch unload -- Catch unloads of shared libraries
    catch vfork -- Catch calls to vfork

    catch/throw都是捕获exception,但是区别是:throw是The throwing of a C++ exception,catch是The catching of a C++ exception。

    通过info break可以查看设置的Catchpoints。

    有时候通过catch捕获到异常,此时已经进入异常处理函数中,有可能栈并不能指示确切发生了什么。

    这时候可以通过在__raise_exception()打断点,能看到更直接的现场。

    2. 构造Catchpoints

    2.1 catch和throw

    C++的异常有很多,下面以bad_function_call为例。

    对如下程序编译:g++ -o bad_function_call bad_function_call.cc -g -Wall --std=c++11。

    // bad_function_call example
    #include <iostream>     // std::cout
    #include <functional>   // std::function, std::plus, std::bad_function_call
    
    int main () {
      std::function<int(int,int)> foo = std::plus<int>();
      std::function<int(int,int)> bar;
    
      try {
        std::cout << foo(10,20) << '
    ';
        std::cout << bar(10,20) << '
    ';
      } catch (std::bad_function_call& e) {
        std::cout << "ERROR: Bad function call
    ";
      }
    
      return 0;
    }

    在gdb中设置catch catch和catch throw之后结果如下:

    (gdb) r
    Starting program: /home/al/temp/catch/bad_function_call 
    30
    
    Catchpoint 2 (exception thrown), 0x00007ffff7ae28bd in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    (gdb) bt full
    #0  0x00007ffff7ae28bd in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    No symbol table info available.
    #1  0x00007ffff7b0b8b2 in std::__throw_bad_function_call() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    No symbol table info available.
    #2  0x0000000000400f44 in std::function<int (int, int)>::operator()(int, int) const (this=0x7fffffffdb00, __args#0=10, __args#1=20) at /usr/include/c++/5/functional:2266
    No locals.
    #3  0x0000000000400c74 in main () at bad_function_call.cc:11
            foo = {<std::_Maybe_unary_or_binary_function<int, int, int>> = {<std::binary_function<int, int, int>> = {<No data fields>}, <No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, 
                _M_functor = {_M_unused = {_M_object = 0x470, _M_const_object = 0x470, _M_function_pointer = 0x470, 
                    _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x470, this adjustment 4295032831}, _M_pod_data = "p040000000000003773770000010000"}, 
                _M_manager = 0x40104b <std::_Function_base::_Base_manager<std::plus<int> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, 
              _M_invoker = 0x400ff3 <std::_Function_handler<int (int, int), std::plus<int> >::_M_invoke(std::_Any_data const&, int&&, int&&)>}
            bar = {<std::_Maybe_unary_or_binary_function<int, int, int>> = {<std::binary_function<int, int, int>> = {<No data fields>}, <No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, 
                _M_functor = {_M_unused = {_M_object = 0x2, _M_const_object = 0x2, _M_function_pointer = 0x2, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x2, this adjustment 4199101}, 
                  _M_pod_data = "020000000000000027522@00000000"}, _M_manager = 0x0}, _M_invoker = 0x0}
    (gdb) c
    Continuing.
    
    Catchpoint 1 (exception caught), 0x00007ffff7ae1711 in __cxa_begin_catch () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    (gdb) bt full
    #0  0x00007ffff7ae1711 in __cxa_begin_catch () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    No symbol table info available.
    #1  0x0000000000400cd0 in main () at bad_function_call.cc:12
            e = @0x400d5b: <incomplete type>
            foo = {<std::_Maybe_unary_or_binary_function<int, int, int>> = {<std::binary_function<int, int, int>> = {<No data fields>}, <No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, 
                _M_functor = {_M_unused = {_M_object = 0x470, _M_const_object = 0x470, _M_function_pointer = 0x470, 
                    _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x470, this adjustment 4295032831}, _M_pod_data = "p040000000000003773770000010000"}, 
                _M_manager = 0x40104b <std::_Function_base::_Base_manager<std::plus<int> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, 
              _M_invoker = 0x400ff3 <std::_Function_handler<int (int, int), std::plus<int> >::_M_invoke(std::_Any_data const&, int&&, int&&)>}
            bar = {<std::_Maybe_unary_or_binary_function<int, int, int>> = {<std::binary_function<int, int, int>> = {<No data fields>}, <No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, 
                _M_functor = {_M_unused = {_M_object = 0x2, _M_const_object = 0x2, _M_function_pointer = 0x2, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x2, this adjustment 4199101}, 
                  _M_pod_data = "020000000000000027522@00000000"}, _M_manager = 0x0}, _M_invoker = 0x0}

     可以看出catch throw比catch catch更接近异常现场。

    std::exception》还包含了很多C++异常,通过catch throw/catch catch可以看到详细的异常栈。

    2.2 assert异常

    assert在当前环境下不支持,但是通过catch signal SIGABRT或者不使用都可以看到完整的异常栈信息。

    #include <assert.h>
    
    void g(int i) {
        assert(0);
    }
    
    void f(int i) {
        g(i);
    }
    
    int main(void) {
        f(1);
    }

    结果如下:

    gcc -o assert assert.c -g 
    gdb ./assert 
    
    (gdb) r Starting program: /home/al/temp/catch/assert assert: assert.c:4: g: Assertion `0' failed. Program received signal SIGABRT, Aborted. 0x00007ffff7a42438 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory. (gdb) bt full #0 0x00007ffff7a42438 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 resultvar = 0 pid = 9886 selftid = 9886 #1 0x00007ffff7a4403a in __GI_abort () at abort.c:89 save_stage = 2 act = {__sigaction_handler = {sa_handler = 0x4, sa_sigaction = 0x4}, sa_mask = {__val = {0, 0, 140737488345552, 0, 140737354096640, 4195853, 4, 4195855, 0, 0, 140737348441484, 140737349538576, 140737349552224, 896, 140737349538576, 4195853}}, sa_flags = -134258688, sa_restorer = 0x40060d} sigs = {__val = {32, 0 <repeats 15 times>}} #2 0x00007ffff7a3abe7 in __assert_fail_base (fmt=<optimized out>, assertion=assertion@entry=0x40060d "0", file=file@entry=0x400604 "assert.c", line=line@entry=4, function=function@entry=0x40060f <__PRETTY_FUNCTION__.1847> "g") at assert.c:92 str = 0x602080 "" total = 4096 #3 0x00007ffff7a3ac92 in __GI___assert_fail (assertion=0x40060d "0", file=0x400604 "assert.c", line=4, function=0x40060f <__PRETTY_FUNCTION__.1847> "g") at assert.c:101 No locals. #4 0x000000000040054a in g (i=1) at assert.c:4 __PRETTY_FUNCTION__ = "g" #5 0x000000000040055f in f (i=1) at assert.c:8 No locals. #6 0x0000000000400570 in main () at assert.c:12 No locals.

    2.3 load和unload

    #include <stdio.h>
    
    void f(int i) {
        printf("%s i=%d
    ", __func__, i);
    }
    
    int main(void) {
        f(1);
    }

    结果如下:

    (gdb) i b
    Num     Type           Disp Enb Address            What
    1       catchpoint     keep y                      load of library
    2       catchpoint     keep y                      unload of library
    (gdb) r
    Starting program: /home/al/temp/catch/load_unload 
    
    Catchpoint 1
      Inferior loaded /lib/x86_64-linux-gnu/libc.so.6
    dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:2206
    2206    rtld.c: No such file or directory.
    (gdb) c
    Continuing.
    f i=1
    [Inferior 1 (process 10848) exited normally]

    可以看出断住了load of libc.so,但是没有抓住unload of libc.so。

    2.4 signal

    对signal设置可以catch signal或者catch signal <sig num>,修改gdb对具体signal触发后的行为可以通过handle进行,具体参考《Signals》。

    支持的signal可以通过catch signal,然后Tab查看:

    32                   SIG107               SIG123               SIG43                SIG59                SIG75                SIG91                SIGEMT               SIGPROF              SIGUSR2
    33                   SIG108               SIG124               SIG44                SIG60                SIG76                SIG92                SIGFPE               SIGPWR               SIGVTALRM
    34                   SIG109               SIG125               SIG45                SIG61                SIG77                SIG93                SIGGRANT             SIGQUIT              SIGWAITING
    EXC_ARITHMETIC       SIG110               SIG126               SIG46                SIG62                SIG78                SIG94                SIGHUP               SIGRETRACT           SIGWINCH
    EXC_BAD_ACCESS       SIG111               SIG127               SIG47                SIG63                SIG79                SIG95                SIGILL               SIGSAK               SIGWIND
    EXC_BAD_INSTRUCTION  SIG112               SIG32                SIG48                SIG64                SIG80                SIG96                SIGINFO              SIGSEGV              SIGXCPU
    EXC_BREAKPOINT       SIG113               SIG33                SIG49                SIG65                SIG81                SIG97                SIGINT               SIGSOUND             SIGXFSZ
    EXC_EMULATION        SIG114               SIG34                SIG50                SIG66                SIG82                SIG98                SIGIO                SIGSTOP              
    EXC_SOFTWARE         SIG115               SIG35                SIG51                SIG67                SIG83                SIG99                SIGKILL              SIGSYS               
    SIG100               SIG116               SIG36                SIG52                SIG68                SIG84                SIGABRT              SIGLOST              SIGTERM              
    SIG101               SIG117               SIG37                SIG53                SIG69                SIG85                SIGALRM              SIGLWP               SIGTRAP              
    SIG102               SIG118               SIG38                SIG54                SIG70                SIG86                SIGBUS               SIGMSG               SIGTSTP              
    SIG103               SIG119               SIG39                SIG55                SIG71                SIG87                SIGCANCEL            SIGPHONE             SIGTTIN              
    SIG104               SIG120               SIG40                SIG56                SIG72                SIG88                SIGCHLD              SIGPIPE              SIGTTOU              
    SIG105               SIG121               SIG41                SIG57                SIG73                SIG89                SIGCONT              SIGPOLL              SIGURG               
    SIG106               SIG122               SIG42                SIG58                SIG74                SIG90                SIGDANGER            SIGPRIO              SIGUSR1              

    当前gdb对signal处理方式可以通过info signals查看:

    SIGHUP        Yes    Yes    Yes        Hangup
    SIGINT        Yes    Yes    No        Interrupt
    SIGQUIT       Yes    Yes    Yes        Quit
    SIGILL        Yes    Yes    Yes        Illegal instruction
    SIGTRAP       Yes    Yes    No        Trace/breakpoint trap
    SIGABRT       Yes    Yes    Yes        Aborted
    SIGEMT        Yes    Yes    Yes        Emulation trap
    SIGFPE        Yes    Yes    Yes        Arithmetic exception
    SIGKILL       Yes    Yes    Yes        Killed
    SIGBUS        Yes    Yes    Yes        Bus error
    SIGSEGV       Yes    Yes    Yes        Segmentation fault
    SIGSYS        Yes    Yes    Yes        Bad system call
    SIGPIPE       Yes    Yes    Yes        Broken pipe
    SIGALRM       No    No    Yes        Alarm clock
    SIGTERM       Yes    Yes    Yes        Terminated
    SIGURG        No    No    Yes        Urgent I/O condition
    SIGSTOP       Yes    Yes    Yes        Stopped (signal)
    SIGTSTP       Yes    Yes    Yes        Stopped (user)
    SIGCONT       Yes    Yes    Yes        Continued
    SIGCHLD       No    No    Yes        Child status changed
    SIGTTIN       Yes    Yes    Yes        Stopped (tty input)
    SIGTTOU       Yes    Yes    Yes        Stopped (tty output)
    SIGIO         No    No    Yes        I/O possible
    SIGXCPU       Yes    Yes    Yes        CPU time limit exceeded
    SIGXFSZ       Yes    Yes    Yes        File size limit exceeded
    SIGVTALRM     No    No    Yes        Virtual timer expired
    SIGPROF       No    No    Yes        Profiling timer expired
    SIGWINCH      No    No    Yes        Window size changed
    SIGLOST       Yes    Yes    Yes        Resource lost
    SIGUSR1       Yes    Yes    Yes        User defined signal 1
    SIGUSR2       Yes    Yes    Yes        User defined signal 2
    SIGPWR        Yes    Yes    Yes        Power fail/restart
    SIGPOLL       No    No    Yes        Pollable event occurred
    SIGWIND       Yes    Yes    Yes        SIGWIND
    SIGPHONE      Yes    Yes    Yes        SIGPHONE
    SIGWAITING    No    No    Yes        Process's LWPs are blocked
    SIGLWP        No    No    Yes        Signal LWP
    SIGDANGER     Yes    Yes    Yes        Swap space dangerously low
    SIGGRANT      Yes    Yes    Yes        Monitor mode granted
    SIGRETRACT    Yes    Yes    Yes        Need to relinquish monitor mode
    SIGMSG        Yes    Yes    Yes        Monitor mode data available
    SIGSOUND      Yes    Yes    Yes        Sound completed
    SIGSAK        Yes    Yes    Yes        Secure attention
    SIGPRIO       No    No    Yes        SIGPRIO
    SIG33         Yes    Yes    Yes        Real-time event 33
    SIG34         Yes    Yes    Yes        Real-time event 34
    ...

    2.5 exec、fork、vfork、syscall

    exec、fork、vfork并不等同于syscal的同名调用,是由c库函数的实现决定的。

    # include <stdio.h>
    # include<sys/types.h>
    # include <unistd.h>
    int main()
    {
        pid_t pid=fork();
    
        if(pid==0)
            printf("Child
    ");
        else
            printf("Father
    ");
    }

    通过catch fork查看:

    (gdb) catch fork 
    Catchpoint 1 (fork)
    (gdb) r
    Starting program: /home/al/temp/catch/fork 
    
    Catchpoint 1 (forked process 12992), 0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
    warning: Source file is more recent than executable.
    (gdb) bt full
    #0  0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
            resultvar = 18446744073709551578
            pid = <optimized out>
            allp = 0x0
            multiple_threads = false
            runp = <optimized out>
            ppid = 0
            parentpid = 0
            __PRETTY_FUNCTION__ = "__libc_fork"
    #1  0x0000000000400573 in main () at fork.c:6
            pid = 0

    通过catch syscall查看:

    (gdb) catch syscall fork
    Catchpoint 1 (syscall 'fork' [57])
    (gdb) catch syscall clone
    Catchpoint 2 (syscall 'clone' [56])
    (gdb) r
    Starting program: /home/al/temp/catch/fork 
    
    Catchpoint 2 (call to syscall clone), 0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
    warning: Source file is more recent than executable.
    (gdb) bt full
    #0  0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
            resultvar = 18446744073709551578
            pid = <optimized out>
            allp = 0x0
            multiple_threads = false
            runp = <optimized out>
            ppid = 0
            parentpid = 0
            __PRETTY_FUNCTION__ = "__libc_fork"
    #1  0x0000000000400573 in main () at fork.c:6
            pid = 0
    (gdb) c
    Continuing.
    Child
    
    Catchpoint 2 (returned from syscall clone), 0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
    (gdb) bt full
    #0  0x00007ffff7ad949a in __libc_fork () at ../sysdeps/nptl/fork.c:145
            resultvar = 13452
            pid = <optimized out>
            allp = 0x0
            multiple_threads = false
            runp = <optimized out>
            ppid = 0
            parentpid = 0
            __PRETTY_FUNCTION__ = "__libc_fork"
    #1  0x0000000000400573 in main () at fork.c:6
            pid = 0

    可以看出C函数fork()对应的系统调用是clone()。

    在gdb中catch syscall对所有syscall打断点,还可以单独对某个syscall打断点。可以在catch syscall基础上Tab显示当前所支持的syscall:

    _llseek                 dup2                    getgroups               kill                    nanosleep               readdir                 sendfile                sigprocmask             umask
    _newselect              execve                  getitimer               lchown                  nfsservctl              readlink                setdomainname           sigreturn               umount
    _sysctl                 exit                    getpgid                 lchown32                nice                    readv                   setfsgid                sigsuspend              umount2
    access                  fchdir                  getpgrp                 link                    oldfstat                reboot                  setfsuid                socketcall              uname
    acct                    fchmod                  getpid                  lock                    oldlstat                rename                  setgid                  ssetmask                unlink
    adjtimex                fchown                  getpmsg                 lseek                   oldolduname             restart_syscall         setgroups               stat                    uselib
    afs_syscall             fcntl                   getppid                 lstat                   oldstat                 rmdir                   sethostname             stat64                  ustat
    alarm                   fdatasync               getpriority             lstat64                 olduname                rt_sigaction            setitimer               statfs                  utime
    bdflush                 flock                   getresgid               mkdir                   open                    rt_sigpending           setpgid                 stime                   vfork
    break                   fork                    getresuid               mknod                   pause                   rt_sigprocmask          setpriority             stty                    vhangup
    brk                     fstat                   getrlimit               mlock                   personality             rt_sigqueueinfo         setregid                swapoff                 vm86
    capget                  fstat64                 getrusage               mlockall                pipe                    rt_sigreturn            setresgid               swapon                  vm86old
    capset                  fstatfs                 getsid                  mmap                    poll                    rt_sigsuspend           setresuid               symlink                 wait4
    chdir                   fsync                   gettimeofday            mmap2                   prctl                   rt_sigtimedwait         setreuid                sync                    waitpid
    chmod                   ftime                   getuid                  modify_ldt              pread64                 sched_get_priority_max  setrlimit               sysfs                   write
    chown                   ftruncate               getuid32                mount                   prof                    sched_get_priority_min  setsid                  sysinfo                 writev
    chroot                  ftruncate64             gtty                    mprotect                profil                  sched_getparam          settimeofday            syslog                  
    clone                   get_kernel_syms         idle                    mpx                     ptrace                  sched_getscheduler      setuid                  time                    
    close                   getcwd                  init_module             mremap                  putpmsg                 sched_rr_get_interval   sgetmask                times                   
    creat                   getdents                ioctl                   msync                   pwrite64                sched_setparam          sigaction               truncate                
    create_module           getegid                 ioperm                  munlock                 query_module            sched_setscheduler      sigaltstack             truncate64              
    delete_module           geteuid                 iopl                    munlockall              quotactl                sched_yield             signal                  ugetrlimit              
    dup                     getgid                  ipc                     munmap                  read                    select                  sigpending              ulimit       
  • 相关阅读:
    DDD实战进阶第一波(三):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架二)
    DDD实战进阶第一波(二):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架一)
    01-JavaScript之变量
    18-TypeScript模板方法模式
    17-TypeScript代理模式
    16-TypeScript装饰器模式
    15-TypeScript策略模式
    真的可以「 人人都是产品经理 」吗
    如何从程序员到架构师?
    除代码之外,程序员还有哪些能力也非常的关键?
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/13815087.html
Copyright © 2020-2023  润新知