基本概念(4)——调试器
一个非常好的参考资料《100个gdb技巧》
https://wizardforcel.gitbooks.io/100-gdb-tips/content/
gdb与lldb不同点对比
https://lldb.llvm.org/use/map.html
运行lldb
lldb main.out
或者直接lldb,进入后 使用file main.out指定要调试的文件
注意:运行之前要clang++ -g
,一定要有-g
参数才能用lldb调试。
另一种执行:lldb main.out example1
假设可执行程序名为 main.out,参数为 example1
q\quit
离开lldb
tab键
支持tab键联想,输入br然后按下tab就会联想出breakpoint
然后再次按下tab会联想出子命令:
(lldb) breakpoint
Available completions:
clear
command
delete
disable
enable
list
modify
name
read
set
write
(lldb) breakpoint
help
查看帮助,支持help b查看b命令的帮助:
_regexp-break <filename>:<linenum>
main.c:12 // Break at line 12 of main.c
_regexp-break <linenum>
12 // Break at line 12 of current file
_regexp-break 0x<address>
0x1234000 // Break at address 0x1234000
_regexp-break <name>
main // Break in 'main' after the prologue
_regexp-break &<name>
&main // Break at first instruction in 'main'
_regexp-break <module>`<name>
libc.so`malloc // Break in 'malloc' from 'libc.so'
_regexp-break /<source-regex>/
/break here/ // Break on source lines in current file
// containing text 'break here'.
'b' is an abbreviation for '_regexp-break'
help breakpoint list //查看breakpoint list的帮助
执行程序
run/r
给定程序参数
run <args>
的方式gdb或者lldb都支持,或者用简写:r <args>
,但是这种方式每次run都需要给定被调试程序的参数
想要每次run的时候不用这么麻烦,可以在启动gdb/lldb的时候给定参数,种类gdb和lldb有些区别
gdb:
gdb --args ***.exe <args>
lldb:
lldb -- ***.exe <args>
也可以启动gdb/lldb之后给定参数:
gdb:
set args <args>
lldb:
settings set -- target.run-args "--framework" "100" "--comm" "tcp" "-p" "35900" "-t" "0" "--run"
breakporint 断点
help breakpoint 查看命令
_regexp-break <filename>:<linenum>
main.c:12 // Break at line 12 of main.c main.c文件的12行
_regexp-break <linenum>
12 // Break at line 12 of current file当前文件12行
_regexp-break 0x<address>
0x1234000 // Break at address 0x1234000 地址断点
_regexp-break <name>
main // Break in 'main' after the prologue
_regexp-break &<name>
&main // Break at first instruction in 'main'
_regexp-break <module>`<name>
libc.so`malloc // Break in 'malloc' from 'libc.so'
_regexp-break /<source-regex>/
/break here/ // Break on source lines in current file
// containing text 'break here'.
'b' is an abbreviation for '_regexp-break'
删除断点
l/list
查看当前文件代码
看某个函数的代码:直接输入某个函数的名字即可
(lldb) help list
List relevant source code using one of several shorthand formats. Expects 'raw' input (see 'help raw-input'.)
Syntax:
_regexp-list <file>:<line> // List around specific file/line
_regexp-list <line> // List current file around specified line
_regexp-list <function-name> // List specified function
_regexp-list 0x<address> // List around specified address
_regexp-list -[<count>] // List previous <count> lines
_regexp-list // List subsequent lines
如果程序编译的时候是由很多文件组成的,那么就可以使用list 文件名
看其他文件的代码, 以后再执行 list 3 的时候,看的就是你前面设置的文件名的第三行。
c 、n、 s、 finsh
c/continue,继续执行
n/next,单步调试
s/step,进入一个函数
finsh,执行完当前函数
查看变量、跳帧查看变量
使用po或p,po一般用来输出指针指向的那个对象,p一般用来输出基础变量。普通数组两者都可用
(lldb) po result_array
查看所有帧(bt)
(lldb) bt
函数调用栈由连续的栈帧组成。每个栈帧记录一个函数调用的信息,这些信息包括函数参数,函数变量,函数运行地址。
当程序启动后,栈中只有一个帧,这个帧就是main函数的帧。我们把这个帧叫做初始化帧或者叫做最外层帧。每当一
个函数被调用,一个新帧将被建立,每当一个函数返回时,函数帧将被剔除。如果函数是个递归函数,栈中将有很多帧是
记录同一个函数的。但前执行的函数的帧被称作最深帧,这个帧是现存栈中最近被创建的帧。
在程序内部,函数栈帧用函数的地址来标记。一个帧由一定字节的内存组成,每个字节都有自己的地址 。每种类型的计
算机有个约定,用一个特殊字节的地址存放函数帧的地址。通常函数帧的地址存放在一个称作帧指针的寄存器中--$fp.
gdb 为所有存活的栈帧分配一个数字编号,最深帧的编号是0,被它调用的内个帧的编号就是1。这些编号子程序中是不
存在的,只不过时调试的时候被gdb用的。
关于函数帧的两个指令:
frame args
移动到args指定的栈帧中去,并打印选中的栈的信息。args可以时帧编号或者时帧的地址。如果没有args,则打印当前帧的信息。
select-frame args
移动到指定的帧中去,不打印信息。
跳帧(frame select)
(lldb) frame select 1
查看当前帧中所有变量的值(frame variable)
(lldb) frame variable
查看内存
格式x /nfu
x 是检查的意思(examine)
n 表示要显示的内存单元的个数,比如:20
f 表示显示方式, 可取如下值:
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。
u表示一个地址单元的长度:
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节
gdb插件
https://github.com/cyrus-and/gdb-dashboard
只在linux下正常使用,mingw等都没过,下图是wsl2