When it comes to LLDB debugging, many iOS developers are probably stuck at a simple breakpoint, using the most command, which is Po. Needless to say, these simple debugs should be handy for simple problems. But if you have a slightly more complicated problem, like I had a problem with the Umeng SDK. I wanted to create a breakpoint, but for the static library of.A, it was impossible, so we used the command to create breakpoints and solved the problem. I felt that this knowledge was necessary, so I took a look at the basic LLDB debugging commands and shared them with you.

Although the blog is a long one, reading it patiently and then practicing it will definitely help a lot.

breakpoint

To break a line of a file. There are two ways to do this: for example, I want to set a breakpoint on line 26 of foo. m. You can use the following methods.

(lldb) breakpoint set --file Foo.m --line 26Copy the code

If the following information is displayed, the breakpoint is set successfully

Breakpoint 2: where = BreakPointDemo`-[Foo foo] + 23 at Foo.m:26.address = 0x000000010b22e687Copy the code




Breakpoint success

You can also use the shorthand form as follows.

(lldb) breakpoint set -f Foo.m -l 26Copy the code

Of course, we can also create breakpoints directly on a function, using the following two methods

(lldb) breakpoint set --name foo
(lldb) breakpoint set -n fooCopy the code

Of course we can also set breakpoints for multiple functions in a single command

(lldb) breakpoint set --name foo --name barCopy the code

We can also specify the method more explicitly. If the method is C, we can use the following two methods to break the point. The second method, M, needs to be capitalized

(lldb) breakpoint set --method cplusFoo
(lldb) breakpoint set -M cplusFooCopy the code

If the OC method is used, you can use the following two ways to break the point, the second one requires a capital S

(lldb) breakpoint set --selector foo

(lldb) breakpoint set -S fooCopy the code

If C is used, you can only use the above -name method, and cannot directly specify the corresponding method

Of course, another killer is to use the re to match the function at which you want to break. This is in any language

(lldb) breakpoint set -r cFoo
(lldb) breakpoint set -r fooCopy the code

You can also specify dynamic libraries to load

(lldb) breakpoint set --shlib foo.dylib --name foo 
(lldb) breakpoint set -s foo.dylib -n fooCopy the code

We can also abbreviate commands. The following two commands have the same effect

(lldb) breakpoint set -n "-[Foo foo]"
(lldb) br s -n "-[Foo foo]"Copy the code

Want to see how many breakpoints are available

(lldb) breakpoint listCopy the code

The printed result is as follows

Current breakpoints:
1: file = '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.m', line = 20.exact_match = 0.locations = 0 (pending)
2: file = '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm', line = 33.exact_match = 0.locations = 1.resolved = 1, hit count = 0
  2.1: where = BreakPointDemo`::-[ViewController viewDidLoad]() + 186 at ViewController.mm:34.address = 0x0000000105f8362a, resolved, hit count = 0.Copy the code

We can perform operations on breakpoints, such as printing the trace at 2.1 breakpoint. Bt is

(lldb) breakpoint command add 21.
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> DONECopy the code

In addition to add, there are delete and other commands, which don’t need to be memorized by rote. You can use the help command.

(lldb) help break command

      add    -- Add LLDB commands to a breakpoint, to be executed whenever the
                breakpoint is hit.  If no breakpoint is specified, adds the
                commands to the last created breakpoint.
      delete -- Delete the set of commands from a breakpoint.
      list   -- List the script or set of commands to be executed when the
                breakpoint is hit.Copy the code

To see the use of the command in more detail, use help

. For example, check the use of the add command

(lldb) help break command add. Enter your Pythoncommand(s). Type 'DONE' to end.
> def breakpoint_output (bp_no):
>     out_string = "Hit breakpoint number " + repr (bp_no)
>     print out_string
>     return True
> breakpoint_output (1)
> DONECopy the code

As you can see, most of these commands are Python scripts. I am not familiar with Python and haven’t studied them carefully yet.

How to delete a breakpoint after using it, the command description is as follows.

 breakpoint delete [-Df] [<breakpt-id | breakpt-id-list>]Copy the code

I’m now using the Breakpoint List to look up my progress

Current breakpoints:
1: file = '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.m', line = 20.exact_match = 0.locations = 0 (pending)


2: file = '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm', line = 29.exact_match = 0.locations = 1.resolved = 1, hit count = 1

  2.1: where = BreakPointDemo`::-[ViewController viewDidLoad]() + 105 at ViewController.mm:30.address = 0x00000001025b55c9, resolved, hit count = 1 

4: name = 'foo', locations = 1.resolved = 1, hit count = 0
  4.1: where = BreakPointDemo`-[Foo foo] + 23 at Foo.m:26.address = 0x00000001025b5517, resolved, hit count = 0 

5: regex = 'cFoo', locations = 2.resolved = 2, hit count = 0
  5.1: where = BreakPointDemo`cFoo + 15 at CFoo.c:13.address = 0x00000001025b591f, resolved, hit count = 0 
  5.2: where = libicucore.A.dylib`icu::MeasureUnit::createCubicFoot(UErrorCode&), address = 0x00000001051b808a, resolved, hit count = 0Copy the code

If I want to delete breakpoint 5.1 I use Breakpoint delete 5.1. If I want to delete all breakpoints below 5, I use Breakpoint Delete 5 so that both 5.1 and 5.2 will be deleted.

Delete all breakpoints used

(lldb) breakpoint delete
About to delete all breakpoints, do you want to dothat? : [Y/n] y All breakpoints removed. (4 breakpoints)Copy the code

watchpoint

This is mainly used to observe specific changes in the value of a variable

For example, if I need to observe the change in the value of a variable, I can use the following command

(lldb) watchpoint set variable aCopy the code

The result is as follows after the Watchpoint is added successfully.

Watchpoint created: Watchpoint 1: addr = 0x7fff5913ca3c size = 4 state = enabled type = w
    declare @ '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm:25'
    watchpoint spec = 'a'
    new value: 10Copy the code

You can also add it here.





Add observation

And then we can set it to touch after the value of A has changed to a certain value.

(lldb) watchpoint modify -c '(a=100)'Copy the code

At this point we can look at the parameters of the breakpoint, using the Watchpoint list command

(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7fff4fcb7a3c size = 4 state = enabled type = w
    declare @ '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm:25'
    watchpoint spec = 'a'
    new value: 10
    condition = '(a=100)'Copy the code

We can see the address of the variable we are looking at, what line is the code to declare the variable, the specified variable name is A, the current value is 10, and the trigger condition is ‘(a=100)’.

We then execute the following command to see the breakpoint at the point where the value of a becomes 100

(lldb) c
Process 16596 resuming
2017- 02-09 11: 12: 14.693 BreakPointDemo[16596:6050498] foo is foo
2017- 02-09 11: 12: 14.693 BreakPointDemo[16596:6050498] bar is bar

Watchpoint 1 hit:
old value: 10
new value: 100Copy the code

You can see that this is where the value of A has changed. We can use the Watchpoint list command again to see how the specific values change

(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x7fff4fcb7a3c size = 4 state = enabled type = w
    declare @ '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm:25'
    watchpoint spec = 'a'
    old value: 10
    new value: 100
    condition = '(a=100)'Copy the code

And, of course, one of the things that’s really useful is the bt command that you can use to track what’s going on in your program.

(lldb) bt
* thread #1: tid = 0x5c52c2, 0x000000010ff465fe BreakPointDemo`::-[ViewController viewDidLoad] (self=0x00007f932cc07c50, _cmd="viewDidLoad") + 158 at ViewController.mm:36, queue = 'com.apple.main-thread', stop reason = watchpoint 1
  * frame #0: 0x000000010ff465fe BreakPointDemo`::-[ViewController viewDidLoad] (self=0x00007f932cc07c50, _cmd="viewDidLoad") + 158 at ViewController.mm:36
    frame #1: 0x000000011112ba3d UIKit`-[UIViewController loadViewIfRequired] + 1258.Copy the code

We can use the frame command to check the value of variable A.

(lldb) frame variable a
(int) a = 100Copy the code

One last thing about the Watchpoint List. This command includes three optional parameters, which you can see using the help command

(lldb) help watchpoint list

       -b ( --brief )
            Give a brief description of the watchpoint (no location info).

       -f ( --full )
            Give a full description of the watchpoint and its locations.

       -v ( --verbose )
            Explain everything we know about the watchpoint (for debugging
            debugger bugs).Copy the code

-b indicates brief information, -f indicates comprehensive information, and -v indicates complete information. After my experiments, if watchpoint List is used, the default is Watchpoint list -f.

process

You can also do a lot of interesting things with the process command. What can be done, we can also use the help command to view

(lldb) process help

      attach    -- Attach to a process.
      connect   -- Connect to a remote debug service.
      continue  -- Continue execution of all threads in the current process.
      detach    -- Detach from the current target process.
      handle    -- Manage LLDB handling of OS signals for the current target.Copy the code

For more detailed commands use help

. Such as

(lldb) help process attachCopy the code

These commands are not often used in my daily development, maybe I am not good enough.

thread

In fact, this function is mainly breakpoint debugging inside the following function.





step-in

We can use thread command to perform some breakpoint operations. We can use Thread help to view the specific commands.

(lldb) thread help.select         -- Change the currently selected thread.
      step-in        -- Source level single step, stepping into calls. 
                        Defaults to current thread unless specified.
      step-inst      -- Instruction level single step, stepping into calls. 
                        Defaults to current thread unless specified.
      step-inst-over -- Instruction level single step, stepping over calls. 
                        Defaults to current thread unless specified.
      step-out       -- Finish executing the current stack frame and stop after
                        returning.  Defaults to current thread unless
                        specified.
      step-over      -- Source level single step, stepping over calls. 
                        Defaults to current thread unless specified.
      step-scripted  -- Step as instructed by the script class passed in the -C
                        option.
      until          -- Continue until a line number or address is reached by
                        the current or specified thread.  Stops when returning
                        from the current function as a safety measure.Copy the code

The most commonly used commands are those at the beginning of step-, which are easy to use. I personally think it’s much better than clicking on a breakpoint with the mouse

EXAMINING THREAD STATE

This also uses the thread command, mainly using the following commands.

To check the status of the current process, run the following command:

lldb)  thread list
Process 22323 stopped
* thread #1: tid = 0x62d0d7, 0x00000001082185fe BreakPointDemo`::-[ViewController viewDidLoad] (self=0x00007ff81b60ab20, _cmd="viewDidLoad") + 158 at ViewController.mm:36, queue = 'com.apple.main-thread', stop reason = step until
......Copy the code

* indicates the current thread, and you can use the following command to get the thread backtrace.

 lldb) thread backtrace
* thread #1: tid = 0x62d0d7.0x00000001082185fe BreakPointDemo`::-[ViewController viewDidLoad](self=0x00007ff81b60ab20, _cmd="viewDidLoad") + 158 at ViewController.mm:36, queue = 'com.apple.main-thread', stop reason = step until
  * frame #0: 0x00000001082185fe BreakPointDemo`::-[ViewController viewDidLoad](self=0x00007ff81b60ab20, _cmd="viewDidLoad") + 158 at ViewController.mm:36
    frame #1: 0x00000001093fda3d UIKit`-[UIViewController loadViewIfRequired] + 1258
    frame #2: 0x00000001093fde70 UIKit`-[UIViewController view] + 27
    frame #3: 0x00000001092c74b5 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 71
    frame #4: 0x00000001092c7c06 UIKit`-[UIWindow _setHidden:forced:] + 293
    frame #5: 0x00000001092db519 UIKit`-[UIWindow makeKeyAndVisible] + 42
    frame #6: 0x0000000109253f8d UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
    frame #7: 0x000000010925a0ed UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
    frame #8: 0x000000010925726d UIKit`-[UIApplication workspaceDidEndTransaction:] + 188
    frame #9: 0x000000010c3886cb FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    frame #10: 0x000000010c388544 FrontBoardServices`-[FBSSerialQueue _performNext] + 189
    frame #11: 0x000000010c3888cd FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
    frame #12: 0x0000000108ddc761 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #13: 0x0000000108dc198c CoreFoundation`__CFRunLoopDoSources0 + 556
    frame #14: 0x0000000108dc0e76 CoreFoundation`__CFRunLoopRun + 918
    frame #15: 0x0000000108dc0884 CoreFoundation`CFRunLoopRunSpecific + 420
    frame #16: 0x0000000109255aea UIKit`-[UIApplication _run] + 434
    frame #17: 0x000000010925bc68 UIKit`UIApplicationMain + 159
    frame #18: 0x000000010821899f BreakPointDemo`main(argc=1, argv=0x00007fff579e7600) + 111 at main.m:14
    frame #19: 0x000000010bbee68d libdyld.dylib`start + 1Copy the code

If we want to see the backtrace of all threads, we can use the thread backtrace all command. I’m not going to show log output here because it’s too much.

If we want to see a thread in isolation, we can jump to a specific thread using Thread Select 2 and then do something else, such as Thread backtrace

EXAMINING STACK FRAME STATE

To easily observe schema parameters and local variables, we can use the frame variable command

If I add no arguments, all the arguments and local variables will be printed out.

(lldb) frame variable 
(ViewController *) self = 0x00007ff81b60ab20
(SEL) _cmd = "viewDidLoad"
(int) a = 100
(Foo *) foo = 0x000061800000e820
(BreakPointDemoNameSpace::BreakPointClass *) cplusFoo = 0x3ff0000000000000Copy the code

To print a variable you need to specify it in the argument, which we used earlier, for example to view self

(lldb) frame variable self
(ViewController *) self = 0x00007ff81b60ab20Copy the code

Further, we can look at some child elements

(lldb) frame variable self->isa
(Class) self->isa = ViewControllerCopy the code

The command, though not a full expression interpreter, recognized basic operations such as &, *, ->, and [], not overloaded operators, and arrays could also be used, since arrays themselves are Pointers.

(lldb) frame variable *self 

(ViewController) *self = {
  UIViewController = {
    UIResponder = {
      NSObject = {
        isa = ViewController}... }Copy the code

Similar to the thread command, I can use frame Select to select another frame

(lldb) frame select 9Copy the code

To see more complex data, we can use the expression command

(lldb) expression self
(ViewController *) $0 = 0x00007fefa4705110Copy the code

To make it more complicated, we can use it to print an expression

(lldb) expr (int) printf ("I have a pointer 0x%llx.\n", self)
I have a pointer 0x7fefa4705110.
(int$1 =)33Copy the code

We can continue with the previous command

(lldb) expr self = $0
(ViewController *) $2 = 0x00007fefa4705110Copy the code

Of course, this EXPR doesn’t feel very useful.

call

Call can be used to call debugging commands that do not require a return value, such as changing the background color of the View. The following two commands can achieve a similar effect: change the background color of the current View.

(lldb) po [self.view setBackgroundColor:[UIColor redColor]]
(lldb) call [self.view setBackgroundColor:[UIColor redColor]]Copy the code

image

It’s just a simple command, but I feel it’s an important and useful command that can be used for addressing. A more practical use is to find the code location corresponding to the stack address. So let me write a little bit of code

  // Test image command usage
    NSArray *arr=[[NSArray alloc] initWithObjects:@ "1".@ "2".nil];
    NSLog(@ "% @",arr[2]);Copy the code

We can obviously see that the array is out of bounds. Then we run the program and we can see that the program reports the following error

This is cplusFoothis is cfoo2017-02-09 16:33:52.143 BreakPointDemo[26121:6901793] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000104d67d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010471e21e objc_exception_throw + 48
    2   CoreFoundation                      0x0000000104ca22bb -[__NSArrayI objectAtIndex:] + 155
    3   BreakPointDemo                       -[ViewController viewDidLoad] + 340
    4   UIKit                               0x000000010675ba3d -[UIViewController loadViewIfRequired] + 1258
    5   UIKit                               0x000000010675be70 -[UIViewController view] + 27
    6   UIKit                               0x00000001066254b5 -[UIWindow addRootViewControllerViewIfPossible] + 71
    7   UIKit                               0x0000000106625c06 -[UIWindow _setHidden:forced:] + 293
    8   UIKit                               0x0000000106639519 -[UIWindow makeKeyAndVisible] + 42
    9   UIKit                               0x00000001065b1f8d -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
    10  UIKit                               0x00000001065b80ed -[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
    11  UIKit                               0x00000001065b526d -[UIApplication workspaceDidEndTransaction:] + 188
    12  FrontBoardServices                  0x00000001083456cb __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    13  FrontBoardServices                  0x0000000108345544 -[FBSSerialQueue _performNext] + 189
    14  FrontBoardServices                  0x00000001083458cd -[FBSSerialQueue _performNextFromRunLoopSource] + 45
    15  CoreFoundation                      0x0000000104d0c761 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    16  CoreFoundation                      0x0000000104cf198c __CFRunLoopDoSources0 + 556
    17  CoreFoundation                      0x0000000104cf0e76 __CFRunLoopRun + 918
    18  CoreFoundation                      0x0000000104cf0884 CFRunLoopRunSpecific + 420
    19  UIKit                               0x00000001065b3aea -[UIApplication _run] + 434
    20  UIKit                               0x00000001065b9c68 UIApplicationMain + 159
    21  BreakPointDemo                      0x000000010414794f main + 111
    22  libdyld.dylib                       0x00000001062b068d start + 1
    23 ????? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSExceptionCopy the code

We can probably guess that the program crashed on the third line of log, which is 0x0000000104147544. In fact, the principle is very simple, because my Demo is called BreakPointDemo. The other names are obviously system libraries. Although line 21 of log also has BreakPointDemo, it should be considered as main after observation.

We use the image lookup command to quickly locate the specific code line.

(lldb) image lookup --address 0x0000000104147544
      Address: BreakPointDemo[0x0000000100001544] (BreakPointDemo.__TEXT.__text+ 644).Summary: BreakPointDemo`: : -[ViewController viewDidLoad]() + 340 at ViewController.mm: 46Copy the code

Take a look at the code in our Xcode file. It’s 46 lines





Breakpoint location

Of course, there are many other commands that we can explore and view by using image Help. I have not been exposed to these commands for the time being, and I will update them when I use them in my follow-up work or study.

Set an alias for the command

For example, p is an alias for frame variable, and P view is actually frame variable View. In addition to system-created LLDB aliases, you can also create custom aliases. Take the following command for example. Once we know the rules, we can alias any command ourselves.

(lldb) command alias bfl breakpoint set -fThe % 1-l %2
(lldb) bfl Foo.m 12Copy the code

Use if you want to undo an alias

(lldb) command unalias bflCopy the code

There are also some LLDB commands that you can check out on The LLDB Debugger website

The Demo address

conclusion

It’s not easy to read this long article, but I’m sure you’ve learned something. In addition, my blog for a long time to welcome comments, mutual discussion, inadequate welcome approval correction.