GDB

Linux中包含有一个很有用的调试工具 gdb(GNU Debuger),它可以用来调试C和C++程序,功能不亚于Windows下的许多图形界面的调试工具。

和所有常用的调试工具一样,gdb提供了以下功能:

  • 监视程序中变量的值
  • 在程序中设置断点
  • 程序的单步执行

在使用gdb前,必须先载入可执行文件,因为要进行调试,文件中就必须包含调试信息,所以在用gcc或cc编译时就需要用-g参数来打开程序的调试选项。

调试开始时,必须先载入要进行调试的程序,可以用以下两种方式:

  • 在启动gdb后执行以下命令
    • file 可执行文件路径
  • 在gdb启动时就载入程序
    • gdb 可执行文件路径

载入程序后,接下来就是要进行断点的设置,要监视的变量的添加等工作,下面对在这个过程中常会用到的命令逐一进行介绍:

list

显示程序中的代码,常用使用格式有:

  • list 输出从上次调用list命令开始往后的10行程序代码。
  • list - 输出从上次调用list命令开始往前的10行程序代码。
  • list n 输出第n行附近的10行程序代码。
  • list function 输出函数function前后的10行程序代码。

### forward/search

从当前行向后查找匹配某个字符串的程序行。使用格式:

  • forward/search 字符串 查找到的行号将保存在$_变量中,可以用print $_命令来查看。

### reverse-search

和forward/search相反,向前查找字符串。使用格式同上。

break

在程序中设置断点,当程序运行到指定行上时,会暂停执行。使用格式:

  • break 要设置断点的行号

### tbreak

设置临时断点,在设置之后只起作用一次。使用格式:

  • tbreak 要设置临时断点的行号

clear :和break相反,clear用于清除断点。使用格式:
clear 要清除的断点所在的行号
run :启动程序,在run后面带上参数可以传递给正在调试的程序。
awatch :用来增加一个观察点(add watch),使用格式:
awatch 变量或表达式
当表达式的值发生改变或表达式的值被读取时,程序就会停止运行。
watch :与awatch类似用来设置观察点,但程序只有当表达式的值发生改变时才会停止运行。使用格 式:
watch 变量或表达式
需要注意的是,awatch和watch都必须在程序运行的过程中设置观察点,即可运行run之后才能设置。
commands :设置在遇到断点后执行特定的指令。使用格式有:
commands
设置遇到最后一个遇到的断点时要执行的命令
commands n
设置遇到断点号n时要执行的命令
注意,commands后面跟的是断点号,而不是断点所在的行号。
在输入命令后,就可以输入遇到断点后要执行的命令,每行一条命令,在输入最后一条命令后输入end就可以结束输入。
delete :清除断点或自动显示的表达式。使用格式:
delete 断点号
disable :让指定断点失效。使用格式:
disable 断点号列表
断点号之间用空格间隔开。
enable :和disable相反,恢复失效的断点。使用格式:
enable 断点编号列表
ignore :忽略断点。使用格式:
ignore 断点号 忽略次数
condition :设置断点在一定条件下才能生效。使用格式:
condition 断点号 条件表达式
cont/continue :使程序在暂停在断点之后继续运行。使用格式:
cont
跳过当前断点继续运行。
cont n
跳过n次断点,继续运行。
当n为1时,cont 1即为cont。
jump :让程序跳到指定行开始调试。使用格式:
jump 行号
next :继续执行语句,但是跳过子程序的调用。使用格式:
next
执行一条语句
next n
执行n条语句
nexti :单步执行语句,但和next不同的是,它会跟踪到子程序的内部,但不打印出子程序内部的语句。使用格式同上。
step :与next类似,但是它会跟踪到子程序的内部,而且会显示子程序内部的执行情况。使用格式同上。
stepi :与step类似,但是比step更详细,是nexti和step的结合。使用格式同上。
whatis :显示某个变量或表达式的数据类型。使用格式:
whatis 变量或表达式
ptype :和whatis类似,用于显示数据类型,但是它还可以显示typedef定义的类型等。使用格式:
ptype 变量或表达式
set :设置程序中变量的值。使用格式:
set 变量=表达式
set 变量:=表达式
display :增加要显示值的表达式。使用格式:
display 表达式
info display :显示当前所有的要显示值的表达式。
delete display/undisplay :删除要显示值的表达式。使用格式:
delete display/undisplay 表达式编号
disable display :暂时不显示一个要表达式的值。使用格式:
disable display 表达式编号
enable display :与disable display相反,使用表达式恢复显示。使用格式:
enable display 表达式编号
print :打印变量或表达式的值。使用格式:
print 变量或表达式
表达式中有两个符号有特殊含义:$和表示给定序号的前两个序号。
如果$和$$后面不带数字,则给定序号为当前序号。
backtrace :打印指定个数的栈帧(stack frame)。使用格式:
backtrace 栈帧个数
frame :打印栈帧。使用格式:
frame 栈帧号
info frame :显示当前栈帧的详细信息。
select-frame :选择栈帧,选择后可以用info frame来显示栈帧信息。使用格式:
select-frame 栈帧号
kill :结束当前程序的调试。
quit :退出gdb。</p>

如要查看所有的gdb命令,可以在gdb下键入两次Tab(制表符),运行“help command”可以查看命令command的详细使用格式。
本文仅对使用gdb调试过程中的一些常用指令的用法进行简单地总结,如要获取关于gdb的更详细的资料,请参阅gdb的官方文档:
http://www.gnu.org/software/gdb/documentation/

LLVM/GDB Debugger

以下的表格罗列了LLDB命令 以及相对应的GDB的命令,并且列举了双方内置的兼容的命令别名。

执行类命令集

LLDB GDB
Launch a process no arguments.
(lldb) process launch
(lldb) run
(lldb) r
(gdb) run
(gdb) r
Launch a process with arguments <args>.
(lldb) process launch — <args>
(lldb) r <args>
(gdb) run <args>
(gdb) r <args>
Launch a process for with arguments a.out 1 2 3 without having to supply the args every time.
% lldb — a.out 1 2 3
(lldb) run

(lldb) run
% gdb –args a.out 1 2 3
(gdb) run

(gdb) run
Launch a process with arguments in new terminal window (Mac OS X only).
(lldb) process launch –tty — <args>
(lldb) pro la -t — <args>
Launch a process with arguments in existing terminal /dev/ttys006 (Mac OS X only).
(lldb) process launch –tty=/dev/ttys006 — <args>
(lldb) pro la -t/dev/ttys006 — <args>
Attach to a process with process ID 123.
(lldb) process attach –pid 123
(lldb) attach -p 123
(gdb) attach 123
Attach to a process named “a.out”.
(lldb) process attach –name a.out
(lldb) pro at -n a.out
(gdb) attach a.out
Wait for a process named “a.out” to launch and attach.
(lldb) process attach –name a.out –waitfor
(lldb) pro at -n a.out -w
(gdb) attach -waitfor a.out
Do a source level single step in the currently selected thread.
(lldb) thread step-in
(lldb) step
(lldb) s
(gdb) step
(gdb) s
Do a source level single step over in the currently selected thread.
(lldb) thread step-over
(lldb) next
(lldb) n
(gdb) next
(gdb) n
Do an instruction level single step in the currently selected thread.
(lldb) thread step-inst
(lldb) si
(gdb) stepi
(gdb) si
Do an instruction level single step over in the currently selected thread.
(lldb) thread step-inst-over
(lldb) ni
(gdb) nexti
(gdb) ni
Step out of the currently selected frame.
(lldb) thread step-out
(lldb) finish
(gdb) finish
Backtrace and disassemble every time you stop.
(lldb) target stop-hook add
Enter your stop hook command(s). Type ‘DONE’ to end.
> bt
> disassemble –pc
> DONE
Stop hook #1 added.

 

断点类命令集

LLDB GDB
Set a breakpoint at all functions named main.
(lldb) breakpoint set –name main
(lldb) br s -n main
(lldb) b main
(gdb) break main
Set a breakpoint in file test.c at line 12.
(lldb) breakpoint set –file test.c –line 12
(lldb) br s -f test.c -l 12
(lldb) b test.c:12
(gdb) break test.c:12
Set a breakpoint at all C++ methods whose basename is main.
(lldb) breakpoint set –method main
(lldb) br s -M main
(gdb) break main
(Hope that there are no C funtions named main).
Set a breakpoint at and object C function: -[NSString stringWithFormat:].
(lldb) breakpoint set –name “-[NSString stringWithFormat:]”
(lldb) b -[NSString stringWithFormat:]
(gdb) break -[NSString stringWithFormat:]
Set a breakpoint at all Objective C methods whose selector is count.
(lldb) breakpoint set –selector count
(lldb) br s -S count
(gdb) break count
(Hope that there are no C or C++ funtions namedcount).
List all breakpoints.
(lldb) breakpoint list
(lldb) br l
(gdb) info break
Delete a breakpoint.
(lldb) breakpoint delete 1
(lldb) br del 1
(gdb) delete 1

 

监视点(WATCHPOINT)命令集

LLDB GDB
Set a watchpoint on a variable when it is written to.
(lldb) watchpoint set variable -w write global_var
(lldb) watch set var -w write global_var
(gdb) watch global_var
Set a watchpoint on a memory location when it is written into. The size of the region to watch for defaults to the pointer size if no ‘-x byte_size’ is specified. This command takes raw input, evaluated as an expression returning an unsigned integer pointing to the start of the region, after the ‘–’ option terminator.
(lldb) watchpoint set expression -w write — my_ptr
(lldb) watch set exp -w write — my_ptr
(gdb) watch -location g_char_ptr
Set a condition on a watchpoint.
(lldb) watch set var -w write global
(lldb) watchpoint modify -c ‘(global==5)’
(lldb) c

(lldb) bt
* thread #1: tid = 0x1c03, 0x0000000100000ef5 a.outmodify + 21 at main.cpp:16, stop reason = watchpoint 1
frame #0: 0x0000000100000ef5 a.out
modify + 21 at main.cpp:16
frame #1: 0x0000000100000eac a.outmain + 108 at main.cpp:25
frame #2: 0x00007fff8ac9c7e1 libdyld.dylib
start + 1
(lldb) frame var global
(int32_t) global = 5
List all watchpoints.
(lldb) watchpoint list
(lldb) watch l
(gdb) info break
Delete a watchpoint.
(lldb) watchpoint delete 1
(lldb) watch del 1
(gdb) delete 1

 

检查变量

LLDB GDB
Show the arguments and local variables for the current frame.
(lldb) frame variable (gdb) info args
and
(gdb) info locals
Show the local variables for the current frame.
(lldb) frame variable –no-args
(lldb) fr v -a
(gdb) info locals
Show the contents of local variable “bar”.
(lldb) frame variable bar
(lldb) fr v bar
(lldb) p bar
(gdb) p bar
Show the contents of local variable “bar” formatted as hex.
(lldb) frame variable –format x bar
(lldb) fr v -f x bar
(gdb) p/x bar
Show the contents of global variable “baz”.
(lldb) target variable baz
(lldb) ta v baz
(gdb) p baz
Show the global/static variables defined in the current source file.
(lldb) target variable
(lldb) ta v
n/a
Display a the variable “argc” and “argv” every time you stop.
(lldb) target stop-hook add –one-liner “frame variable argc argv”
(lldb) ta st a -o “fr v argc argv”
(lldb) display argc
(lldb) display argv
(gdb) display argc
(gdb) display argv
Display a the variable “argc” and “argv” only when you stop in the function named main.
(lldb) target stop-hook add –name main –one-liner “frame variable argc argv”
(lldb) ta st a -n main -o “fr v argc argv”
Display the variable “*this” only when you stop in c class named MyClass.
(lldb) target stop-hook add –classname MyClass –one-liner “frame variable *this”
(lldb) ta st a -c MyClass -o “fr v *this”

 

检查线程状态

</tr> </tr> </tr> </tr> </tbody> </table>

 

</div>

可执行文件和共享库查询命令

LLDB GDB
Show the stack backtrace for the current thread.
(lldb) thread backtrace
(lldb) bt
(gdb) bt
Show the stack backtraces for all threads.
(lldb) thread backtrace all
(lldb) bt all
(gdb) thread apply all bt
Select a different stack frame by index for the current thread.
(lldb) frame select 12 (gdb) frame 12
List information about the currently selected frame in the current thread.
(lldb) frame info
Select the stack frame that called the current stack frame.
(lldb) up
(lldb) frame select –relative=1
(gdb) up
Select the stack frame that is called by the current stack frame.
(lldb) down
(lldb) frame select –relative=-1
(lldb) fr s -r-1
(gdb) down
Select a different stack frame using a relative offset.
(lldb) frame select –relative 2
(lldb) fr s -r2 </p>

(lldb) frame select –relative -3
(lldb) fr s -r-3</td>

(gdb) up 2
(gdb) down 3
Show the general purpose registers for the current thread.
(lldb) register read (gdb) info registers
Show the general purpose registers for the current thread formatted as signed decimal. LLDB tries to use the same format characters as printf(3) when possible. Type “help format” to see the full list of format specifiers.
(lldb) register read –format i
(lldb) re r -f i </p>

LLDB now supports the GDB shorthand format syntax but there can’t be space after the command:
(lldb) register read/d</td>

Show all registers in all register sets for the current thread.
(lldb) register read –all
(lldb) re r -a
(gdb) info all-registers
Show the values for the registers named “rax”, “rsp” and “rbp” in the current thread.
(lldb) register read rax rsp rbp (gdb) info all-registers rax rsp rbp
Show the values for the register named “rax” in the current thread formatted as binary.
(lldb) register read –format binary rax
(lldb) re r -f b rax </p>

LLDB now supports the GDB shorthand format syntax but there can’t be space after the command:
(lldb) register read/t rax
(lldb) p/t $rax</td>

(gdb) p/t $rax
Read memory from address 0xbffff3c0 and show 4 hex uint32_t values.
(lldb) memory read –size 4 –format x –count 4 0xbffff3c0
(lldb) me r -s4 -fx -c4 0xbffff3c0
(lldb) x -s4 -fx -c4 0xbffff3c0 </p>

LLDB now supports the GDB shorthand format syntax but there can’t be space after the command:
(lldb) memory read/4xw 0xbffff3c0
(lldb) x/4xw 0xbffff3c0
(lldb) memory read –gdb-format 4xw 0xbffff3c0</td>

(gdb) x/4xw 0xbffff3c0
Read memory starting at the expression “argv[0]“.
(lldb) memory read argv[0]
NOTE: any command can inline a scalar expression result (as long as the target is stopped) using backticks around any expression:
(lldb) memory read –size sizeof(int) argv[0]
(gdb) x argv[0]
Read 512 bytes of memory from address 0xbffff3c0 and save results to a local file as text.
(lldb) memory read –outfile /tmp/mem.txt –count 512 0xbffff3c0
(lldb) me r -o/tmp/mem.txt -c512 0xbffff3c0
(lldb) x/512bx -o/tmp/mem.txt 0xbffff3c0
(gdb) set logging on
(gdb) set logging file /tmp/mem.txt
(gdb) x/512bx 0xbffff3c0
(gdb) set logging off
Save binary memory data starting at 0×1000 and ending at 0×2000 to a file.
(lldb) memory read –outfile /tmp/mem.bin –binary 0×1000 0×1200
(lldb) me r -o /tmp/mem.bin -b 0×1000 0×1200
(gdb) dump memory /tmp/mem.bin 0×1000 0×2000
Disassemble the current function for the current frame.
(lldb) disassemble –frame
(lldb) di -f
(gdb) disassemble
Disassemble any functions named main.
(lldb) disassemble –name main
(lldb) di -n main
(gdb) disassemble main
Disassemble an address range.
(lldb) disassemble –start-address 0x1eb8 –end-address 0x1ec3
(lldb) di -s 0x1eb8 -e 0x1ec3
(gdb) disassemble 0x1eb8 0x1ec3
Disassemble 20 instructions from a given address.
(lldb) disassemble –start-address 0x1eb8 –count 20
(lldb) di -s 0x1eb8 -c 20
(gdb) x/20i 0x1eb8
Show mixed source and disassembly for the current function for the current frame.
(lldb) disassemble –frame –mixed
(lldb) di -f -m
n/a
Disassemble the current function for the current frame and show the opcode bytes.
(lldb) disassemble –frame –bytes
(lldb) di -f -b
n/a
Disassemble the current source line for the current frame.
(lldb) disassemble –line
(lldb) di -l
n/a
LLDB GDB
List the main executable and all dependent shared libraries.
(lldb) image list (gdb) info shared
Lookup information for a raw address in the executable or any shared libraries.
(lldb) image lookup –address 0x1ec4
(lldb) im loo -a 0x1ec4
(gdb) info symbol 0x1ec4
Lookup information for an address in a.out only.
(lldb) image lookup –address 0x1ec4 a.out
(lldb) im loo -a 0x1ec4 a.out
Lookup information for for a type Point by name.
(lldb) image lookup –type Point
(lldb) im loo -t Point
(lldb) ptype Point
Dump all sections from the main executable and any shared libraries.
(lldb) image dump sections (gdb) maintenance info sections
Dump all sections in the a.out module.
(lldb) image dump sections a.out
Dump all symbols from the main executable and any shared libraries.
(lldb) image dump symtab
Dump all symbols in a.out and liba.so.
(lldb) image dump symtab a.out liba.so

 

杂项

LLDB GDB
Echo text to the screen.
(lldb) script print “Here is some text” (gdb) echo Here is some textn

 

 

 

在xcode中打开一个app,在想要break的行号上单击,即可生成一个深色的箭头标识--断点。如下图,在viewDidLoad:中设置了断点。

运行app,等待。。。就可以看到xcode在断点处进入调试模式,现在让我们把视线移到xcode右下角的控制台,有木有看到(lldb)这样一行,鼠标移到此行,输入

1 po [self view]

回车,看看控制台上是不是多了一些view的信息,如下图:

po(print object)是LLDB的一个命令,其主要功能是输出objective-c中对象(objects)的信息,与之相似的另外一个命令是 p(print),其主要功能是输出原生类型(boolean、integer、float、etc)的信息。

控制台输入

p (int)[[[self view] subviews] count]

结果如下

(int) $2 = 2

注意这个使用了类型转换告知调试器应该如何处理返回值。

技巧一:运行时修改变量的值

你以前怎么验证是不是某个变量的值导致整段程序不能正常工作?修改代码中的变量的值,然后cmd+r重新启动app?现在你不需要这么做了,只需要设置一个断点,当程序在这进入调试模式后,使用expr命令即可在运行时修改变量的值。

假如有一个loginWithUsername:方法,需要两个参数:username,password。

首先设置好断点,如下图所示:

运行app,进入断点模式后,在(lldb)后输入

 

1 expr username = @"username"
2 expr password = @"badpassword"

控制台会返回以下信息

 

 

1 (NSString *) $0 = 0x3d3504c4 @"username"
2 (NSString *) $1 = 0x1d18ef60 @"badpassword"

现在跳出断点,执行断点之后的两条输出语句,控制台会有以下输出

 

 

1 (0x1c59aae0) A line for the breakpoint
2 (0x1c59aae0) Username and Password after: username:badpassword

看到看吧,我们在运行时修改了变量的值,事情还可以变的更简单一些,我们可以编辑断点,让它自动填充需要的修改的变量的值,并且可以选择在此断点处不进入断点模式,仅仅修改指定变量的值,然后自动执行后续代码。

 

右击断点选择“Edit Breakpoint...”(或者按住cmd+option,单击断点),然后如下图所示设置断点

注意选中了最后一行(“Automatically continue after evaluating”)的选择框,这就保证运行到这个断点的时,填充变量的值,然后继续运行,并不在此处断点进入调试模式。

运行app,你会得到和上述手动设置变量的值一样的输出。

接下来单击断点,使其处于禁用状态,现在箭头的颜色应该是浅蓝色的,重新运行app,你会发现username和password的值没有在运行时被改变了。

技巧二:设置断点触发条件

断点的另外一个重要作用,是可以设置触发断点生效的条件,这样我们就可以在运行时针对特定的数据进行分析,观察app是否运行在正确的轨道上。如下图:

上述截图可以看到如下语句

 

1 (BOOL)[(NSString*)[item valueForKey:@"ID"] isEqualToString:@"93306"]

通过这行语句,我们告诉编译器:当item中ID等于93306时,此断点生效,进入断点调试模式。

 

技巧三:格式化输出数据

如果你厌倦了代码里无穷无尽的NSLog,幸运的是我们可以在编辑断点使其输出格式化字符串就像平常编码时一样。不过有一点需要注意,平常编码时可能会使用NSString‘s stringWithFormat:输出格式化字符串,不过这个方法貌似在断点中木有效果,你需要使用alloc/init形式的方法,如下:

 

1 po [[NSString alloc] initWithFormat:@"Item index is: %d", index]

 

运行app,就能在控制台看到想要的输出啦!

简单!强大!这就是LLDB给你的选择,从此代码里可以不用再有NSLog满天飞的情况了,代码变得更干净了,心情变得更愉悦了!

LLDB还有很多强大的地方,本教程只不过揭开了它的面纱,即便如此,仍让我们沉醉不已。

如此你有让xcode中debug变的更轻松加简单的方法,请在评论中尽情的分享!