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: wwdc2000-196
$eventId
ID of event: wwdc2000
$eventContentId
ID of session without event part: 196
$eventShortId
Shortened ID of event: wwdc00
$year
Year of session: 2000
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC00 • Session 196

Debugging Applications on Mac OS X

Tools • 49:47

Learn how to debug Carbon and Cocoa applications in the new Project Builder for Mac OS X, how to use the Metrowerks CodeWarrior source-level debugger under Mac OS X, and advanced debugging techniques using the powerful gdb debugger.

Speakers: Dave Payne, Rab Hagy, Klee Dienes, Ken Ryall

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. It's Friday morning. How many people are awake? It's great to see such a good-sized crowd here on the last day of the conference. I hope you have a good day today. We have a great show this morning. I'd like to bring on Dave Payne, Manager of Developer Tools Applications Group, and he's going to talk about debugging.

Thanks, Jeffrey. So did you all enjoy our campus last night? Nice place, isn't it? Alright, so lots to cover this morning. Let's go ahead and get started. What we're going to cover today is an overview of debugging on Mac OS X in general. And there's a variety of ways to go about it.

We have two integrated development environments at this point, Project Builder and CodeWarrior. So we'll be taking a look at debugging with both of those. First off, we'll dive into Project Builder, take a look at some more detailed stuff than I did in my sessions on Wednesday. Then we'll take a look at some advanced debugging techniques that are really getting down and dirty with gdb, the command line debugger. Then bring Metroworks on stage to take a look at debugging in CodeWarrior Pro 6.

So really, which of these that you use depends on what you're building on Mac OS X. There's a lot of different pieces of software that you can build. And how you debug it depends on how you built it. You can't build the same things with the various sets of tools at the moment. So there are different debugging techniques depending on what you're doing.

So let's first talk about the overall debugging architecture. Both Apple and Metrowerks have worked together to come up with mechanisms that will work with the system. Both Project Builder and CodeWarrior do take advantage of gdb as the common debugging services substrate. gdb handles all the interaction with the Core OS and the interaction with your target application.

In the case of Project Builder, we talk pretty directly to it on Mac OS X itself. In the case of CodeWarrior, Apple and Metrowerks work together to design a mechanism to allow the CodeWarrior IDE to work with debugging just as it always has. Specifically, it's taking advantage of the Metrowerks debugging protocol to go through and talk to a debug knob that we worked on together.

The debug knob handles communication to gdb. So the debug knob runs on the Mac OS X machine with gdb and with your target application. Because it's going through the Metrowerks-- it's actually a remote debugging protocol-- CodeWarrior can work on Mac OS 9 or Mac OS X and do debugging of applications there. But again, which one you use depends on what you're building.

With Carbon applications, you can build them as either Maco or with Pef. For all else in the system, Maco is the primary binary format for the system. It's the native format. It's what we use for everything. So we've built the OS with that since essentially the dawn of time from the base that we developed it from back at Next. So all of our tools are optimized for that, from the development tools on through the performance tools.

So you can get access to the entire system by making Maco applications. So all of the BSD APIs are accessible, all of your frameworks. So Cocoa, all of the Carbon frameworks are actually implemented as Maco frameworks on the system. So you can write all different types of applications as Maco. If you're writing-- say an I/O Kit module, so a device driver or kernel extension, you would do that in Maco as well.

You can use Maco in a Pef application by doing a Maco plugin, a CFPlugin, that loads transparently. You don't have to know that it's Maco, really. So the new project builder IDE fully supports development with Maco. Gdb supports debugging of that. MetroWorks will be working to provide a full Maco development suite as well. So you'll have that option as well in a future release of MetroWorks.

Your other option for Carbon binaries is to develop a PEF binary. That's the native format on Mac OS 9. We've provided a code fragment manager to load PEF containers on Mac OS X to provide transitional support for Carbon applications coming in. You can have-- one of these PEF binaries can actually run on both Mac OS 9 or Mac OS X.

Our preference, actually, is that you use the new bundle packaging scheme to come up with an app package that you can take advantage of localization and everything else from the app package. You can have a Mako binary for Mac OS X and a PEF binary for Mac OS 9 and be double-clickable on each. But PEF alone is an acceptable option. To do that today, you would use CodeWarrior to do that. So Metrowerk has provided full debug and support for CodeWarrior binaries running on-- or Carbon PEF binaries running on Mac OS X.

So again, Metroworks is also working to provide Mako functionality. And in my project builder session on Wednesday, we also demonstrated kind of a bleeding edge functionality of generating a PEF binary from the project builder IDE as well. So we're trying to move in that direction. The goal is that both environments provide all options and give you as much choice as possible. So with that introduction, I'd like to bring on Rab Hagy, debugging engineer for Project Builder. And he'll take you through some of the Project Builder debugging details. Thanks, Dave.

Good morning, everyone. I'll be talking about Project Builder and debugging on Mac OS X and some of the larger issues that you might run into specific to Mac OS X. Project Builder has a number of features for debugging, the most obvious of which is a graphical user interface for displaying threads and stacks and such. In addition, we have features for supporting the so-called Mac OS X platform initiatives, mainly Carbon and its use of opaque data types, and the semantics of the Mac OS X dynamic shared library mechanism.

Uh, Because we encourage the use of frameworks and shared libraries, Project Builder supports cross-project debugging where one project can build the framework and another project can use it. Finally, we have full breakpoint support. We'll be talking about each of these in turn. First let's start off with opaque data types.

Now, if you looked at Carbon or Core Foundation, you know that many of the data types, all you get is a reference. You don't know what the actual structure of the data type is. And as a developer, you can appreciate the advantages to hiding your implementation so you can change it later on. But it's also difficult to debug with opaque data types or abstract data types because you can't look into them. So how can we debug these in the Mac OS X environment? Well, we have to change our approach a little bit.

Typically, a debugger reaches in and looks at the state of data structures, but you can't do that with opaque data types. So what we can do is we can take advantage of the debugging system on Mac OS X and run functions during the debugging session inside the debugged process. And these functions can inspect and provide information about the state of these data types.

Principle 1 and core foundation is CF-SHOW, which gives a textual representation of the CFData types, dictionaries, and strings, and so forth. In Carbon, there are a number of functions specifically there for debugging. Some take as a parameter a data type such as a dialog, a menu, or a window and print out information.

Some just print out information about the application as a whole, like a print resource chain prints all the information about the resources active at the moment. And then there's some in the QuickDraw framework that instead of providing a textual information provide a graphical information such as flashing a clip region or visible region, which actually is a lot easier than looking at numbers representing pixel positions.

So now, let's talk a bit about shared libraries. And we use the term dynamic shared libraries, or DILIBs, on Mac OS X to emphasize the specific semantics of shared libraries on Mac OS X. They come in, well, we say two types, but it's more of two ways of representing on the file system, a standalone shared library or a shared library packaged inside a framework. And if you work with shared libraries, you know that if you have multiple versions of them on your system and you start running your application and debugging with the wrong version, you can waste a lot of time till you come to that realization.

So how do we avoid this problem? The general question we want to explore is that on Mac OS X, shared libraries and frameworks are designed to be installed at a specific location. And how do you do development when you don't want to install them? So let's look at the details of how this process works. At build time, when you build your shared library, part of your configuration information you give to the build system in Project Builder is the path where you're going to install the framework or the shared library. And that path is independent of where you actually build it.

At build time of the executable, the static link editor, LD, takes this install path from the shared library and records it in the executable. So that at runtime for the executable, the dynamic editor then has a very easy way to find the shared library. It's been told, ah, here's the path, where to look for it. So that's very efficient, but during development, there are a couple reasons why you wouldn't want to install your shared library or framework.

The first is, as a developer, you're probably running as your own user ID with permissions that don't allow you to actually install or modify files inside the file system. Usually that's done as root or administrator. The second reason you may not be able to install a shared library is, well, you've already got one there. You've got a stable version of your system, and you as a developer want to develop, move forward on your own set of code. And therefore you want to develop and run and compile with your own copy of the shared library.

So at the lowest level, here's how we can address this. There are various environment variables that control how the dynamic link editor, DYLD, Accesses and Finds Shared Libraries. And if you do end up using these, you should consult the manual page, man-dyld, and find out all you can do with the dynamic link editor.

If you're also going to MacHack, it might provide some interesting tips. If you do do this, be sure to be aware of the gdb command info shared libraries, which prints out all the shared libraries used by the debug process. Once again, we're just trying to save you time from debugging the wrong thing.

If you use Project Builder, we take care of all this for you so that you can just build your projects and click the run button or the debug button and we run with the right versions. And we'll go into detail about how to set up Project Builder to do that.

As a heads up, coming down the road, we're really encouraging using the app packaging model. And one of the things we plan to put into the package are private frameworks used by the application. We're not quite there yet, but the intent is the application will be completely self-contained.

Now that, if you put your shared library inside a framework, inside the packaging, it, not only do you get the advantages of the framework, being able to put your header files there and so forth, but we also have the ability to put more than one copy of a shared library inside the framework.

Typically, these are binary files. The shared libraries implement the same set of API. They're the same version, but they're just compiled differently with different options tailored towards specific operations you might want to do. The standard version of a shared library is optimized code, minimal symbols. That's what's used at runtime and for deployment. The debug version is tailored towards The development process and doing runtime error checking at core foundation and Carbon do extra checking of parameters. They can do internal state checking to ensure consistency that the APIs are used correctly.

And finally, there's a profiled version which is used with the Gprof profiling system. Again, the low level environment variable that controls this is DYLD image suffix, but Project Builder allows you to access these directly. So now we'll talk a bit more in detail about Project Builder itself, go over how to configure things, breakpoints, and then we'll have a demo.

As we've said, Project Builder enables cross-project debugging. And to do that, you need to build your projects, and specifically the products of your project, and have them put in a common location so that we can find them at compile time and at run time. We'll go over in the demo how to do that.

We use the term executable for the thing that you're going to debug. It can be, for right now, it's a binary. It can eventually be Java or it could be some other kind of program. You can set various settings on the executable, such as the arguments, essentially the command line arguments, argv and argc, passed to the program at launch. You can also control choosing the debugger and such.

Just to go over a few things about breakpoints, Project Builder has, of course, standard breakpoints and files at specific locations. There are also ways to set breakpoints on functions, just via the name of the function symbolically. And that's useful if you want to break inside a library function, such as Carbon, and see why this function is being called and what the stack backtrace is.

We have a summary of all the breakpoints in your project, and the key word is here, they're your breakpoints, and each member of the development team has his or her own set of breakpoints that are stored independently, but persistently. So now I'd like to bring up Dave Ewing, who's going to help me with the... there he is, the demo. Dave works on our Java debugger.

All right, if we can have-- all right, demo three. All right. First, let's look at the build preferences in Project Builder. And the key here is just want to show you that there are two settings, which are file system paths, which are where you build. And this is very important to set for cross project debugging and runtime settings so we can find your frameworks. OK.

Now let's look at the Targets tab. A target has many settings, and a target logically has an executable or a set of executables. You can add your own if your project builds a and more. The project builder is a library. Or if you're working with a legacy project, so-called, that uses the Make system, it's an open source project, you can point Project Builder to the executable you want to run. Down at the bottom, you can see three tabs that control the settings for the executable arguments.

Let's go to Debugger just quickly. We know, in most cases, what debugger to use. There are various corner cases. If you're debugging Java and you've got native code and you want to use the gdb debugger, that's what you'd come here and select that. Then there's a Runtime tab.

I should mention that this version of Project Builder is post-DP4. The Runtime tab isn't on there. But it gives you an idea. We can now select which variants of the system framework. Same works to use. Standard, Debugger, Profile. All right. So let's go back to the Arguments tab.

Great. And we've added some arguments there, and let's select the trace and crash option. and we'll run with it. This is appearance sample, it's the standard example. We've just added a few features to look at these arguments. If you see, we go to the slider, and just move the slider a little bit.

We're printing out the value of the slider. The trace option controls that. And if we move the slider all the way to the left, it's too big and we've crashed. Just to note how to read the output here, it says your program is exited with a signal. That's the Unix way of saying there was an error, in this case a bus error, which means we've referenced some memory that we shouldn't have.

All right. Let's quickly look at the breakpoints tab. Just so you know, list the breakpoints in a file. If you double-click on it, it will take you to the file and show you the breakpoint. Good way to navigate. Breakpoints in Project Builder can be enabled or disabled. A disabled breakpoint remains in your persistent set of breakpoints, but it's not used by the debugger. It's a good way to keep breakpoints around but not trip over them every time. Now let's fire up the debugger.

Let's make it a little bigger. And we move the slider this time. Trap on the breakpoints, get a list of threads in the stack we're in and variables. All right, let's step-- Just a little bit to show you. Can you close up the console, Dave, so we can-- there we go. You can see the local item value changed.

Now this is C++ program. There's the implicit this pointer pass to each method. The way data is displayed for C++ objects is each superclass of your... its member data is represented as kind of a pseudo-structure that you can disclose. Now we see there's a F dialog pointer, and if we want to look at that, we say, oops, we don't have the type information for that. That's an opaque type. So how would we go about seeing what information about this particular dialog? So right now, we bring down the console and we can talk to gdb directly. And Dave's going to enter the magic command. We tell gdb to call a function.

In the inferior, so this function is actually going to be executed by the debug process in its address space, and gdb is going to evaluate symbolic arguments like F dialogue and pass, essentially set up the calling call site to call this function. And you can see now we've got some information about the dialogue and Let's just do print resource change just to show kind of some global information that you can get from Carbon.

We're running a little short, so let's just demo the symbolic breakpoint just to show you how that works quickly. So you click New and you get a text field to enter the name of a function that you'd like to break at, and you can just select and some text in your code, paste it in there. And now if we continue. We see that we hit that and can crawl up the stack to find what we want. Okay, I think we're running a little short on time, so thanks Dave.

If we can go back to the slides, please. Just a note about what we're planning in the future. Project Builders are work in progress. We know we have a large constituency and each group has different needs. Some want low-level, some want more high-level. We're planning to do all that in the coming months. Dave's working on Java debugging which is coming along.

So later on we'll be giving you the email list for feedback and we'd really appreciate any feedback on the debugging system and specifically what features you'd uh... like to see in a priority order so we can get into you When you need them. All right. Now I'd like to bring up Klee Dienes. He's our gdb expert.

[Transcript missing]

Once it's connected, we're going to go ahead and set a breakpoint in Open, which is the Unix system call for opening a file. And we're going to go ahead and continue the finder. and Dave is going to do something to cause it to open a file.

And you'll see over in the command window, the finder has stopped and opened. We're going to take a look around, sort of snoop a bit to see where we are, maybe get a backtrace. So you can see we've gone through some chain of C++ calls, as I wouldn't even dream of understanding. We're going to take a look at our registers.

Take a look at our list of threads. Basically, any information you might want about the process is there. I'm not going to go through the whole list of commands, but there's a ton of them. You can, more interestingly, give it some expressions to look at. So you might want to... I guess first we'll be looking at the... You may as well finish. You can give it commands to apply to all commands. In this case, we've gotten a backtrace of all of the threads.

Or you might want to just give it an expression. So you might want to just tell it to print the contents of register 3. And I'll give it to you as an integer, which isn't very useful, so you might tell it to print it in hex. Which really still isn't all that useful. What you really want to do is cast it to a car star and take a look at it that way.

I'm not going to go too far into the expression. Basically, anything that's a legal C expression is also an expression that you can type here, including function calls, commas, anything you feel like. As long as it's not a statement, you're good to go. What I'm going to do a little bit more interesting is, let's say I want to see all files that have been opened by this program. I'm going to set a command on this breakpoint, and what I'm going to do is I'm going to tell it first to not print the breakpoint line every time.

Then I'm going to tell it to print the first argument, which I just happen to know is R3 still. And then I'm going to tell it to just go ahead and continue without stopping for me. And now when I continue... I can see all the files and all their gory detail that's being opened. This is interesting also just because it's highly scriptable, so if you need to get output logs or in some other capacity, you can do that.

You'll notice a lot of those files are not all that meaningful. They're basically an artifact of our HFS implementation. In this case, what I'd like to do is just focus on the plist, on our property list files. So let's add a condition to the breakpoint that lets us filter out everything that does not end in .plist. So if we're just interested to see which files that end in plist are getting opened, we can actually attach a condition. In this case, we're just going to take R3 and we're going to string compare it with .plist.

And if that expression doesn't evaluate true, in this case if it doesn't end in plist, it's going to just skip that breakpoint entirely. Subtitles by the Amara.org community And so now when we continue, Now we just see the plists that are being loaded by the system. Thank you.

And certainly you can come up with more elaborate examples. I'm sure everybody will have their own things that they might want to do with that. And really what this leads into is how to extend gdb. And so as I mentioned before, gdb has a configuration language. It's used by our system to configure gdb with specific user interface things. But it's also something that you can use on your own to extend gdb.

So what I'm going to be showing is basically a small profiling library that we-- basically I had to add to gdb one night about 3:00 in the morning. I was desperate to figure out a profiling problem, desperate to get something to speed up. And I just really needed to know how long individual source lines in my program were taking to execute.

So I genned up a little tiny program. Most of that's documentation that defines two commands. One is mark, which sets a stopwatch for CPU time. And the other is cur, which tells you how much CPU time you've used since the last time you typed mark. Basically it just calls the getRUsage function in the system to tell you how much CPU time has been used. And so what we're going to do is we're going to take a look at a small program that I rigged up that's just basically allocates 64 meg of memory and zeroes it two different ways. One way is the slow way.

The other way is the fast way. And it's not a very interesting program. But when we take a look at it, it's pretty interesting. Look at it. He's just going to load the iProfiling library. So we take a look at main. One more. You'll see it makes a big buffer, zeroes it the fast way, if you can list that.

and then list the zeros at the slow way. And what we'll find is that when we run this, I'm sorry? I've got a break point. Oh, I'd love a break point. Thank you. That would be very good. When we run this, we're going to step forward to where we want to end up.

We're going to step to right before the FAST 0. We're going to set a mark. We're going to step over FAST 0. Step over, zero. We're going to allocate 64 meg, and then we're going to type curr to see how long that took. It should take about half a second. Now we're going to reset the mark.

We're going to step over to the next line. And this one, I'm guessing, will take about two seconds. So, you know, you can just step right through your program, see how long each individual function is taking, real visual way of finding the bottlenecks. And what's interesting about this isn't so much its use as a profiling tool, although it really saved my butt one Sunday night. It's something that you're able to rig up with very little effort just by adding a few commands to gdb.

And I'm sure many of you will find interesting ways to expand gdb in ways appropriate to what you're working on. You know, one thing that surprised me just before we were putting DP4 out was one of our support engineers sent us an email saying, "Hey, I really missed all those max bugs commands, so I rigged up a quick max bug compatibility library." And so, you know, I gave it a try and I even, I tried actually many of the Mac's bugs, most of the Mac's bugs commands that I knew and discovered actually a good number of them work. And again, you know, it's not so much the commands as the fact that you can add these things pretty much however you might like. So with that, I will turn the stage back over to Dave and I thank you very much.

[Transcript missing]

Well, if you were here at our session last year, we talked about debugging on Mac OS X. And at that point, Apple had helped us put together a debug node that let us debug remotely from a Mac OS 9 machine to Mac OS X. And we've been busy since then bringing CodeWarrior to Mac OS X.

And so today, I'd like to talk a little bit about what we've got now. This is all on a beta CD of the Pro 6 tools that we've been handing out here at the show. The actual tools will ship later this year, but the CD has tools that you can start now building and debugging applications on Mac OS X. So what's really new in these tools is that the IDE is fully carbonized. It runs natively on Mac OS X. You can do single machine debugging.

So you can take all of the Carbon PEF apps that you've been running on 9, carbonize them, and use the same debugger on 10 that you've been debugging on 9. We've also done a lot more work in two-machine debugging for this release. Before we did 9 to 9 debugging, and we also did debugging from 9 to 10.

But we've fleshed that out now so you can really do four-way debugging from Mac OS 9 to Mac OS 9, from 9 to 10, from 10 to 9, and also from 10 to 10. So you'll have a lot of options based on what your host and target platform is. And as Dave talked about, the Metrix tools right now build Carbon PEF applications for Mac OS X. Our Maco tools are in development, and you'll see those in later releases.

Right now these are designed to transition your Carbon PEF apps that you've been building on 9 and bring them to 10 and let you debug them the same way. So with that, I'll go to a demo and show you what you can do with the tools that are on the Pro 6 CD.

The first thing I'm going to do is start DebugNub. DebugNub is the piece of debugging technology that Dave and Klee and his team collaborated on with Metroworks to provide a link between the CodeWarrior debug protocols and gdb. With the tools on the CD, you have to start DebugNub manually, but be sure and sign up to the beta program and future releases will do this for you automatically, so you'll have the same seamless debugging experience you have online. So now I'll start the IDE and open up a power plant sample.

Now, if any of you have tried to use the two-machine debugger that we shipped with the 5.3 tools, I'll show you a couple differences in doing remote debugging. The user interface for setting up remote debugging could be kind of complicated with those tools and wasn't very straightforward, so we've done a lot of work on that.

Now, you can create a whole set of address book-style remote connection preferences where you can name specific remote connections, pick the debugger you're going to use, and then enter the specific IP address for them. So, after you create a connection type, In the Remote Connections panel, you can go to your project's target settings.

and and tell it specifically, you can enable remote debugging, and tell it specifically which connection type you're going to work with. Also specify the remote download path, which if you're debugging on Mac OS X needs to be a Unix-style path, and also tell the debugger to launch a remote host application if you're remote debugging a shared library. But for this demo, I'm going to show single machine debugging, so I'll leave that turned off. and go to the power plant sample. And ask it to debug. So what it's doing now is launching your Carbon PEF app and stopping at the first breakpoint.

Let me resize a couple windows. So at this point, you're in the same CodeWarrio debugger that you've been using on 9. You can do all the normal things, step, set breakpoints, look at variables, look at registers, processes, all the same user interfaces here available to you. Breakpoints, run to those, and then we continue running. We have our power plant appearance application. find the source that creates a new window instead of breakpoint here.

And then we stop at that breakpoint. So as you can see here, I'm not going to try and demo all the debugger features in CodeWarrior, because you're probably familiar with them. Really, our news here is just that all those same features are available natively on Mac OS X for single machine debugging.

So that's really the demonstration that all the same things you've been used to doing on 9 can now work on 10 on CodeWarrior. We're also in the future working with Apple to provide an interface to some of the advanced gdb features that Klee came out and talked about.

So we'll be extending CodeWarrior debugging support on 10 to let you do some of the things that gdb makes possible on 10 that weren't really very easy to do on 9. So we'll be extending the user interface to let you do those things from inside Code Warrior as well.

Well, that's it for the CodeWarrior demo. Again, all of the tools I showed you here are available on the Pro 6 beta CD. And that's been available to people at our sessions. We've been handing those out at the show. And again, please get into the beta program, because we'll have new releases over the summer that add additional debugging support for Mac OS X and just sort of polish up the Pro 6 tools. Be sure and also send your feedback to our website and send that to us through the beta program. Now, here's Godfrey again.

Big hands for Ken. Thank you very much, Ken. Okay, so this is just a slide giving you some contact. I repeat the developer tools group email address, so you can contact us with feedback and your ideas. You can always contact me as the technology manager for development tools at Apple, and Ken Ryall, the core debugger engineering lead at Metrowerks.

And with that, we have one more session in the tools track. Immediately after this one in Hall C, we have a feedback forum for the Apple developer tools. Hope to see a good number of you there. Okay, with that, we can bring the house lights up and start Q&A. Can I ask our presenters to come up on stage?