WWDC2014 Session 409

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Good morning.
I'm Sean Callanan.
[ Applause ]
Thank you, thank you.
I work on the Debugger Team at
Apple and I'm really excited
to show you this stuff today.
Now, a funny thing, before, when
I was preparing these slides,
I thought by the time
I'm giving this talk,
maybe some people have looked at
the language manual for Swift,
maybe some people have
tried a Playground.
It's gone a little
faster than that.
[ Laughter ]
I am really impressed.
You guys, I look on the
labs, I look on Twitter.
You guys have apps
started already,
entire apps running in Swift.
I saw somebody with a raytracer.
It's crazy!
I... This is the sort of
thing that makes me happy
to be working at Apple.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to be working at Apple.
You guys are such
awesome developers.
It's really just an honor
to write the tools you use.
Thank you very much.
[ Applause ]
So, I'm going to tell you about
using LLDB and the Swift REPL.
Now, as you're writing a Swift
app, you're probably going
to write one or two
lines of buggy code
and that's where LLDB fits in.
Now, the good news
is LLDB is better
than it's ever been with Swift.
And I'm going to tell you about
some of the basic features
that LLDB has to help you
find bugs in your program.
LLDB at its core is a tool
to help you fix problems
in your code.
It's got a great set of
basic features for tracking
down where your bugs are.
The first tool is the stack.
The stack is a tool
that lets you look
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The stack is a tool
that lets you look
at why your program has stopped
and what functions got called
to get you to the point
where your program stopped.
Now, your program doesn't
always obediently stop
where you want it to.
In that case, you
need breakpoints.
Breakpoints are another
great tool LLDB provides
that let you stop
whenever you want to.
Finally, no matter how
you stop your program,
we have a great expression
command
that lets you inspect your
data right at the point
where your program stopped.
In today's presentation,
I'm going to show you these
tools working with Swift.
The good news is, the
commands are basically the same
as they used to be but
I'm going to show you how
to find some familiar bugs
that you may have seen before
and also a couple of new
ones that are introduced
when you're working with Swift.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, the cool thing is this
isn't all I have to talk about.
We now have a REPL, too.
The Swift REPL is
built on top of LLDB.
You can access it just at the
LLDB command prompt any time
that you're debugging
your program.
That's not all though.
You can also access
the Swift REPL
when you don't have
a program running.
You can access it directly off
of the shell prompt,
for a clean slate.
I'm going to tell you lots
more about interacting
with the REPL later but
there are two basic ways
that we want you
to use the REPL.
The first is when you've got a
program with some code and you'd
like to find out if it works,
if it's doing what you expect it
to, you can use this LLDB REPL
to test your app interactively.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to test your app interactively.
Another great use for this
LLDB REPL is to add new code.
You can write new functions,
new classes in an existing
LLDB session and they get put
into your code and they can
interact with your code just
as if you'd written them
in your source files.
Now, if you've noticed,
one theme this year,
one thing that's kind of
over and over we're seeing
in all the presentations
starting right
at the keynote is how everything
seems a little bit more alive.
Everything is a little
bit more interactive.
Stuffs coming in, Craig is
telling you about his hair,
it's -- everything is
much more immediate
and LLDB is the same way.
Now we have a couple
of tools that are,
that have already existed
that are very interactive
and we're adding more this year.
First of all, you have
the expression command
as I alluded to earlier.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as I alluded to earlier.
That's a great way for
when you're stopped to poke
at your variables, to call
a couple of local functions.
We also have, if you're a little
bit more experienced LLDB user
or maybe you've watched some
of our previous session videos,
LLDB Python scripting.
I'm not going to tell you too
much about that in this session
but it's a great way to
create new debugger features
and automate your
debugging processes.
This year, we've added
Playgrounds which allow you
to prototype entire
new algorithms
from scratch and learn APIs.
And we've also added
the LLDB REPL.
Now what's the difference
between the LLDB
REPL and Playgrounds?
Playgrounds, as I told you and
as you saw in Wednesday's demo
which Rick and Connor showed
you all the fantastic ways
that you can use Playgrounds,
Playgrounds works
from a blank slate.
You've got a text editor,
you import the stuff you want
to import, then you write some
code and you see how it works.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to import, then you write some
code and you see how it works.
On the other hand, the
point of the LLDB REPL is
to let you investigate
how code would work
if it were inside your
preexisting program.
Now, in today's session,
I'm going to give
you two basic things.
First, an overview of the kind
of skills that you're going
to want to apply just
working with LLDB
in your regular debugging.
And then, I'm going to tell you
about some new debugging
workflows
that the Swift REPL enables.
Finally, I'm going to
sum up and tell you
where you can go for
more information.
So, let's jump right
into basic debugging.
Now in these slides, I'm
going to show you interactions
with LLDB, but really
there's two basic ways
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with LLDB, but really
there's two basic ways
of talking to LLDB.
So, one is if you're
inside Xcode.
In Xcode, you have the debugger
console which is usually
at the bottom of your window
when you're debugging.
You can work with the debugger
console and enter commands
and also Xcode provides you
with a bunch of UI features
that let you automatic --
that let you automate
certain things like stepping.
There's another way of
working with LLDB that some
of you more experienced users
may use from time to time,
and that's the terminal.
The LLDB command line interface
uses the exact same commands
that you're familiar with, with
typing in commands in Xcode.
I'm going to show you the
command line interface
but there's great sessions,
as I'll tell you later,
that tell you more about
how to use it through Xcode.
Now, this is a situation that
probably all of you have run
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, this is a situation that
probably all of you have run
into at one time or another.
You're debugging
along in your program
and your program crashes.
It's stopped and now the
question is, well, what do I do?
There's all this
information here.
There are a couple
of basic questions
that you should be asking that
will help you find your bug
as quickly as possible.
The first is what
is the stop reason?
Why are we stopped?
The stop reason is a
facility that LLDB provides
that tells you why your
program was told to stop.
Something makes it stop.
The next question is, all
right, well, I know what --
that the program was told to
stop, how did it get there?
What code ran to get
us to this bad point?
This is where you use the
stack as I alluded to earlier.
You want to look up the stack
and find your own code so look
through all the framework code,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
through all the framework code,
look through all the standard
library code and find frames
in your own code that
are responsible for this.
Once you've located the place
where things might
have gone wrong,
then you can start investigating
the failure conditions,
looking at your variables
and seeing what variable
values might have gotten you
into a bad state.
So let's get started and look at
applying these basic techniques
to some crashes you might
see out in the world.
Now, let's say your
program's crashed.
The first question as
I told you is you want
to know the stop reason.
Why did it crash?
The command that helps you
find this information is called
Thread Info.
Now, I'm showing you two
versions of the command here.
They both do the
exact same thing.
It's just that the short
version, t i, is very quick
to type and if you're doing
repeated stops and you're trying
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to type and if you're doing
repeated stops and you're trying
to get your work done quickly,
you'll probably use short
versions of commands.
There's also a long, long
version of the command.
Now there are two reasons
why this might be helpful.
The first is it's more
explicit about what you're doing
and the second thing is you
can find it in the help.
So if you use the help command
or the apropos command in LLDB,
you can find this
command very easily.
Now, let's say you typed "thread
info" at the LLDB prompt.
You're going to get
some information
about the thread that's
currently stopped.
There's two pieces of
information that you want
to know when you're crashed.
The first is what
piece of code crashed?
In this case, we're
stopped in a function called
"Swift.-getOptionalValue".
Now the Swift underscore part --
the Swift dot part is
very important here
because it tells you that
you're inside the Swift module.
The Swift module is the
Swift standard library,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so we already know that
standard library code crashed.
Now, what was the reason
that it gave for crashing?
The reason is a bad instruction.
Now, some of you may have
seen this message before.
A bad instruction is when the
CPU doesn't understand the
instruction that's being given.
But wait a second, we've got
a compiler that's being pretty
smart about issuing
the right instructions.
What's going on here?
Well, it turns out that
99 percent of the time,
these bad instructions
are used in assertions.
So, the code says
if something is bad,
if something unexpected
is happening,
I'm going to issue a bad
instruction to the CPU
and the program will stop.
So, when you see
"EXC-BAD-INSTRUCTION",
what you should be thinking
is: assertion failure.
Now, what we can see from this
stop reason without even looking
at the stack or any of the
stack frames is that we failed
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at the stack or any of the
stack frames is that we failed
in assertion in the
standard library.
Now what might cause
that to happen?
OK, let's move on
and have a look.
The next command
you're going to want
to use is the backtrace command.
The thread backtrace
command prints all the frames
on the stack of the
current thread.
That is, it tells you what
functions called each other
on the current thread to get you
to the point where
you're stopped.
In this case, we see that
just below the function
that was actually stopped,
you see your own code.
Now this top level code
function may be familiar to you
if you've played around with
Swift's command line tools.
That's the code that's
outside any function
in your main source file
of a command line tool.
So you recognize this top
level code and you say,
"Aha, this is my code.
Let's have a look."
Top level code is
frame number 1,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Top level code is
frame number 1,
so that means we called directly
into the standard library
and an assertion failed.
Let's have a look at
why that happened.
Well, we select the first frame
and now we have information
about the source code
because it's your own code
and we have that.
Here we see that that we've
got an optional AnyObject
that we got from a source
that looks a little shady
and then we unwrap that
option without checking.
Well, turns out if you print
the value of that unwrap,
of that optional, you see
that optional is None.
Well, that's not too
difficult to fix.
You instead try "if let"
and that will let you--
and that will make the
unwrapping code conditional
and they're actually
being something in there.
Now let's take a little detour
before we look at more examples.
If you're using Xcode,
you're going to see more
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you're using Xcode,
you're going to see more
than just the stack
that I just showed you.
If you're using libdispatch
in your program,
Xcode will additionally
show you all the functions
that got called in order to --
and enqueue your current
block onto a queue.
Then after that, once the queue
started running your code,
you see the stack frames
that your code invoked.
Now why is this relevant
to this talk?
Well, all the stack
frames that are
above the last block
execution, you'll be able
to see local variables for.
However, all the local variables
from before it got
enqueued are not going
to be available to you.
So you need some -- that's why
they're grayed out in Xcode
and you need to make sure
you know, you remember that.
All right, well, Grand
Central Dispatch is cool
but we're focusing on LLDB,
so let's get back to it.
Here's another case where
we've got an assertion.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Here's another case where
we've got an assertion.
Now, I told you earlier that
there's a command called
"thread info" and that's a great
way to find the stop reason
for your current thread.
Another way to find
both the stop reason
and all the stack frames is just
to issue the backtrace command.
If you do this in this
case, we've crashed
in a different spot,
we see still
that the reason we stopped
is a bad instruction
and we're still stopped in
the Swift standard library.
In this case though,
we're stopped
in the getter for
an array member.
Well, let's look
at our own code.
Now this is a function
in your own program.
On the left hand side,
you see the main module
and on the right hand side
of the dot, you see the name
of your function, in
this case, FindElement.
Now what's this code doing?
FindElement is iterating across
an array and for every element
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
FindElement is iterating across
an array and for every element
in the array, it's
applying a function to it.
Once that function returns true,
it returns that element
of the array.
And if that function
never returns true,
the function returns nil.
There's some problem here.
Maybe some of you
have figured it out.
But just, but the way I would
look at this is I would look
at the current index into the
array again using the print
command which is
short for expression.
In this case, we see
that the index is
at the fourth element
of the array.
Well, let's look at the array.
The count property of the array
is 3, we've walked too far.
Well, what happened here?
We used array.count, right?
Didn't we?
Oh, not quite.
So here we have a case of misuse
of the closed range operator.
We shouldn't have used
the closed range operator.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We shouldn't have used
the closed range operator.
We should at least have only
used the half open range
operator because, of course,
the indexes of an array start
with 0, so they end at count
minus 1 instead of count.
But the real answer here is we
shouldn't have been using the
index at all.
We should have just been
using forElementInArray
and let Swift handle all the
details of the indexing for us.
All right, well, let's
look at another crash now
that we've kind of
seen the basics.
This crash looks a lot uglier.
Suddenly, there's a ton
of frames on the stack.
What the heck is going on and
all of this look like they're
in libC++ABI and so forth.
Ohhh, scary.
[ Laughter ]
So, let's use our
method and look
through this systematically.
The first thing you see is that
the stop reason is SIGABRT.
That means that we ran into a
situation where the kernel had
to put the kibosh on
us and say, "Hey, no.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to put the kibosh on
us and say, "Hey, no.
This is going too far.
You're cut off.
Stop." Why does that happen?
That often happens because
an exception gets thrown.
Wait, a second, exceptions?
We're coding in Swift here.
All right, but they're still
there if you look on the stack.
In fact, an Objective-C
exception is being thrown.
All right, well, we've got
to track this down so we look
for our own code here
and in fact we see again a
function called FindElement.
This function is
causing a lot of trouble.
Let's have a look at it.
So we select that frame and
now we see our code again.
This time, FindElement
is looking
across an NSArray instead of a
Swift standard library array.
Again, we've got this
stupid coding error
with the closed range!
But, the more interesting
thing is:
because we were using an
NSArray, we were calling
into Objective-C code.
Now, Objective-C code
does throw exceptions.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, Objective-C code
does throw exceptions.
Now, one, I took an
unofficial survey
of LLDB developers last night
when I was sitting in bed and...
[ Laughter ]
...and the most popular
crash by far was this one,
EXC-BAD-ACCESS at address 0.
This means that you were trying
to access memory
at a bad address.
Nil tends to be a bad address.
So what's going on here?
I'm coding in Swift.
It's supposed to be safe.
Well, let's have a look and
see what our top level code is
doing here.
I go to that frame
and I look at my code.
The code is importing Foundation
and it's getting some NSData
from a URL that looks
kind of suspicious.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And then, it looks at the
bytes property of that,
of the resulting data and pulls
out the first element of it.
What's the problem here?
Well, let's look at chars.
Chars is a null UnsafePointer.
We're working with
Objective-C APIs.
Objective-C APIs can
give you UnsafePointers.
This is -- so, your old
friend, the null pointer,
is still around and
whenever you're dealing
with UnsafePointers,
you have to be very sure
that you know what you're doing.
All right.
So, the lesson here is Swift
is safe but the frameworks
that you're working
with may not be safe.
Let's sum up.
We've looked at a
couple of different ways
in which your code can
crash and we've seen
that the most important thing
to look at is the stop reason.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that the most important thing
to look at is the stop reason.
The next thing, once you figured
out what the stop reason is,
is looking at the stack to find
where your code fits
into the picture.
Your code is going to
have local variables
and you can use the
expression command to figure
out what those local variables
are and how they connect
to the crash you're seeing.
Now, unfortunately, we
don't always live in a world
where your program stops when
it's doing something wrong.
For that world, there's
breakpoints (and also beer).
[ Laughter ]
You can use a breakpoint if any
of the following
things are true.
The first case is when
the problem isn't a crash.
I've been showing
you lots of cases
where your program just can't
move on because it tried
to access something
it shouldn't have.
But that's not the
only kind of bug.
Your program might be outputting
wrong data or it might be,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Your program might be outputting
wrong data or it might be,
you know, sending
something embarrassing
to a social networking site,
that's a little tougher
to set a breakpoint
on but bear with me.
So, and there's another reason,
you may just want to say,
you know, "I saw the
failing code on the stack,
but I'd like to stop
when that happens,
not when the crash occurs."
Well, breakpoints are a great
tool for all of these cases.
Breakpoints have a couple of
attributes and you can edit them
in Xcode really easily as well.
The most important part of a
breakpoint is the specification.
When you say, "I want to stop
at places that look like this,"
what that means to LLDB is,
"this is a specification,
I need to be listening."
Then, LLDB goes out and looks
in the world for locations
that match your specification.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that match your specification.
Now, LLDB is clever about
this, not only the locations
that are already in your program
when you issue the command have
matched the specification...
But also, if you load a bundle
or some other framework
dynamically,
LLDB is still sitting there
in the background saying, "Oh,
is there anything that
matches my location in here?"
Finally, there's a couple of
optional parts to breakpoints
that are important and that
I'll show you in a moment.
The first is the
breakpoint condition.
The condition tells LLDB, "I
only actually want to stop here
when a certain predicate
is true."
Finally, there are actions that
you can perform without having
to type any commands
automatically
when the breakpoint gets hit.
So let's look at some code.
Here I've implemented a very,
very simplistic banking
application based a little bit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
very simplistic banking
application based a little bit
upon my own, like, wishes.
The bank account is, has a value
that's represented in cents.
And you have two functions,
you have a withdraw function
and you have a deposit function.
Now your withdraw function
is a little bit smart
and it charges an overdraft
fee when you go below,
when you withdraw more than
is actually in your account.
Now because this is
my, kindof, wishlist --
this wish list account
function --
this is actually an
overdraft fee of 10 cents,
but bear with me, this
is just an example.
So, you want to stop exactly
when an overdraft
fee is charged.
That's over here on line 6.
And in order to stop there, you
simply type "breakpoint set"
with the file set to
your current source file
and the line set to 6.
The way you were doing this
in Xcode is just click next
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The way you were doing this
in Xcode is just click next
to that line and that
does the exact same thing.
Now, once you've set that
breakpoint, you can look
at the breakpoint list
to see all those
attributes I was talking
to you about earlier.
The first thing that's relevant
here is the specification
of the breakpoint.
LLDB will tell you that
you wanted this breakpoint
at a certain file
and a certain line.
The cool thing is LLDB also
tells you all the locations it
actually found for
that breakpoint.
Now in this case,
there's only one location
but there might be more.
Now let's have a look at a case
where there might
be more locations.
But first, let's
do a quick segue.
I've seen people that have
tons of breakpoint set.
And one thing that can be
frustrating is you've got a bug
you're tracking down and you've
got a bunch of breakpoints
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you're tracking down and you've
got a bunch of breakpoints
that you had set
from an earlier,
from debugging an
earlier problem.
And you want to not
stop at all of those.
Well, we happen to have the
breakpoint disable command
to let you do exactly that.
If you disable a breakpoint, you
can obviously enable it later.
But for the time it's disabled,
LLDB will simply ignore it
and all of its locations.
Now, let's say we have a
little bit different code.
This is a simple example
where we implemented a
function called "timestwo".
Timestwo applies to integers,
it applies to doubles,
and it also applies to strings.
I want to stop whenever
any of these is called.
But wait, they've all got
different line numbers,
how am I going to do that?
In this case, we can use
a symbolic breakpoint.
If I type breakpoint on
timestwo, LLDB will say, "OK.
You want to match all functions
that are called timestwo."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You want to match all functions
that are called timestwo."
And it already tells you it
found 3 locations for that.
Now if you do a breakpoint list,
you see some more information
that can be very valuable.
The first thing of course
is your specification.
You said you wanted the
name to be timestwo.
The next part is
all the locations
that LLDB found for
your breakpoint.
In this case, it
found the location
where you were processing
an int and returning an int.
It also found the
same thing for double
and the same thing for string.
OK, that's cool, what can
I do with this information?
Well, let's say you
only care about the case
where you are handling a string.
In that case, you can disable
all the locations except the
ones you care about.
Now this is of course handy
when you have source code.
But if you don't have source
code this is even better.
You may be saying to yourself,
"Well, I'd really like to know
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You may be saying to yourself,
"Well, I'd really like to know
when I'm in this array
subscript operator
from the standard library."
Well, why not use a
symbolic breakpoint?
We don't need source for that.
And then you say, "Well,
oh I set the breakpoint
on the subscript operator
but there's a bunch of them."
Well, that's OK too.
Just disable the
locations you didn't want.
There's another cool
way of doing this
that uses a scale called
"regular expressions".
Now, some of you may have worked
with regular expressions before,
and if so, you're always looking
for new ways to use them.
[ Laughter ]
This is actually a joke from
another of my colleagues
who used to give the
talk, so blame him.
When you set this breakpoint,
you say, "I want to stop
at timestwo followed by any
sequence of characters as long
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at timestwo followed by any
sequence of characters as long
as there's the word
'string' in there."
So now, you've broke--
you've set a breakpoint
specifically
at the "Swift dot"...
uh, at the version of timestwo
that handles Swift.String.
All right, well we've got
regular expression breakpoints.
That's really cool.
What else can we do?
Well, we can use regular
expression breakpoints
for other types of
categories of functions.
So for example, if you
want to stop on all methods
of a certain class,
you say, well break set
on the regular expression
"Account.". Why all
these backslashes?
Well, the reason for that
is that we want to make,
we want "dot" to
actually be a dot here.
We don't want it to
mean any character.
In regular expressions
that would normally
mean any character.
You can do the same thing
with functions in a module.
Although, watch out.
Once you start setting a
breakpoint on all functions
in a module, you're going
to find out there are a lot
of functions in a module,
including some automatically
generated ones,
so you want to watch
out for that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so you want to watch
out for that.
There's a lot more on
regular expression breakpoints
and a couple of other,
you know, topics like that
in the Advanced Debugging with
LLDB talk that we did last year.
That should all still
apply to Swift as well.
All right, back to our examples.
Now let's say you've
applied symbolic breakpoints,
you've set some file
and line breakpoint
but it gets hit all the time.
And you're like, "Oh man,
this is really tedious
to keep pressing continue,
can't there be a better way?"
Well, in fact there
is a better way.
Now let's go back to our
bank account example.
And let's say, we're
being extremely gracious
and we're not charging
an overdraft fee.
But we find that the users
of this bank account tend
to be kind of irresponsible
in their spending.
Again, we can set our breakpoint
on the withdraw function.
But now, once we've set the
breakpoint, we'd like to know:
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But now, once we've set the
breakpoint, we'd like to know:
"When would we charge an
overdraft fee (if we were
to charge one)?"
The way we find this out is
by setting a breakpoint
condition on the breakpoint.
Now, a condition is simply a
Swift expression that evaluates
to either true or false.
If the Swift expression
evaluates to true,
then LLDB stops at
that breakpoint.
If the condition
evaluates to false,
LLDB automatically continues
and you'd never see it.
Another thing you might want to
do is, instead of just stopping
when you would charge
an overdraft,
you'd like to see what kinds of
amounts are people withdrawing
from their bank account?
What is their balances
looking like?
You can do that too.
In this case, you use a facility
called breakpoint commands.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In this case, you use a facility
called breakpoint commands.
Breakpoint commands
are really cool
because they really
minimize the number of times
where you have to
stop your program.
If you say "breakpoint command
add", you can add the break,
add commands to the last
breakpoint that you created.
In this case, what we want
to do is see the local,
the value of the
current account in cents.
And then we want to continue,
which means that the process
just keeps running as normal.
Finally, once we're done typing
in commands we type
capital D O N E.
And then, when you
run your program,
you get output each time
the breakpoint gets hit.
But you don't have
to touch anything.
This is a really cool
feature that is very useful
for when you've got your program
but you don't want to have
to recompile it to
insert print lines
or do something horrific
like that.
Now, there's a little
bit of a caveat here
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, there's a little
bit of a caveat here
when you're using
mixed projects.
Breakpoint conditions
and breakpoint commands both
use expressions in the language
where the breakpoint is located.
Now, if you set a
breakpoint on a symbol that...
there might be breakpoints
match--
there might be locations
matching that breakpoint
at multiple places
in your program:
some of them implemented
in Objective-C;
some of them implemented
in Swift.
Now, we thought about that.
So we've provided the ability
to set breakpoint conditions
and breakpoint commands
on specific locations
of a breakpoint, not just
on the breakpoint as whole.
So, if you're stop-- if you've
got a breakpoint location
in Swift, you can type
in a Swift condition.
And if you've got another
location in Objective-C,
you can use Objective-C
in your condition.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can use Objective-C
in your condition.
Now that raises the
question, "Well, OK,
I set a symbolic breakpoint.
Now I've got a ton of locations,
how do I tell them apart?
How do I figure out what
language they're in?"
Well, I'll give you a quick
overview of how to read symbols.
In this case, we're looking at
a couple of different symbols
in different-- that
are implemented
in different places
in your code.
The first is called "main".
If you're an Objective-C or C
programmer, you'll recognize
that main is the main
entry point of a C program.
But the more important thing to
notice here as that they're--
it doesn't have any
argument names
and it doesn't have
any adornment.
This typically means that it's
a C or Objective-C function.
There's another thing, though,
that you have to remember...
Especially with, when
you're dealing with Swift,
and that is the top level
code acts the same way.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and that is the top level
code acts the same way.
So just remember that if
there's no adornment like this,
it's either Objective-C or C
or if it's top level
code, then it's Swift.
The next thing is, if you
have an Objective-C class
or instance method, that class
or instance method name is going
to have brackets and
a dash or a plus sign.
Finally, if you have
a Swift function,
then we know the return type
and we know the argument types
for the function, so we're
going to be very verbose
and tell you all that
information right up front.
All right.
So, now we've seen
how to stop an app
at the right time in LLDB.
The tool you use to do
that is breakpoints.
You can set breakpoints
based on all kinds of filters
and you can even set
conditions on your breakpoints,
so that once you know
where you want to stop,
you can also tell LLDB, to some
extent, when you want to stop.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can also tell LLDB, to some
extent, when you want to stop.
You can even set automated
actions on your breakpoints
so that you don't have to stop,
investigate your
variables and then move on.
Well, now let's talk about
some of the new stuff
that we've added into LLDB.
This is the stuff
I've been having fun
with for the last year or so.
The first thing I'm
going to tell you
about is validating your
existing code using the
LLDB REPL.
The LLDB REPL is
really an amazing tool.
And these slides, while they
kind of scratch the surface
of all the stuff you can
do, I'm really excited
to see what you all
can do and what kinds
of workflows you're
going to discover.
So let's try validating some
existing code, but first,
let's figure out
when we can do that.
Well, the REPL and the
LLDB command line exist
in harmony with one another.
You can launch the REPL from a
shell with an empty target just
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can launch the REPL from a
shell with an empty target just
by typing "xcrun swift"
as I told you earlier.
However, also when your
program is stopped at any point,
you can type "repl" and you get
to a REPL prompt just the same.
Finally, you can break back into
LLDB by pressing colon-enter
and that drops you back
into the LLDB prompt.
Why this weird colon-enter
thing?
Well, there's another thing
that colon can do for you.
If you're stopped in the REPL,
you can use the colon prefix
and type an LLDB
command after it.
And that LLDB command
will be executed just
as if you typed it
at the LLDB prompt.
This is a really handy way
and it shows that really LLDB
and the REPL are living
right next to each other.
All right, enough
with the explanation,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All right, enough
with the explanation,
let's go right in to the code.
So I've written a function here
and it does a partition
of an array.
Now you may be asking
yourself, "OK,
what kind of partition are
we talking about here?"
And maybe you have the
implementation handy
or maybe you don't.
How can we use the REPL
to discover more
about this function?
Well, with our program
stopped, we drop into the REPL.
And then we can call the
partition function right
from there.
If we send it the array "3, 4,
5" now what we get back is
the array containing 3 paired
with the array containing
4 and 5.
All right, this looks
interesting.
I guess there's something about
that being the bottom part
of the array and the
top part of the array.
Let's formalize this.
Let's test the theory
about what's going on.
What I'm going to do is now
write my own code in the REPL.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What I'm going to do is now
write my own code in the REPL.
The expression command is
really great for one liners
but the REPL command is
awesome for editing your code
and adding entirely
new functions.
In this case, we're going to
write an "ispartition" function.
That ispartition function
iterates across all the members
of the left-hand array and
checks that they're less than
or equal, that they're less
than or equal to the numbers
in the right-hand array.
If any of them are
greater than a member
of the right-hand array, it
says, "Oh, this doesn't look
like the kind of partition I was
expecting," and returns false.
Finally, once it's checked
everything it returns true.
You can run this ispartition
function on the result
of partitioning a larger array.
And this partition will
tell you indeed that was,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And this partition will
tell you indeed that was,
the result was a partition.
This is something fairly deep.
You've got a function that
exists in your code already,
you've compiled it using
the Swift compiler.
Swift isn't an interpreted
language or anything.
But at the same time,
you've used this REPL
to interactively test
by adding new code.
This is just one of the kinds of
interactive debugging scenarios
that we're hoping will change
the way you work with code.
Let's look at another example.
Using this partition function,
I've implemented a sorting
algorithm called "mysort".
Now I want to validate
that mysort works.
Well, I can enter
the REPL again.
If I enter the REPL, I
can run mysort on an array
and the result is
that the array is --
the result is an
array that I can kind
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the result is an
array that I can kind
of eyeball and see as sorted.
All right, cool, well it fit
on the slide, it looks sorted
to me, this is probably
good enough.
But many of your real data
structures aren't going to fit
on a slide and you can't easily
eyeball them for correctness.
Let's write a function
that checks whether
an array is sorted.
In the REPL, we just type,
we start defining a
function called "issorted".
The notion here is:
we're going to keep track
of the previous element
that we saw of the array.
And for each element in the
array, we're going to check:
is it indeed greater than or
equal to the previous element?
If there's ever an element
of the array that is less
than the previous
element, then we know, "oh,
this array isn't sorted."
Now why are we using an optional
for the previous element?
Why is last an optional?
Well, the first element
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, the first element
of the array doesn't
have a previous element.
So the last element in that
case is going to be nil.
If I run issorted on the result
of using my compiled
mysort function on an array,
I see indeed that the mysort
function was implemented
correctly for this
case and returned,
and the issorted
function returns true.
This is the kind of
ad hoc unit testing
that I was talking about.
Now, let's take another example.
But instead of validating
existing code, we'd like to try
out some new code
in our program.
And this is a good example
to kind of review the role
that the expression parser and
the REPL have in your program.
When you're running your
program, you have two aspects
of your program state.
The one aspect is your stack:
that's all the functions
that your program'ss
called, all the threads
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that your program'ss
called, all the threads
that you're currently
looking at.
For investigating those,
we think the expression
command is a wonderful tool.
However, there's all this
code that you've written
that may not be currently
running,
you may not have a stack frame
representing it, but you'd still
like to play around with it.
That's where the
LLDB REPL comes in.
They're both built on the
same technology but they both,
but they serve distinct
purposes.
Let's look at an example:
dispatching on a queue.
In my program, I've
imported Foundation
and I've declared
a dispatch queue
using dispatch-queue-create.
This code has set up a
queue inside my program
and I can dispatch
blocks onto it.
Well, from the REPL, I
can do the same thing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I stop my program,
enter the REPL,
and then I call dispatch-sync
on the queue
and add a block that
prints "world".
Indeed, if I do that, I
get the output "world".
Now there's one thing
to remember here.
Your program is stopped when
we talk, when we do this.
So let's say you had a queue
that was running in your program
that was already running
some of your code.
Now, if you try to
dispatch to it,
that dispatch isn't going
to, that dispatch isn't going
to return because there's
something happening
on the queue already.
What you should do in that case
is dispatch-async your block,
and then you can continue your
program and your code will run.
This is a great way to
manipulate a resource that needs
to be serialized on
from the debugger.
And actually it lets you
debug in a more safe way.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And actually it lets you
debug in a more safe way.
Let's look at another example.
In this example,
I've got a protocol.
This protocol says, "I'm a type
of thing that can be doubled."
And in my code, I've
implemented an extension
to this built-in
Swift integer class
that makes it be doublable.
So calling twice on an
integer now returns 2 times
that integer.
Using that protocol, I've
implemented a generic function
that knows how to get 4 times
something by doubling it twice.
The REPL can mess around
with this stuff too.
We can write an extension on
the Swift built-in string class
that makes it doublable
right in the REPL.
We can make "twice" concatenate
the string with itself
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We can make "twice" concatenate
the string with itself
and return the result.
And then, we can run print,
and then we can run 4 times
on the result of that
and 4 times works
because we're able --
the REPL is able --
to register that extension.
So then we see the output three,
four times -- just
as we expected.
Now, this is just the beginning.
And I'm sure that next year
I'm going to be, or I am --
or someone of my
colleagues is --
going to be telling you
about all the cool new
debugging workflows
that your colleagues have
found and we're going
to have a pretty
fantastic session next year.
But let's sum up and tell you
about what other resources
there are to help you out.
LLDB provides great tools to
diagnose bugs in your program.
I've told you about
stop reasons, the stack,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I've told you about the
print command (or expression
if you like), I've told
you about breakpoints,
and I've also told you about
the great new LLDB REPL
that lets you debug
in an interactive way:
writing your own
code and interacting
with your program's
existing code.
If you want more information
on this, first of all,
you can contact our awesome
Developer Tools Evangelist,
Dave DeLong.
You can also check out
the Quick Start Guide
in our documentation.
The LLDB Quick Start Guide
is a fantastic document
and it will tell
you lots of stuff
that I haven't covered here.
Finally, you can communicate
with other developers and chat
about LLDB and the kinds
of workflows you discover
on the Apple Developer Forums.
But that's not all, it turns
out we have a great session
for you on Friday morning.
It's going to be just the thing
to go with your morning coffee
because my colleague
Enrico Granata is going
to tell you some really neat
stuff that LLDB does to help you
under the hood in debugging
your Swift programs.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
under the hood in debugging
your Swift programs.
Another thing that you
want to check out is the
"Debugging in Xcode 6" talk.
That was earlier this
week so you want to check
that out online but it tells you
all about the UI implementation
and how to deal with,
for example,
the Grand Central
Dispatch integration.
Finally, as I told you earlier,
we had a session last year
where we talked a little bit
more in depth about working
with LLDB and especially about
breakpoints and stepping.
Again, I want to
reiterate, thank you so much
for being Apple developers.
Thank you so much for
working with LLDB.
I look forward to
seeing you in the labs.
[ Applause ]