LLDB expression for Code Injection in Xcode
LLDB (http://lldb.llvm.org), described on the official webpage as a next generation, high-performance debugger, is the debugger that’s currently shipped with Xcode. Just a few years ago, LLDB replaced gdb, and now LLDB is the only debugger supported by Xcode. In this app developer tutorial, I will show you how to use the LLDB debugger and the expression command when developing iOS and watchOS apps.
You can run LLDB from the Terminal typing: xcrun lldb. The xcrun command points the Terminal to the LLDB version shipped with Xcode (the Xcode bundle contains tons of tools). If you omit the xcrun command, the Terminal executes LLDB installed in the /usr/bin/ directory, which could contain a different version than the LLDB used by Xcode.
Unfortunately, executing LLDB from the Terminal app is only useful to debug apps that are not sandboxed. If you want to debug an iOS or watchOS app, you need to use LLDB from the Xcode console when executing the app with the Apple IDE. After compiling and running an app, you can access the LLDB console at any time by pressing the Pause button in the Xcode UI (see next figure).
The execution of the app is paused and LLDB attaches to it. At this point, you can type LLDB commands in the console, as you can see in the following picture.
You can learn more about the different LLDB commands by typing help in the console or reading the documentation provided with Xcode. Another valuable resource is the previously mentioned LLDB webpage (http://lldb.llvm.org). If you want to know more about a particular command, just type help in the console followed by the command name, and LLDB will print out the help for that particular command.
One of the LLDB commands is expression and in this post, I will show you how to use expression to debug Swift or Objective-C applications by injecting code at runtime and interacting with the objects of your app. You can also use expression in combination with the Xcode breakpoints, which I will show you later.
Evaluating Swift or ObjC code
If you type help expression in the console, you get the following description:
Evaluate an expression(ObjC++orSwift)inthe current program context, using user defined variables and variables currently inscope. This command takes ’raw’ input(no need to quote stuff).
Syntax: expression <cmd-options> — <expr></code>
As you can see, you can evaluate Objective-C++ and Swift expressions, using user defined variables and also variables within the scope. So let’s try to use the expression command. First of all, if you don’t need any of the options provided by expression, you can use the short form p followed by the <expr> that you want LLDB to evaluate. So, instead of writing:
(lldb) expression let hello=”Hello”
you can simply use:
(lldb) p let hello=”Hello”
I have prepared a small example for you. Download from here the Xcode project that contains a navigation controller with its root view controller (ViewController) and an additional view controller (SecondViewController). Open the project and navigate to the ViewController.swift file. Now, add a breakpoint at line 18 in correspondence of the line of code view.backgroundColor = UIColor.redColor().
Then, open the SecondViewController.swift and add another breakpoint at line 16 in correspondence of the line title = “Second View”
Run the project. Of course as expected, the execution pauses at line 18. As you can see, that line of code changes the background color of the view controller’s view to red. Press the Step-over button or press the F6 key on your keyboard or type next (you can also use its short form n) in the LLDB console. This executes the line 18 and pauses the execution at next line (line #19). At this point, the background color of the view has already been set to red. So here, we can change the background color of that view again using the expression command. To do so, enter the following line in the LLDB console:
(lldb) p view.backgroundColor = UIColor.blueColor()
After pressing Enter on the keyboard, type c (short form for continue) and the application will continue to run. As you can see, the view of the view controller is now blue. This is possible because the expression command has injected a new line of code.
Now, suppose that you want to do something more complex. Let’s create a yellow layer (an instance of CALayer) and let’s add it to the layer tree. In the console, you can enter a Swift line of code prefixing it with the p command and one by one. This is ok for most cases. When you have multiple lines of code, you want a faster solution. The alternative is to type p and press Enter. In this way, LLDB enters a special editing mode that allows you to type multiple lines of Swift code. Then, when you are done, you simply press Enter on an empty line and the entire chuck of code is evaluated by LLDB. Here’s an example:
Enter expressions,thenterminate with an empty line toevaluate:
let layer = CALayer()
layer.backgroundColor = UIColor.yellowColor().CGColor
layer.bounds = CGRect(x:0,y:0,width:100,height:100)
layer.position = CGPoint(x:250,y:300)
layer.cornerRadius = 12.0
Now, type c to continue. You will see a yellow layer on top of the view controller’s view, as highlighted in this picture:
If you are working with Objective-C, expression can evaluate Objective-C source code too.
If you need to import a framework to access its classes you simply evaluate the import statement followed by the module name. For example, if you need to access any MapKit class, you can type the following:
Global constants and variables
Now, I want to show you another great, but undocumented expression feature that allows you to tell LLDB to persist a variable or a constant for later use. For example, imagine that you need the previously created yellow layer later in the execution of your app. When you define a new variable or constant, you can simply prefix it with a $ sign. For example, I can define a global constant in this way:
(lldb) let $layer = CALayer()
Now, the constant layer is global. So the next time that I pause the execution of the app during the same run session, I can easily re-use the yellow layer again.
Let’s make an example with this feature. Stop the execution of the download example, if you didn’t do it yet. Then, run it again. When the debugger pauses at line 18, type in the console the following:
(lldb) p let $layer = CALayer()
The constant layer is now a global constant. Now, type c to continue. In the Simulator, press the Next button to navigate to next view controller. The execution will pause again and this time at line 16 of the SecondViewController.swift file. Now, you can type p again and type the following lines:
Enter expressions,thenterminate with an empty line toevaluate:
$layer.backgroundColor = UIColor.yellowColor().CGColor
$layer.bounds = CGRect(x:0,y:0,width:100,height:100)
$layer.position = CGPoint(x:250,y:300)
$layer.cornerRadius = 12.0
The constant layer we use here is the same instance that was created in the previous step. Prefixing layer with the $ sign made it a global constant. Then, when I paused the execution the second time, LLDB was able to reuse it again.
The expression command is really powerful, especially if you want to analyze your variables and constants and you want to modify their behavior at runtime. I use expression in different situations. For example, I use it to highlight layers during an animation or to inspect core data managed objects after a fetch request or to see if a fault has been fired. And I also use it to validate the data I get from the location manager or the accelerometer.
Breaking the points
You can also combine the expression command with breakpoints. Let’s see how to do it. Go back to the first breakpoint in the ViewController.swift class (line 18). Right-click the breakpoint and choose Edit Breakpoint as highlighted in the following image:
In the popup, press the Add Action button and choose Debugger Command in the Action. Here, you can use the expression command again. In the textfield, add the following line:
p let layer = CALayer(); layer.backgroundColor = UIColor.yellowColor().CGColor; layer.bounds = CGRect(x:0,y:0,width:100,height:100); layer.position = CGPoint(x:250,y:300); layer.cornerRadius = 12.0; view.layer.addSublayer(layer)
As you can see, each line of code is separated by a semicolon. Instead of using a long line of code, you can also create multiple actions pressing the plus button and insert a line of code per each action. The best part is that you can share this breakpoint with other developers. To do this go to the Breakpoint Navigator, and right-click the breakpoint. Then, select Share Breakpoint.
The next time, you commit and push the project to Git, the breakpoint will be pulled by every developer that is working with you on this project.
The LLDB expression command is really powerful, so I hope you will use it to inspect your objects or change their behavior at runtime by injecting code with this command. Remember to also use it in combination with the $ sign when you need something for later. And don’t forget about the editable breakpoints.
If you want to learn more about LLDB, you can check the official webpage: http://lldb.llvm.org. And for a more comprehensive understanding of advanced iOS tools, attend one of our iOS bootcamps iOS bootcamps, intensive 5-day classes where you learn tons of advanced tricks for the iOS platform through interactive labs.
I hope you enjoyed this post on debugging with Xcode.
Geppy Parziale (@geppyp) is cofounder of InvasiveCode (@invasivecode). He has developed iOS applications and taught iOS development since 2008. He worked at Apple as iOS and OS X Engineer in the Core Recognition team. He has developed several iOS and OS X apps and frameworks for Apple, and many of his development projects are top-grossing iOS apps that are featured in the App Store.
LLDB Expression for Code Injection in Xcode
Originally published at invasivecode.com on July 4, 2015.