Chapter 12. Monitoring, Analysis, and Performance Tools

This chapter takes a look under the covers at some tools that allow you to understand how your application is performing and where you might be able to improve things.

You could be lucky, of course. If all of your programs perform well and never crash you may never need the tools here. And, frankly, one of the difficulties in writing this chapter is that it is so easy to write well-behaved software in Xcode that it is quite hard to engineer circumstances in which you will need to use these tools! However, by the end of this chapter, you should have a good understanding of where to look in the event that things go awry.

Instruments

Instruments is an analytical tool, and it is clearly the flagship tool in the Xcode toolchest. Use it to monitor your running program and to alert you to issues with, for example, memory leaks, object allocations, and a range of other parameters and problems. In this section, I'm going to focus on using Instruments to locate and help you solve memory-leak problems and to analyze file activity.

Tracking Down a Memory Leak

As with many other modern programming languages, Objective-C v2.0 offers a service called Garbage Collection. Every time a program instantiates an object, it allocates a block of memory space to hold it. As a program progresses, an object may go out of scope (for example, an object created in a loop is no longer needed when the loop is complete) and, therefore, the block of memory is no longer needed. As a good citizen, your computer program should relinquish that memory so that it is available for other purposes (either within the program or elsewhere on your system). The Garbage Collector looks after this, tidying up blocks of memory allocated by your program when you no longer need them.

Before Garbage Collection became available, you as a developer were responsible for allocating objects and releasing the memory when your program had finished with it. Even though Garbage Collection is now available, many developers still prefer the discipline of positively managing the allocation and release of memory. Sometimes that discipline slips and a program may allocate but never release its memory. That is called a memory leak.

Create the Application

It may seem like a slightly perverse thing to do, but in this chapter you're going to write a program that leaks memory (spoiler: you will create a set of objects but never release them). Then you are going to use Instruments to monitor and let you know where it is happening.

In Xcode, create a new Cocoa application (not Core Data- or Document-based). This application is going to be a simple calculator for numbers in the Fibonacci sequence, so call it Fibonacci Fun. Listing 12-1 shows Fibonacci_FunAppDelegate.h—you just need to set up a text field to take the output and to declare the function where you are going to do the calculation (see the lines in bold in Listing 12-1).

Example 12-1. Fibonacci_FunAppDelegate.h

#import <Cocoa/Cocoa.h>

@interface Fibonacci_FunAppDelegate : NSObject <NSApplicationDelegate> {
    NSWindow *window;
    IBOutlet NSTextField *outputField;
}

@property (assign) IBOutlet NSWindow *window;

- (IBAction)startCalculation:(id)sender;

@end

Now for the implementation. You will want the button click to run the calculation—in fact, to make it work a bit harder I am going to run the calculation 10 times. Each time the calculation works out the first 90 members of the Fibonacci series of integers: each member of the series is the sum of the two previous members of the series, so it goes 1, 1, 2, 3, 5, 8, 13, 21 and so on. The numbers get to be pretty big, which is why you need to declare the fibValues array holding the series of numbers to be a long long int. After setting the first two members of the series manually, you go into a loop, setting the nth member of the series to be the sum of the (n−2)th and (n−1)th members. Then you take the current member of the array and append that to a growing, comma-separated string—actually, since NSStrings are immutable, you are creating a new NSString with the new contents. Each time around the loop, you are putting a space between the lines and adding a number at the beginning of the next calculation.

Then finally you put the outputString into a multiline text field. See Listing 12-2.

Example 12-2. Fibonacci_FunAppDelegate.m

-(IBAction)startCalculation:(id)sender {
NSAutoreleasePool *fibPool = [[NSAutoreleasePool alloc] init];
    NSString *outputString;
    outputString = @"1: ";
    for (int x=1; x < 11; x++) {
        long long int fibValues[90];
        fibValues[0] = 0;
        fibValues[1] = 1;
        for (int i=2; i < 90; i++) {
            fibValues[i] = fibValues[i-2] + fibValues[i-1];
            NSString *thisFibString;
            thisFibString = [[NSString alloc]
initWithFormat:@"%lli, ",fibValues[i]];
            outputString = [outputString
stringByAppendingFormat:@"%@", thisFibString];
}
        outputString = [outputString
stringByAppendingFormat:@"

%i: ", x+1];
        [outputField setStringValue:outputString];
    }
[fibPool drain];
}

Building the Interface

OK, over to Interface Builder. Add a button to the window and connect it to the startCalculation: action of Fibonacci_FunAppDelegate. Also, add a multiline TextField and connect the outputField outlet of Fibonacci_FunAppDelegate to it. Add Autosizing parameters to enable the TextField to resize when you resize the window and keep the button at the bottom left (refer back to Chapter 3 if you need a refresher on this). Figure 12-1 shows what the interface should look like at this point.

Fibonacci Fun interface

Figure 12-1. Fibonacci Fun interface

Now go back to the Xcode Workspace and choose Build and Run. Click the button, and you should see something like Figure 12-2.

Fibonacci Fun application running

Figure 12-2. Fibonacci Fun application running

Monitoring the Application in Instruments

OK, we have a working application. Let's see how it looks in Instruments. You can actually run your application with Instruments from within Xcode (make sure that you build it first, though, using

Monitoring the Application in Instruments
Running an application with Instruments

Figure 12-3. Running an application with Instruments

As you can see from Figure 12-3, there is a range of analysis tools available within Instruments. You are also able to run Shark from here—you'll be learning more about Shark later in this chapter. For now, Choose the Run Run with Performance Tool Leaks menu item.

You should see the Instruments window come up, together with the window for Fibonacci Fun. A timeline at the top of the window starts recording as soon as it loads, showing that it is monitoring your application. Click on the Leaks instrument at the top left, then position the Fibonacci Fun window so that you can see both. (See Figure 12-4.) Instruments is now monitoring for memory leaks.

The Leaks instrument monitoring the application

Figure 12-4. The Leaks instrument monitoring the application

We'll do a bit of orientation in a moment, but let's first get on with testing the application. You will see that the top timeline (monitoring object allocation) showed a couple of spikes when the application first loaded, then nothing more. The Leaks timeline is clean. Now click the Run calculation button in Fibonacci Fun and wait for a few seconds (the Leaks instrument samples every 10 seconds by default, though you can change that). Aha! What's this? There is a red spike in the timeline and then blue lines. This is showing a memory leak. Click the button again, and you should see the same again. Leave the monitoring to run, and you should see a theme: clicking the button causes a memory leak, then nothing else until you click again. Click the record button at the top left of the Instruments window; the recording will stop and Fibonacci Fun will quit. The Instruments window will look something like Figure 12-5.

Results from recording memory leaks

Figure 12-5. Results from recording memory leaks

Analyzing the Memory Leaks

Now let's take a closer look at the report. The Instruments window is divided broadly into three sections: the list of instruments with their associated timelines, the Leaks Configuration pane, and the Leaked Object View.

The Instruments list shows all of the instruments that you have chosen to use. You can add other instruments from the Library (you can see the Library using the button at the right-hand end of the toolbar). If you have run the Leaks instrument several times, you can see previous runs by clicking the disclosure arrow to the left of the icon in the Instruments list. The timeline shows the points at which Instruments has discovered memory leaks and indicates the total amount of leakage.

The Leaks Configuration pane allows you to set a variety of parameters for your analysis. For example, you can choose the frequency of sampling or do an ad-hoc check at any point.

The Leaked Object View shows the detailed report on the leakage. The default view (Leaked Objects Block View) is not terribly informative, showing the same leak every time (see Figure 12-6). It does indicate that the problem is something to do with a string, but there is not much more to tell than that.

The Leaked Objects Block View

Figure 12-6. The Leaked Objects Block View

Luckily, there is a more useful view. At the bottom of the window, click on the Call Tree View button (second from left). Open up the call tree all the way down—see Figure 12-7.

The Call Tree View

Figure 12-7. The Call Tree View

Here you can track through the application events: the purple- and pink-labeled entries show where the leak has been detected (the figure shows only black and white, but you will see the colors on your display).

So what do you know now? First, the leak is something to do with a string. Second, things start to go awry near a statement containing initWithFormat. So let's take a look back at the code to see what is occurring there. There is only one occurrence of initWithFormat, and that is in the code shown in Listing 12-3:

Example 12-3. Tracking Down the Source of the Leak—Where Are We Using initWithFormat?

for (int i=2; i < 90; i++) {
            fibValues[i] = fibValues[i-2] + fibValues[i-1];
            NSString *thisFibString;
            thisFibString = [[NSString alloc]
initWithFormat:@"%lli, ",fibValues[i]];
            outputString = [outputString
stringByAppendingFormat:@"%@", thisFibString];
        }

Is there a problem with thisFibString? Well, no. It's being created and populated quite correctly. Each time around the loop you are creating a new instance of thisFibString.

OK... the problem is becoming clearer. You are creating these instances but never disposing of them. In Objective-C, good memory management involves keeping track of your objects and making sure that you get rid of them using the release command when they are no longer needed.

But this function uses an NSAutoreleasePool—shouldn't that take care of releasing objects? It turns out that objects that are created using init are not managed by the autorelease pool. So you do have to explicitly release the memory for such objects. There are two things you could do about this. First, you could create the thisFibString object differently, without using initWithFormat. Second, you could explicitly release the object's memory. Let's take the second option.

Modify the code so that it looks like Listing 12-4:

Example 12-4. Releasing the thisFibString Object

-(IBAction)startCalculation:(id)sender {
    NSAutoreleasePool *fibPool = [[NSAutoreleasePool alloc] init];
    NSString *outputString;
    outputString = @"1: ";
    for (int x=1; x < 11; x++) {
        long long int fibValues[90];
        fibValues[0] = 0;
        fibValues[1] = 1;
        for (int i=2; i < 90; i++) {
            fibValues[i] = fibValues[i-2] + fibValues[i-1];
            NSString *thisFibString;
            thisFibString = [[NSString alloc]
initWithFormat:@"%lli, ",fibValues[i]];
            outputString = [outputString
stringByAppendingFormat:@"%@", thisFibString];
            // we're done with thisFibString now, so release
            [thisFibString release];
        }
        outputString = [outputString stringByAppendingFormat:@"

%i: ", x+1];
        [outputField setStringValue:outputString];
    }
[fibPool drain];
}

Tracking File Activity

For the second example of monitoring using Instruments, open up the Text Pal application from Chapter 3. This is a Document-based application that can read and write files.

With the project open, choose Run Run with Performance Tool File Activity. You will see the Instruments window along with the Text Pal program document window (you may also see a password dialog to authorize Instruments to use admin privileges for some instruments). Add some text to the document, save it, close it, read it in again, modify it, and save again. Once you have done this, click on the Record button in the Instruments timeline. Text Pal will close and leave you the Instruments window, which will look like Figure 12-8.

Looking at Text Pal with the File Activity instrument

Figure 12-8. Looking at Text Pal with the File Activity instrument

There is a wealth of information here, but let's just focus on a couple of items. Click on the Directory I/O instrument. First, as with many of the instruments, events are timestamped, so you can get a good idea of the progress of your program. The second item to look at here provides an interesting insight into the process of saving a file. Take a closer look at the three lines highlighted in the figure:

  • Event: 13

  • Caller: +[NSFileWrapper(NSInternal) _temporaryDirectoryURLForWritingToURL:error:]

  • Function: mkdir

  • Path: /private/var/folders/QW/QWESFte3FhWdBYEpGcMlAU+++TI/TemporaryItems/(A Document Being Saved By Text Pal)

  • Event: 14

  • Caller: -[NSFilesystemItemMoveOperation main]

  • Function: rename

  • Path: /private/var/folders/QW/QWESFte3FhWdBYEpGcMlAU+++TI/TemporaryItems/(A Document Being Saved By Text Pal)/Test file 1.txtp

  • To: /Users/ianpiper/Desktop/Test file 1.txtp

  • Event: 15

  • Caller: +[NSFileWrapper(NSInternal) _removeTemporaryDirectoryAtURL:]

  • Function: rmdir

  • Path: /private/var/folders/QW/QWESFte3FhWdBYEpGcMlAU+++TI/TemporaryItems/(A Document Being Saved By Text Pal)

You can see that Text Pal is not just blindly saving the file. First it creates a temporary directory to hold the file (line 13), then it renames (and in the process moves) the file (line 14), then it deletes the temporary folder (line 15).

Activity Monitor

The last aspect of Instruments that I'll cover here is Activity Monitor. This is quite a simple tool and, unlike the previous two, is designed to give information about the system as a whole rather than just the program under analysis. As a result, it is perhaps best used in conjunction with one of the other tools to provide a picture of the system state as you inspect memory or I/O or other aspects of your program. Figure 12-9 shows detail from the Activity Monitor window.

The Activity Monitor instrument

Figure 12-9. The Activity Monitor instrument

The circled section in the timeline shows the impact on the system as the Fibonacci Fun calculation runs.

The nice thing about the Activity Monitor instrument is the amount of information you can get not just about your own application (you can use the search filter at the bottom of the windows to narrow down to the application you are interested in) but also about the system as a whole, and not just at a point but also along a timeline.

What Else Can You Do with Instruments?

Instruments offers quite a lot of useful information about your application and environment that can help you get to the bottom of problems. To see the full range, click on the Library button. Alternatively, you can start up Instruments from the Finder, and then attach it to a running process. Figure 12-10 shows the welcome window.

The Instruments welcome window

Figure 12-10. The Instruments welcome window

Having chosen the tool that you want to use, you can attach it to a currently running process or launch an application (see Figure 12-11):

Choosing a process to monitor with Instruments

Figure 12-11. Choosing a process to monitor with Instruments

In practice, I find that I use Leaks, ObjectAlloc, File Activity, and Activity Monitor, but not much of the rest of Instruments. And I find the easiest way to utilize it is to use the Run menu within Xcode.

As you get more accomplished in your Xcode development, you may well find that you make much more use of this tool. Try it out—it can be very revealing and very helpful in refining your software.

Shark

I may as well nail my colors to the mast—I don't use Shark. It is a venerable application and I'm sure professional Cocoa developers use it all the time, but I personally find it a complex interface with not much useful information (by the way, a significant amount of the functionality of Shark is also available in the Time Profiler instrument, and you use it in much the same way as I describe Shark here).

That disclaimer is out of the way; so, what is Shark? In a nutshell, it's a tool for optimizing performance. You can use Shark to home in on parts of your program that represent performance problems or computational pinch points—you can even get Shark to carry out a static analysis that can suggest better ways to approach particular parts of your program.

To give you a picture of how Shark works, let's look at Fibonacci Fun again. Before doing this, it would be a good idea to load up the program a little more so that we get a better picture of what the program is doing. In startCalculation change the number of iterations in the main loop from 10 to 100 (see the bold code in Listing 12-5).

Example 12-5. Adding More Tterations to the Main Loop in Fibonacci Fun

-(IBAction)startCalculation:(id)sender {
    NSAutoreleasePool *fibPool = [[NSAutoreleasePool alloc] init];
    NSString *outputString;
    outputString = @"1: ";
    for (int x=1; x < 100; x++) {
        long long int fibValues[90];
        fibValues[0] = 0;
        fibValues[1] = 1;
        for (int i=2; i < 90; i++) {
            fibValues[i] = fibValues[i-2] + fibValues[i-1];
            NSString *thisFibString;
            thisFibString = [[NSString alloc]
initWithFormat:@"%lli, ",fibValues[i]];
            outputString = [outputString
stringByAppendingFormat:@"%@", thisFibString];
            // we're done with thisFibString now, so release
            [thisFibString release];
        }
        outputString = [outputString
stringByAppendingFormat:@"

%i: ", x+1];
        [outputField setStringValue:outputString];
    }
[fibPool drain];
}

Having made that change, Build and Run. Once the program is running (but before you click the button) start up Shark. You will see the initial interface window (see Figure 12-12).

Initial interface for Shark

Figure 12-12. Initial interface for Shark

You can choose from a variety of analysis types, ranging from Time Profile for the current application through to an entire System Trace. Leave the selection as Time Profile. Similarly, you can choose to analyze a process or a file or everything (which seems a little draconian to me, and I can't envision when you would choose it!). Leave this popup as Process. If the currently selected process is not Fibonacci Fun then use the final popup to select it. Then click the Start button or press

Initial interface for Shark

The most obvious information in this window is that over 90% of the processing time in Fibonacci Fun is spent on handling text data. Open the disclosure arrow for the highest percentage entry (at the top). You can trace the symbols right back up to the start of the program (see Figure 12-14):

Time Profile view of Fibonacci Fun in Shark

Figure 12-13. Time Profile view of Fibonacci Fun in Shark

Expanding the symbol track for Fibonacci Fun

Figure 12-14. Expanding the symbol track for Fibonacci Fun

You will see that the highest numbers are from NSNewStringByAppendingStrings onward. Again, this is probably not surprising, since the size of the string being handled is steadily increasing during each trip around the inner loop. You can get some more information out of this analysis. If you double-click on the line in the report for the startCalculation: method it opens a new window enabling you to analyze this method. To get the best from this window, you need to turn on the Advanced Settings option (under the Window menu in Shark) and choose the Show Total Column checkbox (it's surprising that this isn't on by default). Once enabled, this option shows you a line-by-line view of the method with a color-coded performance analysis. If appropriate, you will also see comments and code-tuning advice in the columns to the right. (See Figure 12-15.)

More detailed information on the performance breakdown

Figure 12-15. More detailed information on the performance breakdown

Shark also provides a chart interface that shows the same information graphically. You can zoom in the chart to see each sample. (See Figure 12-16.)

Shark Time Profile for Fibonacci Fun

Figure 12-16. Shark Time Profile for Fibonacci Fun

There is a lot more to Shark (the Developer Documentation for it is over 300 pages!) but in my view the average software developer is only rarely going to make use of it. Time spent learning Instruments thoroughly will be more rewarding, especially as some of Shark's functionality is also available in Instruments (take a look at the Time Profile instrument).

BigTop

Unix veterans will instantly recognize BigTop as a graphical version of top. top is a command-line utility that gives you a regularly updated summary of the processes running on your computer. To run it, just type top at the command prompt, and to stop it press Control+C (see Figure 12-17):

The top utility running in the Terminal

Figure 12-17. The top utility running in the Terminal

The key thing about top is that it lives in the moment. It will tell you how things are now—each current process is a row, and each parameter measured is a column—but not how things change over a period of time. That's where BigTop scores.

You can find BigTop in /Development/Utilities. Start it up and you will see a window like Figure 12-18.

The BigTop user interface

Figure 12-18. The BigTop user interface

The subsystem list to the left shows the different parameters that you can measure with BigTop, and you can choose the processes that you want information on using the popup. Figure 12-18 shows the system load across all processes over a 30-minute period.

Let's take a look at Fibonacci Fun running with BigTop. If you didn't make the changes to Fibonacci Fun in Listing 12-5, make them now. Now start up BigTop and choose Fibonacci Fun from the Processes popup. Try clicking the Run calculation button a couple of times. Since you are now running the loop many more times, it will take longer (about eight seconds on my Mac). If you look at the CPU load trace, you will see bursts in CPU activity each time the calculation runs (see Figure 12-19).

Monitoring activity in Fibonacci Fun using BigTop

Figure 12-19. Monitoring activity in Fibonacci Fun using BigTop

That's about it, really. BigTop is a useful tool to keep running while you are testing, particularly if you think your software is performing a little sluggishly. It's worth mentioning that many of the features are also available within Instruments.

Spin Control

The final tool I'll describe in this chapter is Spin Control. It has just one function—to look out for conditions in running programs in which a hang occurs. It then gives an analysis which in principle allows you to see what is going wrong in your application.

Once again, you need to modify the startCalculation method in Fibonacci Fun. To give us a good chance of getting a beachball (the spinning nonresponsive cursor state familiar to most Mac users), raise the number of iterations around the outer loop again, this time to 200. Then Build and Run but don't press the Run calculation button yet. Find Spin Control in /Developer/Applications/Performance Tools and start it up. The interface is unassuming (see Figure 12-20):

The Spin Control interface

Figure 12-20. The Spin Control interface

Once both programs are running (position them side by side so you can monitor them), click the Run calculation button. All being well, after a few seconds you should see a beachball. Straight away Spin Control will show the message "Fibonacci Fun is unresponsive." Some time later, the calculation will finish and the Spin Control message will change to "Fibonacci Fun is responsive again." (See Figure 12-21.)

Fibonacci Fun was unresponsive but is responsive again

Figure 12-21. Fibonacci Fun was unresponsive but is responsive again

From this you can get two reports. One is a text report. This shows a hierarchical call trace, but I don't find this a very helpful view as a developer. It probably is useful in the event of having to submit a bug report for software that you are testing. The other report is the Call Graph View. To get this, select the Sample report line in the Detected Hangs window and click the Open... button. This shows two tabs (see Figure 12-22), giving access to a more useful hierarchical view.

Call Graph View in Spin Control

Figure 12-22. Call Graph View in Spin Control

Take a look at the times in the rightmost column. You can see the steps that are taking the most time—those string handling steps. One other interesting feature in Spin Control is that where you see the little code icon (circled in Figure 12-22) you can double-click to bring up a code view, revealing the location of that step in the original code. As with BigTop, you can get access to similar features to those of Spin Control using Spin Monitor within Instruments.

Summary

Those are the best known of the Xcode tools for program monitoring and analysis. Try them out—you are almost certain to find a use for Instruments, but perhaps less likely to use the others, especially as many features from the other utilities are now available within Instruments.

Actually, while making use of Xcode, Interface Builder, and Instruments, many developers never venture any deeper into the Xcode Developer Tools chest. And that's unfortunate, because there are some gems waiting there to be discovered (and to enhance your life as a developer, into the bargain). Follow me into the next chapter, and you will discover some of them for yourself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.46.58