LLDB Debugger (LLDB

What is a REPL? R(read), E(evaluate), P(print), L(loop). The read-eval-print Loop (REPL for short), also known as the Interactive toplevel, is a simple, interactive programming environment. The term is often used to refer to a Lisp interactive development environment, but can also refer to a command line pattern. REPL makes exploratory programming and debugging easier, because the read-evaluation-output loop is usually faster than the classic edit-compile-run-debug pattern. (Sometimes translated as an interactive interpreter. You type a line of code into the REPL, and it immediately gives you the result of execution. Unlike C++ and Java, which usually need to be compiled to see the results, Python, Ruby, Scala are all native REPL languages. Read – evaluate – output loop Wikipedia What is a REPL?

LLDB Debugger (LLDB) is an open source Debugger with REPL features and support for C++ and Python plug-ins. LLDB has been built into Xcode, and the console at the bottom of Xcode’s main window is the area where we interact with LLDB. LLDB allows you to at a specific moment in the program is running (such as execution to one line of code, a function, a variable is modified, target stop – hook hits) suspended it (breakpoint/br/b/watchpoint/watch/wa). You can view the value of a variable (p/Po/wa/frame/fr/target/ta), implement customised expression (expression/expr), and follow the steps you think would be appropriate to carry out process, operating procedures (function) for process control process. (How debuggers Work: Part 1-Basics is a general explanation of How the debuggers work.)

A set of tables in the GDB to LLDB Command map gives a good overview of the large blocks of commands provided by the LLDB debugger (general usage, which you probably already know). To get a more “advanced” use of LLDB, we can install Chisel, an open source collection of LLDB commands from Facebook that helps us debug iOS applications. The inside of the Chisel command was based on LLDB support Python script interpreter to run, Chisel each command corresponding Python file stored in the/usr/local/Cellar/Chisel / 2.0.1 / libexec directory path, Familiar with Python’s friend can try to read the contents of these files (specific path may differ, each one different machine such as Intel MAC in/usr/local/Cellar/chisel / 2.0.1 / libexec directory path, M1 MAC in/opt/homebrew/Cellar/chisel / 2.0.1 / libexec directory path).

The LLDB is built into Xcode as a shared library. It is placed under the SharedFrameworks in Xcode. / Applications/Xcode. App/Contents/SharedFrameworks/LLDB framework.

LLDB’s debugging interface is essentially a C++ shared library that, on Mac systems, is packaged as lldb. framework (normally, It exists in/Applications/Xcode. App/Contents/SharedFrameworks/LLDB framework under the path), on unix-like systems, it is LLDB. So (so is the suffix of the Shared object of such a system, So is roughly the abbreviation for shared object. In iOS and macOS, they are called system libraries, shared libraries, and in Programmer Self-Cultivation, they are called shared objects, which means the same thing). These debugging interfaces can be used directly within the LLDB script interpreter, or they can be used by Python scripts introduced into the lldb.py module. The LLDB itself supports user custom commands, such as a script to customize a pviews command that prints all current views of the APP (already implemented in Chisel). LLDB – Pit pointing north (1) – Add batch enable disable breakpoint function to Xcode

Let’s go through the LLDB debugger step by step, starting with the basic LLDB commands.

LLDB basic commands

Everyday we are more likely to use LLDB in Xcode, today we learn LLDB through terminal and Xcode two ways. If you are familiar with the LLDB command, you may have used help before. If you are not familiar with the LLDB command, you can press help and LLDB will print the details of the command. Help help can even teach you how to use help. Start the terminal, type LLDB and press Enter to enter the LLDB environment. Then type help and press Enter to see a list of LLDB commands available on the current machine. My machine has grouped all the LLDB commands into three groups:

  • Debugger Commands (blocks of debugging commands originally supported by LLDB (e.g. Breakpoint breakpoint, Process, Thread, watchpoint)
  • Abbreviations (Type ‘Help Command Alias’ for more info) (Aliases or abbreviations of some commands or subcommands in abbreviations)
  • Chisel has been installed on my machine, so the debug commands provided by Chisel are also directly listed here (Python script).

Let’s start with the simplest help command.

help

Type help directly to display a list of all LLDB commands, and type help [

] to list details about the particular command

. For example, enter help help and press Enter. The following description is displayed:

-h, -u, -h, -u

help help
     Show a list of all debugger commands, orGive details about a specific command. Display a list of all debugger commands, or provide details about a specific command. Syntax: help [<cmd-name>] Command Options Usage (help) : Syntax: help [<cmd-name>] Command Options Usage// help can view details not only for individual commands, but also for subcommands,
  // For example: help breakpoint (view breakpoint command details), help breakpoint clear (view breakpoint clear command details)
  help [-ahu] [<cmd-name> [<cmd-name> [...]]]

       -a(--hide-aliases)(-a is short for --hide-aliases option) hide aliases in the command list. Help -a will hide the order from the Current Command Abbreviations Group in the three groups of commands listed by help) -h(-- show-hidres-commands)(-h is short for -- show-hidres-commands) Include commands prefixed with an underscore. (help -h and Same as help, all commands are listed) -u(--hide-user-commands)(--hide-user-commands is short for the -u option.) Hide user-defined commands from the list. (Relative help The command prints a list of all commands, and help -u hides the commands grouped in the three groups of commands listed by help. This command takes optionsand free-form arguments.  If your arguments resemble option specifiers (i.e., they start with a - or-), you
     must use ' -- ' between the end of the command options and the beginning of the arguments.
     The help command takes options and free-form arguments. If your arguments resemble option specifiers (for example, they start with - and --), you must use a '--' separator between the end of the options command and the beginning of the arguments.
     // That is, when a command has both options and parameters, the options should be placed first, and then '--' inserted at the end of the command to separate the following parameters.
Copy the code

print/po/p

Print /prin/pri/p/Po: print/prin/pri/p/Po: print/prin/pri For example, run the following command in the LLDB debugging window:

  • p a(A is a variable name, here is the expression a and output the result, that is, the value of a)
  • p a + 2(Here is the result of executing a + 2 and printing the value of a + 2.)

LLDB actually does prefix matching, so it’s exactly the same if we use print/prin/pri/p, but we can’t use pr because LLDB doesn’t disambiguate print and process. Fortunately we can use p directly (probably the most used p print command in LLDB debugging), LLDB attributes the simplest abbreviation/alias to the print command. (Here we use the help pr command, which prompts us to use an ambiguous command name, and lists all the commands that start with PR to prompt us for the details of which particular command we want to see.)

(lldb) help pr
Help requested with ambiguous command name, possible completions:
    process
    print
    present
    presponder
Copy the code

Let’s distinguish between print/prin/pri/p and Po. Although they are both print commands, they are printed in different formats.

Print /p is the same command. Print /p is short for expression. They are printed according to the default LLDB format, and Po is short for expression -O, which, if you read carefully, you will remember – is used to mark the end of a command’s options, followed by the command’s options, followed by the command’s parameters. The -o option in expression -o — is short for object-description: -O ( –object-description ) Display using a language-specific description API, if possible. In Objective-C, you call a description instance function or a class function, so when you print using Po, it prints based on the result returned by calling the Description API in the current locale. (In OC we can override the +/-description function to get a custom print.)

Print /prin/pri/ Po /p calls expression, executes the expression (the variable is also an expression) and prints the return value of the expression.

print    -- Evaluate an expression on the current thread.  Displays any returned value with LLDB's default formatting.
p        -- Evaluate an expression on the current thread.  Displays any returned value with LLDB's default formatting.
po       -- Evaluate an expression on the current thread.  Displays any returned value with formatting controlled by the type's author.
Copy the code

Both help print and help p output the following help information:


(lldb) help print
     Evaluate an expression on the current thread.  Displays any returned value with LLDB's default formatting.  Expects 'raw' input (see 'help raw-input'.). Syntax: print <expr> Command Options Usage: print <expr>'print' is an abbreviation for 'expression --'
Copy the code

Help Po displays the following help information:

(lldb) help po
     Evaluate an expression on the current thread.  Displays any returned value with formatting controlled by the type's author.  Expects 'raw' input (see 'help raw-input'.). Syntax: po <expr> Command Options Usage: po <expr>'po' is an abbreviation for 'expression -O --'
Copy the code

P/Po can only be used on the current thread (expression is only used on the current thread). This means that we can only print the return value of an expression from the current thread. If we print an expression from another thread, we will get an undefined error:

:1:1: use of undeclared identifier ‘XXX’, XXX denotes the name of the variable we want to print. Example code:

.int main_a = 10;
NSLog(@"%d", main_a);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {int local_variable = 11;
    NSLog(@"🎉 🎉 🎉 local_variable = % d", local_variable); }); .// We define a breakpoint inside dispatch_async and execute the program. When the breakpoint hits, we execute p main_a and print the following error:

(lldb) p main_a
error: <user expression 0> :1:1: use of undeclared identifier 'main_a'
main_a
^

Copy the code

If we have used the p command, we must remember that every time the p command prints the result, it directly outputs a variable starting with $. The value of this variable is the return value of the expression we want to print. That is, we can understand the variable starting with $as the variable of the value we print using the P command. These variables start with $in the LLDB namespace and can be used in subsequent command operations based on their names (if you have seen the previous article, in the class Structure chapter, we output variables starting with $from the p command). Cast layer by layer to see the structure of the class based on the value of the variable starting with $. In the LLDB namespace, you need to define a variable name that starts with $.

(lldb) p 123
(int) $0 = 123

(lldb) p $0 + 7
(int) $1 = 130

(lldb) p $1
(int) $1 = 130
Copy the code

The p command Displays any returned value with LLDB’s default formatting. The LLDB default format is mentioned for output return values. Are there any other formats besides the LLDB default format? Probably the ones we use most are P /x: return value in hexadecimal, P /t: return value in binary (the letter T stands for two), P /d: return value in signed decimal, and P /u: return value in unsigned decimal. We can also use p/ C to print characters, or p/s to print strings ending with a null character (\0). The following is a simple example of printing:

// Default format
(lldb) p 123
(int) $0 = 123

// Hexadecimal
(lldb) p/x 123
(int) $1 = 0x0000007b

/ / binary
(lldb) p/t 123
(int) $2 = 0b00000000000000000000000001111011
(lldb)

// Integer to character
(lldb) p/c 65
(int) $9 = A\0\0\0

// String print
(lldb) p/s "qwer"
(const char [5]) $10 = "qwer"
Copy the code

10.5 Output Formats has a complete list of print Formats for those interested.

expression

From the above learning, we know that p is the abbreviation of expression — and Po is the abbreviation of expression -o. In this section, we will learn expression command separately, expr is the abbreviation of expression command.

The Po /p command can only print the return values of expressions (variables are also expressions). Of course not, that is, the return value of the printed expression. What if the expression is a function, an assignment statement, etc.? If we print the return value of the expression, do we have to execute the expression to get the return value of the expression? What if we change the value of a variable in our program during expression execution? Will the modified value remain in the variable? The answer is yes, changing the values of variables not only in the LLDB command space, but also in our program. The following is an example:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {int local_variable = 11; // ⬅️ set a breakpoint for LLDB debugging
    NSLog(@"🎉 🎉 🎉 local_variable = % d", local_variable);
});

// Console output:

(lldb) expression local_variable
(int) $0 = 11

(lldb) expression $0 = $0 + 11
(int) $1 = 22

(lldb) expression local_variable
(int) $2 = 22

(lldb) expression local_variable = 33
(int) $3 = 33

(lldb) expression local_variable
(int) $4 = 33

(lldb) expression $2
(int) $2 = 22

// ⬇️ step: NSLog local_variable = 33
(lldb) step
2021- 09- 05 10:32:55.667121+0800 TEST_Fishhook[29717:14465661] 🎉 🎉 🎉 local_variable =33
Copy the code

Through the example above we will think, we can directly through the LLDB command to change the value of a variable in the program, without the need to recompile the run the program, we directly so that we can modify the value of a variable in the process of debugging and then change the execution process, and we don’t need to recompile the application.

In the previous section we focused on using expression to change the value of a variable in an application (Po /p can do the same thing, after all, they are abbreviations related to expression). Can we create variables in the LLDB? Does expression create a variable by declaring the expression directly after the variable? The answer is yes, for example we use int a = 2 in our program; To declare a variable, we can use the expression command in the LLDB to do the same thing, but in order to continue to use the declared variable in the LLDB command space, the variable name must start with $. The following is an example:

// declare a variable of type int $a
(lldb) e int $a = 2

// Prints the value of the $a variable multiplied by 19
(lldb) p $a * 19
(int) $0 = 38

// Declare an array variable $array
(lldb) e NSArray *$array = @[@"Saturday"The @"Sunday"The @"Monday"]

// Prints the length of $array
(lldb) p [$array count]
(NSUInteger) $1 = 3

// Take the variable with subscript 0 from the $array array and uppercase all characters
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY

$array = $array; $array = $array; $array = $array
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: <user expression 5> :1:28: no known method '-characterAtIndex:'; cast the message send to the method's return type
[[$array objectAtIndex:$a] characterAtIndex:0] ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~// Specify the return value type to get the correct print
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
(char) $3 = 'M'

// Convert characters to integer output
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
(char) $4 = 77
Copy the code

Here we see that expression can be printed, modified, and declared variables. Let’s take a look at the detailed information about the expression command output from the help expression command. There are many command options involved in the command, so we do not list them all here. Let’s focus on some important information.

Single and multi-line expressions

For single-line and multi-line expressions, when we want to enter a multi-line expression, we can first type expression/expr/e and press Enter to enter the LLDB multi-line expression editing mode. Then we can enter the desired expression and then when we want to end the expression, We type return on an empty line to finish editing the multi-line expression and execute the multi-line expression we typed.

Single and multi-line expressions:

    The expression provided on the command line must be a complete expression with no newlines.  To evaluate a multi-line expression, hit a return after an empty expression, and lldb will enter the
    multi-line expression editor. Hit return on an empty line to end the multi-line expression.
    
Copy the code

The following is an example:

// Enter expression/expr/e and press Enter to enter the LLDB multi-line expression editing mode. If you do not enter anything, press Enter to exit the editing mode
(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
1 

(lldb) expr
Enter expressions, then terminate with an empty line to evaluate:
1

(lldb) e
Enter expressions, then terminate with an empty line to evaluate:
1

// Enter the LLDB multi-line expression editing mode, press Enter again to exit the editing mode and execute the command
(lldb) e 
Enter expressions, then terminate with an empty line to evaluate:
1 local_variable = local_variable + 10
2 
(int) $0 = 21

// Local_variable has been changed to 21
(lldb) p local_variable
(int) $1 = 21 
Copy the code

Timeouts (expression execution timeout)

If the expression can be evaluated statically (without running code), it will be executed directly. Otherwise, by default, the expression will run on the current thread, but with a very short timeout: 0.25 seconds currently. If there is no return within that time, expression execution is interrupted and all threads are resumed at runtime. You can disable retry on all threads with the -a option. You can also use the -t option to set shorter timeouts.

Timeouts:

    If the expression can be evaluated statically (without running code) then it will be.  Otherwise, by default the expression will run on the current thread with a short timeout: currently 25.
    seconds.  If it doesn't return in that time, the evaluation will be interrupted and resumed with all threads running. You can use the -a option to disable retrying on all threads. You can use the -t option to set a shorter timeout.Copy the code

User defined variables (users customize variables in the LLDB)

For convenience, you can define your own variables or use them in subsequent expressions. They are defined in the same way as variables are defined in C. If the first character of the name of a defined variable is $, the value of the variable will be available in subsequent expressions, otherwise it will only be available in the current expression.

User defined variables:

    You can define your own variables for convenience or to be used in subsequent expressions.  You define them the same way you would define variables in C.  If the first character of your user
    defined variable is a $, then the variable's value will be available in future expressions, otherwise it will just be available in the current expression.
Copy the code

Continuing evaluation after a breakpoint is hit

Continuing evaluation after a breakpoint:

    If the "-i false" option is used, and execution is interrupted by a breakpoint hit, once you are done with your investigation, you can either remove the expression execution frames from the stack
    with "thread return -x" or if you are still interested in the expression result you can issue the "continue" command and the expression evaluation will complete and the expression result will be
    available using the "thread.completed-expression" key in the thread format.
Copy the code

Here are some examples of expr in use:

Examples:

    / / assignment
    expr my_struct->a = my_array[3]
    
    / / calculate
    expr -f bin -- (index * 8) + 5
    
    // Declare variables (available throughout the following LLDB environment)
    expr unsigned int $foo = 5
    
    // Declare variables (only available on the current line)
    expr char c[] = \"foo\"; C [0] // Since this command takes' raw 'input, if you use any command options, you must use' -- 'between the end of the command options and the beginning of the original input. 'expr -f bin -- (index * 8) + 5' Important Note: Because this command takes 'raw' input, if you use any command options you must use ' -- ' between the end of the command options and the beginning of the raw input.Copy the code

thread/process/frame

When we run the program in Xcode and hit the breakpoint we added to the program, execution is paused at our breakpoint (all other threads in the process are paused at this point, and the thread list lists all current threads with the star being the current thread). The console at the bottom of Xcode will enter LLDB debug mode, and the debug bar will say Pause Program Execution /Continue Program Execution: The Pause/Continue button, in the Continue state, allows our process to Continue endlessly until it terminates or hits the next breakpoint in our program. When our process enters THE LLDB debug state, the three buttons to the right of the pause/Continue button also become highlighted clickable (these three buttons only become clickable when the application is in LLDB debug mode, they are all gray and unclickable when the application is running properly). At this point we have four buttons that control the flow of the program execution (if we add the button next to activate/close all breakpoints, we have five buttons that control the flow of the program execution). The buttons are as follows:

  1. Activate breakpoints/Deactivate breakpoints(Activate/deactivate all breakpoints)
  2. Pause program execution/Continue Program executionTo pause/continue program execution, the LLDB command is:process interrupt/process continue
  3. Step over/Step over instruction(hold Control)/Step over thread(hold Control-Shift)
  • Step over: Click the button directly for a single line of code step at the source level
  • Step over instruction(hold Control): Hold down the Control button on the keyboard and click this button to perform single-line instruction step at the assembly instruction level, which is also executed by other threads.
  • Step over thread(hold Control-Shift): Hold down the Control and Shift buttons on the keyboard at the same time and click on this button for a single line of code step at the source level, executing only the current thread and continuing to suspend other threads, both of which continue to execute all threads, so thisthreadThe level ofStep overThis ensures that debugging only takes place on the current thread, shielding the current thread from other threads.
  1. Step into/Step into instruction(hold Control)/Step into thread(hold Control-Shift)(Ditto, source code level, assembly instruction level, execute only the current thread. andStep overThe difference is that when a single line of code is a function call, clicking this button goes inside the function, otherwise it basically addsStep overThe same.
  2. Step out

Let’s look at these buttons in more detail:

Activate their context/Deactivate breakpoints: Activate/deactivate all breakpoints. For example, if we want to close all breakpoints and execute the Program to see what it ends up doing on the page, we can deactivate all breakpoints using this button and then click the Continue Program Execution button. We can use Breakpoint Disable and Breakpoint Enable in the LLDB debugger to achieve the same effect (with a slight difference, if you are interested, try it yourself).

(lldb) breakpoint enable
All breakpoints enabled. (7 breakpoints)
(lldb) breakpoint disable
All breakpoints disabled. (7 breakpoints)
Copy the code

Pause program Execution /Continue Program Execution: Pauses/continues program execution (the corresponding LLDB command is process interrupt/process Continue). When a program is paused, clicking this button causes the program to continue executing until the next breakpoint is reached, or until there is no next breakpoint. In the LLDB debugger we can achieve the same effect using process Continue /continue/c (continue/c is short for Process Continue). They can also be followed by a -i option. Here is the help information for the c command:

If c is a thread that continues to execute the current process, do you have a command that only executes the current thread? There is, and we’ll find out below.)

(lldb) help c Continue execution of all threads in the current process. Continue executing all threads in the current process. Syntax: c <cmd-options> Command Options Usage: c [-i <unsigned-integer>]

       -i <unsigned-integer> ( --ignore-count <unsigned-integer> )
            Ignore <N> crossings of the breakpoint (if it exists) forthe currently selected thread. Ignores <N> intersections of breakpoints (if present) for the currently selected thread. 'c' is an abbreviationfor 'process continue'
Copy the code

Repeating Command in LLDB: Repeating Command in LLDB: Repeating Command in LLDB: Repeating Command in LLDB: Repeating Command in LLDB: Repeating Command in LLDB

int j = 0;

while (true) {
    j++;
    NSLog(@"%d", j);
}

// We break a breakpoint at the while, then run the program and the breakpoint is hit, and enter: c-i 3 in the LLDB debugger

// Console print:
(lldb) c -i 3
Process 85687 resuming
2021- 09- 08 09:44:24.859226+0800 TEST_Fishhook[85687:1017243] 1
2021- 09- 08 09:44:24.866394+0800 TEST_Fishhook[85687:1017243] 2
2021- 09- 08 09:44:24.873266+0800 TEST_Fishhook[85687:1017243] 3
2021- 09- 08 09:44:24.878565+0800 TEST_Fishhook[85687:1017243] 4
(lldb) 
Copy the code

Let’s look at what the next button does:

Step over/Step over Instruction (hold Control)/Step over Thread (hold Control-Shift) executes a line of code as a black box. Even if this line of code is a function call it’s executed directly, it doesn’t jump into the function itself, As opposed to Step into Instruction (hold Control)/Step into Thread (hold Control-shift), it can jump inside the function called (single line of code), Of course, only our own custom functions, the system of those closed source functions we are not able to enter. In LLDB debugging we can use thread step-over/next/n (next/n is short for Thread step-over) to achieve the same effect.

n         -- Source level single step, stepping over calls.  Defaults to current thread unless specified.
next      -- Source level single step, stepping over calls.  Defaults to current thread unless specified.
nexti     -- Instruction level single step, stepping over calls.  Defaults to current thread unless specified.
ni        -- Instruction level single step, stepping over calls.  Defaults to current thread unless specified.
Copy the code

N /next is source level and skips function calls. Nexti/NI is assembly instruction level and also skips function calls.

Step into instruction(hold Control)/Step into thread(hold Control-shift) You can use this button if you want to jump into a function call to debug or see what’s going on inside it. Step over/Step into is the same if the current line is not a function call. During LLDB debugging, you can run the thread step-in/thread step-inst/step-inst-over/s/step/si/stepi command to achieve the same effect.

thread step-in        -- Source level single step, stepping into calls.  Defaults to current thread unless specified.
thread step-inst      -- Instruction level single step, stepping into calls.  Defaults to current thread unless specified.
thread step-inst-over -- Instruction level single step, stepping over calls.  Defaults to current thread unless specified.

s                     -- Source level single step, stepping into calls.  Defaults to current thread unless specified.
step                  -- Source level single step, stepping into calls.  Defaults to current thread unless specified.
si                    -- Instruction level single step, stepping into calls.  Defaults to current thread unless specified.
stepi                 -- Instruction level single step, stepping into calls.  Defaults to current thread unless specified.
Copy the code

If we Step into a function call and it’s too long, we don’t want to click Step into/Step over again and again until the function is done, we want to quickly complete the current function, Click the Step out button to do this. Step out continues to the next return statement (until the end of a stack frame) and then stops again. In LLDB debugging we can use the Thread step-out/finish command to achieve the same effect.

thread step-out       -- Finish executing the current stack frame and stop after returning.  Defaults to current thread unless specified.
finish                -- Finish executing the current stack frame and stop after returning.  Defaults to current thread unless specified.
Copy the code

In addition to the debug commands for the debug buttons directly provided by Xcode above, there is one particularly useful command: Thread Return, which takes an optional parameter and executes it by loading the optional parameter into the return register and then immediately executing the return command to jump out of the current stack frame. This means that the rest of the function will not be executed. This can cause some problems with ARC reference counting, or it can invalidate the cleanup part of the function. But executing this command at the beginning of a function is a great way to isolate the function and falsify its return value.

(lldb) help thread return
     Prematurely return from a stack frame, short-circuiting execution of newer frames and optionally yielding a specified value.  Defaults to the exiting the current stack frame.  Expects 'raw' input
     (see 'help raw-input'.). Syntax: threadreturn

Command Options Usage:
  thread return [-x] -- [<expr>]
  thread return [<expr>]

       -x ( --from-expression )
            Return from the innermost expression evaluation.
     
     Important Note: Because this command takes 'raw' input, if you use any command options you must use The '-' between the end of the command options and the beginning of the raw input.
(lldb) 
Copy the code

The commands we used above are basically located under thread command, which also involves frame command (such as checking the current function call stack frame content) and process command (controlling the process to continue execution). Let’s list their subcommands directly to impress you.

thread

Thread: Command used to operate on one or more threads in the current process.

(lldb) help thread
     Commands for operating on one or more threads in the current process.

Syntax: thread <subcommand> [<subcommand-options>]

The following subcommands are supported:

      backtrace      -- Show thread call stacks.  Defaults to the current thread, thread indexes can be specified as arguments.
                        Use the thread-index "all" to see all threads.
                        Use the thread-index "unique" to see threads grouped by unique call stacks.
                        Use 'settings set frame-format' to customize the printing of frames in the backtrace and 'settings set thread-format'to customize the thread header. Displays the thread call stack. The default is the current thread, or you can specify a thread, using the thread index as an argument. Using thread indexes"all"View all threads. Using thread indexes"unique"View threads grouped by unique call stack. use'settings set frame-format'Custom traceback frame print, using'settings set thread-format'Custom thread title.continue       -- Continue execution of the current target process.  One or more threads may be specified, by default all threads continue. Continue the current target process. One or more threads can be specified, and by default all threads continue to execute. (threadcontinue <thread-index> [<thread-index> [...]])
                    
      exception      -- Display the current exception object fora thread. Defaults to the current thread. Displays the current exception object for the thread. Default is the current thread. info -- Show an extended summary of oneormore threads. Defaults to the current thread. Displays an extended summary of one or more threads. Default is the current thread. jump -- Sets the program counter to anewAddress. Sets the program counter (PC register) to the new address. list -- Show a summary of each thread in the current target process. Use'settings set thread-format'to customize the individual thread listings. Displays a summary of each thread in the current target process. use'settings set thread-format'Customize a list of individual threads. plan -- Commandsformanaging thread plans that control execution. Commands used to manage thread schedules that control execution.return         -- Prematurely return from a stack frame, short-circuiting execution of newer frames and optionally yielding a specified value.  Defaults to the exiting the current stack frame. 
                        Expects 'raw' input (see 'help raw-input'.). Prematurely returns from the stack frame, short-circuits the execution of the newer stack frame, and optionally specifies the return value. Exits the current stack frame by default. For example, we used thread step-in to enter a return value ofboolIn this case, we directly enter threadreturnThe NO command enters, the function call ends immediately and returns NO. select -- Change the currently selected thread. Changes the currently selected thread. step-in -- Source level single step, stepping into calls. Defaults to current thread unless specified. Source-level step into a function call. Default to the current thread unless specified. step-inst -- Instruction level single step, stepping into calls. Defaults to current thread unless specified. Instruction level step, step into function call. Default to the current thread unless specified. step-inst-over -- Instruction level single step, stepping over calls. Defaults to current thread unless specified. Instruction level single step, across function calls. Default to the current thread unless specified. step-out -- Finish executing the current stack frameandstop after returning. Defaults to current thread unless specified. Completes execution of the current stack frame (function call) and stops when it returns. Default to the current thread unless specified. step-over -- Source level single step, stepping over calls. Defaults to current thread unless specified. Source-level single step, across function calls. Default to the current thread unless specified. step-scripted -- Step as instructed by the script class passed in the -C option. You can also specify a dictionary ofkey (-k) and value (-v) pairs that will be used to populate an
                        SBStructuredData Dictionary, which will be passed to the constructor of the class implementing the scripted step.  See the Python Reference forFollow the steps indicated by the script class passed in the -c option. You can also specify the dictionary of key (-k) and value (-v) pairs to populate the SBStructuredData dictionary, which will be passed to the constructor of the class that implements the scripted step. until -- Continue until a line numberor address is reached by the current or specified thread.  Stops when returning from the current function as a safety measure.  The target line
                        number(s) are given as arguments, and ifmore than one is provided, stepping will stop when the first one is hit. Continues until the current or specified thread reaches the line number or address. As a safety measure, stop when the current function returns. The target line number is given as an argument, and if multiple line numbers are provided, stepping will stop when the first line number is hit. For more help on any particular subcommand, type'help <command> <subcommand>'.
Copy the code

process

Process Commands that interact with processes on the current platform.

(lldb) help process
     Commands forinteracting with processes on the current platform. Syntax: process <subcommand> [<subcommand-options>] The following subcommands are supported: attach -- Attach to a process. Attach to the process. connect -- Connect to a remote debug service. Connect to the remote debug service.continue-- Continue execution of all threads in the current process. Continue executing all threads in the current process. detach -- Detach from the current target process. Detach the current target process. handle -- Manage LLDB handling of OS signalsforthe current target process. Defaults to showing current policy. Manages LLDB processing of operating system signals for the current target process. The current policy is displayed by default. interrupt -- Interrupt the current target process. Interrupts the current target process. kill -- Terminate the current target process. Terminates the current target process. launch -- Launch the executable in the debugger. Start the executable in the debugger. load -- Load a shared library into the current process. Load the shared library into the current process. plugin -- Send a custom command to the current target process plug-in. Sends the custom command to the current target process plug-in. save-core -- Save the current process as a core fileusingAn appropriate file type. Save the current process as core file with the appropriate file type. signal -- Send a UNIX signal to the current target process. Sends a UNIX signal to the current target process. status -- Show statusand stop location forThe current target process. Displays the status and stop position of the current target process. unload -- Unload a shared library from the current processusing the index returned by a previous call to "process load".use the previously called"process load"The returned index unloads the shared library from the current process. For more help on any particular subcommand, type'help <command> <subcommand>'.
Copy the code

frame

Frame Command for selecting and examining the current thread stack frame.

(lldb) help frame
     Commands for selecting and examing the current thread's stack frames. Syntax: frame <subcommand> [<subcommand-options>] The following subcommands are supported: info -- List information about the current stack frame in the current thread. Lists information about the current stack frame in the current thread. recognizer -- Commandsfor editing andViewing frame recognizers. Commands for editing and viewing frame recognizers. select --Select the current stack frame by index from within the current thread (see 'thread backtrace'.).Select the current stack frame by index from the current thread (see 'Thread backtrace'). variable -- Show variablesfor the current stack frame. Defaults to all arguments and local variables in scope. Names of argument, local, file static and file global variables can be specified.
                    Children of aggregate variables can be specified such as 'var->child.x'.  The -> and [] operators in 'frame variable' do not invoke operator overloads if they exist, but directly
                    access the specified element.  If you want to trigger operator overloads use the expression command to print the variable instead.
                    It is worth noting that except for overloaded operators, when printing local variables 'expr local_var' and 'frame var local_var' produce the same results.  However, 'frame
                    variable' is more efficient, since it uses debug information and memory reads directly, rather than parsing and evaluating an expression, which may even involve JITing andrunning code in the target program. Displays the variables of the current stack frame. The default is all parameters and local variables in scope. You can specify argument, local, and filestaticAnd file Global variable name. You can specify children of aggregate variables, such as 'var->child.x'. If the -> and [] operators in 'frame variable' are present, operator overloading is not invoked, but the specified element is accessed directly. If you want to trigger operator overloading, print variables using the 'expression' command. It is worth noting that the same result is produced when printing the local variables 'expr local_var' and 'frame var local_var' in addition to the overloaded operator. However, 'Frame variable' is more efficient because it uses debugging information and memory reading directly, rather than parsing and evaluating expressions, and may even involve JITing and running code in the target program. For more help on any particular subcommand, type 'help <command> <subcommand>'.Copy the code

breakpoint

In daily development, breakpoints are used as a way to run a stop program, check the current state, and track bugs. Visual breakpoint debugging in Xcode is very convenient, it feels much more convenient than using LLDB command, so let’s look at Xcode visual breakpoint operation and LLDB debugger breakpoint command corresponding relationship.

breakpoint list/disable/enable/delete

By clicking the Show the Breakpoint Navigator button in the left pane of Xcode, we switch to a pane that can quickly manage all breakpoints (only those we added in Xcode using the graphical interface, excluding those we added using the LLDB command). In the LLDB debug window we can use the breakpoint list/br li command to list all breakpoints, including those we added using the Xcode GUI and those we added using the LLDB command. The LLDB breakpoint command does the same in Xcode with the check button:

  • breakpoint disable [<breakpt-id | breakpt-id-list>]Close the breakpoint
  • breakpoint enable [<breakpt-id | breakpt-id-list>]Open the breakpoint
  • breakpoint delete <cmd-options> [<breakpt-id | breakpt-id-list>]Remove breakpoints

The following is an example:

(lldb) br li
Current breakpoints:
1: file = '/Users/hmc/Documents/iOSSample/TEST_Fishhook/TEST_Fishhook/main.m', line = 29, exact_match = 0, locations = 1, resolved = 1, hit count = 1

  1.1: where = TEST_Fishhook`main + 34 at main.m:29:29, address = 0x0000000102a2df32, resolved, hit count = 1 

2: file = '/Users/hmc/Documents/iOSSample/TEST_Fishhook/TEST_Fishhook/main.m', line = 30, exact_match = 0, locations = 1, resolved = 1, hit count = 0

  2.1: where = TEST_Fishhook`main + 56 at main.m:30:29, address = 0x0000000102a2df48, resolved, hit count = 0 

(lldb) br dis 1
1 breakpoints disabled.
(lldb) br li
Current breakpoints:

// ⬇️ see that breakpoint 1 is set to disabled

1: file = '/Users/hmc/Documents/iOSSample/TEST_Fishhook/TEST_Fishhook/main.m', line = 29, exact_match = 0, locations = 1 Options: disabled 

  1.1: where = TEST_Fishhook`main + 34 at main.m:29:29, address = 0x0000000102a2df32, unresolved, hit count = 1 

2: file = '/Users/hmc/Documents/iOSSample/TEST_Fishhook/TEST_Fishhook/main.m', line = 30, exact_match = 0, locations = 1, resolved = 1, hit count = 0

  2.1: where = TEST_Fishhook`main + 56 at main.m:30:29, address = 0x0000000102a2df48, resolved, hit count = 0 

(lldb) br del 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) br li
Current breakpoints:

// ⬇️ See that breakpoint 1 has been deleted
 
2: file = '/Users/hmc/Documents/iOSSample/TEST_Fishhook/TEST_Fishhook/main.m', line = 30, exact_match = 0, locations = 1, resolved = 1, hit count = 0

  2.1: where = TEST_Fishhook`main + 56 at main.m:30:29, address = 0x0000000102a2df48, resolved, hit count = 0 

(lldb) 
Copy the code

Create a breakpoint

To set breakpoints in the LLDB debugger, you can use the breakpoint set

command, as shown in the following example to set a breakpoint (br is short for breakpoint) on line 35 of the main.m file:

(lldb) breakpoint set -f main.m -l 35
Breakpoint 3: where = TEST_Fishhook`main + 146 at main.m:35:29, address = 0x0000000102a2dfa2

(lldb) br set -f main.m -l 35
Breakpoint 5: where = TEST_Fishhook`main + 146 at main.m:35:29, address = 0x0000000102a2dfa2
Copy the code

We can also use the b command:

(lldb) help b
     Set a breakpoint using one of several shorthand formats.  Expects 'raw' input (see 'help raw-input'.). Syntax: _regexp-break <filename>:<linenum>
              main.c:12             // Break at line 12 of main.c

_regexp-break <linenum>
              12                    // Break at line 12 of current file

_regexp-break 0x<address>
              0x1234000             // Break at address 0x1234000

_regexp-break <name>
              main                  // Break in 'main' after the prologue

_regexp-break &<name>
              &main                 // Break at first instruction in 'main'

_regexp-break <module>`<name>
              libc.so`malloc        // Break in 'malloc' from 'libc.so'

_regexp-break /<source-regex>/
              /break here/          // Break on source lines in current file
                                    // containing text 'break here'.


'b' is an abbreviation for '_regexp-break'
Copy the code

C :35 Set a breakpoint on line 35 of the main.m file:

(lldb) b main.m:35
Breakpoint 6: where = TEST_Fishhook`main + 146 at main.m:35:29, address = 0x0000000102a2dfa2
Copy the code

B isEven creates a breakpoint on a symbol (C function) without specifying a line at all:

(lldb) b isEven
Breakpoint 7: where = TEST_Fishhook`isEven + 11 at main.m:12:9, address = 0x0000000102a2e08b
Copy the code

OC method can also:

(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
Breakpoint 8: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00007fff204a2f77

(lldb) b -[NSArray objectAtIndex:]
Breakpoint 9: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00007fff204a2f77

(lldb) breakpoint set -F "+[NSSet setWithObject:]"
Breakpoint 10: where = CoreFoundation`+[NSSet setWithObject:], address = 0x00007fff20434b0f

(lldb) b +[NSSet setWithObject:]
Breakpoint 11: where = CoreFoundation`+[NSSet setWithObject:], address = 0x00007fff20434b0f
Copy the code

Now let’s look at how to add breakpoints in Xcode’s graphical interface. You can click the + button at the bottom of the Breakpoint Navigator and then choose Symbolic Breakpoint… Then we can set the symbol to hit, the module to which the breakpoint belongs, the hit condition, the number of times the breakpoint is ignored (for example, the number of times the breakpoint is called before the breakpoint is hit), the activity at the time of the breakpoint hit, and whether the process should continue after the activity is executed:

For example, if we type -[NSArray objectAtIndex:] in the Symbol input box in the popup box above, the breakpoint will be hit every time the process calls this function, both our own and the system’s.

If (I % 2 == 0) {if (I % 2 == 0) {if (I % 2 == 0) {if (I % 2 == 0) {if (I % 2 == 0) {I % 2 == 99; Can indicate that the first five calls are ignored before subsequent calls hit this breakpoint.

static BOOL isEven(int i) {
    if (i % 2= =0) {
        NSLog(@"✳ ️ ✳ ️ ✳ ️ % d is even!", i);
        return YES;
    }
    
    NSLog(@"✳ ️ ✳ ️ ✳ ️ % d is odd!", i);
    return NO;
}
Copy the code

Activity at breakpoint hit, such as printing the value of I on each hit (execute p I command) :

In addition to executing LLDB on breakpoint hits, we can add other activities: executing shell commands, printing logs on the console or reading logs directly by voice, executing Apple scripts, capturing GPU stack frames, and playing sounds (which is hilarious).

You can also do this with commands in LLDB, as follows:

(lldb) breakpoint set -F isEven
Breakpoint 2: where = TEST_Fishhook`isEven + 11 at main.m:12:9, address = 0x0000000107ee508b
(lldb) breakpoint modify -c 'i == 99' 2
(lldb) breakpoint command add 2
Enter your debugger command(s).  Type 'DONE' to end.
> p i
> DONE
(lldb) br li 2
2: name = 'isEven', locations = 1, resolved = 1, hit count = 0
    Breakpoint commands:
      p i

Condition: i == 99

  2.1: where = TEST_Fishhook`isEven + 11 at main.m:12:9, address = 0x0000000107ee508b, resolved, hit count = 0 

(lldb) 
Copy the code

If you look at the bottom of the Edit Breakpoints pop-up, you will see an option: Automatically continue after evaluation Actions. It’s just a selection box, but it’s powerful. Check it, and the debugger runs all the commands in the breakpoint, and then continues. It looks like no breakpoints were executed (unless there are so many breakpoints that it takes a while to run and slows down the program).

This box has the same effect as making the last behavior of the last breakpoint process Continue. Checkboxes just make it easier. The same effect can be achieved with the LLDB command:

(lldb) breakpoint set -F isEven
Breakpoint 3: where = TEST_Fishhook`isEven + 11 at main.m:12:9, address = 0x0000000107ee508b
(lldb) breakpoint command add 3
Enter your debugger command(s).  Type 'DONE' to end.
> continue
> DONE
(lldb) br li 3
3: name = 'isEven', locations = 1, resolved = 1, hit count = 0
    Breakpoint commands:
      continue

  3.1: where = TEST_Fishhook`isEven + 11 at main.m:12:9, address = 0x0000000107ee508b, resolved, hit count = 0 

(lldb) 
Copy the code

Breakpoints continue automatically, allowing you to modify the program entirely through breakpoints! You can stop on a line, run an expression command to change the variable, and then continue running. Or add a breakpoint on the first line of a function and run a Thread Return 9 command to terminate the function and return a self-defined value.

(lldb) help breakpoint
     Commands for operating on breakpoints (see 'help b' for shorthand.)Syntax: breakpoint <subcommand> [<command-options>] The following subcommands are supported: clear -- Delete or disable breakpoints matching the specified source file and line. Deletes or disables breakpoints that match the specified source file and line. command -- Commands for adding, removing and listing LLDB commands executed when a breakpoint is hit. Commands to add, remove, and list LLDB commands when a breakpoint is hit. Delete -- delete the specified breakpoint(s). If no breakpoint are specified, delete them all. Deletes the specified breakpoint. If no breakpoints are specified, all breakpoints are deleted. disable -- Disable the specified breakpoint(s) without deleting them. If none are specified, disable all breakpoints. Disables specified breakpoints without removing them. If not specified, all breakpoints are disabled. enable -- Enable the specified disabled breakpoint(s). If no breakpoints are specified, enable all of them. Enables the specified disabled breakpoint. If no breakpoints are specified, all breakpoints are enabled. list -- List some or all breakpoints at configurable levels of detail. List some or all breakpoints at a configurable detail level. modify -- Modify the options on a breakpoint or set of breakpoints in the executable. If no breakpoint is specified, acts on the last created breakpoint. With the exception of -e, -d and -i, passing an empty argument clears the modification. Modify options on executable breakpoints or a set of breakpoints. If no breakpoint is specified, it takes effect on the last breakpoint created. Except for -e, -d and -i are cleared with an empty parameter. name -- Commands to manage name tags for breakpoints. Command to manage breakpoint name labels. read -- Read and set the breakpoints previously saved to a file with"breakpoint write". Read and set before saving to"breakpoint write"A breakpoint in the file. set -- Sets a breakpoint or set of breakpoints in the executable. Sets a breakpoint or set of breakpoints in an executable program. write -- Write the breakpoints listed to a file that can be read in with"breakpoint read". If given no arguments, writes all breakpoints. Write the listed breakpoints available"breakpoint read"In the file read. If there are no arguments, all breakpoints are written. For more help on any particular subcommand, type'help <command> <subcommand>'.
Copy the code

Here are the breakpoint-related commands. Let’s look at some LLDB usage at the view level of the iOS App.

Debug LLDB in the iOS App

&emap; We’ve seen the pause/continue button on the LLDB debug bar, where our main focus is on the continue button. Now let’s try hitting the pause button after running the iOS App.

Prints the current App view hierarchy

After clicking the pause button, we can see that our iOS App locates the mach_MSG_trap of the main thread and our Xcode console goes into LLDB debug mode. Clicking the pause button will suspend the iOS App, just like the process Interrupt command, because LLDB is always running behind Xcode. Although we entered the LLDB debugging mode at this time, our iOS App did not pause at the specific functions or specific codes we wrote ourselves, so there was not much we could do, but we could try to access the global variables of the iOS App. Here, we can access the view hierarchy of our iOS App via keyWindow:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x7fb4555062a0; frame = (0 0; 428 926); gestureRecognizers = <NSArray: 0x600000164ab0>; layer = <UIWindowLayer: 0x600000f665c0>>
   | <UITransitionView: 0x7fb457005d60; frame = (0 0; 428 926); autoresize = W+H; layer = <CALayer: 0x600000f41140>>
   |    | <UIDropShadowView: 0x7fb457006680; frame = (0 0; 428 926); autoresize = W+H; layer = <CALayer: 0x600000f41da0>>
   |    |    | <UILayoutContainerView: 0x7fb45720a9e0; frame = (0 0; 428 926); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x60000011fea0>; layer = <CALayer: 0x600000f08740>>
   |    |    |    | <UINavigationTransitionView: 0x7fb45720b7e0; frame = (0 0; 428 926); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x600000f08880>>
   |    |    |    |    | <UIViewControllerWrapperView: 0x7fb457509d60; frame = (0 0; 428 926); autoresize = W+H; layer = <CALayer: 0x600000f51d40>>
   |    |    |    |    |    | <UIView: 0x7fb4575092e0; frame = (0 0; 428 926); autoresize = W+H; layer = <CALayer: 0x600000f51e00>>
   |    |    |    |    |    |    | <UILabel: 0x7fb457509450; frame = (182 453; 64.3333 20.3333); text = 'CENTER'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600002c506e0>>
   |    |    |    | <UINavigationBar: 0x7fb45741dce0; frame = (0 47; 428 44); opaque = NO; autoresize = W; layer = <CALayer: 0x600000f31480>>
   |    |    |    |    | <_UIBarBackground: 0x7fb45740af90; frame = (0 - 47; 428 91); userInteractionEnabled = NO; layer = <CALayer: 0x600000f315e0>>
   |    |    |    |    |    | <UIVisualEffectView: 0x7fb45700f790; frame = (0 0; 428 91); layer = <CALayer: 0x600000f12060>> effect=none
   |    |    |    |    |    |    | <_UIVisualEffectBackdropView: 0x7fb45700ff30; frame = (0 0; 428 91); autoresize = W+H; userInteractionEnabled = NO; layer = <UICABackdropLayer: 0x600000f12300>>
   |    |    |    |    |    | <_UIBarBackgroundShadowView: 0x7fb45700fb60; frame = (0 91; 428 0.333333); layer = <CALayer: 0x600000f12160>> clientRequestedContentView effect=none
   |    |    |    |    |    |    | <_UIBarBackgroundShadowContentImageView: 0x7fb4557180d0; frame = (0 0; 428 0.333333); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x600000f3bf40>>
   |    |    |    |    | <_UINavigationBarContentView: 0x7fb457415480; frame = (0 0; 428 44); layer = <CALayer: 0x600000f31600>> layout=0x7fb45741ac30
   |    |    |    |    | <UIView: 0x7fb45700ce70; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x600000f10040>>
Copy the code

Debug App UI in LLDB

Text is the UILabel of CENTER. Now we create a variable in the LLDB namespace to retrieve this UILabel:

(lldb) expression UILabel *$myLabel = (UILabel *)0x7fb457509450
Copy the code

Then we change the background color of myLabel:

(lldb) expression [$myLabel setBackgroundColor: [UIColor blueColor]]
Copy the code

After executing this command, the background color of our Label does not change, because the changed content needs to be sent to the rendering service, so that the content displayed on our screen can be refreshed. At this point, we can click the Continue button on the LLDB debug bar again to continue running our iOS App. Only then can our App interface be refreshed and the background color of our Label become blue.

The render service is actually another process (called: backboardd), which means that even if our current App process is suspended, backboardd is still running, which means we can run the following command instead of continuing to run our program:

(lldb) expression [CATransaction flush]

error: <user expression 3> :1:16: no known method '+flush'; cast the message send to the method's return type
[CATransaction flush]
~~~~~~~~~~~~~~~^~~~~~

(lldb) expression (void)[CATransaction flush] // ⬅️ you need to add a return value type before the function call, otherwise an error will be reported
Copy the code

After expression (void)[CATransaction Flush] is executed, our iOS App is still suspended, we are still in LLDB debug mode, However, whether on the simulator or the real machine, we can see that the background color of our Label has changed to blue, that is, the UI of our iOS App has been updated in real time.

Now let’s do a bigger update, where we push a new VC from the current VC.

// Get the root controller of the current program (navigation controller)
(lldb) expression id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController]

Create a new controller
(lldb) expression id $vc = [UIViewController new]
(lldb) expression (void)[[$vc view] setBackgroundColor: [UIColor yellowColor]]
(lldb) expression (void)[$vc setTitle:@"New!"]

(lldb) expression (void)[$nvc pushViewController:$vc animated:YES]

// Render service
(lldb) expression (void)[CATransaction flush]
Copy the code

Let’s use LLDB debugging to find a button click event.

First get a myButton variable, can pass ‘Po [recursiveDescription [[UIApplicationsharedApplication] keyWindow]]’ in the UI layer, or stopped at a breakpoint in a local variable, MyButton [[[UIApplication sharedApplication] keyWindow] recursiveDescription] Or is stopped at a breakpoint in a local variable, and then we find myButton variables, can through the ‘Po [[[UIApplicationsharedApplication] keyWindow] recursiveDescription]’ in the UI layer, Or a local variable retrieved when stopped at a breakpoint. Next we find the click event for the myButton button:

(lldb) expression id $myButton = (id)0x7ff1e9412bc0

(lldb) po [$myButton allTargets]
{(
    <ViewController: 0x7ff1ec0087c0>
)}

(lldb) po [$myButton actionsForTarget:(id)0x7ff1ec0087c0 forControlEvent:0]
<__NSArrayM 0x6000022dd050>(
buttonAction:
)
Copy the code

We can then set a symbolic breakpoint at -[ViewController buttonAction:], which will be hit when the button is clicked.

Watch the instance variable change. Suppose we have a UIView whose _layer instance variable has been overwritten for some reason (bad). We cannot use symbolic breakpoints because methods may not be involved. Instead, we want to monitor when the address is written. First, we need to find the relative position of the _layer variable on the object:

 (lldb) p (ptrdiff_t)ivar_getOffset((CALayer *)class_getInstanceVariable([UIView class], "_layer"(a))ptrdiff_t) $3 = 40
Copy the code

Now we know that $myView + 40 is the memory address of the _layer instance variable:

 (lldb) expression id $myView = (id)0x7ff1e9412340
 (lldb) watchpoint set expression -- (int *)$myView + 40
 Watchpoint created: Watchpoint 1: addr = 0x7ff1e94123e0 size = 8 state = enabled type = w
     new value: 0x0000000000000000
 (lldb) watchpoint list
 Number of supported hardware watchpoints: 4
 Current watchpoints:
 Watchpoint 1: addr = 0x7ff1e94123e0 size = 8 state = enabled type = w
     new value: 0x0000000000000000
Copy the code

Symbolic breakpoints for non-overridden methods:

Suppose you want to know when -[ViewController viewDidAppear:] is called. What if this method is not implemented in MyViewController, but in its parent class? If you try to set a breakpoint, the following results appear:

(lldb) b -[ViewController viewDidAppear:]
Breakpoint 2: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
Copy the code

Because the LLDB looks for a symbol that is not actually found on the class, the breakpoint will never fire. All you need to do is set a condition for the breakpoint [self isKindOfClass:[MyViewController Class]] and then put the breakpoint on UIViewController. Normally this setting of a condition will work. But not here, because we don’t have an implementation of the superclass. ViewDidAppear: is the method apple implements, so it doesn’t have its notation; There’s no self inside the method. If you want to use self on a symbol breakpoint, you have to know where it is (it could be in a register or on the stack; On x86, you can find it at $ESP +4). But this is painful, because now you have to know at least four architectures (x86, x86-64, ARMV7, armV64). Imagine how much time it would take you to learn the command set and the calling conventions for each of them, and then write a command that sets breakpoints on your superclass with the right conditions. Fortunately, this was taken care of in Chisel. This is called a bmessage:

(lldb) bmessage -[ViewController viewDidAppear:]
Setting a breakpoint at -[UIViewController viewDidAppear:] with condition (void*)object_getClass((id)$rdi) = =0x0000000105154570
Breakpoint 1: where = UIKitCore`-[UIViewController viewDidAppear:], address = 0x00007fff23f6968e
Copy the code

Chisel overview

Chisel can be installed using Brew Install Chisel, Then according to the prompt similar command line script import/usr/local/opt/chisel/libexec/fbchisellldb py command is added to the ~ /. Lldbinit file, If the.lldbinit file does not exist, we can create one ourselves (path similar to: /Users/ HMC /.lldbinit), the contents of.lldbinit will be executed when Xcode starts, the above line is to load Chisel when Xcode starts.

Command script import/usr/local/opt/chisel/libexec/fbchisellldb py LLDB was the command of a command, it is used to manage LLDB custom command in the command.

(lldb) command
     Commands for managing custom LLDB commands.

Syntax: command <subcommand> [<subcommand-options>]

The following subcommands are supported:

      alias   -- Define a custom command in terms of an existing command. 
                 Expects 'raw' input (see 'help raw-input'.). Define a custom command based on an existing command. (Give an individual name to a command)delete  -- Delete one or more custom commands defined by 'command regex'Deleted by'command regex'Defines one or more custom commands. history -- Dump the history of commands inthis session.
                 Commands in the history list can be run again using
                 ! "" 
      
       "
      .   ! "" -
      
       "
       will re-run the command that is
                 <OFFSET> commands from the end of the list (counting the current command). regex -- Define a custom command in terms of existing commands by matching regular expressions. Define custom commands based on existing commands by matching regular expressions. script -- Commandsformanaging custom commands implemented by interpreter scripts. Custom command management commands implemented by Interpreter Scripts. source -- Readandexecute LLDB commands from the file <filename>. Read and execute the LLDB command <filename> from the file. unalias -- Delete oneormore custom commands defined by 'command alias'. Deletes one or more custom commands defined by 'Command alias'. For more help on any particular subcommand, type 'help <command> <subcommand>'.Copy the code

Command script import is used to import script modules in LLDB.

(lldb) help command script import
     Import a scripting module in LLDB.

Syntax: command script import <cmd-options> <filename> [<filename> [...]]

Command Options Usage:
  command script import [-r] <filename> [<filename> [...]]

       -r ( --allow-reload )
            Allow the script to be loaded even if it was already loaded before.
            This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not. Allows scripts to be loaded even if they have been loaded before. This parameter is backward compatible, but reloading is always allowed whether you specify it or not.Copy the code

The command script import/usr/local/opt/chisel/libexec/fbchisellldb py’s trip is the command / usr/local/opt/chisel/libexec/fbchisellldb py the script in the import into LLDB. So how many commands did Chisel provide, as follows:

Current user-defined commands: alamborder -- Put a border around views with an ambiguous layout alamunborder -- Removes the border around views with an  ambiguous layout bdisable -- Disable a set of breakpointsfor a regular expression
  benable       -- Enable a set of breakpoints for a regular expression
  binside       -- Set a breakpoint for a relative address within the framework/library that's currently running. This does the work of finding the offset for the framework/library and sliding your
                   address accordingly.
  bmessage      -- Set a breakpoint for a selector on a class.even if the class itself doesn't override that selector. It walks the hierarchy until it finds a class that does implement the selector
                   and sets a conditional breakpoint there.
  border        -- Draws a border around <viewOrLayer>. Color andwidth can be optionally provided. Additionally depth can be provided in order to recursively border subviews. caflush --  Force Core Animation to flush. This will'repaint' the UI but also may mess with ongoing animations.
  copy          -- Copy data to your Mac.
  dcomponents   -- Set debugging options for components.
  dismiss       -- Dismiss a presented view controller.
  fa11y         -- Find the views whose accessibility labels match labelRegex and puts the address of the first result on the clipboard.
  findinstances -- Find instances of specified ObjC classes.
  flicker       -- Quickly show and hide a view to quickly help visualize where it is.
  fv            -- Find the views whose class names match classNameRegex and puts the address of first on the clipboard.
  fvc           -- Find the view controllers whose class names match classNameRegex and puts the address of first on the clipboard.
  heapfrom      -- Show all nested heap pointers contained within a given variable.
  hide          -- Hide a view or layer.
  mask          -- Add a transparent rectangle to the window to reveal a possibly obscured or hidden view or layer's bounds
  mwarning      -- simulate a memory warning
  pa11y         -- Print accessibility labels of all views in hierarchy of <aView>
  pa11yi        -- Print accessibility identifiers of all views in hierarchy of <aView>
  pactions      -- Print the actions and targets of a control.
  paltrace      -- Print the Auto Layout trace for the given view. Defaults to the key window.
  panim         -- Prints if the code is currently execution with a UIView animation block.
  pbcopy        -- Print object and copy output to clipboard
  pblock        -- Print the block`s implementation address and signature
  pbundlepath   -- Print application's bundle directory path. pcells -- Print the visible cells of the highest table view in the hierarchy. pclass -- Print the inheritance starting from an instance of any class. pcomponents -- Print a recursive description of components found  starting from <aView>. pcurl --Print the NSURLRequest (HTTP)as curl command. pdata -- Print the contents of NSData object as string. pdocspath -- Print application's 'Documents' directory path. pinternals -- Show the internals of an object by dereferencing it as a pointer. pinvocation -- Print the  stack frame, receiver,and arguments of the current invocation. It will fail to print all arguments if any arguments are variadic (varargs).
  pivar         -- Print the value of an object's named instance variable.
  pjson         -- Print JSON representation of NSDictionary or NSArray object
  pkp           -- Print out the value of the key path expression using -valueForKeyPath:
  pmethods      -- Print the class and instance methods of a class.
  poobjc        -- Print the expression result, with the expression run in an ObjC++ context. (Shortcut for "expression -O -l ObjC++ -- ") pproperties -- Print the properties of an instance or Class present -- Present a view controller. presponder -- Print the responder chain starting from a specific responder. psjson -- Print JSON representation of Swift Dictionary or Swift  Array object ptv -- Print the highest table view in the hierarchy. pvc -- Print the recursion description of <aViewController>. pviews -- Print the recursion description of <aView>. rcomponents -- Synchronously reflow and update all components. sequence -- Run commands in sequence, stopping on any error. setinput -- Input text into text field or text view that is first responder. settext -- Set text on text on a view by accessibility id. show -- Show a view or layer. slowanim -- Slows down animations. Works on the iOS  Simulator and a device. taplog -- Log tapped view to the console. uikit -- Imports the UIKit module to get access to the types while in lldb. unborder -- Removes border around <viewOrLayer>. unmask -- Remove mask from a view or layer unslowanim -- Turn off slow animations. visualize -- Open a UIImage, CGImageRef, UIView, CALayer, or CVPixelBuffer in Preview.app on your Mac. vs -- Interactively search for a view by walking the hierarchy. wivar -- Set a watchpoint for an object's instance variable. xdebug -- Print debug description the XCUIElement in human readable format. xnoid -- Print XCUIElement objects with label but without identifier. xobject -- Print XCUIElement details. xtree -- Print XCUIElement subtree. zzz -- Executes specified lldb command after delay.Copy the code

This article is going to be a long one, so let’s go into more detail about the commands Chisel provides in the next one. ⛽ ️ ⛽ ️

Refer to the link

Reference link :🔗

  • Debugging with Xcode
  • LLDB Quick Start Guide
  • Advanced Debugging with Xcode and LLDB
  • Xcode 10.2 Release Notes
  • Little Stupid Wolf’s LLDB trick: Chisel
  • How to debug more efficiently in iOS development: LLDB vs. Chisel
  • Dancing with the debugger – the waltz of LLDB
  • Chisel-lldb command plug-in to make debugging easier
  • LLDB advanced debugging +Cycript
  • IOS reverse LLDB debugging
  • The LLDB command is commonly used for iOS debugging
  • LLDB study notes
  • Chisel for iOS debugging
  • IOS debugging Advanced – More efficient use of Xcode and LLDB
  • New feature added to LLDB in Xcode10.2
  • WWDC 2018: Xcode and LLDB debugging tips for improving efficiency
  • LLDB – Pit pointing north (1) – Add batch enable disable breakpoint function to Xcode
  • XCode LLDB debugging tips basics improve assembly
  • Basic commands for LLDB in Xcode (Swift)
  • LLDB is not just Po
  • facebook/chisel
  • What are the use of Step Over Thread and Step Into Thread commands?
  • A detailed exploration of Xcode menu options