精华内容
参与话题
问答
  • lldb 会默认从~/.lldbinit 加载自定义脚本。 新增command script之后,重启Xcode,或者直接在lldb交互模式下输入command source ~/.lldbinit来加载脚本。 Custom Commands: 使用正则command regex,进行 自定义命令...

    前言

    lldb 会默认从~/.lldbinit 加载自定义脚本。

    新增command script之后,重启Xcode,或者直接在lldb交互模式下输入command source ~/.lldbinit来加载脚本。

    I 、Custom Commands:

    1.1 使用正则command regex,进行 自定义命令

    • 对类的所有方法下断点并跟踪打印调用参数

    bclass: help rb Sets a breakpoint or set of breakpoints in the executable.; 对类的所有方法进行下断点

    command regex bclass 's/(.+)/rb \[%1 /'
    command source ~/.lldbinit
    
    
    Executing commands in '/Users/devzkn/.lldbinit'.
    command regex bclass 's/(.+)/rb \[%1 /'
    command regex ls 's/(.+)/po @import Foundation; [[NSFileManager
    defaultManager] contentsOfDirectoryAtPath:@"%1" error:nil]/'
    command regex dump_stuff "s/(.+)/image lookup -rn '\+\[\w+(\(\w+\))?\ \w+
    \]$' %1 /"
    command script import /usr/local/opt/chisel/libexec/fblldb.py
    command script import /Users/devzkn/code/lldb/LLDB/lldb_commands/dslldb.py
    
    
    • 使用
    (lldb) bclass FLSocketManager
    Breakpoint 5: 41 locations.
    
    

    1.2 #!/usr/bin/python脚本调用lldb API 进行自定义命令

    • source the commands in lldbinit
    command script import /path/to/fblldb.py
    script fblldb.loadCommandsInDirectory('/magical/commands/') ##loadCommandsInDirectory in the fblldb.py module
    
    
    • example
    #!/usr/bin/python
    # Example file with custom commands, located at /magical/commands/example.py
    
    import lldb
    import fblldbbase as fb
    
    def lldbcommands():
      return [ PrintKeyWindowLevel() ]
      
    class PrintKeyWindowLevel(fb.FBCommand):
      def name(self):
        return 'pkeywinlevel'
        
      def description(self):
        return 'An incredibly contrived command that prints the window level of the key window.'
        
      def run(self, arguments, options):
        # It's a good habit to explicitly cast the type of all return
        # values and arguments. LLDB can't always find them on its own.
        lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]')
    
    

    1.2 小结

    这两种方式最终都是可以从~/.lldbinit加载;

    使用python脚本利用lldb的API进行自定义往往提供更强大的功能,更利于维护。

    使用command regex往往是对现有原生的command进行进一步简单封装。

    II 、Installation

    ➜  lldb git clone git@github.com:DerekSelander/LLDB.git
    
    

    1、To Install, copy the lldb_commands folder to a dir of your choosing.
    2、Open up (or create) ~/.lldbinit
    3、Add the following command to your ~/.lldbinit file: command script import /path/to/lldb_commands/dslldb.py

    • import script
    echo -e "\ncommand script import /Users/devzkn/code/lldb/LLDB/lldb_commands/dslldb.py" >> ~/.lldbinit
    
    

    2.1. 同时推荐安装brew install chisel

    touch .lldbinit 
    open .lldbinit
    command script import /usr/local/opt/chisel/libexec/fblldb.py
    
    

    III、例子

    3.1 定位按钮的action事件:

    对应的oc API [0x10b35a6d0 actionsForTarget:0x10a7caeb0 forControlEvent:64]

    使用python脚本之后,就不用通过po 按钮的方法3次进行定位,而只需pactions 一次就行了

    3.2 查看控件的响应链:

    对应私有API:nextResponder

    • presponder

    3.3 查看界面控件和布局

    • 使用Xcode自带的《debug View Hierarchy》

    • pviews : 由fblldb.py提供

    • 使用私有API

    [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString()
    
    
    [UIWindow keyWindow].recursiveDescription().toString()
    
    
    
    • 基于layout展示View架构
    cy# [[UIWindow keyWindow] _autolayoutTrace].toString()
    
    

    3.4 查看block参数 :pblock

    /usr/local/Cellar/chisel/1.8.0/libexec/commands/FBClassDump.py
    class FBPrintBlock(fb.FBCommand):
      def run(self, arguments, options):
            struct Block_descriptor_1 {
                      const char *signature;                         // IFF (1<<30)
    
    def getMethods(klass):
        Method *methods = (Method *)class_copyMethodList((Class)$cls, &outCount);
          SEL name = (SEL)method_getName(methods[i]);
                char * encoding = (char *)method_getTypeEncoding(methods[i]);
          NSInteger args = (NSInteger)method_getNumberOfArguments(methods[i]);
    
    
    
    

    3.4.1 没有参数的block

    • typedef void (^dispatch_block_t)(void);
    (lldb) po $x19
    <__NSMallocBlock__: 0x1172ea120>
    (lldb) pblock 0x1172ea120
    Imp: 0x10690b24c    Signature: void ^();
    (lldb) po $x23
    <__NSMallocBlock__: 0x1172ea120>
    
    
    (lldb) po (char *)$x23
    <__NSMallocBlock__: 0x1140c7060>
    
    (lldb) po $x23
    <__NSMallocBlock__: 0x1140c7060>
    (lldb) pblock 0x1140c7060  # 应当block的参数类型比较特殊,比如ID什么的,不是具体的类型;或者当前的断点并不在block内部,才导致失败的
    Traceback (most recent call last):
      File "/usr/local/opt/chisel/libexec/fblldb.py", line 84, in runCommand
        command.run(args, options)
      File "/usr/local/Cellar/chisel/1.8.0/libexec/commands/FBClassDump.py", line 142, in run
        signature = json['signature']
    TypeError: 'NoneType' object has no attribute '__getitem__'
    
    

    3.4.2 有参数的block

    typedef void(^FLSocketDidCloseBlock)(NSInteger code,NSString *reason,BOOL wasClean);
    
    
    (lldb) po $x8
    <__NSMallocBlock__: 0x11bb4e380>
    
    (lldb) pblock 0x11bb4e380
    Imp: 0x10698bc18    Signature: void ^(long long, NSString *, bool);
    
    

    3.5 查看特定class的所有方法以及对应的内存地址,直接下断点

    下断点,可以使用methods 或者_shortMethodDescription获取方法地址

    • methods:获取方法地址
    (lldb) methods 0x10b45cb90
    <KNSocketManagerTool: 0x10b45cb90>:
    in KNSocketManagerTool:
    	Class Methods:
    		+ (id) shareSocketManagerTool; (0x1069a1510)
    		+ (id) allocWithZone:(struct _NSZone*)arg1; (0x1069a167c)
    	Properties:
    		@property (retain, nonatomic) GACConnectConfig* connectConfig;  (@synthesize connectConfig = _connectConfig;)
    	Instance Methods:
    		- (void) SRWebSocketClose; (0x1069a3b50)
    
    
    • 对SRWebSocketClose 进行下断点
    b 0x1069a3b50
    Breakpoint 2: where = libweixinDylib.dylib`-[KNSocketManagerTool SRWebSocketClose] at KNSocketManagerTool.m:252, address = 0x00000001069a3b50
    
    
    • breakpoint delete

    3.5.1 通过_shortMethodDescription 用于LLDB 进行方法打断点的步骤:

    • LLDB连接到程序
    • 找到需要下断点的类,如CMessageMgr,然后在LLDB命令行输入po [className _shortMethodDescription],来查看方法对应的内存地址
    • b 内存地址 下断点;这样的好处是避免ASRL偏移量的计算

    3.6打印class 对应的对象数组: search 控件名称 《=》 cycript 的choose

    • search UITextFild
    (lldb) search UITextField
    <UISearchBarTextField: 0x114114bd0; frame = (8 8; 304 28); text = ''; opaque = NO; gestureRecognizers = <NSArray: 0x116d27e40>; layer = <CALayer: 0x114115b40>>
    
    
    • 定位UIButton class 对应的对象,返回的是数组
    cy# choose(UIButton).toString() 
    
    

    IV、查看地址所在模块的信息、在模块中查看与xxx 相关的符号信息

    4.0 获取寄存器的值: register read --all

    -获取寄存器的值: register read --all

           x23 = 0x000000011bb4e180
           x24 = 0x00000001069f1240  libdispatch.dylib`_dispatch_call_block_and_release
           x25 = 0x0000000106add938  SocketRocket`__27-[SRWebSocket _pumpWriting]_block_invoke at SRWebSocket.m:1193
    
    

    4.1查看地址所在模块的信息: image lookup -a 0x1922da0c8

    • -[UIApplication(UIApplicationKeyboardTesting) runTestForKeyboardBringupAndDismissalWithName:withShowKeyboardBlock:withHideKeyboardBlock:withExtraResultsBlock:withCleanupBlock:] 在文件中的偏移为:0x0000000187c720c8
    (lldb) image lookup -a 0x1922da0c8
          Address: UIKit[0x0000000187c720c8] (UIKit.__TEXT.__text + 7688388)
          Summary: UIKit`-[UIApplication(UIApplicationKeyboardTesting) runTestForKeyboardBringupAndDismissalWithName:withShowKeyboardBlock:withHideKeyboardBlock:withExtraResultsBlock:withCleanupBlock:]
    
    

    4.2 在模块中查看与xxx 相关的符号信息

    • image lookup -rn xxx
    (lldb) image lookup -rn CContassctMgr 
    18 matches found in /Users/devzkn/Library/Developer/Xcode/DerivedData/weixin-fqltqjyxecppixbrhoqzzbtyrgmz/Build/Products/Debug-iphoneos/weixin.app/Frameworks/libweixinDylib.dylib:
            Address: libweixinDylib.dylib[0x0000000000038668] (libweixinDylib.dylib.__TEXT.__text + 200068)
            Summary: libweixinDylib.dylib`_logos_meta_method$friend$CContactMgr$setupdoVerifybywxid$greetings$(objc_class*, objc_selector*, NSString*, NSString*) at weixinDylib.xm:490        Address: libweixinDylib.dylib[0x0000000000037a9c] (libweixinDylib.dylib.__TEXT.__text + 197048)
    
    

    V、 see also

    command regex bclass 's/(.+)/rb \[%1 /'
    command regex ls 's/(.+)/po @import Foundation; [[NSFileManager
    defaultManager] contentsOfDirectoryAtPath:@"%1" error:nil]/'
    command regex dump_stuff "s/(.+)/image lookup -rn '\+\[\w+(\(\w+\))?\ \w+
    \]$' %1 /"
    command script import /usr/local/opt/chisel/libexec/fblldb.py
    
    command script import /Users/devzkn/code/lldb/LLDB/lldb_commands/dslldb.py
    
    
    展开全文
  • LLDB 常用命令

    千次阅读 2018-03-22 00:19:39
    LLDB 小结 简介 LLDB 是新一代高性能调试器,其是一组可重用组件的集合,这些组件大多是 LLVM 工程中的类库,如 Clang 表达式解析器或 LLVM 反汇编程序等。LLDB 是 Xcode 中默认的调试器,并且支持调试 C/C++ ...

    LLDB 小结

    简介

    LLDB 是新一代高性能调试器,其是一组可重用组件的集合,这些组件大多是 LLVM 工程中的类库,如 Clang 表达式解析器或 LLVM 反汇编程序等。LLDB 是 Xcode 中默认的调试器,并且支持调试 C/C++ 程序,详情参考官方文档

    LLDB 命令

    LLDB 的命令格式不同于 GDB 的命令格式的随意,而是统一为如下格式:

    <命令名称> <命令动作> [-可选项 [可选项的值]] [参数1 [参数2···]]

    需要注意的是,不管是命令名称、命令动作、可选项还是参数,它们都是由空格分隔的,所以如果,参数本身包含由空格符号,那么,使用双引号来包裹参数,如果,参数中包含有反斜杠或双引号,那么使用反斜杠对它们进行转义。

    多个可选项在命令行中的顺序并不是固定的,但是如果可选项后的参数是以 - 开始的,那么需要使用 -- 标记可选项的结束,如下面的命令:

    (lldb) process launch --stop-at-entry -- -program_arg_1 value -program_arg_2 value
    
    //可选项顺序可以不固定
    (lldb) breakpoint set -l 34 -f UIView+Theme.m
    (lldb) breakpoint set -f UIView+Theme.m -l 35

    开启调试

    打开命令行,使用如下命令:

    Martin-mini:Applications hanxuejian$ lldb zoom.us.app/
    (lldb) target create "zoom.us.app/"
    Current executable set to 'zoom.us.app/' (i386).

    或者

    Martin-mini:Applications hanxuejian$ lldb
    (lldb) file zoom.us.app/
    Current executable set to 'zoom.us.app/' (i386).
    (lldb) quit
    Martin-mini:Applications hanxuejian$ 

    或者使用 Xcode 进入调试模式。

    结束调试

    (lldb) quit

    设置命令别名

    对于常用的命令,可以设置别名,使其简单化,如:

    (lldb) breakpoint set --file foo.c --line 12
    
    (lldb) command alias bfl breakpoint set -f %1 -l %2 
    (lldb) bfl foo.c 12

    取消命令的别名

    (lldb) command unalias b
    (lldb) command alias b breakpoint

    设置断点

    1. 指定文件及代码行

      (lldb) breakpoint set --file foo.c --line 12 
      (lldb) breakpoint set -f foo.c -l 12
    2. 指定函数名称

      (lldb) breakpoint set --name foo 
      (lldb) breakpoint set -n foo
      (lldb) breakpoint set --name foo --name bar
    3. 指定方法名称

      (lldb) breakpoint set --method foo 
      (lldb) breakpoint set -M foo
    4. 指定 OC 选择器

      (lldb) breakpoint set --selector alignLeftEdges: 
      (lldb) breakpoint set -S alignLeftEdges:
    5. 指定映像文件

      (lldb) breakpoint set --shlib foo.dylib --name foo 
      (lldb) breakpoint set -s foo.dylib -n foo
      
      //--shlib 可以重复使用
      (lldb) breakpoint set --shlib foo.dylib --name foo --shlib foo1.dylib --name foo1

    编辑断点

    1. 查看设置的断点

      (lldb) breakpoint list

      如下面的例子:

      (lldb) breakpoint set -M load
      Breakpoint 12: 25 locations.
      (lldb) breakpoint list
      Current breakpoints:
      12: name = 'load', locations = 25, resolved = 25, hit count = 0
        12.1: where = MobileCoreServices`_LSPreferences::load(), address = 0x00000001072bc684, resolved, hit count = 0 
        12.2: where = libicucore.A.dylib`icu::CollationRoot::load(UErrorCode&), address = 0x00000001074eef18, resolved, hit count = 0 
        12.3: where = libicucore.A.dylib`icu::VTimeZone::load(icu::VTZReader&, UErrorCode&), address = 0x00000001075c2b34, resolved, hit count = 0 
        12.4: where = WebCore`WebCore::CachedFont::load(WebCore::CachedResourceLoader&, WebCore::ResourceLoaderOptions const&), address = 0x000000010aa16a20, resolved, hit count = 0 
        12.5: where = WebCore`WebCore::CachedImage::load(WebCore::CachedResourceLoader&, WebCore::ResourceLoaderOptions const&), address = 0x000000010aa18170, resolved, hit count = 0 
        12.6: where = WebCore`WebCore::CachedResource::load(WebCore::CachedResourceLoader&, WebCore::ResourceLoaderOptions const&), address = 0x000000010aa1dc40, resolved, hit count = 0 
        12.7: where = WebCore`WebCore::CachedSVGDocumentReference::load(WebCore::CachedResourceLoader&, WebCore::ResourceLoaderOptions const&), address = 0x000000010aa2ad20, resolved, hit count = 0 
        12.8: where = WebCore`WebCore::CSSFontFace::load(), address = 0x000000010aafab20, resolved, hit count = 0 
        12.9: where = WebCore`WebCore::CSSFontFaceSource::load(WebCore::CSSFontSelector&), address = 0x000000010ab028a0, resolved, hit count = 0 
        12.10: where = WebCore`WebCore::FontFace::load(), address = 0x000000010ad5a2a0, resolved, hit count = 0 
        12.11: where = WebCore`WebCore::FontFaceSet::load(WTF::String const&, WTF::String const&, WebCore::DOMPromise<WTF::Vector<WTF::RefPtr<WebCore::FontFace>, 0ul, WTF::CrashOnOverflow, 16ul> >&&), address = 0x000000010ad5ad60, resolved, hit count = 0 
        12.12: where = WebCore`WebCore::FrameLoader::load(WebCore::DocumentLoader*), address = 0x000000010ad9fc70, resolved, hit count = 0 
        12.13: where = WebCore`WebCore::HRTFDatabaseLoader::load(), address = 0x000000010ae22e20, resolved, hit count = 0 
        12.14: where = WebCore`WebCore::HTMLMediaElement::load(), address = 0x000000010ae7e7f0, resolved, hit count = 0 
        12.15: where = WebCore`WebCore::JSFontFace::load(JSC::ExecState&), address = 0x000000010b1017f0, resolved, hit count = 0 
        12.16: where = WebCore`WebCore::MediaPlayer::load(WebCore::URL const&, WebCore::ContentType const&, WTF::String const&), address = 0x000000010b3d3ad0, resolved, hit count = 0 
        12.17: where = WebCore`WebCore::NullMediaPlayerPrivate::load(WTF::String const&), address = 0x000000010b3d6820, resolved, hit count = 0 
        12.18: where = WebCore`WebCore::MediaPlayerPrivateAVFoundation::load(WTF::String const&), address = 0x000000010b3d7880, resolved, hit count = 0 
        12.19: where = WebCore`WebCore::TextTrackLoader::load(WebCore::URL const&, WTF::String const&, bool), address = 0x000000010b8d89d0, resolved, hit count = 0 
        12.20: where = WebCore`WebCore::FrameLoader::load(WebCore::FrameLoadRequest const&), address = 0x000000010ada2570, resolved, hit count = 0 
        12.21: where = NLP`NL::SpotlightQueryConverter::load(NLSearchAppContext), address = 0x000000010fc3a4ca, resolved, hit count = 0 
        12.22: where = NLP`NL::SearchDateDisplayFormatter::load(), address = 0x000000010fc7c354, resolved, hit count = 0 
        12.23: where = libmarisa.dylib`marisa::Trie::load(char const*), address = 0x000000010fda4618, resolved, hit count = 0 
        12.24: where = JavaScriptCore`JSC::DFG::ByteCodeParser::load(unsigned int, unsigned int, JSC::DFG::GetByOffsetMethod const&, JSC::DFG::NodeType), address = 0x0000000110184900, resolved, hit count = 0 
        12.25: where = JavaScriptCore`JSC::DFG::ByteCodeParser::load(unsigned int, JSC::ObjectPropertyConditionSet const&, JSC::DFG::NodeType), address = 0x0000000110184ad0, resolved, hit count = 0 
      
      (lldb)

      在使用命令设置断点时,符合条件的断点可能不止一个,并且,如果新加载文件后,符合条件的断点可能还会增加。当然,设置不存在的断点,断点信息也可以查看得到,但是其 locations 为 0 。

      (lldb) breakpoint set --file test.m --line 100
      Breakpoint 13: no locations (pending).
      WARNING:  Unable to resolve breakpoint to any actual locations.
      (lldb) breakpoint list
      Current breakpoints:
      13: file = 'test.m', line = 100, exact_match = 0, locations = 0 (pending)
    2. 将断点置为不可用

      (lldb) breakpoint disable 1
      1 breakpoints disabled.
    3. 将断点置为可用

      (lldb) breakpoint enable 1
      1 breakpoints enabled.
    4. 删除断点

      (lldb) breakpoint delete 1
      1 breakpoints deleted; 0 breakpoint locations disabled.
    5. 为断点添加命令或脚本

      (lldb) breakpoint command add
      Enter your debugger command(s).  Type 'DONE' to end.
      > bt
      > DONE
      (lldb) breakpoint command list 2
      Breakpoint 2:
          Breakpoint commands:
            bt
      (lldb) 

      从下面的错误命令可知断点所支持的语言:

      (lldb) breakpoint command add -s OC
      error: invalid enumeration value, valid values are: "command", "python", "default-script"

    观察变量

    1. 设置观察变量

      (lldb) watchpoint set variable backgroundColor
      Watchpoint created: Watchpoint 2: addr = 0x7fff52dcdb38 size = 8 state = enabled type = w
          declare @ '/Users/hanxuejian/Downloads/ThemeChange-master/TestTheme/TestTheme/UIView+Theme.m:31'
          watchpoint spec = 'backgroundColor'
          new value: 0x000061000007e780
    2. 查看观察变量

      (lldb) watchpoint list
      Number of supported hardware watchpoints: 4
      Current watchpoints:
      Watchpoint 1: addr = 0x7fff52dcd6d8 size = 8 state = disabled type = w
          declare @ '/Users/hanxuejian/Downloads/ThemeChange-master/TestTheme/TestTheme/UIView+Theme.m:31'
          watchpoint spec = 'backgroundColor'
          old value: 0xffff8067ddcff720
          new value: 0xffff8067ddcff720
      Watchpoint 2: addr = 0x7fff52dcdb38 size = 8 state = enabled type = w
          declare @ '/Users/hanxuejian/Downloads/ThemeChange-master/TestTheme/TestTheme/UIView+Theme.m:31'
          watchpoint spec = 'backgroundColor'
          new value: 0x000061000007e780
    3. 设置查看条件

      (lldb) watchpoint set variable num
      Watchpoint created: Watchpoint 1: addr = 0x10f932558 size = 4 state = enabled type = w
          declare @ '/Users/hanxuejian/Downloads/ThemeChange-master/TestTheme/TestTheme/UIView+Theme.m:31'
          watchpoint spec = 'num'
          new value: 16
      
      //添加条件
      (lldb) watchpoint modify -c "(num == 25)"
      (lldb) watchpoint list
      Number of supported hardware watchpoints: 4
      Current watchpoints:
      Watchpoint 1: addr = 0x10f932558 size = 4 state = enabled type = w
          declare @ '/Users/hanxuejian/Downloads/ThemeChange-master/TestTheme/TestTheme/UIView+Theme.m:31'
          watchpoint spec = 'num'
          old value: 20
          new value: 21
          condition = '(num == 25)'
      
      //条件达成
      2018-03-21 19:11:44.302 TestTheme[6938:828079] XPC connection interrupted
      
      Watchpoint 1 hit:
      old value: 21
      new value: 25

    开始程序

    使用 LLDB 调试程序,可以直接加载可执行程序,或者绑定已经在执行的程序,而后开始进行调试。

    如下面的例子,先创建一个简单 C 程序。

    #include <stdio.h>
    int main(int argc, char const *argv[])
    { 
        int i = 1;
        while(true) {
            char *c = "this is a test";
            printf("%s %i\n", c,i++);   
        }
        return 0;
    }

    编辑后,保存为 test.cpp 文件,然后在命令行中执行下面的命令:

    gcc test.cpp -o test

    得到可执行文件,而后执行下面的命令。

    (lldb) target create test
    Current executable set to 'test' (x86_64).
    (lldb) process launch test
    Process 7169 launched: '/Users/hanxuejian/Desktop/Temp/test' (x86_64)
    this is a test 1
    this is a test 2
    this is a test 3
    this is a test 4

    当然,也可以先运行 test 可执行文件,而后使用下面的命令进行调试绑定。

    (lldb) process attach --pid 7232 
    (lldb) process attach --name test 
    (lldb) process attach --name test --waitfor

    但是要注意的是,如果存在两个名称相同的进程,那么绑定是无法成功的。

    线程控制

    在加载进程开始调试后,当执行到设置断点时,可以使用 thread 命令控制代码的执行。

    1. 线程继续执行

      (lldb) thread continue 
      Resuming thread 0x2c03 in process 46915 
      Resuming process 46915 
      (lldb)
    2. 线程进入、单步执行或跳出

      (lldb) thread step-in    
      (lldb) thread step-over  
      (lldb) thread step-out
    3. 单步指令的执行

      (lldb) thread step-inst  
      (lldb) thread step-over-ins
    4. 执行指定代码行数直到退出当前帧

      (lldb) thread until 100
    5. 查看线程列表,第一个线程为当前执行线程

      (lldb) thread list
      Process 46915 state is Stopped
      * thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
        thread #2: tid = 0x2e03, 0x0000
    6. 查看当前线程栈

      (lldb) thread backtrace
      thread #1: tid = 0x2c03, stop reason = breakpoint 1.1, queue = com.apple.main-thread
       frame #0: 0x0000000100010d5b, where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405
       frame #1: 0x00007fff8602d152, where = AppKit`-[NSApplication sendAction:to:from:] + 95
       frame #2: 0
      
      //查看所有线程的调用栈
      (lldb) thread backtrace all
    7. 设置当前线程

      (lldb) thread select 2

    帧状态

    查看当前线程中当前帧(或者说方法/函数)中的变量/参数,可以使用 frame 命令。

    1. 查看本地变量及参数

      (lldb) frame variable 
      (ViewController *) self = 0x00007f8289d08b50
      (SEL) _cmd = "testCoreGraphics"
      (TestView *) view = 0x00007f828c102760
      
      //指定要查看的变量
      ((lldb) frame variable self->isa
      (Class) self->isa = ViewController
    2. 打印对象

      (lldb) frame variable -o self
      (ViewController *) self = 0x00007f8289d08b50 <ViewController: 0x7f8289d08b50>
    3. 选择栈中的方法

      (lldb) frame select 2
      frame #2: 0x000000010ca7446c UIKit`-[UIViewController loadViewIfRequired] + 1235
      UIKit`-[UIViewController loadViewIfRequired]:
          0x10ca7446c <+1235>: movl   (%r14), %eax
          0x10ca7446f <+1238>: testl  %eax, %eax
          0x10ca74471 <+1240>: je     0x10ca7447f               ; <+1254>
          0x10ca74473 <+1242>: cmpl   $0x70000, %eax            ; imm = 0x70000 
      (lldb) 

    修改变量或调用函数

    使用 expression 命令可以在调试过程中修改变量的值,或者执行函数。

    1. 修改变量值

      (lldb) expression a
      (int) $0 = 9
      (lldb) frame variable a
      (int) a = 9
      (lldb) expression a=10
      (int) $1 = 10
      (lldb) frame variable a
      (int) a = 10
      (lldb) 
    2. 调用函数

      (lldb) expression printf("execute function %i",a)
      (int) $2 = 19
      execute function 10

      对于执行结果都会自动保存,以备他用。

    展开全文
  • LLDB浅析

    2019-08-06 22:35:14
    文章目录一、LLDB命令结构二、命令选项三、基础执行命令(Execution Commands)流程控制processthread断点命令(Breakpoint Commands)管理断点实例疑问操作断点创建编辑删除breakpoint观察点命令(Watchpoint ...


    现LLDB调试器已经成为Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板。


    一、LLDB命令结构

    <noun> <verb> [-options [option-value]] [argument [argument...]]
    
    
    <command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
    
    
    1. <command>(命令)和<subcommand>(子命令):LLDB调试器对象的名称。命令和子命令按层级结构来排列:一个特定的命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
    2. <action>:我们想在组合的调试器对象(前面的命令序列)的上下文中执行的一些操作。
    3. <options>:行为修改器(action modifiers)。通常带有一些值。
    4. <argument>:根据使用的命令的上下文来表示各种不同的东西。例如,使用process launch命令启动进程,该上下文中的参数就是您在命令行中输入的参数,就好像你在调试会话之外调用进程一样。

    LLBD命令行解析是在命令执行之前完成的,因此在所有命令中都是统一的。基本命令的命令语法非常简单,参数、选项和选项值都用空格分隔,并且双引号用于保护参数中的空格。如果需要在参数中使用反斜杠或双引号,可以在参数中使用反斜杠。这使得命令语法更加规范

    如下所示:

    (lldb) command [subcommand] -option "some \"quoted\" string"
    
    (lldb) command [subcommand] -option 'some "quoted" string'
    

    二、命令选项

    LLDB中的命令选项有规范形式(canonical,也称为“discoverable”)和缩写形式(abbreviated)。

    规范形式:

    expression –object-description – foo
    

    缩写形式:

    e -0 – foo
    

    别名:

    po foo
    

    选项可以放在命令行的任何位置,但如果参数以“-”开头,则必须通过添加选项终止来告诉LLDB您已经完成了当前命令的选项:“–”,例如,如果要启动进程并提供“进程启动”“command the”–stop at entry“选项,但是您希望将要启动的进程使用参数”-program_arg value“启动,您将键入:

    (lldb) process launch --stop-at-entry -- -program_arg value
    
    

    三、基础

    执行命令(Execution Commands)

    流程控制

    在这里插入图片描述
    从左到右,四个按钮分别是:continue,step over,step into,step out。

    1. continue
      取消程序的暂停,允许程序正常执行 (要么一直执行下去,要么到达下一个断点)。
      LLDB中使用process continue命令。
    (lldb) process continue 
    (lldb) continue 
    (lldb) c
    
    1. step over
      以黑盒的方式执行一行代码。如果所在这行代码是一个函数调用,那么就不会跳进这个函数,而是会执行这个函数,然后继续。
      LLDB中使用thread step-over命令。
    (lldb) thread step-over 
    (lldb) next 
    (lldb) n 
    
    1. step in
      如果你确实想跳进一个函数调用来调试或者检查程序的执行情况。当前行不是函数调用时,next和step效果是一样的。
      在LLDB中使用thread step-in命令。
    (lldb) thread step-in 
    (lldb) step 
    (lldb) s
    
    1. step out
      如果你曾经不小心跳进一个函数,但实际上你想跳过它,常见的反应是重复的运行step over直到函数返回。其实这种情况,step out会继续执行到下一个返回语句 (直到一个堆栈帧结束) 然后再次停止。
      在LLDB中使用thread step-out命令。
    (lldb) thread step-out 
    (lldb) finish 
    

    process

    使用参数启动进程

    (lldb) process launch -- <args> 
    (lldb) run <args> 
    (lldb) r <args>
    

    取消程序的暂停

    (lldb) process continue 
    (lldb) continue 
    (lldb) c
    

    thread

    在当前选定的线程中执行源级单步。

    (lldb) thread step-in 
    (lldb) step 
    (lldb) s
    

    在当前选定的线程中执行源级别单步操作。

    (lldb) thread step-over 
    (lldb) next 
    (lldb) n 
    

    在当前选定的线程中执行指令级单步。(遇到汇编函数,会进入汇编函数内部)

    (lldb) thread step-inst 
    (lldb) si
    

    在当前选定的线程中执行指令级别的单步操作。

    (lldb) thread step-inst-over 
    (lldb) ni
    

    走出当前选择的帧的。

    (lldb) thread step-out 
    (lldb) finish 
    

    立即从当前选定的帧返回,并带有可选的返回值。

    (lldb) thread return <RETURN EXPRESSION>
    

    运行直到我们达到第12行或控制离开当前函数(行数只能在该函数之内)

    (lldb) thread until 12
    

    断点命令(Breakpoint Commands)

    管理断点

    在这里插入图片描述

    在Xcode的左侧面板,有一组按钮。其中一个看起来像断点。点击它打开断点导航,这是一个可以快速管理所有断点的面板。
    在LLDB中使用breakpoint list命令。

    (lldb) breakpoint list 
    (lldb) br l
    

    可以点击单个断点来开启或关闭。
    在LLDB中使用breakpoint enable 和breakpoint disable 。

    (lldb) breakpoint enable <breakpointID> 
    (lldb) br e <breakpointID>
    
    (lldb) breakpoint disable <breakpointID> 
    (lldb) br disable <breakpointID>
    

    显示断点详细信息

    (lldb) breakpoint list -verbose
    (lldb) br l -v
    

    实例

    (lldb) br l
    Current breakpoints:
    1: name = 'UIApplicationMain', locations = 1, resolved = 1, hit count = 1
    
      1.1: where = UIKitCore`UIApplicationMain, address = 0x0000000108d52791, resolved, hit count = 1 
    
    2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2, resolved = 2, hit count = 0
    
      2.1: where = libobjc.A.dylib`objc_exception_throw, address = 0x000000010548a705, resolved, hit count = 0 
      2.2: where = libc++abi.dylib`__cxa_throw, address = 0x00000001076df089, resolved, hit count = 0 
    
    3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1
    
      3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1 
    
    4: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 29, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    
      4.1: where = lldbTest`-[AppDelegate testExecutionCommands] + 23 at AppDelegate.m:29, address = 0x0000000104b6c647, resolved, hit count = 0 
    

    解释一下输出的信息

    3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1
    

    1.冒号前面的数字(3)是断点组的组号。
    2.file = ‘/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m’, line = 29 断点的生成方式.
    3.locations = 1 该组有几个断点。
    4.resolved = 1 该组可用的断点的数量。
    5.hit count = 1 该组断点被段过的次数。每一次程序在断点中暂停一次这个计数+1。

    3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1 
    
    1. 3.1,3组号,1是组里编号,这个断点是第三组中的第一个断点。
    2. lldbTest是工程名称。
    3. -[AppDelegate application:didFinishLaunchingWithOptions:] 是断点所在类的函数。
      • 70指的是该断点所在指令在其所在栈空间的偏移量(十进制)。
    4. AppDelegate.m:23 表示该断点所在文件行数。
    5. address = 0x0000000104b6c5e6 表示该断点所在的物理地址。
    6. resolved 表示是否可用,不可用是unresolved。
    7. hit count = 1 表示该断点拦截次数。

    疑问

    LLDB的断点命令都是临时生效?
    user级别断点和project级别断点,使用breakpoint list查看不出区别?

    操作断点

    在Xcode中可以通过在源码页面器的滚槽上点击来创建断点。通过把断点拖拽出滚槽,然后释放鼠标来删除断点。
    也可以点击断点栏左侧的 + 按钮创建断点,在断点导航页选择断点,然后按下删除键删除。
    在这里插入图片描述

    创建

    在LLDB中使用breakpoint set命令。

    可以使用缩写形式 br。虽然 b 是一个完全不同的命令 (_regexp-break 的缩写),但恰好也可以实现和上面同样的效果。

    给某个函数设置断点(私有方法亦可设置)

    (lldb) breakpoint set --name main 
    (lldb) br s -n main 
    (lldb) b main
    
    (lldb) breakpoint set --name "-[NSString stringWithFormat:]" 
    (lldb) b -[NSString stringWithFormat:]
    

    某个文件中的某行设置一个断点

    (lldb) breakpoint set --file test.c --line 12 
    (lldb) br s -f test.c -l 12 
    (lldb) b test.c:12
    

    给C++中所有命名为main的方法设置断点

    (lldb) breakpoint set --method main 
    (lldb) br s -M main
    

    Objective-C中所有命名为count的选择器设置断点

    (lldb) breakpoint set --selector count 
    (lldb) br s -S count
    

    使用–shlib 来将断点限定在一个特定的可执行库中

    (lldb) breakpoint set --shlib foo.dylib --name foo
    

    通过函数名称上的正则表达式设置断点

    (lldb) breakpoint set --func-regex regular-expression 
    (lldb) br s -r regular-expression
    

    编辑

    在这里插入图片描述

    1. Symbol:
      符号断点。填入想设置断点的方法 (例如:-[NSException raise],-号是实例方法,+号是类方法)
    2. Module
      填入要设置断点的方法或函数是否在位于dylib中,默认不填。
    3. Condition
      填入条件。返回值必须为BOOL类型。确定类类型isKindOfClass:,确定对象在继承体系中的位置的isMemberOfClass:,判断一个对象是否能接收某个特定消息的respondsToSelector:,判断一个对象是否遵循某个协议的conformsToProtocol:,以及提供方法实现地址的methodForSelector:
    4. Ignore
      忽略前n次不执行断点,优先级在condition之上
    5. Action
      在这里插入图片描述
    • AppleScript
      内置的一种功能强大的脚本语言。
    • Capture GPU Frame
      捕捉动画帧速。
    • Debugger Command
      调试器命令。
      可输入LLDB相关命令。
    • Log Message
      日志信息。
      %B会打印断点的名字,%H会打印断点的调用次数,@@中间可以输入表达式。打印内容直接输入。
    • Shell Command
      终端命令。
    • Sound
      播放声音。
    1. Options
      勾选Automatically continue after evaluating actions之后程序会在断点产生后继续运行。这个属性是相当有用的,可以输入调试信息至于不暂停程序。

    删除

    在LLDB中使用breakpoint delete命令。

    (lldb) breakpoint delete 1 
    (lldb) br del 1
    

    breakpoint

    在名为main的所有函数上设置断点。

    (lldb) breakpoint set --name main 
    (lldb) br s -n main 
    (lldb) b main
    

    在文件test.c的第12行中设置断点。

    (lldb) breakpoint set --file test.c --line 12 
    (lldb) br s -f test.c -l 12 
    (lldb) b test.c:12
    

    给C++中所有命名为main的方法设置断点。

    (lldb) breakpoint set --method main 
    (lldb) br s -M main
    

    在Objective-C函数-[NSString stringWithFormat:]中设置断点。

    (lldb) breakpoint set --name "-[NSString stringWithFormat:]" 
    (lldb) b -[NSString stringWithFormat:]
    

    在其选择器为count的所有Objective-C方法中设置断点

    (lldb) breakpoint set --selector count 
    (lldb) br s -S count
    

    通过函数名称上的正则表达式设置断点。

    (lldb) breakpoint set --func-regex regular-expression 
    (lldb) br s -r regular-expression
    

    通过源文件内容上的正则表达式设置断点。

    (lldb) breakpoint set --name foo --condition '(int)strcmp(y,"hello") == 0' 
    (lldb) br s -n foo -c '(int)strcmp(y,"hello") == 0'
    

    列出所有断点。

    (lldb) breakpoint list 
    (lldb) br l
    

    删除断点。

    (lldb) breakpoint delete 1 
    (lldb) br del 1
    

    观察点命令(Watchpoint Commands)

    设置观察点

    有时候我们会关心类的某个属性什么时候被人修改了,最简单的方法当然就是在setter的方法打断点,或者在@property的属性生命行打上断点。这样当对象的setter方法被调用时,就会触发这个断点。
    在这里插入图片描述
    当然这么做是有缺点的,对于直接访问内存地址的修改,setter方法的断点并没有办法监控得到,因此我们需要用到watchpoint命令。

        _watchPointInteger = 8;
    

    watchpoint命令在XCode的GUI中也可以直接使用,当程序暂停时,我们能对当前程序栈中的变量设置watchpoint。值得注意的是,watchpoint是直接设置到该变量所在的内存地址上的,所以当这个变量释放了后,watchpoint仍然是对这个地址的内存生效的。
    在这里插入图片描述
    受限于CPU的支持程度,在intel平台上,提供了4个slot供watchpoint使用,所以同时只可设置最多4个watchpoint,arm上是2个。

    在LLDB中使用用watchpoint命令(不能使用点语法,因为是getter函数)

    (lldb) wa set variable self->_watchPointInteger
    Watchpoint created: Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
        watchpoint spec = 'self->_watchPointInteger'
        new value: 1
    (lldb) wa modify -c 'self->_watchPointInteger == 5'
    
    Watchpoint 1 hit:
    old value: 1
    new value: 5
    (lldb) wa list
    Number of supported hardware watchpoints: 4
    Current watchpoints:
    Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
        watchpoint spec = 'self->_watchPointInteger'
        old value: 1
        new value: 5
        condition = 'self->_watchPointInteger == 5'
    

    watchpoint

    在写入变量时在变量上设置观察点。

    (lldb) watchpoint set variable global_var 
    (lldb) wa s v global_var 
    

    在写入时在内存位置设置观察点。 如果未指定“-x byte_size”,则要监视的区域的大小默认为指针大小。 此命令采用原始输入,作为表达式计算,返回指向区域开头的无符号整数,位于’–'选项终止符之后。

    (lldb) watchpoint set expression -- my_ptr 
    (lldb) wa s e -- my_ptr
    

    在观察点上设置条件。

    (lldb) watch set var global 
    (lldb) watchpoint modify -c '(global==5)' 
    (lldb) c 
    ... 
    (lldb) bt 
    * thread #1: tid = 0x1c03, 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16, stop reason = watchpoint 1 
    frame #0: 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16 
    frame #1: 0x0000000100000eac a.out`main + 108 at main.cpp:25 
    frame #2: 0x00007fff8ac9c7e1 libdyld.dylib`start + 1 
    (lldb) frame var global 
    (int32_t) global = 5
    

    列出所有观察点。

    (lldb) watchpoint list 
    (lldb) watch l 
    

    删除观察点。

    (lldb) watchpoint delete 1 
    (lldb) watch del 1 
    

    检查变量(Examining Variables)

    查看调用栈状态

    检查帧参数和本地变量的最简便的方式是使用frame variable命令

    (lldb) frame variable 
    (lldb) fr v 
    

    如果没有指定任何变量名,则会显示所有参数和本地变量。

    (lldb) frame variable
    (AppDelegate *) self = 0x000060000123ba00
    (SEL) _cmd = "application:didFinishLaunchingWithOptions:"
    (UIApplication *) application = 0x00007f8cb6c02780
    (NSDictionary *) launchOptions = nil
    

    如果指定参数名或变量名,则只打印指定的值。

    (lldb) frame variable self
    (AppDelegate *) self = 0x000060000123ba00
    

    frame variable命令不是一个完全的表达式解析器,但它支持一些简单的操作符,如&,*,->,[]。这个数组括号可用于指针,以将指针作为数组处理。
    frame variable命令会在变量上执行”对象打印”操作。目前,LLDB只支持Objective-C打印,使用的是对象的description方法。

    (lldb) frame variable *self
    (AppDelegate) *self = {
      _window = 0x00007f8cb6c2a620
      _stepInString = 0x0000000108c74070 @"stepInString"
      _stepOutString = 0x0000000108c740b0 @"12345"
      _watchPointInteger = 5
      _watchPointView = 0x00007f8cb8904320
    }
    
    (lldb) frame variable launchOptions[0]
    (NSObject) launchOptions[0] = <parent is NULL>
    

    如果想查看另外一帧,可以使用frame select命令

    (lldb) frame select 1
    frame #1: 0x000000010ce50bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
    UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]:
        0x10ce50bde <+280>: cmpb   $0x0, -0x29(%rbp)
        0x10ce50be2 <+284>: setne  %r13b
        0x10ce50be6 <+288>: andb   %al, %r13b
        0x10ce50be9 <+291>: movl   -0x38(%rbp), %r15d
    

    frame

    显示当前帧的参数和局部变量。

    (lldb) frame variable 
    (lldb) fr v 
    

    显示当前帧的局部变量。

    (lldb) frame variable --no-args 
    (lldb) fr v -a 
    

    显示局部变量“bar”的内容。

    (lldb) frame variable bar 
    (lldb) fr v bar 
    (lldb) p bar 
    

    显示格式为十六进制的局部变量“bar”的内容。

    (lldb) frame variable --format x bar 
    (lldb) fr v -f x bar 
    

    target

    显示全局变量“baz”的内容。

    (lldb) target variable baz 
    (lldb) ta v baz 
    

    显示当前源文件中定义的全局/静态变量。

    (lldb) target variable 
    (lldb) ta v 
    

    每次停止时显示变量“argc”和“argv”。

    (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
    

    仅当你在main函数中停止时,才显示变量“argc”和“argv”。

    (lldb) target stop-hook add --name main --one-liner "frame variable argc argv" 
    (lldb) ta st a -n main -o "fr v argc argv"
    

    仅当您在名为MyClass的c类中停止时,才显示变量“* this”。

    (lldb) target stop-hook add --classname MyClass --one-liner "frame variable *this" 
    (lldb) ta st a -c MyClass -o "fr v *this"
    

    评价表达式(Evaluating Expressions)

    打印

    打印值很简单;只要试试 print 命令:

    (lldb) expr (int) printf ("Print nine: %d.", 4 + 5) 
    or using the print alias: 
    (lldb) print (int) printf ("Print nine: %d.", 4 + 5) 
    

    结果中有个1使1。实际上你可以使用它来指向这个结果。另外后面的数值是递增的,每打印一个与对象相关的命令,这个值都会加1

    (lldb) p self.stepOutString
    (__NSCFConstantString *) $1 = 0x0000000106999090 @"stepOutString"
    

    赋值

    如果想改变一个值怎么办?你或许会猜 modify。其实这时候我们要用到的是 expression 这个方便的命令。

    (lldb) print self.watchPointInteger
    (NSInteger) $2 = 1
    (lldb) expression  self.watchPointInteger=8
    (NSInteger) $3 = 8
    (lldb) print self.watchPointInteger
    (NSInteger) $4 = 8
    

    打印对象

    上面的print命令会打印出对象的很多信息,如果我们只想查看对象的值的信息,则可以使用po(print object的缩写)命令。

    (lldb) expr -o -- [SomeClass returnAnObject] 
    or using the po alias: 
    (lldb) po [SomeClass returnAnObject] 
    

    打印字符串

    (lldb) po self.stepOutString
    12345
    
    (lldb) p self.stepOutString
    (__NSCFConstantString *) $6 = 0x00000001069990b0 @"12345"
    (lldb) 
    

    打印数组

    (lldb) p @[ @"foo", @"bar" ]
    (__NSArrayI *) $7 = 0x00006000018d56c0 @"2 elements"
    (lldb) po $7
    <__NSArrayI 0x6000018d56c0>(
    foo,
    bar
    )
    
    

    expr

    评估当前帧中的广义表达式。

    (lldb) expr (int) printf ("Print nine: %d.", 4 + 5) 
    or using the print alias: 
    (lldb) print (int) printf ("Print nine: %d.", 4 + 5) 
    

    为便利变量创建和赋值。

    (lldb) expr unsigned int $foo = 5 
    

    打印对象的ObjC的"description(描述)"。

    (lldb) expr -o -- [SomeClass returnAnObject] 
    or using the po alias: 
    (lldb) po [SomeClass returnAnObject] 
    

    打印表达式结果的动态类型。

    (lldb) expr -d 1 -- [SomeClass returnAnObject] 
    (lldb) expr -d 1 -- someCPPObjectPtrOrReference 
    or set dynamic type printing to be the default: (lldb) settings set target.prefer-dynamic run-target
    

    调用函数使您可以在函数中的断点处停止。

    (lldb) expr -i 0 -- function_with_a_breakpoint()
    

    调用崩溃的函数,并在函数崩溃时停止。

    (lldb) expr -u 0 -- function_which_crashes() 
    

    检查线程状态(Examining Thread State)

    查看线程状态

    在进程停止后,LLDB会选择一个当前线程和线程中当前帧(frame)。很多检测状态的命令可以用于这个线程或帧。
    为了检测进程的当前状态,可以从以下命令开始:

    (lldb) thread list
    
    Process 59154 stopped
    * thread #1: tid = 0x56c99e, 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
      thread #3: tid = 0x56ca07, 0x00000001099b15be libsystem_kernel.dylib`__workq_kernreturn + 10
      thread #4: tid = 0x56ca09, 0x00000001099b31b2 libsystem_kernel.dylib`__psynch_cvwait + 10, name = 'JavaScriptCore bmalloc scavenger'
      thread #5: tid = 0x56ca0a, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'WebThread'
      thread #6: tid = 0x56ca0b, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
      thread #7: tid = 0x56ca0d, 0x00000001099f2dec libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 268, queue = 'com.apple.root.utility-qos'
    (lldb) 
    

    星号(*)表示thread #1为当前线程。为了获取线程的跟踪栈,可以使用以下命令:

    (lldb) thread backtrace 
    (lldb) bt
    

    例如

    (lldb) thread backtrace
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
      * frame #0: 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28
        frame #1: 0x000000010ab75bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
        frame #2: 0x000000010ab775cb UIKitCore`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3979
        frame #3: 0x000000010ab7cc2f UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1623
        frame #4: 0x000000010a39b4e9 UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 866
        frame #5: 0x000000010a3a429c UIKitCore`+[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
        frame #6: 0x000000010a39b126 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 233
        frame #7: 0x000000010a39bae0 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1085
        frame #8: 0x000000010a399cb5 UIKitCore`__82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 795
        frame #9: 0x000000010a39995f UIKitCore`-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 435
        frame #10: 0x000000010a39ea90 UIKitCore`__125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 584
        frame #11: 0x000000010a39f80e UIKitCore`_performActionsWithDelayForTransitionContext + 100
        frame #12: 0x000000010a39e7ef UIKitCore`-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 221
        frame #13: 0x000000010a3a393a UIKitCore`-[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
        frame #14: 0x000000010ab7b44e UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 515
        frame #15: 0x000000010a71fd09 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 357
        frame #16: 0x00000001133b42da FrontBoardServices`-[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 448
        frame #17: 0x00000001133bf443 FrontBoardServices`__56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 271
        frame #18: 0x00000001133beb3a FrontBoardServices`__40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 53
        frame #19: 0x0000000109616602 libdispatch.dylib`_dispatch_client_callout + 8
        frame #20: 0x0000000109619b78 libdispatch.dylib`_dispatch_block_invoke_direct + 301
        frame #21: 0x00000001133f3ba8 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
        frame #22: 0x00000001133f3860 FrontBoardServices`-[FBSSerialQueue _performNext] + 457
        frame #23: 0x00000001133f3e40 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
        frame #24: 0x0000000107c7d721 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
        frame #25: 0x0000000107c7cf93 CoreFoundation`__CFRunLoopDoSources0 + 243
        frame #26: 0x0000000107c7763f CoreFoundation`__CFRunLoopRun + 1263
        frame #27: 0x0000000107c76e11 CoreFoundation`CFRunLoopRunSpecific + 625
        frame #28: 0x00000001103f81dd GraphicsServices`GSEventRunModal + 62
        frame #29: 0x000000010ab7e81d UIKitCore`UIApplicationMain + 140
        frame #30: 0x0000000106997200 lldbTest`main(argc=1, argv=0x00007ffee9267fd0) at main.m:14
        frame #31: 0x000000010968c575 libdyld.dylib`start + 1
    (lldb) 
    

    如果想查看所有线程的调用栈,则可以使用以下命令:

    (lldb) thread backtrace all 
    (lldb) bt all
    

    thread

    列出程序中的线程。

    (lldb) thread list
    

    选择线程1作为后续命令的默认线程。

    (lldb) thread select 1 
    (lldb) t 1 
    

    显示当前线程的堆栈回溯。

    (lldb) thread backtrace 
    (lldb) bt
    

    显示所有线程的堆栈回溯。

    (lldb) thread backtrace all 
    (lldb) bt all
    

    回溯当前线程的前五帧。

    (lldb) thread backtrace -c 5 
    (lldb) bt 5 (lldb-169 and later) 
    (lldb) bt -c 5 (lldb-168 and earlier)
    

    frame

    按索引为当前线程选择不同的堆栈帧。

    (lldb) frame select 12 
    (lldb) fr s 12 
    (lldb) f 12 
    

    列出有关当前线程中当前所选帧的信息。

    (lldb) frame info 
    

    选择调用当前堆栈帧的堆栈帧。

    (lldb) up 
    (lldb) frame select --relative=1
    

    选择当前堆栈帧调用的堆栈帧。

    (lldb) down 
    (lldb) frame select --relative=-1 
    (lldb) fr s -r-1 
    

    使用相对偏移量选择不同的堆栈帧。

    (lldb) frame select --relative 2 
    (lldb) fr s -r2 
    
    (lldb) frame select --relative -3 
    (lldb) fr s -r-3 
    

    可执行和共享库查询命令(Executable and Shared Library Query Commands)

    image

    image命令的用法也挺多,首先可以用它来查看工程中使用的库,如下所示:

    (lldb) image list
    
    [  0] D96F1640-B0C5-33FC-A8BB-3290BAA51E88 0x0000000106996000 /Users/lixingdong/Library/Developer/Xcode/DerivedData/lldbTest-fkaqwvfpyrrqthbnxsglafxirqyy/Build/Products/Debug-iphonesimulator/lldbTest.app/lldbTest 
    [  1] D6387150-2FB8-3066-868D-72E1B1C43982 0x000000010e532000 /usr/lib/dyld 
    [  2] C3514384-926E-3813-BF0C-69FFC704E283 0x00000001069a2000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim 
    [  3] E5391C7B-0161-33AF-A5A7-1E18DBF9041F 0x0000000106c8b000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation 
    [  4] 177A61B3-9E02-3A09-9A98-C1C3C9AB7958 0x00000001072b1000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib 
    ......
    

    我们还可以用它来查找可执行文件或共享库的原始地址,这一点还是很有用的,当我们的程序崩溃时,我们可以使用这条命令来查找崩溃所在的具体位置,如下所示:

        NSArray *array = @[@1, @2];
        NSLog(@"item 3: %@", array[2]);
    

    这段代码在运行后会抛出如下异常:

    2019-08-06 22:00:34.724090+0800 lldbTest[59567:5748056] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 2 beyond bounds [0 .. 1]'
    *** First throw call stack:
    (
    	0   CoreFoundation                      0x000000010143d1bb __exceptionPreprocess + 331
    	1   libobjc.A.dylib                     0x00000001009db735 objc_exception_throw + 48
    	2   CoreFoundation                      0x00000001013894ec _CFThrowFormattedException + 194
    	3   CoreFoundation                      0x00000001014bfb00 +[__NSArrayI allocWithZone:] + 0
    	4   lldbTest                            0x00000001000bc311 -[AppDelegate testImageCommands] + 241
    	5   lldbTest                            0x00000001000bc0ae -[AppDelegate application:didFinishLaunchingWithOptions:] + 110
    ......
    
    (lldb) image lookup --address 0x1ec4 
    (lldb) im loo -a 0x1ec4 
    

    根据以上信息,我们可以判断崩溃位置是在AppDelegate.m文件中,要想知道具体在哪一行,可以使用以下命令:

    (lldb) image lookup --address 0x00000001000bc311
          Address: lldbTest[0x0000000100001311] (lldbTest.__TEXT.__text + 945)
          Summary: lldbTest`-[AppDelegate testImageCommands] + 241 at AppDelegate.m:45
    (lldb) image lookup --address 0x00000001014bfb00
          Address: CoreFoundation[0x00000000001aab00] (CoreFoundation.__TEXT.__text + 1741536)
          Summary: CoreFoundation`+[__NSArrayI allocWithZone:]
    

    可以使用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"
    

    补充

    • lldb导入UIKit
    expr @import UIKit
    

    在lldbinit中设置全局自动导入UIKit

    echo display @import UIKit >> ~/.lldbinit
    

    摘要

    https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/Introduction.html
    http://lldb.llvm.org/use/tutorial.html
    https://southpeak.github.io/2015/01/25/tool-lldb/
    https://huos3203.github.io/2018/09/05/调试/使用LLDB独立调试APP/
    https://www.jianshu.com/p/e48b9912b011
    https://www.jianshu.com/p/39fb3d4a1368
    https://www.bbsmax.com/A/6pdDEQLlzw/
    https://blog.csdn.net/xuhen/article/details/77747456
    https://www.jianshu.com/p/2d4083d27653
    https://www.jianshu.com/p/d6a0a5e39b0e
    https://objccn.io/issue-19-2/
    http://openfibers.github.io/blog/2016/02/24/auto-import-frameworks-in-xcode-lldb/

    展开全文
  • 【工具使用】Android调试利器之LLDB

    千次阅读 2018-07-16 18:32:39
    Android Studio作为官方IDE,对NDK的支持也日趋完善,到目前为止,集成了基于CMake的构建工具,以及基于LLDB的NDK调试工具,早在Android之前,LLDB已经成为iOS开发中不可或缺的工具,本文简单介绍一下LLDB的一些常规...

    本文来自 网易云社区 。

    在Android开发当中,或多或少都会接触到NDK的开发。Android Studio作为官方IDE,对NDK的支持也日趋完善,到目前为止,集成了基于CMake的构建工具,以及基于LLDB的NDK调试工具,早在Android之前,LLDB已经成为iOS开发中不可或缺的工具,本文简单介绍一下LLDB的一些常规使用,抛砖引玉,先来看一下我们的示例程序。

     

    在页面启动的时候,会从native-lib中读取字符串,然后展示在UI上,native代码如下

     

    进入调试状态

    通过Android Studio的菜单,选择Attach process,或者直接以调试方式启动程序。 Debugger选择Auto/Native/Hybrid都可以,当然也可以自己push LLDB server到手机设备,然后手动远程连接上去,可以参见远程调试

     

    成功连接调试器之后,Android中集成了LLDB的控制台,在控制台中我们可以通过一系列命令和调试程序进行交互。

    LLDB语法简介

    <noun> <verb> [-options [option-value]] [argument [argument…]]
    

    LLDB的命令都符合上述格式,argument option 以及option-value用空格隔开,LLDB支持前缀匹配,大多数情况下并不需要输入完整命令。如果参数中有空格,则需要用“”进行保护。option可以放在命令中的任意位置,不过如果argument中有以-开始的值,可以通--显示的标识option的结束。 另外还有一些转义规则请参考官方文档

    设置断点

    进入调试状态之后,紧接着就是下断点了,在LLDB中通过breakpoint set命令进行断点的设置。

    #按文件和行号设置断点
    (lldb) breakpoint set -f native-lib.cpp -l 9
    Breakpoint 2: where = libnative-lib.so`getString(int) + 6 at native-lib.cpp:9, address = 0x73a3cf5a
    #按函数名设置断点,不区分C函数和C++Method
    (lldb) breakpoint set -n getString
    Breakpoint 3: 20 locations.
    #按C++方法名设置断点
    (lldb) breakpoint set -M getString
    Breakpoint 4: 18 locations.
    #通过-s参数制定module
    (lldb) breakpoint set -s libnative-lib.so -n getString
    Breakpoint 5: where = libnative-lib.so`getString(int) + 6 at native-lib.cpp:9, address = 0x73a3cf5a
    

    条件断点

    (lldb) breakpoint modify 2 -c 'index == 0'
    (lldb) breakpoint list
    Current breakpoints:
    2: file = 'native-lib.cpp', line = 9, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    Condition: index == 0
    
      2.1: where = libnative-lib.so`getString(int) + 6 at native-lib.cpp:9, address = 0x73a3cf5a, resolved, hit count = 0
    

    通过-c参数告知调试器,当给定表达式的计算结果为true时,触发断点。

    地址断点

    在逆向其他应用,或则无源码调试的时候,我们往往需要按照地址设置断点

    (lldb) breakpoint set -a 0x73a3cf5a
    

     

    通过-a参数指定地址,在没有其他参数的情况下,这个地址是进程空间的地址,在实际无源码调试过程中,我们可以通过静态分析工具(arm-eabi-objdump/ida)获取到需要断点指令的file offset,然后通过下面的指令设置断点。

    (lldb) breakpoint set -s libnative-lib.so -a 0xf04
    

     

    其他断点相关命令

    #查看所有断点
    (lldb) breakpoint list
    Current breakpoints:
    2: file = 'native-lib.cpp', line = 9, exact_match = 0, locations = 1, resolved = 1, hit count = 0
      2.1: where = libnative-lib.so`getString(int) + 6 at native-lib.cpp:9, address = 0x73a3cf5a, resolved, hit count = 0
    #禁用断点
    (lldb) breakpoint disable 2
    1 breakpoints disabled.
    (lldb) breakpoint list
    Current breakpoints:
    2: file = 'native-lib.cpp', line = 9, exact_match = 0, locations = 1 Options: disabled
      2.1: where = libnative-lib.so`getString(int) + 6 at native-lib.cpp:9, address = 0x73a3cf5a, unresolved, hit count = 0
    #启用断点
    (lldb) breakpoint enable 2
    1 breakpoints enabled.
    #删除断点
    (lldb) breakpoint delete 2
    1 breakpoints deleted; 0 breakpoint locations disabled.
    

    程序流程控制

    当调试器命中断点之后,我们可以开始程序的流程控制。

    #源码级别的流程控制
    (lldb) thread step-in
    (lldb) thread step-over
    (lldb) thread step-out
    (lldb) thread continue
    #指令级别的流程控制
    (lldb) thread step-inst
    (lldb) thread step-inst-over
    #所有线程恢复执行
    continue
    #查看调用栈
    (lldb) thread backtrace
    * thread #3: tid = 8148, 0x73a3cf5a libnative-lib.so`getString(index=0) + 6 at native-lib.cpp:9, name = 'om.netease.demo', stop reason = breakpoint 2.1
      * frame #0: 0x73a3cf5a libnative-lib.so`getString(index=0) + 6 at native-lib.cpp:9
        frame #1: 0x73a3cf9c libnative-lib.so`::Java_com_netease_demo_MainActivity_stringFromLibNative(env=0x41894638, (null)=0x3770001d) + 24 at native-lib.cpp:20
        frame #2: 0x409043d0 libdvm.so`dvmPlatformInvoke + 116
        frame #3: 0x40934d92 libdvm.so`dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*) + 402
        frame #4: 0x4090d8a8 libdvm.so`dvmJitToInterpNoChain + 696
    

     

    内存修改

    在调试的过程中,我们往往需要查看内存变量的值,可以通过frame或则p/po/x指令来实现。frame用来查看当前栈帧的变量值,p和po用来打印表达式的结果,x用来查看内存中某个地址开始的值,对于不可打印的值,可以通过x查看。frame select指令可以用来切换当前所处的栈帧,but Android Studio使用的lldb server有问题,目前这个指令无法生效,详见issues

    (lldb) frame info
    frame #0: 0x73a3cf5a libnative-lib.so`getString(index=0) + 6 at native-lib.cpp:9
    (lldb) frame variable
    (int) index = 0
    #打印变量的值
    (lldb) p index
    (int) $2 = 0
    (lldb) po index
    0
    #修改变量的值
    (lldb) expression index = 1
    (int) $0 = 1
    

     

    expression指令用于在当前栈帧中计算表达式,我们可以用其修改变量的值,在上面的列子中,我们把index的值换成1,从而返回data[1],那考虑下如果我们期望返回一个全新的字符串,比如”New String from Lib Native”?

    (lldb) thread step-out
    (lldb) expression str = "New String from Lib Native"
    

     

    很遗憾,这样的命令并不能达到预期的效果。要找到原因,我们可以使用disassemble命令进行反编译。

    (lldb) disassemble
    libnative-lib.so`::Java_com_netease_demo_MainActivity_stringFromLibNative(JNIEnv *, jobject):
        0x73a3df84 <+0>:  push   {r7, lr}
        0x73a3df86 <+2>:  mov    r7, sp
        0x73a3df88 <+4>:  sub    sp, #0x18
        0x73a3df8a <+6>:  mov    r2, r1
        0x73a3df8c <+8>:  mov    r3, r0
        0x73a3df8e <+10>: str    r0, [sp, #0x14]
        0x73a3df90 <+12>: str    r1, [sp, #0x10]
        0x73a3df92 <+14>: movs   r0, #0x0
        0x73a3df94 <+16>: str    r2, [sp, #0x8]
        0x73a3df96 <+18>: str    r3, [sp, #0x4]
        0x73a3df98 <+20>: blx    0x73a3ddd4                ; symbol stub for: getString(int)
    ->  0x73a3df9c <+24>: str    r0, [sp, #0xc]
        0x73a3df9e <+26>: ldr    r1, [sp, #0x14]
        0x73a3dfa0 <+28>: str    r0, [sp]
        0x73a3dfa2 <+30>: mov    r0, r1
        0x73a3dfa4 <+32>: ldr    r1, [sp]
        0x73a3dfa6 <+34>: blx    0x73a3dde0                ; symbol stub for: _JNIEnv::NewStringUTF(char const*)
        0x73a3dfaa <+38>: add    sp, #0x18
        0x73a3dfac <+40>: pop    {r7, pc}
    

     

    由此可见getString返回之后的结果从r0存储到栈上,然后加载到r1中作为NewStringUTF的参数。

    #读寄存器
    (lldb) register read r0
          r0 = 0x73a3f628  
    (lldb) po (const char*)0x73a3f628
    "Hello from Lib Native”
    

    当用expression指令修改str的值,改的只是内存里面的值,r0中的地址并没有改变,所以要实现我们预期的效果,只需要修改r0中的值即可。

    (lldb) expression str = "New String from Lib Native"
    (const char *) $13 = 0x7437b020 "New String from Lib Native"
    (lldb) register write r0 0x7437b020
    

     

    线程

    #查看线程
    (lldb) thread list
    Process 8148 stopped
      thread #1: tid = 6997, 0x40052534 libc.so`__ioctl + 8, name = 'Binder_8'
    * thread #3: tid = 8148, 0x73a3cf5a libnative-lib.so`getString(index=0) + 6 at native-lib.cpp:9, name = 'om.netease.demo', stop reason = breakpoint 2.1
      thread #4: tid = 8152, 0x40053844 libc.so`__futex_syscall3 + 8, name = ‘GC'
    #切换线程
    (lldb) thread select 3
    

     

    结语

    LLDB的命令非常强大,熟练掌握常用命令对JNI开发调试过程大有裨益,本文只简单介绍了一些基本用法,后续在使用过程中对命令有问题,可以通过help或者官方文档获取帮助。

    原文:【工具使用】Android调试利器之LLDB

    本文来自网易云社区,经作者唐杰授权发布。

    展开全文
  • LLDB是个开源的内置于XCode的调试工具,它能帮助我们在开发中更快的定位和调试bug,无论正向和逆向开发中都有很大的作用。lldb对于命令的简称,是头部匹配方式,只要不混淆(不提示歧义),你可以随意简称某个命令。 ...
  • iOS 之 LLDB(上)

    2020-11-10 10:27:04
    断点 设置断点 $breakpoint set -n XXX set 是子命令 -n 是选项 是–name 的缩写! 查看断点列表 $breakpoint list 删除断点 $breakpoint delete 组号 禁用/启用 $breakpoint disable 禁用 $breakpoint enable 启用 ...
  • LLDB 调试进阶

    千次阅读 2017-08-28 16:50:28
    什么是LLDB LLDB是XCode内置的为我们开发者提供的调试工具,它与LLVM编译器一起,存在于主窗口底部的控制台中,能够带给我们更丰富的流程控制和数据检测的调试功能。在调试过程中熟练使用LLDB,可以让你debug...
  • 动态调试及LLDB技巧集合

    千次阅读 2018-09-29 14:10:51
    学习背景:在非越狱逆向开发的过程中,我们可以从界面(Reveal)、类与方法(class-dump)、或者网络(Charles)等方面及一个应用的功能点入手去寻找一些需要实现功能的入口线索。但是在未知源代码的情况下,想要精确...
  • 本来一直想给大家普及一下lldb的使用,因为身边有很多朋友虽然开发了很久,但是还是不会使用调试器,还傻傻的打一个log,重启程序再看这个值。说,是不是你,中招没。这一篇也是为了帮这些朋友认识一下调试器的强大。...
  • LLDB

    2016-03-21 14:41:18
    文/小笨狼(简书作者)...LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xcode运行程序,实际走的都是LLDB。熟练使用LLDB,可以让你debug事半功倍 LLDB基础知识
  • LLDB使用详解

    千次阅读 2016-12-26 14:25:16
    LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xcode运行程序,实际走的都是LLDB。熟练使用LLDB,可以让你debug事半功倍 LLDB基础知识 LLDB控制台 Xcode中...
  • 常用的LLDB命令

    千次阅读 2017-10-12 18:28:00
    常用的lldb命令大全
  • 前言Hopper 是一种适用于 OS X 和 ...LLDB是Low Level Debugger的简称,在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具。使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理,因为d
  • LLDB和GDB比较

    2017-10-05 21:53:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • LLDB调试器总结

    2016-02-19 13:09:16
    LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所...
  • The LLDB Debugger

    2014-07-10 20:06:57
    GDB TO LLDB COMMAND MAP Below is a table of GDB commands with the LLDB counterparts. The built in GDB-compatibility aliases in LLDB are also listed. The full lldb command names are often long, but
  • 编译测试 LLVM与LLDB

    千次阅读 2016-02-27 20:39:12
    首先让我们先通过百度了解一下LLVM链接:点击打开链接然后重点了解一下LLDB这个东西随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和...
  • GDB/LLDB调试指令

    千次阅读 2016-05-11 14:06:51
    转自:http://lldb.llvm.org/lldb-gdb.html GDB TO LLDB COMMAND MAP Below is a table of GDB commands with the LLDB counterparts. The built in GDB-compatibility aliases in LLDB are also listed. ...
  • lldb与gdb命令比较

    千次阅读 2015-08-17 17:25:40
    转:http://lldb.llvm.org/lldb-gdb.html ...The LLDB Debugger GDB to LLDB Command Map Below is a table of GDB commands with the LLDB counterparts. The built in GDB-compatibility aliases in
  • LLDB 调试

    2019-02-20 10:49:00
    LLDB是 XCode 内置的为我们开发者提供的调试工具。 LLDB 可以提供的服务: 允许你在程序的特定时刻暂停它; 允许你查看变量的值; 执行自定的指令; 按照你所认为合适的步骤来操作程序的进展。 语法 &...

空空如也

1 2 3 4 5 ... 20
收藏数 9,863
精华内容 3,945
关键字:

LLDB