目录html
@linux
online doc of Compiling for Debugging正则表达式
gcc/g++是在编译时加入-g(注意 -g 参数要在 -o 以前,不然可能没有调试信息)
-g分4个等级:shell
1) gdb ${你的程序} 进入gdb后,输入run(简写r) ${arg1} ${arg2} … ${argN}
2) gdb --args ${你的程序} ${arg1} ${arg2} … ${argN} 进入gdb后,运行run。
3) gdb进入gdb后,输入file ${你的程序}。而后使用set args ${arg1} ${arg2} … ${argN} 设定好你的程序参数,再运行run。数组
info 简写为 i info locals :查看局部变量,能够简写: i lo. info args :查看函数入参 :i ar info break : 查看断点: i b info registers :查看寄存器的状况 info threads :当前已知线程
allows overriding the output format used by the command
/o - octal
/x - hexadecimal
/d - decimal
/u - unsigned decimal
/t - binary
/f - floating point
/a - address
/c - char
/s - string安全
p arr[0]@len
bash
set max-value-size unlimited
多线程
ptype/pty 后跟结构体名,或结构体变量
set print pretty
backtrace :显示栈信息(调用链)。简写为bt。 frame x 切换到第x帧。其中x会在bt命令中显示,从0开始。0表示栈顶。简写为f。 up/down x 往栈顶/栈底移动x帧。当不输入x时,默认为1。
(gdb) layout src:显示源码窗口: la sr (gdb) layout asm:显示汇编窗口 (gdb) layout regs:显示寄存器窗口 (gdb) layout split:显示源码和汇编窗口 (gdb) layout next:显示下一个layout窗口 (gdb) layout prev:显示上一个layout窗口 Ctrl + L:刷新窗口 Ctrl + x,再按1:单窗口模式 Ctrl + x,再按2:双窗口模式 Ctrl + x,再按a:退出layout,回到执行layout以前的调试窗口。
1, set substitute-path from_path to_path
,替换源码文件路径。当编译机与运行程序的机器代码路径不一样时,须要使用该指令替换代码路径,不然你没法在gdb中看到源码。
2, 为静态库设置源码路径: dir pathapp
简写为: comm 先查看现有的断点,而后 comm 后跟上断点序号,写入这个断点要作的事,end结束。
step
单步调试,步入当前函数。可简写为snext
单步调试,步过当前函数。可简写为nuntil
执行到当前循环完成。可简写为ufinish
执行到当前函数返回continue
继续运行程序到下一个断点,或结束。可简写为creturn
: 强制函数返回。能够指定返回值jump
使当前执行的程序跳转到某一行,或者跳转到某个地址。因为只会使程序跳转而不会改变栈值,所以若跳出函数到另外的地方 会致使return出错。另外,熟悉汇编的人都知道,程序运行时,有一个寄存器用于保存当前代码所在的内存地址。因此,jump命令也就是改变了这个寄存器中的值。因而,你可使用“set $pc”来更改跳转执行的地址。如: set $pc = 0x485call
调用函数。command n >printf "x is %d\n",x >c >end
set var x=10
改变当前变量x的值。也能够这样用:set {int}0x83040 = 10把内存地址0x83040的值强制转换为int并赋值为10是指当执行到程序某一步时,程序交出控制权进入调试器。值得注意的是,break会有一些变体:tbreak,hbreak,thbreak与rbreak。tbreak与break功能相同,只是所设置的断点在触发一次后自动删除。hbreak是一个硬件断点。thbreak则既是一个临时的硬件断点。注意硬件断点须要硬件支持,某些硬件可能不支持这种类型的断点。rbreak稍微特殊一些,它会在匹配正则表达式的所有位置加上断点,后面会有详细讲解。除去rbreak,其余break家族的使用方法以下:函数
查看断点: info break
, 简写i b
break 能够缩写为 b
1) break xxx.cpp:y
。在文件 xxx.cpp 的第 y 行加入断点。
不指定文件时候,则会以当前执行的文件做为断点文件。
若程序未执行,则以包含main函数的源代码文件做为断点文件。
若x.cpp和y都不指定,则以当前debugger的点做为断点处。
2) break x.cpp:func
。在x.cpp的func函数入口处加入断点。x.cpp能够不提供直接使用break func。注意因为重载(overload)的存在,所以gdb可能会询问你但愿在哪一个函数加上断点。
3) break 0xN
。在地址N处加入断点。N必须为一个有效的代码段(code segment)地址。
4) 删除多个断点 del 1-10
1, save break file.bp
保存断点到 file.bp
2, source file.bp
加载断点(so file.bp)
监视点是监视内存中某个地址,当该地址的数据被改变(或者被读取)时,程序交出控制权进入调试器。注意监视点分为软件模式和硬件模式:GDB 使用软件监视点的方式是在单步执行你的程序的同时测试变量的值,因此执行程序的速度会变慢。同时,软件监视点仅在当前线程有效。幸运的是,32 位的 Intel x86 处理器提供了 4 个特殊的调试寄存器用来方便调试程序,GDB 可使用这些寄存器创建硬件监视点。GDB 老是会优先使用硬件监视点,由于这样不会减慢程序的执行速度。然而,可用的(enable的)硬件监视点的个数是有限的。若是你设置了过多的硬件监视点,当程序从中断的状态变为执行的状态(例如continue,until或者finish)时,GDB 可能没法把它们所有激活。另外,活动的硬件监视点的数量只有在试图继续执行程序时才能知道,也就是说,即便你设置了过多的硬件监视点,gdb在你运行程序以前也不会警告你。
设置监视点的命令有3个,watch(写监视),rwatch(读监视)以及awatch(读写监视)。他们的使用方法同样,皆为如下几种:
1) (r/a)watch var
。var是一个变量名。当x的值改变/被读取时,程序交出控制权进入调试器。
2) (r/a)watch *0xdeadbeef
。0xdeadbeef为一个有效地址。当该地址的内容变化/被读取时,程序交出控制权进入调试器。
3) (r/a)watch *(int *)0xdeadbeef
。0xdeadbeef为一个有效地址。当该地址的中的int指针指向的内容变化/被读取时,程序交出控制权进入调试器。
4) (r/a)watch -l *(int *)0xdeadbeef
。0xdeadbeef为一个有效地址。当该地址的中的int指针指向的内容变化/被读取,或者该地址的内容变化/被读取时,程序交出控制权进入调试器。
注意3)和4)的区别在于,当加入-l选项后,会同时监视表达式自己以及表达式指向的内容。
注意:watch 地址的时候,要加解引用符号 *
If you watch for a change in a numerically entered address you need to dereference it, as the address itself is just a constant number which will never change. GDB refuses to create a watchpoint that watches a never-changing value
(gdb) watch 0x600850
Cannot watch constant value 0x600850.
(gdb) watch (int ) 0x600850
Watchpoint 1: (int ) 6293584
跟踪点与上面三个断点不一样之处在于,它只是跟踪记录信息而不会中断程序的运行。当你的程序是realtime程序,或者与其余的程序有交互时,你可能会但愿使用跟踪点达到监视程序而又不破坏程序自身行为的目的。与断点相同的是,跟踪点会保存下在跟踪点时的一些内存信息供使用者查阅,例如数组或者对象。
另外,tracepoints能够经过save命令保存,以方便使用者下次再次进入程序调试时不须要重设这些跟踪点。
gdb能够经过fork当前进程的映像,而且稍后又能够返回到这个状态。这个称之为checkpoint。
每一个检查点是进程的一个拷贝。这样当一个bug很难重现,而又担忧调试过头了又要从头开始重现时,能够在估计要重现这个bug以前,作一个checkpoint,这样即便debug过头了,也能够从这个checkpoint开始,而不用重启整个程序而且期待它重现这个bug。
用法:
在当前位置设置检查点
(gdb) checkpoint
查看检查点
(gdb) info checkpoints
回到设置的检查点,
(gdb) restart checkpoint-id
core dump又叫核心转储, 当程序运行过程当中发生异常, 程序异常退出时, 由操做系统把程序当前的内存情况存储在一个core文件中, 叫core dump. (linux中若是内存越界会收到SIGSEGV信号,而后就会core dump)
1.内存访问越界
a) 因为使用错误的下标,致使数组访问越界
b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,可是字符串没有正常的使用结束符
c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操做函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。
2 多线程程序使用了线程不安全的函数。
3 多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,不然很容易形成core dump
4 非法指针
a) 使用空指针
b) 随意使用指针转换。一个指向一段内存的指针,除非肯定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,不然不要将它转换为这种结构或类型 的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是由于若是这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它 时就很容易由于bus error而core dump.
5 堆栈溢出.不要使用大的局部变量(由于局部变量都分配在栈上),这样容易形成堆栈溢出,破坏系统的栈和堆结构,致使出现莫名其妙的错误。
首先经过ulimit命 令查看一下系统是否配置支持了dump core的功能。经过ulimit -c或ulimit -a,能够查看core file大小的配置状况,若是为0,则表示系统关闭了dump core。能够经过ulimit -c unlimited来打开。若发生了段错误,但没有core dump,是因为系统禁止core文件的生成。
解决方法:
$ulimit -c unlimited (只对当前shell进程有效)
或在~/.bashrc 的最后加入: ulimit -c unlimited (一劳永逸)
gdb [exec file] [core file]
如: gdb ./test test.core
能够产生core的c语言语句: *(char *)0 = 0;
修改文件命令:
把core文件与执行程序相同路径
echo "core-%e-%p" > /proc/sys/kernel/core_pattern
或者:
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t kernel.core_pattern = /corefile/core-%e-%p-%t
能够将core文件统一辈子成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
如下是参数列表:
%e - insert coredumping executable name into filename 添加致使产生core的命令名 %p - insert pid into filename 添加pid(进程id) %u - insert current uid into filename 添加当前uid(用户id) %g - insert current gid into filename 添加当前gid(用户组id) %s - insert signal that caused the coredump into the filename 添加致使产生core的信号 %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间 %h - insert hostname where the coredump happened into filename 添加主机名
在内核中还有一个与coredump相关的设置,就是/proc/sys/kernel/core_uses_pid。若是这个文件的内容被配置成1,
那么即便core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID。
emacs :https://www.cnblogs.com/gaowengang/p/5799292.html
https://www.cnblogs.com/xsln/p/gdb_instructions1.html
汇编:https://www.cnblogs.com/zhangyachen/p/9227037.html