Transcript
[ Music ]
[ Applause ]
>> 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
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
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.
[ Applause ]
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,
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
to LLDB.
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
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
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
then exit.
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
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,
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.
[ Applause ]
>> Thank you, Kate.
Hi, everyone.
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
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
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
right after.
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
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
debugger customizations.
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
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
great responsibility.
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
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
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.
[ Applause ]
Thank you.
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.
[ Applause ]
Thank you.
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
to them.
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.
[ Laughter ]
Unfortunately, sometimes
you just really have to.
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
in registers.
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
those rules.
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
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
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,
return value of that
function and if I read it,
that's just the bad
pointer value.
Interesting.
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.
about the great new features
in the expression parser.
Thank you.
[ Applause ]
>> 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
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
expression works.
All right, but why
did I have to do that?
It's already there.
I hear you cry.
[ Laughter ]
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.
[ Applause ]
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.
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
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
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.
All right.
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,
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.
All right.
Now the functions aren't
the only reusable things you
can define.
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.
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.
That works.
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
semicolon here.
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.
[ Laughter ]
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.
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.
All right.
[ Applause ]
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
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.
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.
All right.
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.
[ Applause ]
>> 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
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
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
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
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
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
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
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.
[ Laughter ]
[ Applause ]
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,
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
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
quite difficult.
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
and then using these source
regular expression breakpoints
to catch it.
So here's how the source
breakpoint matching
command works.
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 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.
That's that.
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.
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.
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
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.
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
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
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.
[ Applause ]