126

Is there an LLDB command that can cast a raw address into a usable Swift class?

For example:

(lldb) po 0x7df67c50 as MKPinAnnotationView

I know that this address points to a MKPinAnnotationView, but it is not in a frame that I can select. But, I want to cast the raw address into a MKPinAnnotationView so that I can examine its properties. Is this possible?

jarrodparkes
  • 2,378
  • 2
  • 18
  • 26

12 Answers12

215

Under Xcode 8.2.1 and Swift 3, the lldb command po or p won't work with the typed variable. You will need to use the swift command print to examine the properties of the typed object instance. (Thanks to cbowns's answer!) E.g.:

expr -l Swift -- import UIKit
expr -l Swift -- let $pin = unsafeBitCast(0x7df67c50, to: MKPinAnnotationView.self)
expr -l Swift -- print($pin.alpha)
cbowns
  • 6,295
  • 5
  • 47
  • 64
Xi Chen
  • 2,166
  • 1
  • 11
  • 5
  • 69
    This really shouldn't be so hard – Lucas van Dongen Nov 07 '17 at 22:24
  • This was a little counter intuitive. I thought I didn't need to type the `(lldb)` in my console. But it didn't work without that. – mfaani Dec 13 '17 at 16:53
  • 2
    Is there a way to do this in objective-c? – p0lAris May 11 '18 at 01:43
  • I keep getting back at this. I probably should create a lldb alias for `expr -l Swift -- `.. – Koen. Dec 03 '18 at 19:48
  • 3
    As was mentioned in a different answer to this question, typing `settings set target.language swift` once is enough to avoid `expr -l Swift -- ` in subsequent commands. – Max Desiatov Jan 17 '22 at 13:44
  • I was just passing by and wanted to share my little observation with you: the `print` function is redundant there! it will print `() $R0 = {}` which means that expression executed and return nothing. Well I personally have no idea where it was printed, but not in this stupid debugger. So you should write just `expr -l Swift -- $pin.alpha` to see its value on next line – vlastachu Sep 08 '22 at 12:33
59

You can use Swift's unsafeBitCast function to cast an address to an object instance:

(lldb) e let $pin = unsafeBitCast(0x7df67c50, MKPinAnnotationView.self)
(lldb) po $pin

Then you can work with $pin as usual – access properties, call methods, etc.

Check out this article for more information: Swift Memory Dumping.

gregheo
  • 4,182
  • 2
  • 23
  • 22
  • For the first statement i think you forgot the 'expr' or 'expression'. Otherwise it is working great! – jarrodparkes Apr 04 '15 at 04:13
  • 2
    I'm getting "error: use of undeclared identifier 'unsafeBitCast'" in Xcode 7.2. – devios1 Mar 04 '16 at 17:47
  • 9
    Besides that error (@devios) there's another error it shows in 7.3.1: "error: unknown type name 'let'" – carlos_ms Apr 22 '16 at 20:10
  • 8
    Note that depending on context you might need to switch lldb to Swift mode first using `(lldb) settings set target.language swift`. Also, in some cases (e.g. when breaking outside of your app's module while casting to a type from your app) you might need to follow that with a `e import MyApp` – Patrick Pijnappel Nov 14 '18 at 05:27
  • That doesn't seem to work for tagged pointers. Short NSString, NSIndexPath, NSNumber etc. are implemented as tagged pointers where the content is part of the pointer itself. – Ortwin Gentz Nov 23 '22 at 16:59
28

The lldb format for expression seems to have changed in Xcode 7.3. The following got me started:

(lldb) expr -l Swift -- import UIKit
(lldb) expr -l Swift -- let $view = unsafeBitCast(0x7fb75d8349c0, UIView.self)
pkamb
  • 33,281
  • 23
  • 160
  • 191
sfaxon
  • 551
  • 6
  • 10
23

For Custom Classes you need to import your project

expr -l Swift -- import MyTestProject
expr -l Swift -- let $vc = unsafeBitCast(0x7fad22c066d0, ViewController.self)
expr -l Swift -- print($vc.view)
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
afinlayson
  • 241
  • 2
  • 3
22

Objective-C version

po ((MKPinAnnotationView *)0x7df67c50).alpha
rockhard
  • 349
  • 3
  • 8
  • 3
    This worked perfectly for me. In my case I was in the `Debug View Hierarchy` view, right clicked on a view, then selected `Print description of...`. That gave me a memory address and type I could drop into the code above. Nice to know that the visual debugger puts the console into an Obj-C frame. – Trev14 Jan 18 '19 at 21:09
13

As of Xcode 8/Swift 3, here's what worked for me. (This is based off @sfaxon's answer.)

(lldb) expr -l Swift -- import UIKit
(lldb) expr -l Swift -- let $nav = unsafeBitCast(0x1030ff000, to: UINavigationController.self)
Community
  • 1
  • 1
cbowns
  • 6,295
  • 5
  • 47
  • 64
11

Thanks to all the answers above, unsafeBitCast also works well with Xcode 8.3.2 / Swift 3 / macOS / Cocoa Application.

Memorize an address of current instance

(lldb) p tabView.controlTint
(NSControlTint) $R10 = defaultControlTint

(lldb) p self
(LearningStoryboard.NSTabViewController) $R11 = 0x00006080000e2280 {
.....

Later, examine them

(lldb) p unsafeBitCast(0x00006080000e2280, to: NSTabViewController.self).tabView.controlTint
(NSControlTint) $R20 = graphiteControlTint

(lldb) p $R11.tabView.controlTint
(NSControlTint) $R21 = graphiteControlTint

If something like this happens

(lldb) p unsafeBitCast(0x00006080000e2280, to: NSTabViewController.self).tabView.controlTint
error: use of undeclared identifier 'to'

(lldb) p $R11.tabView.controlTint 
error: use of undeclared identifier '$R11'

make sure that choose one of the stack frames of Swift source code rather than assembler one.

It is likely to happen when the application was paused by clicking a Pause button or stopped with an exception. By choosing a stack frame accordingly, let lldb infer a proper programing language.

Tora
  • 970
  • 1
  • 8
  • 15
8

It took me longer to figure out that I'd like to admit. It's similar to @afinlayson answer, but with better explanation (I hope!) and fixed syntax

If you want to check properties of an objects using Xcode’s view hierarchy debugger then this will work: You’re in objc context by default so you’ll have to switch it to Swift context

  1. First import your project (if you want to use some of the classes defined there)

expr -l Swift -- import <YOUR PROJECT NAME>

  1. Cast object using it’s memory address to whatever class you want

expr -l Swift -- let $vc = unsafeBitCast(0x7fb7c51cb270, to: <YOUR PROJECT NAME>.<YOUR CUSTOM CLASS NAME>.self)

  1. Access any value you want from the object

expr -l Swift -- print($vc.<PROPERTY NAME>)

Example:

expr -l Swift -- import Football

expr -l Swift -- let $vc = unsafeBitCast(0x7fb7c51cb270, to: Football.Ball.self)

expr -l Swift -- print($vc.velocity)

Bartosz Kunat
  • 1,485
  • 1
  • 23
  • 23
7

@Xi Chen's answer works perfectly when your LLDB session was started in a Swift context. However, in some cases you might have stopped in a breakpoint outside a Swift context; for example, when it's a symbolic breakpoint to Objective-C API, or when in Debug View Hierarchy mode (at least as of Xcode 11.4).

error: unknown type name 'let'
error: use of undeclared identifier 'unsafeBitCast'

In that case, you'll need to do it the old way using Objective-C:

e MKPinAnnotationView *$pin = (MKPinAnnotationView *)0x7df67c50

and now you can use $pin as you would.

Gobe
  • 2,559
  • 1
  • 25
  • 24
3

The easiest way, swift 4

expr unsafeBitCast(0x7df67c50, to: MKPinAnnotationView.self)

Answer by : https://www.kproapps.com

Kingsley Mitchell
  • 2,412
  • 2
  • 18
  • 25
3

po is an alias, which means it can be overridden. You can override po by handling hex addresses using objc:

command regex po
s/(0x[[:xdigit:]]+)/expression -l objc -O -- %1/
s/(.+)/expression -O -- %1/

To see what effect this has, you can tell lldb to expand these aliases:

(lldb) settings set interpreter.expand-regex-aliases true

Also I have created https://github.com/kastiglione/swift_po, which is a substitute po for Swift. It handles object addresses, and has a few other improvements too.

Dave Lee
  • 6,299
  • 1
  • 36
  • 36
  • from your link, `expression -l objc -O -- 0x76543210` is just the response for me, and it doesn't need to know the variable class from the adress! – tontonCD Mar 24 '20 at 16:48
1

When in Swift lldb context and dealing with NSObject subclass such as MKPinAnnotationView it's arguably easier to deliberately switch back to obj-c lldb context using this 1-liner:

e -O -l objc -- 0x7df67c50

where e -O -- is equivalent to po in lldb when in obj-c context.

Kamil.S
  • 5,205
  • 2
  • 22
  • 51