码迷,mamicode.com
首页 > 数据库 > 详细

Xcode GDB 命令list

时间:2015-04-01 21:43:03      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:

此文下半部分为转载:但是这里有一些我自己使用技巧,结合下面的文章,我们会有更多的收获,在此感谢原创者。
   
---------------------

关于调试异常崩溃:

一般崩溃是由内存使用错误导致的,要么多了,要么少了。

xcode的调试提示可以知道是什么原因导致的崩溃。

xcode中product àedit scheme à diagnostics 将enable Zombie objects 和 Malloc Stack 选中, 如果是内存释放错误,则gdb会提示release dealloc object。

然后可以用断点缩小错误范围, 在可能出现错误的地方用单步调试, 当执行到有错误代码时, gdb会再次提示release dealloc object。

其实XCODE内嵌GDB,那个 lldb就是gdb!
-----------------------
 
关于GDB

对于大多数Cocoa程序员来说,最常用的debugger莫过于Xcode自带的调试工具了。而实际上,它正是gdb的一个图形化包装。相对于gdb,图形化带来了很多便利,但同时也缺少了一些重要功能。而且在某些情况下,gdb反而更加方便。因此,学习gdb,了解一下幕后的实质,也是有必要的。

gdb可以通过终端运行,也可以在Xcode的控制台调用命令。本文将通过终端讲述一些gdb的基本命令和技巧。

首先,我们来看一个例子:

 #import 

 int main(int argc, char **argv)
 {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 NSLog(@"Hello, world!");
 [pool release];

 return 0;
 }


糟糕,程序竟然exited normally了(==|||)。这可不行,我们得让他崩溃才行。所以我们给这个小程序添加一个bug:

 int x = 42;
 NSLog("Hello, world! x = %@", x);
nice。这样一来程序就会漂亮地崩溃了:

 (gdb) run

 Starting program: /Users/mikeash/shell/a.out
 Reading symbols for shared libraries .++++....................... done

 Program received signal EXC_BAD_ACCESS, Could not access memory.
 Reason: 13 at address: 0x0000000000000000
 0x00007fff84f1011c in objc_msgSend ()
 (gdb)

如果我们是在shell中直接运行的程序,在崩溃后就会回到shell。不过现在我们是通过gdb运行的,所以现在并没有跳出。gdb暂停了我们的程序,但依然使之驻留在内存中,让我们有机会做调试。

首先,我们想知道具体是哪里导致了程序崩溃。gdb已经通过刚才的输出告知了我们: 函数objc_msgSend就是祸之根源。但是这个信息并不足够,因为这个objc_msgSend是objc运行时库中的函数。我们并不知道它是怎么调用的。我们关注的是我们自己的代码。
要知道这一点,我们需要得到当前进程的函数调用栈的情况,以此回溯找到我们自己的方法。这时我们需要用到backtrace命令,一般简写为bt:

 (gdb) bt
 #0 0x00007fff84f1011c in objc_msgSend ()
 #1 0x00007fff864ff30b in _CFStringAppendFormatAndArgumentsAux ()
 #2 0x00007fff864ff27d in _CFStringCreateWithFormatAndArgumentsAux ()
 #3 0x00007fff8657e9ef in _CFLogvEx ()
 #4 0x00007fff87beab3e in NSLogv ()
 #5 0x00007fff87beaad6 in NSLog ()
 #6 0x0000000100000ed7 in main () at test.m:10

现在我们可以看到,程序在test.m的第10行,调用NSLog方法时崩溃了。接下来我们想看一下这次调用的详细信息。这时我们要用到up命令。up命令可以在栈的各层之间跳转。本例中,我们的代码main是#6:

 (gdb) up 6
 #6 0x0000000100000ed7 in main () at test.m:10
 9NSLog("Hello, world! x = %@", x);
这回不仅是函数名,连出错的那行代码也打印出来了。但是,我们还可以使用list(简写为l)命令,打印出更多信息:
ps: 如果需要回到栈列表。可以使用down命令。

 (gdb) l
 5
 6int main(int argc, char **argv)
 7{
 8NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 9int x = 42;
 10NSLog("Hello, world! x = %@", x);
 11[pool release];
 12
 13return 0;
 14}

啊,整个代码都被列出来了。虽然我们用编辑器打开test.m文件然后找到第10行也可以打到同样效果,但显然没有上面的方法更有效率。(当然没有Xcode自带的那个快就是了)

好了,现在我们再来看看这个bug(虽然是我们自己弄出来的)。很明显,在格式化字符串前少加了一个@。我们改正它,并重新运行一遍程序:

 (gdb) run
 Starting program: /Users/mikeash/shell/a.out
 Reading symbols for shared libraries .++++....................... done

 Program received signal EXC_BAD_ACCESS, Could not access memory.
 Reason: KERN_INVALID_ADDRESS at address: 0x000000000000002a
 0x00007fff84f102b3 in objc_msgSend_fixup ()
 (gdb) bt
 #0 0x00007fff84f102b3 in objc_msgSend_fixup ()
 #1 0x0000000000000000 in ?? ()

啊咧,程序还是崩溃了。更杯具的是,栈信息没有显示出这个objc_msgSend_fixup方法是从哪里调用的。这样我们就没法用上面的方法找到目标代码了。这时,我们只好请出一个debugger最常用的功能:断点。

在gdb中,设置断点通过break命令实现。它可以简写为b。有两种方法可以确定断点的位置:传入一个已定义的符号,或是直接地通过一个file:line对设置位置。
现在让我们在main函数的开始处设置一个断点:

 (gdb) b test.m:8
 Breakpoint 1 at 0x100000e8f: file test.m, line 8.

debugger给了我们一个回应,告诉我们断点设置成功了,而且这个断点的标号是1。断点的标号很有用,可以用来给断点排序&停用&启用&删除等。不过我们现在不需要理会,我们只是接着运行程序:

 (gdb) run
 The program being debugged has been started already.
 Start it from the beginning? (y or n) y
 Starting program: /Users/mikeash/shell/a.out

 Breakpoint 1, main (argc=1, argv=0x7fff5fbff628) at test.m:8
 8NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
debugger在在我们期望的地方停下了。现在我们使用next(简写n)命令单步调试程序,看看它到底是在哪一行崩溃的:

 (gdb) n
 9int x = 42;
 (gdb)
 10NSLog(@"Hello, world! x = %@", x);
 (gdb)

 Program received signal EXC_BAD_ACCESS, Could not access memory.
 Reason: KERN_INVALID_ADDRESS at address: 0x000000000000002a
 0x00007fff84f102b3 in objc_msgSend_fixup ()
值得注意的是,我只键入了一次n命令,随后直接敲了2次回车。这样做的原因是gdb把任何空输入当作最近一次输入命令的重复。所以这里相当于输入了3次n。

现在我们可以看到,崩溃之处依然是NSLog。原因嘛,当然是在格式化输出的地方用%@表示int型变量x了。我们仔细看一下输出信息:崩溃原因是错误地访问了0x000000000000002a这个地址。而2a的十进制表示正是42--我们为x赋的值。编译器把它当作地址了。

输出数值

一个很重要的调试方法是输出表达式和变量的值。在gdb中,这是通过print命令完成的。

 (gdb) p x
 $1 = 42

在print命令后追加/format可以格式化输出。/format是一个gdb的格式化字符串,比较有用的格式化字符有 x:十进制数; c:字符; a:地址等。

 (gdb) p/x x
 $2 = 0x2a

print-object方法(简写为po)用来输出obj-c中的对象。它的工作原理是,向被调用的对象发送名为debugDescription的消息。它和常见的description消息很像。

举例来说,让我们输出一下autorelease pool:

 (gdb) po pool
 

这个命令不仅仅可以输出显式定义的对象,也可以输出表达式的结果。这次我们测试一下nsobject中debugDescription的方法签名:


返回值是对象的表达式可以用po命令输出结果,那么返回值是基本类型的方法又怎样呢?显然,它们是可以用p命令输出的。但是要小心,因为gdb并不能自动识别出返回值的类型。所以我们在输出前要显式地转换一下:

 (gdb) p [NSObject instancesRespondToSelector: @selector(doesNotExist)]
 Unable to call function "objc_msgSend" at 0x7fff84f100f4: no return type information available.
 To call this function anyway, you can cast the return type explicitly (e.g. ‘print (float) fabs (3.0)‘)
 (gdb) p (char)[NSObject instancesRespondToSelector: @selector(doesNotExist)]
 $5 = 0 ‘00‘

你也许发现了,doesNotExist方法的返回值是BOOL,而我们做的转换却是char。这是因为gdb也不能识别那些用typedef定义的类型。不仅仅是你定义的,即使是Cocoa框架里定义的也不行。

你也许已经注意到,在用p进行输出的时侯,输出值前面会有一个类似"$1="的前缀。它们是gdb变量。它们可以在后面的表达式中使用,来指代它后面的值。在下面的例子里,我们开辟了一块内存,将其置零,然后释放。在这个过程中,我们使用了gdb变量,这样就不用一遍遍地复制粘贴地址了。

 (gdb) p (int *)malloc(4)
 $6 = (int *) 0x100105ab0
 (gdb) p (void)bzero($6, 4)
 $7 = void
 (gdb) p *$6
 $8 = 0
 (gdb) p (void)free($6)
 $9 = void

我们也想把这个技巧用到对象上,但不幸的是po命令并不会把它的返回值存储到变量里。所以我们在得到一个新的对象时必须先使用p命令:

 (gdb) p (void *)[[NSObject alloc] init]
 $10 = (void *) 0x100105950
 (gdb) po $10
 
 (gdb) p (long)[$10 retainCount]
 $11 = 1
 (gdb) p (void)[$10 release]
 $12 = void

检查内存

有些时候,仅仅输出一个数值还不能帮助我们查找出错误。我们需要一次性地打印出一整块内存来窥视全局。这时候我们就需要使用x命令。

x命令的格式是x/format address。其中address很简单,它通常是指向一块内存的表达式。但是format的语法就有点复杂了。它由三个部分组成:

第一个是要显示的块的数量;第二个是显示格式(如x代表16进制,d代表十进制,c代表字符);第三个是每个块的大小。值得注意的是第三部分,即块大小是用字符对应的。用b, h, w,g 分别表示1, 2, 4, 8 bytes。举例来说,用十六进制方式,打印从ptr开始的4个4-byte块应该这样写:

 (gdb) x/4xw ptr

接下来举一个比较实际的例子。我们看一下NSObject类的内容:

 (gdb) x/4xg (void *)[NSObject class]
 0x7fff70adb468 : 0x00007fff70adb4400x0000000000000000
 0x7fff70adb478 :0x0000000100105ac00x0000000100104ac0

接下来再看看一个NSObject实例的内容:

 (gdb) x/1xg (void *)[NSObject new]
 0x100105ba0:0x00007fff70adb468

现在我们看到,在实例开头引用了类的地址。

设置变量

有时,查看数值程度的能力还是稍弱了一点,我们还想能够修改变量。这也很简单,只需要使用set命令:

 (gdb) set x = 43

我们可以用任意表达式给一个变量赋值。比如说新创建一个对象然后赋值:

 (gdb) set obj = (void *)[[NSObject alloc] init]

断点

我们可以在程序的某个位置设置断点,这样当程序运行到那里的时候就会暂停,而把控制权转移给调试器。就像之前提到的,我们用break命令来设置断点。下面详细地列出了如何设置断点的目标:

SymbolName: 为断点指定一个函数名。这样断点就会设置在该函数上。
file.c:1234: 把断点设置在指定文件的一行。
-[ClassName method:name:]: 把断点设置在objc的方法上。用+代表类方法。
*0xdeadbeef: 在内存的指定位置设置断点。这不是很常用,一般在没有源码的调试时使用。

断点可以用enable命令和disable命令来切换到使用和停用状态,也可以通过delete命令彻底删除。想要查看现有断点的话,使用info breakpoints命令(可以简写成info b,或是i b)。

另外,我们也可以用if命令,把断点升级成条件断点。顾名思义,条件断点只会在设定的条件成真时起作用。举例来说,下面的语句为MyMethod添加了一个条件断点,它只在参数等于5的时候有效:

 (gdb) b -[Class myMethod:] if parameter == 5

最后,在断点上可以附加gdb命令。这样,当断点中断时,附带的命令会自动执行。附加命令使用commands breakpointnumber。这时gdb就会进入断点指令输入状态。

断点指令就是一个以end结尾的标准gdb指令序列。举个例子,我们想在每次NSLog被调用时输出栈信息:

 (gdb) b NSLog
 Breakpoint 4 at 0x7fff87beaa62
 (gdb) commands
 Type commands for when breakpoint 4 is hit, one per line.
 End with a line saying just "end".
 >bt
 >end
 
  1. 命 令                        解释  
  2. break NUM               在指定的行上设置断点。  
  3. bt                      显 示所有的调用栈帧。该命令可用来显示函数的调用顺序。  
  4. clear                   删 除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。  
  5. continue                继续执行正在调试的程序。该命令用在程序 由于处理信号或断点而  
  6.                         导致停止运行 时。  
  7. display EXPR            每次程序停止后显示表达式的值。表达式由程序定 义的变量组成。  
  8. file FILE               装载指定的可执行文件进行调试。  
  9. help NAME               显 示指定命令的帮助信息。  
  10. info break              显 示当前断点清单,包括到达断点处的次数等。  
  11. info files              显 示被调试文件的详细信息。  
  12. info func               显示所有的函数名称。  
  13. info local              显 示当函数中的局部变量信息。  
  14. info prog               显示被调试程序的执行状 态。  
  15. info var                显示所有的全局和静态变量名称。  
  16. kill                    终 止正被调试的程序。  
  17. list                    显示源代码段。  
  18. make                    在 不退出 gdb 的情况下运行 make 工具。  
  19. next                    在 不单步执行进入其他函数的情况下,向前执行一行源代码。  
  20. print EXPR              显 示表达式 EXPR 的值。  
  21.   
  22. print- object            打印一个对象  
  23. print (int) name      打印一个类型  
  24. print- object [artist description]   调用一个函数  
  25. set artist = @"test"    设置变量值  
  26. whatis                      查 看变理的数据类型  
 

Xcode GDB 命令list

标签:

原文地址:http://www.cnblogs.com/fgyqbs/p/4385296.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!