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) << '\n'; std::cout << bar(10,20) << '\n'; } catch (std::bad_function_call& e) { std::cout << "ERROR: Bad function call\n"; } 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 = "p\004\000\000\000\000\000\000\377\377\000\000\001\000\000"}, _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 = "\002\000\000\000\000\000\000\000\275\022@\000\000\000\000"}, _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 = "p\004\000\000\000\000\000\000\377\377\000\000\001\000\000"}, _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 = "\002\000\000\000\000\000\000\000\275\022@\000\000\000\000"}, _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\n", __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\n"); else printf("Father\n"); }
通过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