我不生产博客,我只是博客界的搬运工
首先让我们先通过百度了解一下LLVM
链接:点击打开链接
然后重点了解一下LLDB这个东西
随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令。如图1所示:
图1:位于Xcode调试区域的控制台
在本文中,我们主要整理一下LLDB调试器在Xcode中如何调试,更详细的命令和内容可以查看苹果官方文档:Apple文档
在Xcode中调试程序
对于我们日常的开发工作来说,更多的时候是在Xcode中进行调试工作。其实Xcode已经帮我们完成了大部分的工作,而且很多东西也可以在Xcode里面看到。因此,我们可以把精力都集中在代码层面上。
在苹果官方文档中列出了我们在调试中能用到的一些命令,我们在这重点讲一些常用的命令。
打印
打印变量的值可以使用print命令,该命令如果打印的是简单类型,则会列出简单类型的类型和值。如果是对象,还会打印出对象指针地址,如下所示:
(lldb) print a (NSInteger) $0 = 0 (lldb) print b (NSInteger) $1 = 0 (lldb) print str (NSString *) $2 = 0x0000000100001048 @"abc" (lldb) print url (NSURL *) $3 = 0x0000000100206cc0 @"abc"
(lldb) exp $0 = 100 (NSInteger) $9 = 100 (lldb) p a (NSInteger) $10 = 100
上面的print命令会打印出对象的很多信息,如果我们只想查看对象的值的信息,则可以使用po(print object的缩写)命令,如下所示:
(lldb) po str abc
对于简单类型,我们还可以为其指定不同的打印格式,其命令格式是print/,如下所示:
(lldb) p/x a (NSInteger) $13 = 0x0000000000000064
在开发中,我们经常会遇到这样一种情况:我们设置一个视图的背景颜色,运行后发现颜色不好看。嗯,好吧,在代码里面修改一下,再编译运行一下,嗯,还是不好看,然后再修改吧~~这样无形中浪费了我们大把的时间。在这种情况下,expression命令强大的功能就能体现出来了,它不仅会改变调试器中的值,还改变了程序中的实际值。我们先来看看实际效果,如下所示:
(lldb) exp a = 10 (NSInteger) $0 = 10 (lldb) exp b = 100 (NSInteger) $1 = 100 2015-01-25 14:00:41.313 test[18064:71466] a + b = 110, abc
image
image命令的用法也挺多,首先可以用它来查看工程中使用的库,如下所示:
(lldb) image list [ 0] 432A6EBF-B9D2-3850-BCB2-821B9E62B1E0 0x0000000100000000 /Users/**/Library/Developer/Xcode/DerivedData/test-byjqwkhxixddxudlnvqhrfughkra/Build/Products/Debug/test [ 1] 65DCCB06-339C-3E25-9702-600A28291D0E 0x00007fff5fc00000 /usr/lib/dyld [ 2] E3746EDD-DFB1-3ECB-88ED-A91AC0EF3AAA 0x00007fff8d324000 /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation [ 3] 759E155D-BC42-3D4E-869B-6F57D477177C 0x00007fff8869f000 /usr/lib/libobjc.A.dylib [ 4] 5C161F1A-93BA-3221-A31D-F86222005B1B 0x00007fff8c75c000 /usr/lib/libSystem.B.dylib [ 5] CBD1591C-405E-376E-87E9-B264610EBF49 0x00007fff8df0d000 /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation [ 6] A260789B-D4D8-316A-9490-254767B8A5F1 0x00007fff8de36000 /usr/lib/libauto.dylib ......
NSArray *array = @[@1, @2]; NSLog(@"item 3: %@", array[2]);
2015-01-25 14:12:01.007 test[18122:76474] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]' *** First throw call stack: ( 0 CoreFoundation 0x00007fff8e06f66c __exceptionPreprocess + 172 1 libobjc.A.dylib 0x00007fff886ad76e objc_exception_throw + 43 2 CoreFoundation 0x00007fff8df487de -[__NSArrayI objectAtIndex:] + 190 3 test 0x0000000100000de0 main + 384 4 libdyld.dylib 0x00007fff8f1b65c9 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) image lookup --address 0x0000000100000de0 Address: test[0x0000000100000de0] (test.__TEXT.__text + 384) Summary: test`main + 384 at main.m:23
我们还可以使用image lookup命令来查看具体的类型,如下所示:
(lldb) image lookup --type NSURL Best match found in /Users/**/Library/Developer/Xcode/DerivedData/test-byjqwkhxixddxudlnvqhrfughkra/Build/Products/Debug/test: id = {0x100000157}, name = "NSURL", byte-size = 40, decl = NSURL.h:17, clang_type = "@interface NSURL : NSObject{ NSString * _urlString; NSURL * _baseURL; void * _clients; void * _reserved; } @property ( readonly,getter = absoluteString,setter = <null selector>,nonatomic ) NSString * absoluteString; @property ( readonly,getter = relativeString,setter = <null selector>,nonatomic ) NSString * relativeString; @property ( readonly,getter = baseURL,setter = <null selector>,nonatomic ) NSURL * baseURL; @property ( readonly,getter = absoluteURL,setter = <null selector>,nonatomic ) NSURL * absoluteURL; @property ( readonly,getter = scheme,setter = <null selector>,nonatomic ) NSString * scheme; @property ( readonly,getter = resourceSpecifier,setter = <null selector>,nonatomic ) NSString * resourceSpecifier; @property ( readonly,getter = host,setter = <null selector>,nonatomic ) NSString * host; @property ( readonly,getter = port,setter = <null selector>,nonatomic ) NSNumber * port; @property ( readonly,getter = user,setter = <null selector>,nonatomic ) NSString * user; @property ( readonly,getter = password,setter = <null selector>,nonatomic ) NSString * password; @property ( readonly,getter = path,setter = <null selector>,nonatomic ) NSString * path; @property ( readonly,getter = fragment,setter = <null selector>,nonatomic ) NSString * fragment; @property ( readonly,getter = parameterString,setter = <null selector>,nonatomic ) NSString * parameterString; @property ( readonly,getter = query,setter = <null selector>,nonatomic ) NSString * query; @property ( readonly,getter = relativePath,setter = <null selector>,nonatomic ) NSString * relativePath; @property ( readonly,getter = fileSystemRepresentation,setter = <null selector> ) const char * fileSystemRepresentation; @property ( readonly,getter = isFileURL,setter = <null selector>,readwrite ) BOOL fileURL; @property ( readonly,getter = standardizedURL,setter = <null selector>,nonatomic ) NSURL * standardizedURL; @property ( readonly,getter = filePathURL,setter = <null selector>,nonatomic ) NSURL * filePathURL; @end"</null selector></null selector></null selector>
image命令还有许多其它功能,具体可以参考 Executable and Shared Library Query Commands
命令别名及帮助系统
LLDB有两个非常有用的特性,即命令别名及帮助。
命令别名
我们可以使用LLDB的别名机制来为常用的命令创建一个别名,以方便我们的使用,如下命令:
(lldb) breakpoint set --file foo.c --line 12
(lldb) command alias bfl breakpoint set -f %1 -l %2
(lldb) bfl foo.c 12
我们可以自由地创建LLDB命令的别名集合。LLDB在启动时会读取~/.lldbinit文件。这个文件中存储了command alias命令创建的别名。LLDB帮助系统会读取这个初始化文件并会列出这些别名,以让我们了解自己所设置的别名。我们可以使用”help -a”命令并在输出的后面来查看这边别名,其以下面这行开始:
... The following is a list of your current command abbreviations (see 'help command alias' for more info): ...
(lldb) command unalias b
帮助系统
LLDB帮助系统让我们可以了解LLDB提供了哪些功能,并可以查看LLDB命令结构的详细信息。熟悉帮助系统可以让我们访问帮助系统中中命令文档。
我们可以简单地调用help命令来列出LLDB所有的顶层命令。如下所示:
(lldb) help The following is a list of built-in, permanent debugger commands: _regexp-attach -- Attach to a process id if in decimal, otherwise treat the argument as a process name to attach to. _regexp-break -- Set a breakpoint using a regular expression to specify the location, where <linenum> is in decimal and <address> is in hex. _regexp-bt -- Show a backtrace. An optional argument is accepted; if that argument is a number, it specifies the number of frames to display. If that argument is 'all', full backtraces of all threads are displayed. … and so forth …</address></linenum>
(lldb) help breakpoint set Sets a breakpoint or set of breakpoints in the executable. Syntax: breakpoint set <cmd-options> Command Options Usage: breakpoint set [-Ho] -l <linenum> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>] breakpoint set [-Ho] -a <address-expression> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] breakpoint set [-Ho] -n <function-name> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>] [-L <language>] breakpoint set [-Ho] -F <fullname> [-s <shlib-name>] [-i <count>] [-c <expr>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-f <filename>] [-K <boolean>] … and so forth …</boolean></filename></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></fullname></language></boolean></filename></queue-name></thread-name></thread-id></thread-index></expr></count></shlib-name></function-name></queue-name></thread-name>
(lldb) apropos file The following commands may relate to 'file': … log enable -- Enable logging for a single log channel. memory read -- Read from the memory of the process being debugged. memory write -- Write to the memory of the process being debugged. platform process launch -- Launch a new process on a remote platform. platform select -- Create a platform if needed and select it as the current platform. plugin load -- Import a dylib that implements an LLDB plugin. process launch -- Launch the executable in the debugger. process load -- Load a shared library into the current process. source -- A set of commands for accessing source file information … and so forth …
(lldb) help b 'b' is an abbreviation for '_regexp-break'
(lldb) help break command add Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit. Syntax: breakpoint command add <cmd-options> <breakpt-id> etc...</breakpt-id></cmd-options>
如果想了解以上输出的参数的作用,我们可以在help后面直接指定这个参数(将其放在尖括号内)来查询它的详细信息,如下所示:
(lldb) help <breakpt-id> <breakpt-id> -- Breakpoint IDs consist major and minor numbers; the major etc...</breakpt-id></breakpt-id>
总结
LLDB带给我们强大的调试功能,在调试过程中充分地利用它可以帮助我们极大地提高调试效率。我们可以不用写那么多的NSLog来打印一大堆的日志。所以建议在日常工作中多去使用它。当然,上面的命令只是LLDB的冰山一角,更多的使用还需要大家自己去发掘,在此只是抛砖引玉,做了一些整理。