Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2008-925
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 925
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC08 • Session 925

Debugging and Profiling Your iPhone Application

Tools • 56:46

Whether tracking down a nagging bug or getting the best performance out of your iPhone application, the Xcode developer tools are ready to help. Learn to debug your applications in the iPhone OS Simulator and on an iPhone to get to the root of problems. See how Instruments can help pinpoint performance bottlenecks. Take a journey through debugging and optimization case studies to see how you can perfect your iPhone application.

Speakers: Dave Payne, Chris Friesen, Steve Lewallen

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good morning. Welcome to session 925, Debugging and Profiling Your iPhone Application. My name is Dave Payne. I'm the manager of the Analysis Tools team at Apple. Now, perhaps you went to the Developer Tools State of the Union address on Monday afternoon where Max Druckmann in the Dashcode demo built an application that he referred to as Where Would Dave Go? So that's easy.

My colleagues and I are very excited this morning to tell you about debugging and profiling your iPhone applications. We'll be doing this with both the iPhone itself as well as the iPhone Simulator. We'll start out with an overview of the tools, then get into the heart of the content. Along the way, we'll look at specific situations that are fairly common to run into in development. So debugging with Objective-C properties, finding buffer overruns, finding over-released objects, and profiling application launch time.

The primary tools that you'll use for debugging and profiling for your iPhone applications are the same as those that you would use for your Mac applications. So the Xcode IDE, the Instruments Analysis Tool, and the Shark Profiling Tool. As you know, Xcode provides a full graphical source code debugging environment that gives great ways to view your variable values and modify those both in a variable view window through data tips. It's got full debug breakpoint support with conditional breakpoints and breakpoint commands. Debugging in line with your source editor, the mini debugger to not leave your application environment when you're debugging.

Instruments is a great way to visualize the behavior and the performance of your application or your entire system. It can collect multiple tracks of performance data at the same time, lets you filter in on time slices, you can correlate events between the different event tracks, and just sort down through and really hone in on the information you need.

Shark is a great CPU profiling application. It's got very low overhead. It can profile down to the source line level or even the instruction level. It supports time profile on your iPhone applications. So all of these applications, all these tools are available for both your Mac applications and your iPhone applications. We've got some additional tools as well. So we have the DTrace Scriptable Dynamic Tracing Facility that gives you a lot of power to go in and trace anything in your application or even the kernel.

We've got a debugging malloc library called guard malloc that lets you find buffer overruns. There's a facility in the Cocoa framework called NSZombie that lets you find overreleased objects. And we've got a variety of command line tools that you can run in a terminal window to sample your application or look at its memory usage.

So these are all great. They really help you look at things on your Mac applications, but they're not available on the iPhone. Some of them take too much system resources to be able to run on the iPhone. Or in the case of the command line tools, we don't have a terminal environment available there. But there is a solution that you can use the full suite of tools in the iPhone app simulator. So you can use these techniques on your iPhone applications, and we'll show how to do that.

So how does that come about? Well, of course, Mac applications, the entire stack you've built as a Mac application, you're running on the Cocoa frameworks, the Mac OS X system libraries, the Mac OS X kernel, all on your Mac. Your iPhone applications running on the device, they're all built for that environment. Your application is working with the Cocoa Touch frameworks, the iPhone OS system libraries, the iPhone OS kernel, on the iPhone or the iPod touch.

But the iPhone Simulator provides kind of a hybrid environment that you have your application built as if it was actually a Mac application. For example, built for x86. It's running on a version of the Cocoa Touch frameworks that are built for x86. But then these are running on the Mac OS X system libraries, the Mac OS X kernel, interacting with the Mac OS X daemons.

And so because it's kind of an iPhone app that's really running as a Macintosh application, that's how the tools are then able to apply in this situation. So let's go ahead and take a look at debugging with the iPhone SDK. I'd like the debugger team manager, Chris Friesen, to take you through that. Thanks, Dave. Good morning, everyone. I'm Christopher Friesen, manager of the debugger team. Now, with the iPhone SDK, Apple has delivered to us the desktop development experience for your mobile applications.

Now, this means that that the debugging interface is the same for your mobile applications as it is for developing Cocoa Apps for Mac OS X. This means that you get in-editor debugging, crash catching, data tips, data formatters, and breakpoint actions. Now, debugging in the simulator and debugging on your device have some differences. As Dave just said, when you're debugging in the simulator, you're doing an API simulation. You're not actually emulating any hardware.

Now, we also only have guard malloc support when you're debugging in the simulator. And if you want to look at your crash logs or system console, you'd go to your console app as you would on Mac OS X. Now, if you're using properties in your simulator app, it requires fully declared instance variables in the interface of that class. And we'll talk more about that later.

To debug on your iPhone, it's as simple as selecting the device in the overview popup in Xcode of your project. And to access the system console and crash logs, you'd visit those tabs in Xcode's organizer. They're called console and crash logs. As you use properties on the device, you don't have to declare the instance variables in the interface of your classes. We'll talk more about how that might be a problem later if you want to run on the simulator. And again, remember that you have limited resources on the phone compared to running in the simulator on your Mac.

Now we're going to talk about Objective-C properties. Quick overview. Properties are an explicit specification of how accessor methods behave. For instance, are you retaining a value? Are you making a copy of the value that's getting set? Or are you simply doing a simple assignment? The compiler can then automatically synthesize these accessors for you. So you don't have to remember how to code for these different types of accessor behaviors.

Here's an instance of a declared instance variable. Of course, the instance variable is in the interface. Here it's called pwordless data. When it's undeclared, you don't have to declare it. Now, when it's undeclared, this will only run on the iPhone. If you switch to the simulator and try and build, you'll get some compiler errors. So if you're coding, you really want to declare all of your instance variables. And that way, you can switch back and forth between the simulator and the iPhone and be able to use some of the performance tools that aren't available when you're debugging on the device.

I might be thinking, if the compiler is creating these accessors for me, how do I break on them? How do I view the actual values and how do I call these setters and getters myself? To set a breakpoint on a setter when your property is defined as @synthesized P wordless data.

To break up the setter, you'd set a breakpoint at set, and then you'd capitalize the first letter of the first word of the instance variable, in this case, p, wordless data, ending with a semicolon. To break on the getter, you'd set a symbolic breakpoint on the method pwordlessData.

Now you can also create a property where you've mapped the instance variable name to a different name that you're going to actually use in your source code. So in this case, the instance variable is pwordlessdata, but we've mapped that to wordlessdata, so we can just type wordlessdata in our source. That means the accessors are now based on wordlessdata, so to break on the setter, we set a break -- symbolic break point at set capital W wordlessdata colon, and to break at the getter, it's simply breaking on wordlessdata.

Now here we've broken at the setter. This is what our stack trace would look like. We see that we've broken on set word list data colon. Now when that's selected, In the stack frame view, in your source code, the PC will actually show the PC arrow at the end of the implementation.

for that particular class. So you'll need to look at the source--you will need to look at the stack in order to see exactly why you've broken there. and what the method name is. Now if you go up a stack frame, you'll see that we have in fact stopped where we're using the properties dot notation to set the instance variable for wordless data to a value.

How do you go about viewing some of these values? Now, if you happen to still be using undeclared instance variables, they won't show up whenever you turn open the variable in the variables view. If they are declared, then they do show up. You can still access the values by calling an expression in Xcode's debugger console, simply using the normal Objective-C bracket notation. So in this case, in the last line there, it says we're going to evaluate the expression in Xcode's debugger console, PO, open brace, self, word list data, close brace. And that'll print that instance variable's value. Now let's do a quick demo of Objective-C properties and crash logs.

We can switch to Demo Machine 7. Now we'll open our project. And we'll set our breakpoint on setter. Set P word list data colon. And then we'll click build and go. The application is now installing over to the device over USB. It's being sandboxed. User preferences are being archived.

and now starting the remote debugger and launched in GDB. And we see that in our breakpoints window here, our symbolic breakpoint has been grayed out. and the Enable Breakpoints checkbox is now in the mixed state. This means that the debugger was unable to resolve the breakpoint. So we might have used the wrong symbol name. So let's do a project find on that. Synthesize.

and David Gulland: So here we see that it's been mapped to the other name as we saw in one of the example slides earlier. So we actually want to set our breakpoint on set word list data, not set pword list data. So now we'll jump back to our breakpoints. And we'll set our symbolic breakpoint on set word list data colon. And we can also set our breakpoint on the accessor, word list data. And we see by location column that the debugger has actually been able to find symbols for these breakpoints in the executable.

So now I will quickly shake the device and play a little bit of Boggle. We'll hit the breakpoint. And we see here in the stack frame that we've broken that set word list data. and the PCRO is pointing at the at end. So when the PCRO is pointing at the at end, you want to jump, you want to look at the stack view to see exactly why you've broken and what the method is. And here we've set, we're calling the setter.

And if we continue, we see the same thing whenever we're at the getter. What do you do when you're running your application and you're not running it in the debugger and something happens to your application? So if we can switch to the device. If we could switch to the device, please. There we go. And we'll launch our app. It's good old baffle. And instead of shaking it, I will drag my finger around, or no I won't.

There we go. Worked better in practice. All right, so now I'll try to spell a word. There we are. And our applications crashed. So what do we do? We didn't have the debugger attached. Well, - You would go to the organizer back in Xcode. So if we can switch back to the demo machine.

And we'll go to Window and Organizer. And here's our device. And we can click on the console. And then we can search filter on baffle, which is the name of our application. And we'll click reload to make sure we have the latest messages. And here it tells us that Are application deactivated? I don't see any crash logs.

We should check the crash logs anyway. When you click on Crash Logs, it'll go and fetch the crash logs from the device. And here we see that it has in fact symbolicated the crash log. and the crash happened near BGbaffleward.m at line 68. So let's switch back to slides and talk about crash logs.

The crash logs get copied from the device into Xcode every time you visit that tab. They also get copied over when -- the first time you plug in a device when Xcode's launched. Now the symbolication happens on the host immediately after those crash logs are copied over. And it uses the symbols not only from the SDK, but from the DSIM file, which has your debugging symbols, because it has a matching unique identifier inside of it. And we use a spotlight search to find that, and then we look for the dot app in the same directory because we need a couple symbols out of that as well.

So you want to ensure that spotlight indexing is enabled on your laptop in order to get symbolicated crash logs. If you ship your application or give it to development or, I'm sorry, Q&A because you want some testing done and you want to be able to symbolicate those crash logs later, you want to save off the DSIM file as well as the .app in the same location.

Each time you build, those DSIM files get overwritten in order to match the new application that's been generated. So what exactly are DSIM files? Well, they're the fully linked dwarf information for your executable. They're the debugging symbols. And as I said earlier, the DSIM has a unique identifier inside it that matches the executable unique identifier. And they're generated in the same directory by the build system.

These stay on your Macintosh. They don't get sent to the device or anywhere else. And they're necessary for verbose debugging and crash logs and instruments, as we'll see later. Here's a quick graphical representation of your build products directory with an executable and a DSIM with matching unique identifiers.

Now let's talk about memory access issues and how you find them using GuardMalloc. So GuardMalloc catches your memory access problems. It's only available in the Simulator for your iPhone applications. It forces an immediate crash on bad memory access. For instance, buffer overruns, buffer underruns, accessing freed or unallocated memory, and also accessing uninitialized memory. So how does it work? Well, each allocation happens on a separate VM page. To catch buffer overruns, your allocation is aligned with the end of the page. And then a guard page is placed after it, like this.

Now to catch buffer underruns, you have to set one of guard malloc's options. In my environment variable malloc protect before. Then, The allocation starts at the beginning of the VM page and the guard page is placed before that. And this will cause a crash if you try and access memory before the allocation.

To enable GuardMalloc in Xcode, go to the Run menu, go down to the bottom, and check Enable GuardMalloc. This is effectively setting an environment variable, DYLD_INSERT_LIBRARIES, pointing at the libgmalic dilib. Now for more information about GuardMalloc and all its options, you want to look at the man page for libgmalloc. Note that the man page is not called malloc, so you want to make a note of that. And now I'll have a demo of catching memory access exceptions. We can switch to the demo machine.

So here we are and we're going to build and go on our device. It's not the same baffle. It'll behave a little bit better than crashing after selecting four words. So we're still installing now. It's copying it over the USB. Doing the things that it does, all those seven things they talked about in the State of the Union. So now it's running on the device. I'll start a new game. I'll shake it.

Now we can see that it's behaving much better. Now, memory access issues can be rather insidious because your application may seem like it's perfectly fine. You may be corrupting data. Pardon? You may be actually corrupting data. So let's switch to running in the simulator with GuardMalloc enabled. Now we click Build and Go. It's installing the application in the simulator. This is the first time I've run the simulator today after rebooting, so it takes a little while to come up. Here it is. Running baffle. We can shake it.

And we've crashed. In the status bar in the lower corner, it told us that GDB was attaching to the program. And now we see that we are at has suffix. And it looks like And if we do a step over, we'll see that the program received a signal, exe bad access.

So now let's investigate a little bit. So if we hover over Word, our data tip comes up and it has an address. But if we turn it down, we should be seeing the first letter of this char star, and we aren't getting anything. Something's a little fishy. Let's go up here to the Variables view. We'll Control-click on Word and select View as Memory. The debugger comes back telling us, hey, we're unable to read this memory location. So that probably means this memory's been freed.

And if we go up our stack, we can see that we've gone and malloced some memory, done some string copies and string cats to it, and then freed it to make sure we didn't leak it. However, we're using it later. So this is obviously freed too early. Now, this is the same code that seemed to be running fine on the device. And here we see that we actually had a memory problem. Let's switch back to slides.

I'll leave you with some quick troubleshooting tips if your application doesn't launch on the device from Xcode. The first place to check is Xcode's debugger console for error messages. Then you should try and launch the application on your device using your finger, not trying to launch it from Xcode. Then check the Organizer's Console tab for provisioning or signing errors. And finally, check for crash logs in the Organizer. And now let's bring up Steve Lewallen to talk about profiling and analysis with instruments.

Thank you, Chris. Good morning, everyone. So today I'd like to talk to you about one of our premier analysis and profiling tools known as Instruments. We first introduced Instruments in Leopard for analysis and profiling in Mac applications, and now we've extended it to support profiling and analysis in the simulator and on the iPhone.

So, what is Instruments? Well, first and foremost, it's a performance tool. So, you can see that you can use Instruments to find bottlenecks in your own code. You can look at the memory use of your app. You can even identify perhaps inefficient graphics algorithms that you have in your games.

But it's also an analysis tool. So you can observe the history of a pointer, for example, or you can look at how your system is interacting with the file system. You can even see how your app is working on the device in relation to the other processes on the device, how much CPU it's taking versus the CPU they're taking, memory to memory, et cetera. And finally, when you're running in a simulator, again, as Dave said, a simulator app is basically a Mac app. You can actually use DTrace to look at your app from the kernel on up to user space.

So in using Instruments, you can do so either standalone or you can do so from Xcode to extend that edit, build, debug cycle into a full edit, build, debug, profile, and analysis cycle. Whichever way you use it, you use Instruments to record and correlate arbitrary data streams. So for example, you could say, well, how much memory does it take to consume this special file my application reads in? Whatever you do, you can then use to record your data, you can then use Instruments to mine that data. You can turn your data upside down, inside out, filter it on different things and really narrow down the data to just what is the source of your problem.

So now let's talk about using Instruments in the Simulator itself. There are similar capabilities in Instruments when using a Simulator app with Instruments. You have the ability to look at memory issues. You can look at CPU issues. You can look at I/O issues. In this case, using DTrace file activity-based instrumentation. And you can use DTrace to discover other things that we haven't thought of that are specific to your apps.

Now, one thing you need to be made aware of, and I'm sure by this time you probably are, but let's just go over it again. The results that you gather in a profiling and analysis tool such as instruments when you're looking at an app in a simulator has to be interpreted a bit differently, maybe with a healthy dose of skepticism because when the app is running in a simulator, it's compiled for the Mac, linked against iPhone frameworks compiled for the Mac, running on the Mac OS. And therefore, your memory issues, for example, will have a different memory profile when your app is in the simulator than it is in the iPhone itself.

Of course, it goes without saying also that the hardware, whether it's a Mac Pro 8 way or even a cool MacBook Air, is way more powerful than your iPhone. And so you need to be aware that the performance envelope, that your iPhone app is running in, in the simulator is different than the far more constrained environment it will find itself in when it's actually running on the phone.

And that's why I like to say and point out that using instruments or analysis tools against your app in the simulator is best suited for analysis. So you can observe how it's behaving, track down different problems, but when you want to finally get ready to fine tune that performance before you ship it, you really want to use instruments against the app on the device itself where it's operating within the parameters that that device, that piece of hardware has.

So there are unique aspects to using instruments with a simulator. For example, some Mac instruments, they simply don't apply. The UR Recorder Instrument uses accessibility frameworks on the Mac, and we don't have that on the iPhone, so that doesn't apply. We don't have Core Data on the iPhone, so the Core Data instrumentation does not apply. And our new garbage collection instrument, Object Graph, that we introduced in Xcode 3.1, it doesn't apply because we don't have garbage collection on the phone. However, the other instrumentation does.

Now, secondly, applications are quote unquote installed in the simulator. So when you select a simulator app, whether from Instruments or from Xcode, the first thing that's going to happen, if need be, is that app is going to be installed in the simulator and then it's going to be launched for you.

So we take care of all that for you. Now, when you're using Instruments against your app in the simulator, you're probably pursuing one of a few common profiling and analysis goals. You may be seeking to increase the stability of your app. Maybe you're using ObjectAlloc and DTrace to track down an over-release. In fact, we'll see a very useful cool demo of that in a moment.

Or perhaps you're just trying to reduce the memory footprint of your app by tracking down leaked memory. That's very valid to do in the simulator. Or perhaps you're looking for ways to use memory more efficiently. If, for example, you're putting so many objects in the auto release pool that that's growing very large, then your overall memory footprint, even though it will be temporary, will spike. And that could cause a danger to your app when it's actually on that phone. So you want to look for those issues as well.

So now let's talk a bit more about that over-release issue, that increasing stability type of goal you might have using Instruments. So what is an over-release? An over-release is when you have an object that's deallocated and then you message it. So this will lead to unspecified behavior in your app and an eventual crash. And we call these over-released objects zombies. They're dead, but they're still walking around making trouble.

What you need to do is diagnose this using the NS Zombies facility that's on the Mac. This is only available on the Mac whether it's a Mac app or a simulator app. So this type of issue, you must use instruments with the simulator. You can't do it on the iPhone itself.

So what would a typical scenario look like when you're trying to diagnose an over-release problem which quite frankly without instruments and without NSOMBIs would be a very frustrating thing to have to do. And in a retained release sort of memory-managed language like Objective C, this can be a fairly common thing that will happen in your code. So the first thing is it's going to crash.

And you're going to see some message in the console or the debugger that says something happened to some mysterious thing that was at some pointer. And that's not very helpful at all. So what you'll want to do is you'll go to your executable inspector in Xcode and you'll set the environment variable NSZOMBI enabled to yes. And then you'll rerun.

Now you'll get something that's a bit more helpful. At least you'll know what type of object that was at that pointer. But you still don't know how it became over released. So let's go and -- so what do you do now? Well, let's go to Instruments and take a look at how we can fully diagnose this and just nail this problem.

So demo machine six please. All right. So the first thing I'm going to do is launch my poor baffle Cocoa Touch app. You would have thought that this app being front and center at this conference that we would have had this in better shape, but this app has suffered the entire conference. This time it's crashing on us here. So I'm going to build it and I'm going to run it in the simulator.

And I'm going to show you the basic scenario that leads to the crash. When I start a new game and I select my first word, I think is fine. Then I select another one. Oh, GDB is loading. Let's go to GDB in the console. And yes, indeed, we get this not so helpful message that our app has crashed, messaging something that was at some random pointer. So I'm going to use Instruments to diagnose this and track it down.

Now, the first thing that I want to do is I want to stop my app in the debugger here. I want to quit the debugger because right now, Baffle Cocoa Touch is in the simulator suspended. If I would just go to Instruments right now and try to launch a new instance of Baffle Cocoa Touch, it would fail.

And the reason is that the simulator is occupied right now with this app. So if you run into that kind of a problem, say, oh, I remember when Steve said go back to the app. I'm going to go back to the debugger and stop it. So I'm going to stop that now. And I'm going to go down to Instruments.

[Transcript missing]

So, I'm going to use a little DTrace custom instrument that I built to surface the zombie pointer. Now, I'm doing this as a bit of a tease as well. I want to encourage you guys to go to the DTrace session later today. I believe it's at 3:30 and then there's a great advanced DTrace and instrument session tomorrow morning. So, what I'm going to do is I'm going to go up to the upper right of my Instruments window and I'm going to hit the Library button. I'm going to search for zombie. Thank you.

So this is my little zombie detection instrument. Again, you could do all this without this instrument, but it's just going to make it a little cooler and more convenient in Instruments itself. All right. Now I'm ready to try to track down this over-released object. So I'm going to go and I'm going to click the Record button in the upper left of the Instruments window. That's going to start up my app in the simulator again and start recording data. I'm going to go and put it through its paces again that led to that crash.

Okay, crashed. We get the crash dialogue, which we expected. I'll dismiss that. Now if we look at the NS Zombie Detection Instrument, and far right here we see a little spike here. That's an event. And the only event this tracks is messages to zombies. And if I look down here in the detail view, I can even get the pointer to that zombie. So I'm going to copy that out.

And now I'm going to go to my ObjectAlloc instrument. Now, you've seen ObjectAlloc several times during this conference already. Hopefully you've made great use of it on the Mac and your iPhone apps already. But let me just review, just for a second here, what we're seeing. We have three basic detail views in ObjectAlloc, one of which is a summary view. It shows all the different types of objects it's ever seen and categorizes them by how many times they were created and how many are still left when we stopped running, et cetera.

Second view is a call tree view, which will show me all the places where the memory was allocated that we saw with ObjectAlloc. The third view is very relevant for this bug here. This view is an event view that will show me all the pointers where they're allocated, no matter what type they were, from the time I started the app until it crashed.

So I have that little pointer that I copied from my zombie detection instrument. So I'm going to go down to the lower right-hand field, the search field, and I'm going to say search by address. And now I'm going to paste in that address. And now I have the actual object that was overreleased. If I click on this little round circle that has this arrow in it, I'm going to get to the entire history of that object.

So first I can see where it was created. And I can see retains and auto releases and releases. So let's go to the extended detail view. I'll use the view menu in the far right, say extended detail. And I'll reorient my back trace so it's a bit more convenient for us here.

And I'll see that, well, it was malloc'd here. When I'm looking at these frames from top to bottom, I see a call and below it I'll either see a library, we see lib system, lib objc, core foundation, et cetera, or I'll see the actual path to a file. That will be source code that instruments has found that applies to this project. So I'm going to double click on this and I'm going to see what's going on.

Well, It looks like it's created in a little static factory method, Create Table View Cell. Cool, that seems fine. If I look at my other retains and releases in that initial creation, it seems like my retain count's pretty balanced there, everything seems reasonable. But two back-to-back auto releases, that seems a little suspicious. So let's look at where each of these auto releases are.

So again, I'm going to click on the first frame that I have that has source in it that's to my project. I'll double click there. It again takes me to the factory method, and I see that the factory method, appropriately so, is allocating a new instance of this and then returning an auto-release. If the caller doesn't want to do anything with it, it'll be cleaned up automatically. Otherwise, they should retain it.

So that looks cool, that's fine. Let's look at the next auto-release. This is looking pretty suspicious at this point. So I'll select that, and I'll double click on that frame and go to the source. Now what we have here is a table view, cell for row and index at path. So this API is supposed to return an instance of UITableViewCell.

And if I look down here, I can see that it calls the factory method that returns an auto-release version and then auto-releases itself. So maybe this developer was confused and thought, well, I should return an auto-release version from this method. But actually, that's the source of the problem. So since this is already auto-released, what I'm going to do is simply delete this auto-release. So I'll go ahead and do that now. I'm going to hit build and run again.

And it's linking, installing in the simulator and running. And now put it through its paces again to see if it still crashes on me. And it doesn't, so this is working fine, so that's great. Exit Xcode, exit Instruments, and back to slides, please. So now we've seen how we can use Instruments to track down what used to be a truly maddening thing to try to find. That would lead to instability in your app and you'd spend many hours trying to fix yourself. So that was pretty simple. So now let's move on to using Instruments in the iPhone.

So again, similar capabilities as profiling Mac apps or simulator apps. You can look at memory analysis. You can look at CPU bound issues. You can look at I/O. In this case, since DTrace isn't on the phone, we have built a great new instrument called the I/O Activity Instrument in the system usage template that will basically give you Uber on steroids version of SF usage with call trees and back traces, all that kind of great stuff. And we've introduced two new graphics instruments to look at Core Animation and OpenGL ES.

So I'd like to encourage you to be a bit more proactive in your analysis in profiling of apps that are destined for the iPhone because the iPhone runs in a more -- the iPhone app runs in a more sensitive environment. It has more constraints on it. So, for example, you have to remember that applications are Sandboxed on the system.

And so let's say you had a little utility library that had some logging you could turn on and when you turn it on, you scrolled it away and slashed temp. And you built that in the simulator and everything worked great. But when you just took that app and you moved it to the iPhone, suddenly your log wasn't being created anymore.

If you use the FS usage instrument we call IO Activity, you'll be able to see that you get a security permission error when you actually try to create a file. 00:02:00:00 - 00:02:11:00 Unknown So you want to you want to make sure that you've dealt with those types of issues.

Secondly, as we've sort of alluded to earlier, if the iPhone OS determines that the amount of memory that your app is using is becoming a threat to the phone itself and you're listening for it, it will send you a low memory notification. And if you don't clean up or reduce the amount of memory you're using quickly, your app is going to be terminated to save the phone. 00:02:11:00 - 00:02:36:00 Unknown So you want to use the various memory analysis instrumentation that we have when you run it on the phone. Remember how the memory profile is going to be different on the phone than the simulator.

So when you go to the phone, you want to watch that memory, maybe use the activity monitor just to watch the memory and see if it stays about at a certain level. It doesn't just keep growing like crazy. And you really want to make sure that your app is ready to live out in the real world on the phone. So there are unique aspects to using instruments with iPhone itself.

Again, with the simulator, the same instruments don't apply for the same reasons. Now, Instruments will not install an app on the iPhone. We leave that task to Xcode and the Xcode organizer. However, if you use Instruments from Xcode itself, so you go to the run menu, which we'll see in a minute, you launch a performance tool and Instruments starts up and your app is launched on the phone. In that workflow, your app will actually be installed on the phone for you.

So again, you may be pursuing one of various common profiling goals when using Instruments with your iPhone app actually on the device. You may be again seeking to increase stability, but maybe this time you're looking for that increasing memory issue. Or you might have read our developer guidelines for writing iPhone applications. And you read this one guideline that said fast launch, short use. And what that means is your app should launch as fast as it can and it should save out data appropriately so that it connects it quickly.

Or you might be trying to improve the drawing performance of your core animation, your app, or an Open GLS game. And finally, you may be looking again at how your app is sandboxed in the system. So now let's take a look at another demo of using Instruments against your app on the device to look at your startup time and to try to improve that.

Again, this is a perfect scenario for using Instruments with your app on the device itself because if you try to measure the startup time of your app when it's running in the simulator on your 8-way Mac Pro, that's not really going to be reflective of the real world. So let's look at Instruments on the device itself. So if we can switch back to demo six, please.

Okay, so I'm going to start another version of Baffle Cocoa Touch. And it's targeting the device here. And I'm going to build it. And now I'm going to go to the Xcode Run menu. And I'm going to select Start with Performance Tool. CPU Sampler. Now, the app is being installed, as I said, in this particular workflow using Instruments in combination with Xcode. The app will actually be installed on the phone for you. And it's doing all the things that it does.

Now it's launching Instruments itself and then Instruments will actually launch Baffle Cocoa Touch on the device. So one of the great things about having this type of display in Instruments where it's real time, we're seeing all the effects of your app on the system as it happens, is that I can easily judge when startup is over.

So I can see this area in the front, all this activity ceased. The app is basically idle now, so I'm going to hit stop. And I'm going to drill down on that and see what's going on, see if I can improve that. So I'm going to use the time scale here in the left middle section of the window, stretch it out a bit.

And I like using running sample times, which if you attended the Getting Started with Instruments session yesterday, Daniel Delwa did a great job of explaining the differences in the time scale. So I'm going to go back to my previous screen and using Sampler in this feature. So I'm going to select that.

And if I go to the top here and I go to my extended detail view and reorient my stack, I'm going to see that all the time the top heavy hitters here are in my BG Sane Glass view in it with frame calling into UI image image name. But let's double click on that and see what's going on.

So, here's my init method and we have this loop here. What's going on here? Well, we're loading in a bunch of images and stuffing them in this array pcontent's image--images. How many of these images are they? Well, are there? Well, there's about 50 of them. If I double click on this, we can see the actual--it's 51 there. Now, these are actually the background images for the hour sand glass. So, as time progresses and the game is going on, your time is running out, the hour glass runs down. There's a different image for each level of the sand.

Well, it appears that we're loading all 50 plus of these images when we first create the initial UI at startup time for the app. It also appears that we're attempting to cache these away. Well, We don't really need to do either one of these things. We don't need to load all these images all at once, because it's not that big of a hit to load one of these images every so often as the sand progresses downward when the user is playing the game, because for most of the game, the app isn't doing the thinking the user is, trying to find a word.

So there's plenty of horsepower here left to load this image. The second thing is, perhaps this developer thought that they could cache away this image data so the next time they needed it, it would be faster to render. But actually, UIImage, when you do image with name, handles caching behind the scenes for you.

So what I would assert is that the best thing to do is actually simply create new instances of UI image using image named when we actually need the background images. So let's see where we actually use those images. So I'm going to do a project find. Object at index is probably what's going on here. I'll do project.

And we're loading it in two different places here. Let me expand this window. And I'm going to hide my project tree there. OK. So we have one image here, index zero, in the actual init with frame. What that is is the initial background image of the hourglass that you see when the game first starts, but you haven't started playing. And the other one is basically when time is progressing.

So as time progresses, we figure out the next image at a particular index to load in, and we load that in. In this case, we get it from the array because we loaded all these ahead of time. OK. So let's do what I suggested and just load these images in as we need them.

So I'm going to say, I'm going to copy out this little line of code here. I'm going to reuse that and I'm going to delete this section here. We don't need to do that anymore. And the first thing I'm going to do is replace this initial image load, index position zero. And I'm going to go ahead and Save that away. And I'll go to the other line as well. And this is where we're actually loading in the progressively lowering sand background. And I'll load this guy in.

Okay, I'll build that, save everything. Okay, succeeded. So now let's go back to the run menu again. One of the coolest things I like about the integration of Xcode and instruments is that they're really smart about how they interact. So Xcode knows that the last thing it did was to run the CPU sampler in instruments. So I can just select that again and it's going to go and install the app again and do all the things it does.

And it also, working with instruments, will then be able to say to instruments, hey, you know, this is another trace using CPU sampler, but it's against that same thing you just opened up a trace document for. So can you reuse that previous document? We'll see how that becomes handy in a little bit. So now I've launched my app again and again with instruments we can see that we had this little bit of activity and now we seem to have reached idle.

So let's take a look and see if we've cleared up that startup time. So first of all, if I sort by running time again, I can see that, well, all those big samples that I saw earlier, they're gone. We don't see anything in my init method, as we would have expected, that is loading those images.

And if I wanted to visually just to compare it, do a very quick look at whether I improved performance, I could go to the left-hand side of the screen. I can go to the left-hand side of the sampler instrument and twist down this twisty here, and I can visually compare the previous run.

So it's pretty easy to just see that in the first run, without the performance optimization at startup time, it took about this much time. And, you know, when I optimized it, then we're down to just this little bit of activity. So that's great. Excellent. The one last thing to do is to look at what I asserted earlier. I asserted that if I create, say, UI and Android applications, I can visually compare the previous run. So it's pretty easy to just see that in the first run, without the performance optimization at startup time, it took about this much time.

And, you know, when I optimized it, then we're down to just this little bit of activity. So that's great. Excellent. image, image name with BG timer 1, the timer images are called BG timer 1, BG timer 2, BG timer 3, et cetera, if I loaded BG timer 1 and then I created a new instance of UIM image with the same name, BG timer 1, that that data would not be loaded in again even if I deallocated the previous instance. So let's use that IO activity instrument to see if that assertion was actually correct.

So I'm going to go start a new trace document. I'm going to go to my iPhone category of templates now since I'm actually running instruments against the iPhone itself. I'm going to go to the system usage template, select that, And I'm going to launch my bafflecoco touch app again. Instruments conveniently will list just the apps that it sees on the device. And I'm going to start that up again. And if we could go to the iPhone device display for a moment, I'd appreciate that.

Okay, so let's just make sure first of all that the hourglass lowers because we don't want to have optimized by breaking the entire app. So the game starts and we can see down here that the hourglass is lowering. So let's let it go through all of its paces. Let's switch back to the demo machine, please. Demo six.

Now if we go down to the bottom of this I/O Activity Instrument and I search for BG Timer, We're filtered by all those timers so we can see the new images as they were loaded in so the app has ended. The time is over now, but it's still running. So now what I'm going to do is I'm going to start the game again.

and the images, the hourglass will begin to lower again. I'm not going to keep switching back and forth on you. That would be a little crazy. But if we look now at the instrument again, though the hourglass images are appearing, they're not being reloaded from the phone, even though I was just instantiating new UI image instances with new names. So I would say that we successfully optimized the app and dealt with startup time there. Of course, optimization is never over. If we can go back to the slides, please.

There could have been other places where I still could have optimized startup time. So it's always nail the first guy, the biggest guy, and then go down to the next guy, and the next guy, and the next guy, until you can't get to any of your code and it's just the system.

But we did a pretty good job of using Instruments there against the app on the device to optimize startup time. So today we've talked about a lot of very cool tools, very powerful analysis and profiling tools. Our premier tools of Xcode and the debugger, of Instruments, and Shark. We've also talked about our command line tools that I have available on the simulator and on the Mac side.

So I think the tool bag is pretty full in that you have enough to go forth and make your apps robust and fast, make them have a low memory footprint as possible, make efficient use of the file system, and draw really great fast graphics. So with that, of course, the usual, you can contact Michael Jurowicz for more information. You can look up various of these helpful URLs for performance docs.

And there's a bit of a quandary that I have and I'm going to put you into as well today because at 3:30 today, there's two sessions that I'd like to be in. There's optimizing performance for your iPhone application. There's also using DTrace on Mac OS X. So I didn't set that schedule up, but we'll all be running back and forth passing each other, I guess.

Also, performance tuning your application with Sharp. Sharp is an awesome tool. So check that out at 5:00 today. And finally, tomorrow, we have our advanced instruments and DTrace session. So you can look at how one would build custom DTrace instruments as I did earlier for that over-release issue.