Debugging in Xcode 6

Session 413 WWDC 2014

Xcode provides a powerful debugging user interface backed by the new state-of-the-art LLDB debugger. Discover how you can more easily explore and fix your user interfaces, add custom Quick Look support for your own classes and get deeper insight into how and from where your app is enqueuing work.

[ Applause ]

Good morning.

Welcome to Debugging in Xcode 6.

So if you had seen our debugging session last year, you would have noticed our quest to bring more relative debugging information to your fingertips.

This year, we're going to continue with this mission.

Oh, before I forget, my colleague reminded me to introduce myself, but my name has not changed from last year, so I'm still Han Ming Ong and I'm still Debugger UI Engineer.

So, let's briefly look at what we're going to talk about this year.

I'm going to talk about a new feature in Xcode 6 that will help you with debugging your use of Grand Central Dispatch which is GCD.

This feature is called Queue Debugging.

Next up, Troy is going to come up and show you amazing new way to explore and debug your user interface.

And then Kerry is going to talk in detail, how to integrate your the Quick Look Preview into your custom class.

I want to set the stage for Queue Debugging so that we're all on the same page.

My apologies if this is a bit of a bit of it too basic for some of you, but it's important that we get the fundamentals right.

So, when your application hits a break point, the program pauses.

The debugger will go through a process known as unwinding.

What it does is, it looks at the method that contains your break point, and follows the thread pointer back to the call site.

In this case, awakeFromNib.

It goes on until it reaches the beginning of the thread.

Then it comes back with a series of stack frames known as a backtrace.

So a stack frame is an instance of location of your method.

Now backtraces are important because it helps you understand the control flow and the state of your application.

So what happens how does GCD impact backtraces?

But first, what is GCD?

It is a technology that is introduced five or six years ago, in the Snow Leopard timeframe.

Since then, it has taken off like wildfire because it is a very natural way to create concurrent, responsive application.

Basically you divide your task up into smaller work items called blocks.

And then you enqueue the block to a queue using two ways: synchronously or asynchronously.

We're going to focus on the latter because it poses a bigger challenge when it comes to debugging.

You enqueue asynchronously using the dispatch async API.

And then what you're telling the system, the OS, is at some time later, it can be dequeue your block and find a worker thread to start executing it.

Imagine that you've hit a breakpoint in your block right now.

So let's look at a backtrace of Thread 16.

After the debugger has finished its unwinding, the bottommost frame, Frame 9, represents the start of the worker thread, which corresponds to the dequeuing event.

So what do you have to do to understand the control flow before the enqueuing event?

Well, what I used to do is to find the method that contains the enqueuing event, in this case cachedGraphImage, paste it in the Search Navigator, search for all the call sites of it, put break points there, re-launch the application, go through all the crazy UI gestures to make sure I can repeat the similar the same problem to hit the break point.

And heaven forbids if those call sites are also in another dispatch async because that means I have to wash, rinse, and repeat.

But, Xcode is not lying to you when it's giving you this limited backtrace.

That's because the thread that does enqueuing did it asynchronously in the past.

In fact, that thread is possibly doing something completely different or may be gone from memory.

So it's not fair to accuse a debugger of not being able to unwind to the past, but in your head, you must be thinking, "Well, this is accurate but surely Xcode can present a more logical backtrace."

And we agree with you.

So in the scenario that I described above, over here, we're going to splice and Xcode 6 is going to splice in the backtrace of the enqueuing event so that it can show them to you in a more logical manner in the UI.

And if you have one in more than one enqueuing event, Xcode is going to follow the chain so that it shows you everything.

So how did we do that?

Well we worked through several layers of the OS, down to the core OS, we shortcuts all the backtraces of the enqueuing event, so when your application hits a breakpoint, Xcode will ask the debugger to retrieve the backtrace, so that we can show them to you in the UI.

The live stack frames, they have colored icons.

The recorded stack frames, they have gray icons.

But, just like the live stack frame, you can select it and if Xcode has its debug symbols, it will show you the source code, like in this case.

If it doesn't have debug symbol, it will show this assembly that some among us can actually decipher.

So why are the icons gray?

Well a recorded stack frame's icon is gray because it is a visual cue to let you know that it's historical.

And because it doesn't exist in memory anymore, you cannot interact with it in the console.

You cannot run expressions like [inaudible].

And Xcode will not show you its frame variables because recording the frame variables would cause the application to blow up the memory, possibly causing it to be jettisoned on a device.

So that wraps up the first part of Queue Debugging.

Moving onto the second part.

Xcode by default shows you the backtraces in the threads view in the debug navigator.

As you can see here, Threads 5 Thread 5 is showing you its backtrace.

So that just means that a backtrace is organized by its parent threads.

There's another equally interesting view known as Queue's View in the debug navigator.

And if you pay attention to your top right corner, there's a little control known as the Process View Option Selector.

It is a mouthful, but it is useful.

So if you toggle it to the Queue's View, Xcode switches you over to emphasize the relationship between blocks and queues.

So in this slide here, you see that the top queue has one Executing Block.

So what about the blocks that here you have enqueued but not executing yet?

Let's look at this scenario.

So I'm using a Serial Queue as an illustration, but this happens with concurrent queues as well.

The green square is your currently Executing Block.

As long as it has not finished executing, blocks that you submit subsequently to the queue, will get stacked up behind it.

We call those gray squares the Pending Blocks.

So in Xcode 6, we're going to show you the Pending Blocks as well of a queue.

[ Applause ]

So in this case, we have 9 Pending Blocks.

The Pending Blocks are like Executing Block in that if you twist it down, it will show you the a backtrace which by definition, is a recorded backtrace of the enqueuing event.

So we hope that by surfacing this information, you can answer questions like, "How many Pending Blocks have I submitted to the queue?

Am I'm oversaturating the queue?"

And if you have literally tens and thousands of blocks - Pending Blocks - are there some that are not needed, because you may be able to change your algorithm or your granularity so that you do less work?

Now doing less work is important because that would mean saving battery life on your customers' devices.

So on top of that, it will help us solve a class of programming problems that I'm going to go into a demonstration now.

So, running on the simulator is an application called Jogr which helps you track your runs in San Francisco and keep fit while you're at during this WWDC.

Well, I know, developers jogging and keeping fit, but we can all dream.

Now, I have a timer here that tracks your time and your velocity, and a map of all of the routes that I've done.

And I've done exactly one this year, which is one too many for me.

If you click on the date, if you select the date, it will slide in the detail page of my velocity graph.

So notice that it takes a little bit of time to slide in the detail page, and I'm sure you have learned from Apple and your customers that they want your animation to be smooth: smooth like butter.

So this is not.

And I want to figure out why.

So things I know to code pretty well, I'm just going to jump straight into Xcode to the class known GraphView.

GraphView is the class that draws the graph.

And I'm going to jump straight awakeFromNib by going to the implementation field of the [inaudible] bar and start typing "awake."

There I go.

And I've a method here that creates my velocity path.

And I know that it is doing a lot of heavy-duty work, so let's jump into the definition by doing a Command-Click on it.

What I see here is, it's spending time fetching the velocity data from the database, and then it takes a lock to protect it.

And subsequently go through each data point in the velocity data to draw the path.

And when it's done, it releases the lock.

So it's doing all this work on the main thread.

So it's impeding the detail page from sliding in.

And this looks like a good candidate to use this dispatch async.

So let's do that.

Going back to awakeFromNib.

And I'm going to paste pasting a chunk of code which I don't have, but it's alright.

Alright, I'm going to move the line that creates the path into the dispatch async block.

And I'm going to dispatch the block to a simple queue of the graph.

And when I'm done calculating the path, I will update it on the main thread like usual.

So this is a pretty simple change.

Let's rerun by clicking on the Run button to see if it works.

Okay, I'm going to go to the date so that I can select it.

So this time when I select the date, the detail page should slide in very quickly, which it does, but my graph is not drawing.

So, there are many ways to you can try to figure out what is the problem but one of my favorite way is to look at the state of the application.

And to do that, I will look at the backtraces of the application by clicking the Pause button in the debug bar.

And I know that work for my graph is done on the Graph Serial Queue, so I found a thread which is Thread 22.

I'm going to twist it open and you see you may not be able to see at the back, but Frame 2 shows that I'm trying to wait for a lock.

It looks like it.

I'm going to select Frame 3 to confirm it.

And indeed, I'm waiting for the lock.

So those of us who have faced this situation before have a pretty standard following path [inaudible] which is to find a thread that has the lock.

So let's do that.

I'm going to show you a nifty way to show all the backtraces which is to first collapse the process item and by holding down the Option key, you can twist it open.

So now I'm going to visually just scan through to look for the thread that has the lock.

Well this is interesting because besides the Thread 22, which is trying to acquire the lock, I cannot find a thread that has the lock.

So this is a head scratcher.

But what do you know?

We just learned something that may be able to help us.

So I'm going to switch over to Queue Debugging by going to the [inaudible] control.

And here I'm going to use the filter bar to focus on my Graph Serial Queue.

So there it is.

So, it says here, I have one running block which is my the block waiting for the lock.

And I have one Pending Block.

To show my Pending Block, I'm going to go to my Debug menu item, Debug Workflow, and turn on Always Show Pending Blocks and Queues.

Out pops my Pending Block.

I'm going to twist it open and select the first frame which has my source code.

So plotAccelerationGraph Curve, is a method which I enhanced this morning as well, to use dispatch async so that I can work off the main thread.

And it looks like in my haste what I did is, I acquired a lock before I did the dispatch async.

So what's going on is this.

I have an Execution Block waiting for a lock, and stacked right after it, is my Pending Block, which unfortunately has the lock.

So the Pending Block cannot move because it has to wait for the Execution Block to finish.

So this is a kind of deadlock, albeit in a non-classical way.

So thankfully the solution is pretty simple.

I just have to move the line that does the locking, closer to the place, where it's protecting its data, which is velocity data.

This insures that it doesn't try to get a lock until it's dequeued and start executing.

So with this second round of simple change, let's see if this works.

Okay, let's get to the point where I can select the date again.

And voila.

Everything looks correct now.

So without this Queue Debugging, what you would have to do - what I would have to do - is to search for all the places where my velocity data lock is used and visually look at them and reason really hard about how it's used.

And that's error prone and tedious.

In summary, you're going to get Queue Debugging in Xcode 6, which has two parts.

Recorded backtrace is going to get you show you backtrace of the enqueuing event when you're using dispatch async and Pending Blocks, by hopefully by surfacing this information, you can optimize your use of GCD.

With that, let me bring up Troy to talk about view debugging.

[ Applause ]

Hi, my name is Troy.

And thank you for coming to our Debugging in Xcode 6.

As you may know, Xcode is a fantastic tool for discovering things about your application that may be hidden through the debugger.

Han Ming just showed us two new features through the Pending Blocks and recorded stack traces that are hidden pieces of information about your application that Xcode exposes to you.

But we all know that not all of the bugs in our applications are hidden.

Some of them are extremely visible.

They're staring us right in the face, from the user interface of our applications.

And since these are visual bugs, wouldn't it be great if we could debug them visually as well?

And now in Xcode 6, you can.

So what kind of bugs am I talking about?

These are the bugs where you have a mis-positioned view or clipping is wrong.

These are the squished or the stretched views.

And these are the views that are missing entirely because the visibility or "is hidden" attribute is set incorrectly in your application.

But that's not all that this new aspect of the debugger in Xcode helps with.

You can use it when you're not even debugging.

Let's say I have an application like this, and I would like to add a new button down here.

And I don't know, maybe it's a new code base to me, maybe it's been awhile since I've read this, I don't know what this view is behind it.

Wouldn't it be great if I could just click with the mouse and find out what that view is?

Well now, you can.

This view debugger feature is integrated all throughout Xcode.

So let's take a look at the Xcode window and figure out where the different pieces are so that we know what we're talking about when we're talking about this view debugging feature.

Here is the Xcode window that we all know and love.

When your application is running, down at the bottom there's the Debug Bar.

This is where you find your familiar items such as the Pause and Continue buttons.

Well now there's a new button in there, which is the Debug View Hierarchy button, right here.

Simply click this, and Xcode is going to import the user interface of your application into the main editor window of Xcode.

But it's not just imported as a static image, it's imported as separate pieces so that Xcode can actually explode it out into three dimensions and rotate it around if you want.

Seeing your application in this 3D view is very illuminating.

It allows you to traverse the hierarchy, see what the parent views are, see where the siblings are.

But if you want a more concrete representation of your view hierarchy, we turn to the Debug Navigator.

This is as Han Ming talked about, the place where you find your threads and your queues.

And that's available here in this Process View Option Selector.

If you select the View option in there, now there's threads, queues, and views, down below we have, your view hierarchy in the application which is right here.

Down below, we have some filtering options to help you tame that list.

We have the Show Only Primary Views and we have a Show Only Displayed Views.

Let me stop on this second one, because this one's important.

If you have a view that's missing in your application, and maybe the "is hidden" is set on it.

And so Xcode is also going to hide it in the Xcode UI, but Xcode knows about it.

It could show it to you.

So, you can simply turn off this option and Xcode will show all of your views, even the ones that are hidden.

Finally, there's the string comparison there, which we'll do in an exact match of that list in the navigator to help you drill down to exactly the item you're looking for.

Now let's turn our attention over to the right hand side of the Xcode window.

This is the Inspector Area.

And you might be familiar with this if you've ever used an interface builder, because these are very similar.

We have an object inspector, and there's a size inspector as well.

Here we see the object inspector for a UIImageView.

And not only that, this is a specific UIImageView because we're selecting we're clicking on one that's in our application, so we actually have the address of that object as well, which is Resident in Memory, on your Mac of on your iOS device.

We have the thumbnail and all the other interesting attributes about that view listed in this Inspector.

Finally, we have the Size Inspector.

This is going to show you the size and position, as you might expect.

It's also going to show a list of the auto-layout constraints that are affecting that view, with two exceptions.

Down at the bottom, you see two constraints which are deemphasized.

These are not affecting the layout of your view currently, but they're still attached to the view and they still have meaning.

In this case, we have a self.width of 320, and in the parentheses, you see that's content size.

That's because the system added this implicitly to this view to represent the content size.

They're deemphasized because up above, they're redundant with two explicit constraints that were added in Interface Builder, and those also have a width of 320, in this case, but they might not always match.

So this is a great place to check if you think you're having constraint issues, as we're going to see in a demo which I'd like to show you right now.

Alright, here we have the application that Han Ming started with.

And if you have super, eagle eyes, or maybe not, you'll notice that there's a pretty bad visual bug down here.

I wonder if the View Debugging in Xcode can help us find that.

So I'm going to switch over to Xcode, find this Debug View Hierarchy button, and simply press it.

What's happening now is Xcode's going through all of the views in the application, and pulling them back into the Xcode window.

Now we see, the user interface right here.

And already, you're getting a better look at what might be the issue in this application.

You see, some of the views, some of the images in this UICollectionView, are outside the view where they are supposed to be contained.

That's a pretty interesting fact to help diagnose the problem but it doesn't help us solve the problem, so we're going to have to dig deeper.

To explode the View Hierarchy of you application, all you need to do is simply click and drag anywhere in the canvas.

This allows us to see the parent-child relationships between some of these views.

I have open on the right hand side, the Assist Editor which is in Automatic Mode.

This allows Xcode to track the file that I click over here in the view debugger, with a source code file that matches with it.

So if I click here, I get the source code file for UIImageView.

I'm going to traverse backwards up through the hierarchy, until I get to the custom cell that has been written for this class.

The parent of this UIImageView is a UIView, and this is the Content View of that cell.

And the one behind that is the Run-Details Collection View Cell which is custom code in my application.

I'm also going to open up the Inspector here and check out some of the positions of these views as because it looks like there's probably a position bug here.

So traversing back up the stack, starting with one of these views that has a missing image, I'm going to click here and see that the position is 160 45.

Its child is 45 45, because the position is relative to its parent, but where is this final view?

I'm going to go over to the Navigator and find the view I have selected is this UIView, and I can simply find where its child is.

I don't know where it is in spatial space, but I know where it is in the real hierarchy of the view.

So if I click here, I see that the position is 160 45.

And remember, that's based on the parent.

So what I'm actually looking at is this view over here.

I thought that was a working one, but the view has been shifted off of the cell and it's that's the reason it's missing.

So let's go back up to the cell class and take a look at the implementation of that class.

I'm going to Command Click on the class name here to dive into the implementation.

And here you see a fairly simple implementation for a cell view.

All it does, is it overrides the initWithFrame, it creates a new UIImageView, and then it adds it as the a sub view of the Content View right down here.

Thinking about the information we found earlier.

We know that there's a position issue and I can see that we're passing the frameRect of the cell to this sub view.

That's not what I want.

I want the sub view to have the same bounds as the cell.

So instead of frameRect, I'm going to type self.bounds.

This should be enough to fix our problem, so I'm going to build and run.

Switch over to the simulator.

And dive back into our Run Details View.

Sure enough, we have the fully functioning UICollectionView with all our images, tightly spaced as expected.

Now that that bug's fixed, I do see another issue.

You see, this label up here, San Fran, is kind of a cheeky nickname for San Francisco, but this is a serious running app.

This is an app for those of us that go every morning and we don't call this fair city San Fran.

This is San Francisco.

So let's go back to the View Debugger and see if we can understand what might be going on there.

I'm going to take a moment to admire the fully functioning, UICollectionView.

Looks great.

I'm going then turn back to two dimensional mode.

This button down here allows us to reset the view hierarchy, and I'm going to click on this label.

Alright, I know that I'm using Auto Layout in this application, so let's go ahead and see if there's anything having to do with constraints that might be an issue.

Down here is the Show Constraints button.

This, it shows all the constraints, and it also dims out the views that are not currently related to those to that view or its constraints.

This allows us to really focus in on the work at hand.

When I zoom in, it's plain to see that there are two constraints attached to this label.

And if I look over here, I see the width is 95.

There's an explicit constraint of 95, but down in the bottom, the content size is 109.5.

So clearly, somebody added an explicit constraint in Interface Builder and that's clipping the implicit size of that label.

So I'm just going to switch over to my storyboard, find that label, and delete that explicit constraint.

If I had the time to show you guys, I would add a constraint that properly limited that label so it didn't go beyond the bounds of the screen, but I'm sure you can find an Interface Builder session.

For example, this afternoon at 3:15 that will go into more detail about how to lay out constraints.

And then we have the full string of that label.

So we've seen that the View Debugger, is a fantastic way to look take a look at your application.

And I invite you to do just that.

Go back today, just after the session, open up your computers and just try hitting the View Debugger button on your application.

I think you'll find it illuminating and the integration with Xcode is fantastic.

Now I'd like to turn it over to Kerry, who's going to teach you a little bit more about Quick Look Previews.

[ Applause ]

Thank you, Troy.

My name is Kerry.

And how is everyone enjoying the conference so far?

[ Applause ]

That's great.

I know that we are very excited to be here as well, to show some great new debugging features in Xcode 6.

But first, let's take a step back.

Last year, Xcode 5 debuted with this great new feature, whereby as you are running and debugging your application, you can get a live Quick Look preview of your data.

So simply by clicking on the Quick Look icon in the Debug Bar, you can get this popover that would show you a preview.

You could even, simply by hovering over the source of a variable in the Source Editor, get a Quick Look preview right from there, just by clicking on the Quick Look icon in the popover that appears.

Now, Xcode 5 debuted with default support for a number of important and common object types like images and Bezier paths and even things like map locations.

Well, we've gotten a lot of great feedback from this feature and I know that it's been very useful to a number of developers including myself.

So to build on the success, in Xcode 6, there is support for two new object types.

The first is Views.

So whether it be a UIView or an NSView, you can get a Quick Look preview of that, simply by clicking in the Variables view and clicking on the Quick Look icon or my favorite is just hit the Space Bar and it will bring up a nice preview.

And considering how common views are in most applications, I think this will be a very powerful feature.

Now the second object type that was added is, well, I honestly don't know.

That's because it's your own custom class.

So now for the first time, if you have a class that does some custom drawing and perhaps returns a an image or a Bezier path or something like that, you can now get a preview of that as well.

Before now, there wasn't great support for that.

So if you were to try and get a preview, you would get this Default View which had some pertinent information like the name and the address and the type, but no preview.

So I would like to show you how to fix that.

There's a simple method that you can implement in your Classes Implementation and it's called debugQuickLookObject.

Now in this class, you can either call your drawing code or implement it in there, but the important point is, from this method, you return something that Xcode knows how to show.

So one of the default supported types should be returned from this method and then you'll be good to go.

And I would like to show you that now.

So here we have the Jogr application that we've been seeing in previous demos.

And I'm going to click the Routes button, and we'll see Han Ming's run from this morning, if that's indeed the case.

Okay, so this class has a an object class called RoutePathOverlay which accesses the run data to generate this Bezier path that they gets overlaid onto the map.

So let's look at that a little further.

Well I'm going to click on the map and I'm going to hit this whoa, scrolls really fast.

I'm going to hit this break point.

And let's say I'm trying to debug some issue here, now down in the Variables View, we can see we've got a number of local variables like this point.

And if I click on the Quick Look icon, I can see a preview of the point.

This is our route path overlay.

And if I click on that, so I'm going to hit the Space Bar, and what it just comes up with is just some generic information, but we can do better than that.

So I'm going to stop here.

I'm going to go to our RoutePathOverlay.

And I've got the method that I was talking about: debugQuickLookObject.

And it's a sort of a long title but try and remember.

Okay, and if I uncomment that, what this method does, it's not really important because it's going to be different for every custom class, but what it does is it accesses each of the 364 some odd points of map location for that run.

And then generates a Bezier path, scales it a little bit, and then returns from it, a Bezier path, because Xcode 6 knows how to render or Quick Look preview a Bezier path.

So now that we've implemented that or enabled it, I'm going to Run and Debug again.

And we'll go to our routes, and we'll click on the map.

And if all goes well, we should see a nice detailed preview of each of those points of data from the start until the ambulance picked up Han Ming when he collapsed.

Okay. So, what have we seen today?

We've seen Han Ming show us how we can very easily and more effectively debug queues and blocks with the new Queue Debugging feature.

And then Troy showed us how we can more effectively or easily explore, our application's View Hierarchy.

I've used this very effectively myself several times.

It's been very helpful.

And then I showed you how to add Quick Look previews for your own custom classes.

And remember, you can also now preview any view.

So for more information, you can contact our great Developer Tools Evangelist, Dave DeLong.

I've also got a link here to the documentation for Quick Look previews, which documentation will show you a full list of exactly all of the default supported types in Xcode 6.

And then, don't forget the developer forums.

There's a lot of great debugging sessions this week, so be sure to check them out.

Thank you very much.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US