Spend an hour with the team responsible for the technology behind Xcode's debugger: LLDB. Find out what's new, what's been there all along that you didn't know about, and how best to take advantage of it all. This session will cover a range of topics with tips for both newcomers and experienced developers.
[ Music ]
Good afternoon and welcome to debugging tips and tricks.
It warms my heart to see this many people turn
out for this topic.
It's near and dear to my heart.
My name is Kate Stone
and I manage the core team that's responsible
for debugging technology here at Apple.
So again it's really exciting.
I hope we'll be able to bring up a number of engineers
from my team to tell you things that might be novel
if you're just starting but also tips that might be new
to you even if you've been debugging on our platforms
for the last ten years.
It really is a deep area and we've got a lot of great content
for you so let's get started.
Specifically we should note that LLDB is the technology
that I'm talking about.
If you've used Xcode for debugging, you may not be aware
that behind the scenes there's this technology called LLDB,
the low level debugger, providing all
of the core technology, and it's not just
in Xcode it's everywhere you need it.
But of course one of the more common places you'll encounter
it is in Xcode in the debug console.
Xcode hosts that console as part of your debugging area,
and you'll see of course the variables viewed
but also this LLDB console and that's a lot of what we're going
to be talking about here today.
That prompted and the wonderful things
that you can do from that prompt.
Of course that area contains normally not just LLDB's prompt
and allow to interact with the debugger
but it also contains your application's output
and allows you to type input to it
for console based applications.
That may not be the most convenient way
in case your application takes advantage
of other console features like moving the cursor or colors,
other ANSI cursor manipulation features.
So there's a new option in Xcode 8 that allows you
to use a standalone terminal
for your application while LLDB remains
in Xcode and it's simple.
Bring up the scheme options and in the options tab
at the very bottom you'll see this new console that allows you
to switch from the default to use Xcode
to use terminal instead.
And then when you run you'll get a separate terminal
for your application's input and output separate from Xcode.
For the remainder of the talk we'll be really focused on LLDB.
So if you're interested in other Xcode specific features,
definitely check out the two talks noted here.
Of course they've past by now
but the videos are there and waiting for you.
You should also note that LLDB is not just part of Xcode
but the Swift REPL is in fact LLDB.
When you bring up the Swift REPL and start interacting with it,
you're interacting inside a debugging environment already
so that the power
of the debugger is there any time you need it.
In fact every command that we tell you
about here you can use directly from within the REPL just
by prefixing it with a colon from the REPL prompt.
So colon followed by command will issue the command directly
So let's say I'm at the Swift REPL prompt.
At this point I'm interested in looking up some information
on a type and of course I could bring up Xcode and look
at the help, but right from the prompt here I could go ahead
and simply say colon type lookup comparable.
And I'll have looked up this protocol and found out that
in fact it derives from another protocol
and adds the following four functions.
If you're familiar with type lookup,
it's a great feature you use it all the time
but you may not be aware of the fact
that despite the name type lookup it can actually be used
to lookup a wider range of things.
I can actually lookup functions to get the complete signature
or even lookup entire modules, and of course this a lot
of content as it will be the full declaration
of every type in that module.
We've abbreviated it here.
So the REPL is fantastic for these kinds
of additional commands but it's also useful if you want
to interact with the code you're writing in the REPL.
Let's say I write a simple function here.
I've written a function, I'd like to stop when I'm
in the function and find out what's going on at a given line,
so I can issue the breakpoint command by prefixing it
with a colon, colon b 2, I set a breakpoint at line two
and when I call this function, execution stops as I'd expect.
And because execution has stopped it switches immediately
to the LLDB prompt and from the LLDB prompt I can issue other
LLDB commands to interact with and explore my application.
And the REPL will also do this automatically if you run
into an overflow condition or other things
that would normally terminate your application
so that you can take advantage of the full power
of the debugger directly from the Swift REPL.
You should also note that you can switch
between the two prompts at any time.
If you're at the REPL prompt a colon on its own followed
by enter will bring up the LLDB prompt,
and the command REPL will switch back to the REPL prompt
so you can use the two interchangeably at any time.
The REPL has slightly different characteristics
and may even be desirable within a debugging session
where you're debugging Swift code.
So that's great.
It's a couple of the key ways a lot of people interact with LLDB
but LLDB is also a standalone command line tool
and that's fantastic if I'm a remote shell into a machine,
I have a very slow connection, I really want
to use typical benefits of a command line tool.
But it's also useful under other circumstances.
You might want to use it for example if you're going
to automate debugging tasks.
I want to setup my debugger the same way every time I start it,
so I might go ahead and provide a file containing a whole bunch
of LLDB commands to configure things.
LLDB -- source followed by a file name will invoke LLDB
and source all of those commands
to setup my debug session just the way I want it.
If you don't want to go through the trouble of setting
up a file, you can invoke LLDB and provide commands directly
on the command line; -- one-line will be followed by command
and that will be issued as soon as I start LLDB,
and if I want additional commands,
I can just repeat the option here
in its abbreviated form -o followed by another command
as many times as I like.
So it can be really trivial
to setup LLDB just the way I want it in an automated script.
And of course this is especially valuable in a situation
where I have an application that fails only one
out of a hundred times.
It's a race condition, I'd
like to run the same debugging sequence over and over again
and we have a special option for that as well.
LLDB -- batch starts a batch mode.
It will execute the instructions that I provided either
from one line or sourced from file
and presuming my application completes normally it will
If my application crashes, it will stop right
at the LLDB prompt where I can investigate the problem.
And of course just by wrapping this
with a few other shell commands I can repeat that series
of actions ad infinitum or at least
until my application crashes and I'm ready
to investigate the problem.
LLDB has a wide range of options.
If you haven't looked at LLDB --
help before, I'd encourage you to have a look.
It describes these options and many more.
I'm going to introduce one really interesting concept here
for us as a team that's actually probably the least important
thing in this entire talk.
So if you've going to forget one slide, please start
with this one because it's largely transparent.
You shouldn't really notice the effects
but there are some subtle aspects of it
that I want to introduce.
Starting with Xcode 8, LLDB runs
in a completely separate process from Xcode.
It's totally transparent to you.
You start debugging the way you normally did and what
that allows us to do is support multiple different versions
of the debugger.
It's selected for you automatically,
so if I go to debug Swift 3 I get the latest debugger,
I get all of the features that we're going
to introduce in this session.
And in fact if I'm using pure C++ or Objective C,
the same thing is true.
I get the latest debugger with all
of the features we'll talk about here.
On the other hand, if I'm debugging Swift 2.3,
I'm going to wind up with the debugger that's essentially what
we shipped earlier this year with Xcode 7.3.1.
That means some of the newer commands won't be available,
but we have the full support of the debugger from the era
of the Swift compiler.
But most importantly perhaps if you're part
of our open source community, you'll be able
to debug using the version of the debugger
that matches the open source Swift that you're using.
So if you download a snapshot of our work in progress or indeed
if your start adding to it yourself,
you will have a debugger that's immediately available
and you can actually use that concurrently
with debugging other programs written in Swift 3 or Swift 2.3
and everything is completely transparent.
There is one additional benefit here which is that if LLDB gets
into a situation where it cannot proceed and it needs to shut
down the debugging session,
the debug session will be terminated,
LLDB can exit gracefully, and Xcode will stay running.
To talk about what you can do with LLDB a powerful tool
that it is and how you can customize your experience,
I'm going to invite up one of my engineers Enrico Granata.
Thank you, Kate.
One of my favorite things
about LLDB is how customizable of a debugger it is.
It's not only great fun to work in the technology
that enables this, but it's also an amazing way for you
to be more productive in debugging your applications,
and LLDB offers a great many entry points
for you to customize it.
You can start with command aliases and then work your way
to custom commands or custom data formatters
and in Xcode 8 we have one new extension point
for you; stepping actions.
The way to think about stepping actions is do you
like what the next command does?
Do you like what the step command does?
But do you find yourself wanting to tweak its behavior
in just one or two little ways here and there?
With stepping actions you can actually do that.
But the simplest way to get LLDB, to customize LLDB
to suit your needs is to create a command alias which is a way
to take a piece of debugger syntax
for something you frequently do
and attach a shorter piece of syntax to it.
And now in Xcode 8 we also let you attach help text to it
so that for your own purposes
or for whoever you share those aliases
with you can remember what the alias is up to.
Let's see an example.
First of all, to create an alias you start
with the command alias command.
Then as I said you can pass help text to it, you provide us
with a short syntax, and then you give us the full debugger
command you want to replace.
And now shell is just as normal a debugger command as any
of the ones we built into it.
You can for instance say help shell and it will
so you help text including that which you provided to us.
And for those situations
where debugging becomes a little gnarly and you tend to forget
who you are as you pursue your bugs, you can ask the debugger
to remind you your identity and it will tell you
that it looks like I'm Enrico.
And that's great but if you want to do something more advanced
than simply attach a new name to an existing command,
we also vend you a Python API.
It's a fairly extensive option model
that lets you band the debugger to your will.
Getting started is easier than ever.
We've talked about this at great length in previous WWDC sessions
which you can find online.
We have a website with documentation and examples
to look at and if you just search for it online,
there's a community that's doing wonderful things
around scripting LLDB.
Let me give you a quick example.
Let's say that I want a command
that lets me retrieve the return value for the last function
that I exited out of while debugging.
Couple caveats here.
This command will work only if you finish your way
out of a function and don't execute any stepping actions
You can type expressions, you can look at variables,
just don't step around and this will work.
Let's look at an example real quick.
First of all, you import a file that contains that command
and then as I said finish out of the function
and the return values is right there for you to see.
Okay, that's great.
That happens by default, no need for customization here.
But what if I type backtrace for example
and that's quite a verbose backtrace
and now I don't remember where the return value is.
Okay, I could go up in the terminal and try to dig it up
but luckily enough we actually made a command that will tell us
about it, and here that return value is again for us to see.
This is all it takes to make that happen.
And by the way don't really worry about reading
that slide now, it will just be online waiting for you later.
On the other hand, you see that it is slide of text
and now you can type your shell alias, you can type the text,
and you'll find yourself typing this
because they're awesome every time you start debugging
and you'll type them again and again and again
until you're a literal typing monkey
and all you're actually doing is typing these
Nope. I say no to that.
I say save yourselves from repetitive typing.
There's no need to do that.
LLDB has an initialization file.
It's called .llbdinit and it sits in your home folder.
And if you need LLDB
to do something special something different
when launched under Xcode versus under the terminal,
there's a .llbdinit -Xcode file that will be preferred
when launched in the debugger under Xcode.
One more trick.
If you have Python commands that you need at debugger start up,
don't try to type them in .llbdinit.
Put them in a .py file and then source the .py file
that in LLDB init with command script import.
Those of you that have used LLDB
for a while are probably fairly familiar
with the p and po commands.
These are great commands, they're great ways to look
at data because they are full expressions.
They have the entire power
of the language you're writing your application in available
for you at the debugger console.
On the other hand, with great power comes
These commands run coding your target process.
They have potential to cause side effects
and also sometimes it's just not possible
to run the code you want at the point
where you're currently stopped.
And if p runs code once, po will actually run code twice
because not only does it evaluate the expression you
provide it also uses it also evaluates code
to print the customization to show your type
in a way that's customizable by type authors which is great
if the type author customized the display for their type
in exactly the way you want it.
If you're not that lucky,
the p command provides an alternative viewpoint
of variables that may be closer to what you want.
And also po is also a command
that runs again twice coding your target process
with all the potential for side effects.
If that scares you, we have another command to look
at variables; frame variable.
This is a very predictable command,
it will not run any code.
On the other hand, because it doesn't have
that code running ability,
the syntax it offers is also extremely limited.
And that's quite a few ways to look at data already
but spoiler alert, in Xcode 8 we have more.
Two new ones parray and poarray, and yes you're right.
They sound a lot like p and po
but they do something special for arrays.
What do they do especially for arrays?
Well, if you have used NS array in Objective C
or Swift arrays you're used to the safe container
that knows how many things it contains.
C pointers don't do that.
C pointers don't come with a kind of batteries included.
They don't know how many elements they point to and so
when we print a C pointer like this example in the debugger all
that we're told is the pointer value.
But now we know that this points to a bunch of elements
so we can start printing the first one,
printing the second one and we keep going and now we're back
to the little typing monkey situation which we don't like.
Well, in Xcode 8 you can say parray, number of elements,
pointer and it will expand for you that pointer
as if it was an array of the element count you specified.
That's already nice but why do I have
to guess at the element count?
It's right there.
We have the count right there.
What I really want is being able to type parray count dataset.
I almost can.
All I have to do is put count in backticks.
That backtick is a general LLDB syntax facility
which lets you take an expression, evaluate it
and replace the value of that expression
in the command before executing it.
And now I've got my full array shown.
Pretty much the same thing works for po.
poarray, number of elements, pointer
and I get po style descriptions of objects.
On that same topic of po, I'm sure that those of you
that could write Objective C code, quite a few I'm sure,
have actually done something just like that probably
without even thinking about it.
You take po, you say po followed by a number that you happen
to know is a pointer value
and you get a pretty description back.
And you try doing the same thing in Swift
and all you get back is a number.
Why? What's going on?
Well, I'm sure you've heard this quite a few times
but I'll say it one more time.
Swift is a type safer language than Objective C.
We can't assume that numbers are arbitrarily objects
because not all Swift objects have a pointer value connected
So when we say po a number we'll show you the number.
Okay, that's great but come
on I know there's an object there just show it
to me already.
There's a way to do that.
There it is.
It looks like a lot of words, I know it looks like a lot
of words but it's actually just follow me for a second.
Expr -O just means po.
If you're at the LLDB console and you say help po,
what it will tell you is that it's an alias for expr -O.
So all we're actually saying here is po this thing
as if we were in Objective C code and with
that we actually get the pretty description we wanted.
And that's great.
On a topic related to actually inspecting memory addresses
and trying to make sense of them, low level debugging.
If you remember one thing and only one thing
about low level debugging is to stay very far away from it.
Don't do it.
Unfortunately, sometimes you just really have to.
Maybe you're debugging something that only happens
in optimized code in the release build of your app.
That happens to me sometimes.
Or you're debugging third party code
for which you have no debug information.
If any of that applies to you,
follow me as we sail past the Pillars
of Hercules on this journey.
But please know that on this journey you proceed
at your own risk.
It starts just like this; I had a gentleman last year walk
up to me in the lab with his laptop showing me Xcode just
in that state, crashed in Objc msgSend of all places.
And he tells me a story.
I have my app and it's under store and it's great
but then my framework vendor says, hey I have a new version
of my frame, just update, it will be okay.
And I did update, I listened to him,
and now my app crashes on launch.
What do I do, please help me.
And so we sat down and I told him well,
we know pretty much nothing here
but one thing we can do is let's start
by reading machine registers.
LLDB offers a facility to do that and it lets you look
at all the registers, only a few of the registers
and it even lets you play custom formatting.
What does it look like?
You say register read and you get your register values,
and that's a lot of registers.
And why do I even care about all those weird numbers
and the words on the screen?
Well, you do care because often arguments are passed
Okay, that's fun, but that was a lot of registers.
How do I know which ones actually matter to me?
That is a question for your platform's application binary
interface ABI, the colon convention gives you
But LLDB also exposes
to you convenient pseudo registers named $arg1, $arg2,
and so on which in the case
in which your arguments are actually of simple scaler
or pointer types actually mapped one to one
between the registers and the arguments.
Similar convenience is available in the C family expression.
So for example, if I have a function
that takes these three arguments and I call it,
those arguments will actually map one to one
to $arg1, $arg2, and $arg3.
Okay, so that applies to our example.
We're in Objc msgSend, we start by reading arguments.
The first argument is the pointer 0 X 4 D 2,
the second argument is the selector string
by appending string.
We happen to know that Objc msgSends first argument is the
object we're trying to message
and the second argument is the selector we're trying to send,
and we can also use the memory read command to check what's
up with the object that we're messaging.
It turns out that's a bad object.
What is happening is that we're calling this selector
on a bad object.
How did we get there?
Well, we're in Objc msgSend, we're crashed,
something called Objc msgSend, something called the thing
that called it and so on and so forth until we get all the way
to the entry point of our application.
In LLDB we call the frames from frame 0 the youngest all the way
to frame N the oldest and if you want to move around frames,
you can use the up command to go back to an older frame
on the stack and the down command
to go back to a younger frame.
Another thing worth knowing is the disassemble command
which lets you look at the disassembly for a function.
You can do that for the current function,
for an arbitrary frame, for an address, for a function by name,
you can customize the way that disassembly shows
and in some cases it makes debugging
where you do have source code and debug info but you want
to compare those instructions
to machine instructions you can also ask LLDB
to always show you always show you disassembly along
with source code.
So in our case, we crashed there and we can see that the thing
that called our function is an application
that did finish launching.
So let's go there real quick and take a look at what
that function is doing.
That function is calling this initializer
that our framework vendor told us, yes totally code
that initializer, getGlobalToken.
It's moving some stuff around and then it's making the call
to Objc msgSend that will crash us.
So we can step around machine code
and see what these calls are actually doing.
First of all we step over the getGlobalToken call
and then I'm going to cheat for a second here.
I happen to know that the register called rax contains the
return value of that function and if I read it,
that's just the bad pointer value.
Let's step around a couple more times.
No, that isn't changing it, that isn't changing it.
All we're doing is taking that pointer value as is
and moving it into rdi and then calling into Objc msgSend.
I wonder if that's connected?
If I reg read $arg1 at this point right before entering Objc
msgSend, rdi the bad pointer value.
What have we proven to ourselves?
We've proven to ourselves that the getGlobalToken function
that our framework vendor was so excited
to get us calling actually returned to us a bad object
and upon trying to send a message to that bad object,
big surprise, our application ended up crashing.
And on that note of patting ourselves on the back
for conclusively proving our case, I want to hand it
over to Sean Callanan to tell you all
about the great new features in the expression parser.
Isn't that magic?
It feels like magic.
Your program is just storing its data as numbers,
arrays of numbers and yet you can use LLDB this powerful tool
to represent that data in the way you think about it.
Sometimes though it's not quite as easy as just looking
at a number and figuring out what the data is.
Sometimes you need the expression parser.
Now, Enrico has already shown you
where the expression parser fits
in in the general command syntax,
but there's a lot that it can do.
The expression parse's job is to work together with your program
and the SDK and get from where you're currently stopped
through some contortions to get at the data you want to the data
that you're actually trying to inspect.
Now, I said we work with your program
and we work with the SDK.
Working with the SDK hasn't always been easy in LLDB
if you remember from previous years.
So for example if you were stopped
in an Objective C program and you tried
to get the program's undo manager, you probably
at least once or twice got an annoying error.
It wasn't at all relevant to what you were trying to do
and it was really puzzling.
But last year we told you there's a way
to get out of this.
If you just manually import AppKit then you're
All right, but why did I have to do that?
It's already there.
I hear you cry.
You're not the only ones.
So this year we thought what can we do to make this better,
and it was pretty obvious.
We looked at which modules the current source file imports
and we import them for you automatically.
No more of that manual importing business.
Cool. So we're getting out of your way more efficiently.
That's great, but this is supposed to be a powerful tool.
Let me tell you about some of the great things
that you can do with it.
Now, sometimes these conveniences might get
in your way.
You're actually trying
to manually import the things you want.
There's a feature, a setting that you can use
to disable this automatic importing
and get back the feature the way it was last year.
We think you're going to like it though so it's by default on.
Great. Now let's talk more generally
about using the expression parser effectively
by reusing code.
Now, the most simple case
of reusing code is reusing variables.
Now remember I said you might need to do multistep expressions
to get to the data that you actually want from the place
where you currently are.
In Swift you can do something as simple
as defining a temporary variable and using it.
This just works.
It's as if you typed it in your own program.
Now what might be counterintuitive is what happens
if you try to use it again.
Then we say, what's that variable name?
Well, actually we intended it to work this way.
The reason is you might step around, you might stop
in different places, maybe later you're in a place
where you're program actually defined an A.
Do we want the A you used
as a temporary valuable to get in your way?
Probably not, but there is a way out of this.
The affordance we setup to make sure
that your variables don't escape
in that way is we actually setup a local context.
It's as if you actually put a set of braces in your program,
put the let A in there and the print, but if you want the A
to break out, all you have to do is give it a special name,
a name with a dollar sign.
That means that it will never collide
with your own program's names and it means that it will live
as long as your debug session does.
Awesome. What else can you do with this tool?
It turns out quite a lot.
Now in Swift ever since day one with LLDB
and Swift you've been able to do the same trick with functions.
Now when you did that you probably wanted
to use multiline expression modes and in fact
if you typed the expression command and pressed enter,
you're immediately going to get a multiline editor
where you can type in your function.
If you define your function then you can simply reuse it again
remembering the dollar sign.
Now, those of you who tried this in Swift
and said that's awesome might have tried it in Objective C
and it wasn't so great.
Function definition isn't allowed here.
Come on these LLDB guys always get in my way.
Well, turns out we like this feature too,
we think it's awesome, we want it to be better.
But we can't just make it magically work.
The reason is remember we're stopped in your code.
We want to act like we're inside your function.
If you're in Swift you can define nested functions,
it's no big deal, the compiler will love it.
It won't love the dollar sign you can take that out
but the rest it's fine with.
This is totally legal.
In C, C++ and Objective C though trying to make a nested function
like that, that's no good the compiler is going
to yell at you.
Well, the way you get
around that is using the top level expression mode.
That's an extension of the expression command
that makes it break completely
out of the current function you're stopped in
and just define global code whether it's functions
or variables or what you will.
Now you can define your function
and use it just the way you would expect.
Now the functions aren't the only reusable things you
I've already talked about variables.
You can define closures too, they're kind of a merger
of variables and code.
In Swift you can define a closure and use it.
New in Xcode this year you can do the same thing
in Objective C.
Blocks can be defined and reused and for those of you
who are diehard C++ fans,
you can do the exact same thing with lambdas.
Now, what can you do with these blocks?
What's special about them.
Well, you can pass them off to functions for example.
Sometimes you might need
to manually run something on a certain queue.
You can send stuff for example to a global queue
and the block will simply run.
Now, sometimes it gets a little annoying
because these complex expressions result
in much more likelihood of typos.
Now, what's the difference quickly without looking back
to the previous slide between this expression and the one
from the previous slide?
You probably missed it.
The compiler sure didn't.
It will yell at you about the missing semicolon
but there's a better way.
If you were to type this into the source editor,
we would have told you, hey you probably missed a
Did you mean to put it in?
Well, it turns out LLDB can do the same thing,
and we can do one better by just automatically putting
in the semicolon that was missing,
running your expression.
This is called fixit.
It's been in clang for a while
and LLDB now applies the same thing.
Swift has fixit too.
In Swift you're less likely to run into semicolon problems
but boy those exclamation points are annoying.
Yeah. Well, they are as valuable as they are in your own code
for you to understand it,
when you're debugging you just want them the heck
out of your way, and believe me we do too.
So if you try to use something without unwrapping it,
we just apply the fixit and unwrap it for you.
Now, there may be one or two people
who say I don't want debugger touching my code.
Now, for those people I have had those evenings too,
and we have settings that will turn off the entire autoapply
fixit feature and if you just don't
like the debugger acting smug and pointing
out every little thing in your code that it's fixing for you,
then you can turn just that part off too.
Thanks. All right.
Great. That's a nice convenience feature but let me finish
up with just one more thing that you can define that's reusable.
You can define your own types.
In Swift for example you can enter a multiline expression
that defines a class and when you try to instantiate
that class indeed it shows up just the way
as if you had defined the class in your own program.
In the same way in C++, you can define a class and reuse it.
Now let me show you an example of taking all these concepts
and using them in your own program.
Often especially in your programs that interact
with web APIs you get a lot of data back and you want
to filter it especially when you're debugging.
The way you filter data especially say in NS array is
by defining a predicate.
Now in the expression parser you can define custom predicates.
In this case we're taking writing a block
that takes the result strings from the web server
and filters them to find strings
that have the text error in them.
Probably useful for debugging.
Now if you simply take an array full of data
from your web server and you apply the predicate to it,
you can get right down to the message
that you actually cared about.
Now you've hopefully seen how powerful the expression parser
can be for you, I'd like to turn things over to Jim Ingham
to show you more powerful features of LLDB.
Thank you, Sean.
So, so far we've sort of inverted the natural order
of things and told you how to look at the state
of your program when you're stopped but we haven't
yet told you how you would actually get
to such interesting points.
So that's with what I'm going to tell you a little bit about.
Of course breakpoints are the natural way
that you would stop your program.
So I want to talk a little bit about how you might think
about breakpoints naively as the place where I stop my program
but that's actually not how they're implemented
or how LLDB thinks about them.
To LLDB a breakpoint is really is search
through your program space for interesting places to stop
or many different kinds of searches as it turns out.
So breakpoints are really search criteria
and what the individual locations where your stop,
what you thought of naively a breakpoint,
we call breakpoint locations.
So to make this a little more concrete, let me tie this
to Xcode's breakpoints because after all Xcode under the covers
when it debugs is LLDB
so all the Xcode breakpoints must be LLDB breakpoints.
So for instance when you click in the source gutter in Xcode,
what you're really doing is running this command in LLDB,
some breakpoint setting command.
Similarly when you make a symbolic breakpoint you're
running a by name breakpoint setting.
So I want to give you a little sense
that these are really searches by showing you cases
in which you would end up naturally with multiple results
from what you thought of as a simple unitary breakpoint
setting, so the first example
that I'll show you is symbolic breakpoints.
So here's an example where you want
to just set a breakpoint on main.
That should be a simple thing to do, right?
But then it says, no I have 19 locations.
Well, while did you end up with 19?
Do the break list command to see the results
of your breakpoint setting and what you see is
that the breakpoint name search is actually a loose name
matching search, so for instance it picked up the selector names
within a class and that's actually convenient
in many cases because like if you're debugging in C++
and you have a name space, and a name space inside, and a class,
and a method, you don't want to have to type all
of the full path to that but on the other hand it does mean
that the search is perhaps wider than you intended it to be.
We provide many different kinds of searches
so of course we provide a slightly more strict search
which is the full name search that forces the name
to match the entire name of the symbol that you're specifying.
We tried that but even that didn't work right.
Well, for some reason somebody decided their library has
to have a function called main in it, no idea why but it does.
So you can even specify further by limiting your search
to a particular shared library with the shlib option.
So then finally then you get the breakpoint you want.
I'm going to give you one other instance not
because I don't think you believe me
but because this one actually comes up in Swift fairly often
with file and line breakpoints
because Swift has this nice feature
that you can call a function that uses a closure
and define the closure simply by continuing with a curly bracket
and then on with the body of the closure.
But then if you try to set a breakpoint on that line,
what you're going to find out is you have two stop points.
Why do you have that?
When you look it's actually fairly straightforward, right?
That source line actually contributed a little bit of code
to the closure function and you see we have a breakpoint
location on the closure function,
but it also was the invoking site for that function
and so you also have a location for that invoking site.
So anyway that's for that so now having given you a few examples,
let me give you the general form of the command and then go
on to some more interesting uses of it.
So the breakpoint set command works as follows:
You say break set and then there are some options
which specify the type
and that's really specifying the kind
of search that you're doing.
Is it a file and line search, a symbol name search
or so on and so forth?
And the values for that type option will be the data
for the search and then there are other kinds of options
like ignore count, condition and so on.
The way to think about those options is they don't specify
where to break, they specify whether to break.
So that's the whether can be modified after the fact but the
where can't because we've already done the search
and you would just set a new breakpoint
if you wanted to do that.
So let's talk a little bit about these breakpoint location things
which are the places where you're going to stop.
They are the individual search results,
they always have some address which is the address
at which your program is going to halt.
When you look at them they're specified
by the generating breakpoint's number
and a location number written separated by a dot.
So if you actually notice when you're debugging in Xcode
and you stop at one of your breakpoints and you look
at the little PC ribbon, the PC ribbon will have the stop reason
on the far right and it will say breakpoint,
but it always says 2.1.
It never said breakpoint 2
because you only ever really stop at locations
so 1.1, 1.2 or whatever.
By the way, the locations and breakpoints are sort
of symmetrical with respect to all
of these other options that I talked about.
They all take the same sort of generic options like commands
and conditions and so forth,
and you can specify a command condition whatever
on a breakpoint and then it will work for all the locations
but you can also override for a particular location one
of the commands or conditions just by setting it
on the location instead.
One other little convenience,
oftentimes if you have a breakpoint that's generated a
bunch of locations, you want five of them,
you don't want five of them so you disable them
which you can do independently,
but then if you you don't want any of them to hit you want
to be able to disable the whole breakpoint which you can do
by disabling the breakpoint.
But it turns out that doesn't change the enable disable state
of it locations so then you can just turn it back on again
and all of the location state will be as you would expect.
So that's just a little thing.
So now that you've seen a little bit,
the notion of how breakpoints are thought of in LLDB,
let me show you a couple of more powerful types
of breakpoints that LLDB provides.
So again these are searches for places to stop.
What kinds of searches are we going to do?
It's just what name spaces
in your program might be interesting.
It turns out all of the name spaces are name spaces
of stringy things because they're all like names
of functions or whatever, so we always use regular expressions
as the way to express the search patterns.
So if you know regular expressions,
this will make you feel lovely,
and if you don't regular expressions,
I would have said a couple years ago look for somebody
in your office that has books with animals
on them although nowadays if you're looking
for somebody old you probably should just look
for somebody who has books at all.
So anyway, so we provide two kinds of searches,
one is fairly obvious which is a search over the names
of functions in your program so this is the option for that,
and one that might be slightly less obvious
but I'll convince you is interesting I hope
as we go along, are sourced text search breakpoints.
And this is the option for those.
Okay, so let's give you the first one this is function name
pattern matching breakpoints
and I'll just show you some examples.
So suppose somebody has given you a new class
and you don't know what it does, you want to see how it works,
so what you want to do really is break
on all the methods implemented by that class which you could do
by going through the source file in Xcode and clicking
on the beginning of all of them
but that would quickly get tedious.
And by the way, you don't want to stop
on the parent or subclass whatever.
What would be better would be to try to cons
up a regular expression
which matches all the functions in a given class.
So in Swift this is an appropriate regular expression
or in Objective C this is an appropriate regular expression.
So then you would have breakpoints on all
of those then you could run your program and then you could go
through and see what's going on.
And remember because you can disable individual locations,
when you do this kind of experiment,
you find that you hit one of them, you know what it does,
you're uninterested in that one so you just disable
that location then you keep going.
The second one you figure out disable so on and so forth.
So that's kind of a nice way to explore new code.
An even more radical version
of the same thing is somebody is giving you a whole shared
library that does some stuff.
You want to just see what stuff it does
when it's running then set a regular expression breakpoint,
I'm using the short - r form here
and the regular expression matches everything,
that's what .star does, and then limit it
to the library you're interested in.
Combining this with breakpoint commands can often be a really
nice way just to sort of get a quick and dirty trace
of execution through this library, you could backtrace
and maybe print the locals and then continue
and you'll just run your program and get a tape output
of the execution through that library.
Of course it slows down execution
but you know whatever sometimes you pay.
And then the other trick again is as you find ones
that you don't care about, you can disable them.
So let me talk about the other kind
of pattern matching in source.
So the point here is that there are some constructs
that are really obvious when you're looking
at your source text but figuring out how to get to them
in the generated code is really not obvious.
So an example of this is macros
which generally just get substituted as text
into your program and then sort of vanish, but you know
where they're inserted in your code
because they're the things with capital letters.
So you want to do a search maybe for all capital letters
or for the particular macros you care about.
But you can even be more creative than that
so for instance I want to know anywhere
that a particular field is gotten from a pointer
which is something that in source text I can see obviously
because it's going to look like that
but in generated code finding those places would be
So that's another instance where using the pattern matching
in your source can allow you to find constructs
that you might not be able to find otherwise.
Then another use of this is to sort of make topic groupings
that you can set breakpoints on just
by inserting patterns artificially
into your source code like as comments saying break here
or break here when you're interested
in inspecting this particular subsection
of my program's functionality
and then using these source regular expression breakpoints
to catch it.
So here's how the source breakpoint matching
There's source regular expression is the option,
the data you're providing for this search is the pattern,
and then you can limit it to one file, you can limit it
to multiple files by just providing the -f option multiple
times and there's also a flag to search all your source files.
So let me just give you an example to whet your appetite.
Suppose I had like a complex function like a state machine
which is computing stuff and then it's running
from many different places
in some horrible huge case statement or something
like that, and I'm interested in finding out when it's going
to return null but I'd like to know what was the case
in this particular run through at which it returned null,
and that's a hard thing to figure out to do
because you can stop after the function returns
and check whether it's null.
You can go click on all the places where it returns null
but you might miss one or you can just look for the pattern.
There's one other convenience that we offer you
in the source regular expression breakpoint type
which is you can specify not only a file but you can limit it
to a particular function so in
that case I'd do something like this.
I'd break my pattern would be return
and then I'm doing showing that I know regular expressions
because I'm showing off, space star is any number of spaces
and then my null pointer I limit it to a function and I limit it
to a file I'm interested in and then I can find out as I run
through exactly where I've returned null
in this particular usage.
So it's worthwhile talking about a couple
of extra breakpoint options that you might not have heard about.
One of them is along the lines of the where
or the filter kind that's useful now that we have Swift
and Objective C together in programs which is the ability
to specify a language for a breakpoint.
So for instance, there are a lot of count methods everywhere
in the world and if you set a breakpoint
on the name count you're going to set it on a bunch
of Swift code but you're also going to set it on a bunch
of Objective C methods and you don't care
about the Objective C methods, you only want the Swift ones,
then you can just specify the language Swift
and it won't set a breakpoint on any
of the Objective C names that happen to match.
So that's just a useful little convenience and yeah, right.
And one other option that's sometimes useful is being able
to narrow your search to a particular thread.
So you've got some code that's being called and a bunch
of different threads, it's like a kernel or something like that
but you've starting working on the execution in one thread
and you don't want your breakpoints that you're using
for the investigation to take you off
onto other threads, it's fairly simple.
There's a thread ID option and there's one that you can do
by thread name which you set
with this pthread set name np call.
That one is convenient because if you name a thread then
that persists over many debugging sessions
where of course the thread ID is going to change every time,
and you can even restrict it to code that's being serviced
on a particular queue by name.
One other thing that you might note is
that you can add all these options to existing breakpoints
and particularly that's useful if you've set file
and line breakpoint in Xcode in the gutter
but then you decide you want to like for instance limit that one
to a particular thread and you can change all these
after the fact, the command is break modify,
and the other useful thing
in this slide is showing you how you specify them
because you can specify either by breakpoint,
by breakpoint location number
and there's also a little syntax to specify ranges.
So now you've come up with all these clever breakpoints
that you want but you run into a little road block which is
that it turns out Xcode
at present only persists breakpoints
that it knows you set and so the ones that you've managed
to write in by hand it won't know about.
So how do you make them persist?
The first way is what Enrico told you about if you want it
to hold for all projects, you just put it
in your LLDB init file and then you're done.
But if you want to make project specific ones,
here's a cute little trick that you can use
to get the breakpoints loaded every time you debug
that particular project.
What you do is make an Xcode stored breakpoint,
preferably something that's going to get hit early
on in your program execution,
and then you put your breakpoints
as commands in that one.
So you know if you're a main executable for instance,
main is a very convenient place
so you would make a symbolic breakpoint
and then you would put main in
and then you would remember the slide a while ago now
where I told you about our little trouble with main,
so you would specify the shared library,
then you would add an action
which is a debugger command action and then don't type
in all of the breakpoints in one in one in one here
because that's just going to get tedious.
It's much easier to put the commands in a file
and then use LLDB's command source command
to load those commands in, load those breakpoints in
and then finally
if you autocontinue then just every time you run you will
automatically have all of those breakpoints set for you.
So I want to show you one other little convenience we've added
to overcome one particularly annoying problem that you get
in modern languages which is when you're trying
to step you're trying to step into something
but the problem is that in most modern languages most
of the variable access is now done either as properties
or through accessor functions or whatever so that
and they're generally that's not the code you're trying to debug.
So you end up in a scenario
like this you know I'm here I'm trying to get
into this function, do something.
I want to step in there.
So I try that I step and I don't end up there because one
of the arguments that I was passing was an accessor function
so I ended up in the accessor.
I don't want to be there because it's not very interesting
so what I'm going end up doing is finishing
out and stepping back.
So is there any way that we can make that slightly more easy?
And it turns out that we've added something called targeted
stepping so the option is the step in target
that you would say by saying step and what I want
to do is I want you to step but I only want you to stop
in this particular place,
that's what you're expressing with this.
So let's try that in this case and what we'll find is
that it almost but doesn't quite work
and the reason it doesn't quite work is
because though we didn't end up in the accessor,
we ended up on the next source line instead of in our function.
And that makes sense when you think about it
because actually stepping is source line by source line
and that was the multiline call.
So we've also added the ability to specify the end range
of the stepping operation
by saying what the end line number is
or even more conveniently you can say just step
through this block and get me in to do something I don't care.
And there's even an alias for that
which is sif step in function.
So then what you would do is you would be sitting here
and you would say step in function and then you'd land
in the right place or if you didn't I wouldn't have put it
on the slide.
So that I want to conclude with a couple little bits
of troubleshooting information.
One piece of information you often need
to know is what is actually in my running program.
For instance maybe I built the release and debug versions
and I want to know which one I'm actually using
or somebody gave me a library with a dSYM.
Did the dSYM get read in?
So the command that inquiries
about that information is the image list command.
You can either give it a module name
in which case it will tell you information
about just one module loaded into your program
or for amusement's sake you can give no options you'll show all
of them which is sometimes eye opening.
So here's an example just to see how it's used.
I say image list example and I see here is the path
to the binary so for instance if I wanted
to check whether I was using the debug build, yeah,
okay it does look like I'm using the debug build
and if there is a dSYM available,
it will always be listed after the binary
so in this case I see I did get my binary.
I want to tell you one thing about Swift debug information.
I put on this slide the why but I'm not actually going
to tell you the why because we're running a little short
of time, but I'll tell you the TLDR because I'm going
through too fast for you to read.
The TLDR is that because of the way Swift and LLDB work
with one another all the Swift code that you have
that has debug information has to have been built locally
so copying binaries from other people doesn't currently work
and stuff like that.
You have to have made sure
that everything has been built locally and with the compiler
that goes along with the debugger
that you're currently using.
I want to say this is one little convenience we've offered
and so Enrico's rule of optimized code debugging
as we saw earlier is don't do it if you don't have to and then
since most software developers are rational actors you can
write a corollary to that which is that most people
who debug optimized code actually do it by accident.
So now LLDB will tell you
that a file was compiled optimized when you stop in it.
It will only tell you once per library
and you'll get a little message like this.
And then you run quickly to your build settings
and change them back.
One other new feature that was added
to clang a while ago was this notion of modules.
So modules are a way to allow the compiler to look
at all the headers that are the header environment
for your program, compile them, parse them up once
and then reuse that for all the compiles.
So then we thought well why can't we also do the same thing
for the debug information?
Why don't we allow that parsed form of the type information
to be also done once and then shared amongst all the .o files
that you have debug information?
So that's called clang module debug feature.
We can also use PCH files by the way.
The setting in Xcode is clang module debugging
and here's the flag for some reason I put that in.
This is great because again like with the compiler this speeds
up the generation of the debug information, it will speed
up your compile times but it has one caveat, and that caveat is
that or actually one major one.
So that caveat is that now your debugging is depending
on your .o files but also on something that's sitting
in some cache somewhere.
So normally that's not a problem everything is in place
but when you go to ship your library or application
to somebody else, how is that going to work?
Well, if it's an application
or a framework then you just run dsymutil like you already do
and it does the right job,
it gathers everything together and that all works.
But remember that dsymutil only works on linked products,
it doesn't work on .o files.
So if you are shipping static archives with debug information,
then you must turn off this G module's feature
or you will ship broken debug information to your clients.
And also by the way, if you're running out of disk space
and you delete your module cache, now you're not going
to be able to debug anymore.
So that's the only down side to that feature.
So with that let me tell you what you've seen.
We hope that you see that LLDB is an extremely customizable
debugger providing you many ways to look at your data
that expressions actually give you much more power
for investigation and I thought Sean's example was great
of how you actually live go through
and find what you're interested in in a complex array,
that we have more breakpoint types than you know
of in your Xcode and that you can actually get yourself
into super deep trouble with more
than source level debugging,
and in general we hope we provide you a rich set of tools
for exploring your code.
And here are a couple of the previous sessions
that might have interesting information.
There were a couple of sessions earlier on which you didn't see
or did see but anyway they are available on slides.
And with that thank you and I hope
that you enjoy the little tiny bit that remains of your WWDC.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.