Development Tools • 1:03:16
Learn how to configure, run, and debug Carbon, Cocoa, and Java applications in the Project Builder IDE. Learn advanced features of the GDB debugger and useful techniques for getting the most out of this powerful tool.
Speakers: Dave Payne, Rab Hagy, Dave Ewing, Jim Ingham, Klee Dienes
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
Good morning. It's Friday morning. Have a good time at the party last night? Great. OK, we have a rather full schedule for you today, so I'm not going to take any time. One thing I will remind you is that we have a feedback forum this afternoon. I'll also tell you that at the end of the session, so we'd like as many of you as possible to show up for that. And without further ado, let me introduce Dave Payne, the manager of our developer applications team.
on the podium. Thank you. Good morning. I had a good time at the party last night. So what we're going to take a look at today is mostly concentrating on debugging with Project Builder for both C based languages and for Java. We'll also be getting into some advanced debugging situations with GDB. But before we really dive down into that, we'll also take a look at how to look at applications that just crash on your system, or if they're hung, what can you do about that? So let's just dive right in with that. So on Mac OS 7.8.9 previously, you're familiar with if an application crashes, you can have MaxBug installed on your screen or on your system, and you'll crash and catch the crashes in MaxBug. We do now have a facility in Mac OS X to catch crashes and give you stackback traces and things like that. In order to enable this, you have to go in as an admin user and add a configuration line to the etc.host config file. So the facility is called crash reporter, and if you go in and set crash reporter equals dash yes dash, which is the format that all those lines are in, then you'll automatically get this facility turned on. So what happens then, if you're running the console application, it'll ask you, hey, an application just crashed. Do you want to see the stack back trace at this point, or the crash log, yes or no? There's a little dialogue that pops up there. And if you say yes, then it brings console to the foreground. You've got the console window, but now you've also got a crash log window as well that shows you the stack back trace for all the threads in your application, and it shows you the register state. If you want to look at this sometime from terminal outside of the console application, the file is actually kept in slash var slash vm crash.log. So one thing to be aware of, though, is it just appends the crash information to the bottom of this file, and it retains that across reboots. So you can have a week's worth of crashes stored in your file there, and you fire it up, huh, I thought we should fix that one. Well, that's because you're looking at the start of the file, so you may need to clean it out on occasion. But that can be a really useful facility. You can then have people, you know, if you've got beta testers or whatever, they can enable this on their systems and send the backtraces back to you. Actually, if I could have a quick poll of the audience, I'm considering just automatically enabling this when we install the Developer Tools CD. What do you think of that?
Okay, we'll try and do that for next time around then. That's not happening this time on your CD, but you can do this yourself. 12. So another one is, gee, my app is just hanging. What's up with that? How can I take a look at that? I've been running it. Is there anything I can see? Well, in some of the sessions, you've seen the sampler graphical application for doing CPU monitoring, profiling, what's going on. There's also a command line version of that called sample. From the command line, in a terminal, You could execute sample and then the process ID, and it also understands just the name of your binary as well, if there's only one copy of it running, and then just the number of seconds that you want to sample. So I typically say sample my app for 10 seconds. It tells you where it puts the file into slash temp, and then you can go off and take a look at, again, the stack backtraces to see where your app is spending its time. Again, that can be a very useful thing that you could have people send the samples back to you. If you do perchance ever happen to find any bugs in any of our stuff, it's helpful to attach these crash logs or these sample outputs into any bug reports or emails that you would send in to us to tell us what you might have found with our code as well. There's also a facility with GDB where you can attach to a process and if you've got source code for it, then even though you didn't start it up under the debugger, you can attach to it later. We don't have that wired in through Project Builder at this point, but you can do this. You'll see usages of GDB attached later in the session.
So many of you are familiar with some of the debugging facilities of Project Builder. It's full graphical debugging for C-based languages, so C, Objective-C, C++, and Zem inspires me to say Objective-C++ will be supported there too. And also Java. We support all the different types of Maco binaries that you've got on your system, and we'll see how to do that through the course of this session. For the C-based languages, we'd use GDB as the low-level debugger to communicate with the OS. Architecturally, this is what it looks like here. As Project Builder communicates with GDB, GDB handles all the complexity of working with the operating system. So the Unix P-trace and Mach exceptions and your target application, it's a fairly complex interaction. So it takes care of all that for us. Because that is fairly complex, we also work with the Metrowerks folks to get the CodeWarrior debugging working well as well by handling the same level of interaction and then using a debugging protocol. If you're using CodeWarrior at all, you may have heard that they no longer require the use of the debug knob. That aspect of it is built directly into CodeWarrior now. But at the lower levels, we share the same facilities. So without further ado, I'd like to bring out Rab Hagy, senior engineer on the Project Builder team, to actually show you Project Builder debugging in action. Thanks, Rab. Thanks, Dave.
All right, good morning everyone. In this section of our presentation today, I'll be giving you a bit of a status on where we stand with debugging with Project Builder. Talk about how to configure your project for debugging, how, where, and what of setting up your project. Talk a bit about breakpoints. And then my colleague Dave Ewing will come up and talk about debugging Java.
All right, if you're new to Mac OS X and to Project Builder, welcome. I hope you've seen through our other sessions at the conference that we have a full-featured IDE, and with respect to debugging, we have a nice GUI for showing the threads, stacks, and variables in stack frames, and we have a full-featured breakpoint model and support for debugging multiple projects. If you've been with us for a while, the changes from last year's conference, you won't see too many changes at the user interface level.
We've been concentrating on improving the infrastructure, so we have much better C++ support. We've been tracking changes to Macro S10, and we've improved the way Project Builder communicates with the underlying debugger. And of course, last year we showed just a a demo or a technology demonstration of our Java debugging, and now that's fully part of the IDE. As I said, we've been concentrating on infrastructure, so we still have a lot more to do in terms of features and making common operations a little more easy and efficient for you to get to. And we've gotten lots of good feedback from the project builders' users' email list. So thanks for that.
All right, let's talk about the how of building for debugging. With this release of Project Builder, we've introduced build styles, which were covered in detail in the Project Builder in-depth session. Build styles allow you to configure a single target from multiple uses. And out of the box, you get two build styles, one called development and one for deployment. And we encourage you to set up your development build style tailored towards debugging and your day-to-day development and use the deployment build style for when you're getting ready to actually build your product for shipping. Now, the two key aspects of the build style and building related to debugging are the compiler settings for optimization and generation of debugging symbols. So naturally, to do full source level debugging, you need to ensure that you, in fact, are telling the compiler to generate symbols. And you can also vary the optimization level.
Now, just because you can mix generation of symbols and optimization, you need to be aware that at the higher levels of optimization, the mapping that the symbol information provides a mapping from the machine state to the logical state in your source code. And that mapping begins to break down at higher levels of optimization. So we recommend for debugging either no optimization or optimization level one. And I'll give you-- in our demo, I'll cover that a bit more. All right, here's a quick look at the user interface for the target build settings, just so you can get oriented. I've highlighted the pop-up for the development optimization level and down below the checkbox for including debugger symbols.
All right, now let's talk about the where of building your project. Out of the box, by default, Project Builder builds each project in its own build folder, which contains the intermediate results of the build and the final product of the build. So here we have app project that builds in its own build folder and a framework project that builds in its own build folder. Now most projects that you'll be working with are larger ones, And you'll probably have multiple project builder projects. In that case, when you have a set of related projects that you want to build and debug together, the default is not suitable. So what we recommend is that you create what we call a common build folder on your system. And then you need to configure each project to build in that folder. And this is a per user setting. So each user on the development team needs to configure his or her instance of the project to point to a common build folder. It's a little tricky to find where the setting is. You select the top level item in the groups and file view, which represents the project. and then use the Project Show Info menu.
All right, so now we've talked about how to build, where to build. Let's talk about what gets built. Project Builder has an abstraction called an executable, which is the program which is run or debugged. And it's related to a target, so as you change the target, different targets, the executable that would be run follows that target. Implicitly, or I should say certain target types, ones that create binaries that can be run by the system implicitly create an executable in Project Builder. So most cases for your application and tools, you don't have to do anything. But we've had lots of requests and questions about, well, how do I debug a plug-in or a shared library? And to do that, you can add an external program that's outside of the realm of your Project project, you can add, make an executable that points to that program and then you can run the debugger on that program and that program will load your plug-in and I'll give you a demo of that in a bit.
Executables hold the persistent state that you set of the program, kind of the environment of the program, because commonly when you're debugging you want to run it multiple times and it's convenient to set up command line arguments and environment variables that can control how your programs run. Another aspect of executables is you can set at run time which variant of the Apple standard system libraries or system frameworks, which variant is used. The standard variant, which all apps that are launched from Finder use, has minimal symbol information, a high level of optimization. The debug variant can do additional error checking. It can do consistency checking within the library to try to tell you of problems before they arise. And for example, core foundation provides a lot of verbose output and error checking that might be useful. And finally, there's a profile variant for use with the G-profiling system.
All right, here's a screenshot just to get you oriented on the executable setting. You can see on the left we've selected a target and then gone to the executables tab. There's the add button for adding an external executable and I've highlighted the runtime tab to show you how to change the library variant.
Okay, now just a quick note about breakpoints. We have two kinds of breakpoints in Project Builder. File-oriented breakpoints that you set when you're editing or looking at source code, you can click to the left of the source code and set a breakpoint at a particular line in the file. And symbolic breakpoints, where you enter the name of a function that you want the debugger to stop at when that function is first executed. And this is a good way of working, stopping in functions for which you don't have the source code. All right, now I'm going to give you a quick demo of debugging. Let's see.
I'm going to demonstrate a debugging a plug-in. This is a palette that goes into Interface Builder. That's what Interface Builder calls its plug-ins. And this palette is an example in the developer examples Interface Builder progress view palette. And it's just, it's a UI control for representing a progress view. Just a little demonstration of how to write a palette for IB. Let's do a quick tour of Project Builder as what's related to debugging.
Up here we have the debug tab. Contains the output of the previous run so you can refer to it later. This is a pop-up for the list of threads. We don't, we're not running anything. Stack frames and variables. Here are the control buttons that get enabled when we're actually running. And we have, we have a, we have a, we have a, we have a, we have a breakpoint summary tab to show you all the breakpoints in your project. And let's add-- a new symbolic breakpoint. So you click there and you need to enter the name of the function. For Objective-C programmers, it's sometimes handy to break on NSExceptionRays so you know if an exception gets raised inside your program. And let's see, go back.
All right. Let me show you one thing. We have done a few changes in the user interface. We've added a second little tab up here for the output of the running program's standard input and output. And that appears up here. If you're working at the command line tool level and you want to type something at your program while you're running, you would bring this down and type in there. And we've added a few preferences to let you set the fonts and font color for everything. And due to popular demand, you can set the color of the highlighting of the current line. So I'll change that to a nice shade of blue or something.
And before we start running this program, let's look at its target, which builds-- this is a plug-in. And one of the problems you'll have working with plugins is you build them in Project Builder, and now the program you want to run looks for plugins in specific locations. So how do you get your plugin into that location? Well, there are two approaches, a kind of Unix low-level kind of hacky approach that works very well, is you go to the directory or the folder where the containing application expects to find your plugin and use a symbolic link to point back to your build directory and your built plugin. Another approach is to use-- I've just taken a standard set of build phases that builds our plugin and added a shell script build that copies our built plugin into the directory where, in this case, Interface Builder will be looking for it.
On the build settings for the compiler, we've set optimization level one. We'll see what that does. And right now, you'll notice that the run and debug icons are not highlighted. That's because we don't have an executable. So I'm going to add in-- crawl around and add in developer applications interface builder.
And now our icons are highlighted. I'm going to add an environment variable. It's called doLogin. And this is a good way to take your code. When you're doing development, you want it to be a little more verbose, tell you what's going on. So I've put in an environment variable that will cause this example to do a little logging, generate some status information.
So let's see, we've got a breakpoint, so let's start up the debugger. and give Interface Builder a minute to start up. It's got to find all its plug-ins on the system. It comes up, we'll create just an empty window, drag in our little progress view control. And now when I click on this inner region where the actual progress indicator will be drawn, see over here this inspector has come up tailored towards that. This means that this is our plug-in running now, putting up its own custom inspector. So if I type in some code to change the increment, we hit a breakpoint. Now we're in our code executing in the context of this plugin, or in the context of another application. Let's see. As we step along, you can see that, in this case, we assigned a value to f.
And as variables change their values, they show up in red. We'll step over this. You can see we were asking the environment, do we have this environment variable? Yes, we do, because we're doing development. We'll step along that. And we can see that MSG, our message we're going to print out, has been assigned. The folks at O'Reilly say they've sold out of all the Cocoa, learning Cocoa books. So if you're new to Cocoa with respect to debugging, you need to know that many Cocoa objects are opaque. If you inspect them in the view here, you maybe see just-- that fact that it has an is a pointer, that's a pointer to a class. But that's all you can see. So how do you determine what your Cocoa object is? Well, we can use the power of GDB to send a message to this object, ask it to generate a string description of what it is, and then convert that description to something that can be displayed in GDB. So we use the print object command, or PO, the name of the object. And that's actually invoking code in the inferior, running that code, and returning that into our console here. And fortunately, we have the string we've just set up here. Current increment is 5. All right. So we step along. We call NSLOG. The output of the log, which is going to do the, in this case, interface builder standard out or standard error shows up in our log. We've set it up to have a different font.
or a different color that can be useful. Let's step along. And now I just want to show you a few things about debugging optimized code. here. You can see that we've just assigned a value to an inter, a variable called f at an inter-scope, a nested scope level. We can show variables with the same name at different scope levels. And you can also notice that this obj, which was a parameter into this function changed too. Well, that's because the optimizer was reusing that register.
If you look carefully at the code, you can see we declare both an F and an I at the outermost scope, but when we look in the variable view, we see there's only one I, and that would correspond to this if block, and that's because the outermost scope, the I at the outermost scope has been optimized out.
Let's see. I think, yeah, we've got a full day, so I'm going to stop here. I hope you've got a quick introduction to Project Builder and debugging with it, working with optimized code and plugins. And now-- Let's see if I point it right. I'd like to bring up Dave Ewing to talk about Java debugging.
Thanks, Rob. All right. Okay, well, you actually just saw most of the debugging features in the Java debugger and project builder, too, because they are the same--the user interface is the same. It's shared between the two. So I'm gonna go through just some differences, some things you have to do differently when you're debugging in Java.
So the first thing-- well, the three things I'm going to show in my slides before I do a little demo are setting up a project to debug in Java and what you need to do that's special about that. What you need to do to set up the application and its command line arguments, and a little bit about Java exceptions. Java has a very nice exception model where there's a class, Java lang throwable, and every exception that you can throw has to be a subclass of that. So it actually is a very nice Very nice way to program. So setting up your project for debugging.
The one thing you really want to do for Java debugging is turn on the indexer. In future versions, we'll have that turned on by default. But besides giving browser information-- we have a class browser coming in the next version-- right now you can command-double-click to go between symbols, and you get function pop-ups and things like that. So besides getting that when you turn on indexing, you actually get build dependency support for Java builds. And you also get a few enhancements to the debugger support.
So one thing you may not know about class files and what's produced when you compile a Java source file is that there's a source file name in there in the debugger tables that you need for the debugger. But there's not a full path. You don't know exactly what source file that might be on the disk. And Project Builder can try real hard to figure out which source file you mean, but if you have two files with the same base file name, It can't be sure. If you have indexing on, we can be sure.
And the other thing you get if indexing is on and you have breakpoints set is your debugging speed will be vastly improved. It's about 20% to 30% faster on a small project, and it only gets better for larger projects. So. Okay, Java executables. You can create, you know, a standard Mac OS X application. If you do that, one of the files in your project is an mrjapp.properties file, and inside that file there's a main class setting, and that is the... it's really the name of the class that gets run, so you, you know, when you create a new project, there's a default name there, but you actually may be adding sources and want to change what that is. There's also the class path, which jar files get loaded. Command line arguments actually go into that file as well. For Java tools, tools that are meant to be run from the command line, you basically give it standard command line arguments that you would give to the Java executable, in Java being user bin Java. If you actually have a manifest file in your project, which contains a main class entry, then You would give -jar and then the name of the jar file on the command line. And if you don't have a manifest file, then you would just give the class path for your jar file and the name of the main class on the command line.
OK, so our debugger does a few things to handle Java exceptions nicely. It's not all the way there. We have some UI that we want to wrap around some of this. But by default, we stop when there's an exception thrown that is not caught in your code somewhere. Now that's really cool.
But it turns out that sometimes there's code out there that catches these exceptions that the debugger can't detect. If there's Java native code, meaning like code written in C, they can actually catch exceptions, and the debugger has no way to determine that ahead of time. So you actually might stop in a few places that are a bit of a surprise. So this last point here, there's a console just like a GDB console, in our debugger inside Project Builder, and there's a few commands that help you take care of exceptions when you're debugging. And with that, let's show a few features that are specific to Java.
Oh, what a nice screensaver we have. OK, so when I was trying to come up with a demo here, I actually had this really teeny little demo. And I was thinking, that's kind of boring. I need to do something flashy. So I went out and was looking for a quick time for Java demo. And I couldn't really find anything. All of them have little teeny pictures and kind of boring. So instead of doing something flashy, I'm going to do something geeky. So here I have a little project that uses the Antler parser generator to parse Java source files.
And I don't really want to get too much into how a parser works. I mean, this is a debugging session, not a parser session. But it's a cool little program. We have a main class here that basically just takes some command line arguments. At the bottom, there's a routine called-- somewhere near the bottom, called parse file, that creates a lexer, which tokenizes the source file and creates a parser, which then actually does the parsing. And then we have this other routine, do tree action, which actually creates a swing tree view to look at what the parser parsed. So let's take a quick look at the target for this project.
You can see it compiles a bunch of Java source files, no big deal. In the executable setting, it runs the Java command line tool, and the arguments it passes the class path pointing to the jar file that's built by this project, and -show tree is what causes this particular program to build the swing tree. And then I actually have two more arguments over there. that are source files to parse. It's actually parsing the main class file for this demo. So let's just run that so you can see what it's doing.
It comes up reasonably quickly. At the top level, this is basically the root of the file. There's nine import statements here. There's a class declaration, Java parser demo. Inside that, there's a variable and four methods. Woo wee, right? OK, now I've set a breakpoint. Let's just go ahead and debug.
And we hit the breakpoint here. Now it turns out one of the cool things about this parser, the handler parser, is that when it's looking ahead and it sees something that's confused, it actually throws exceptions back to a-- and those exceptions are caught in a method which can handle the errors. So in order to see where it might be throwing exceptions, we want to turn on the catching of exceptions. So catch all, and we'll continue.
We stopped pretty quickly. We actually stopped in system class loader code. In this case, this is a case where it's actually catching the exceptions in Java code. And we can continue on and see it's just going to keep catching exceptions, catching exceptions. So let's turn that off for a moment.
Not much of a typer, I'm afraid. And let's go on. We should get to the breakpoint here in a second. So one of the things it's doing right now is it's debugging very slowly. And that's something that's actually fixed in the Java patch that should be live today. It uses the 131 VM while you're debugging. So that speeds things up a lot. I actually wanted to get that on here, but we were having other problems configuring these systems, so I didn't bother to get it there. Okay, so we hit the breakpoint. And let's turn on the catching of exceptions now. So I've run through this twice. The first time it went and parsed this file, there were all those class load exceptions, and we didn't want to see that. So the second time I hit this breakpoint, all those classes are loaded, so we're not going to hit exceptions for that anymore. So I can turn on-- just use the up arrow-- non-conceptions again.
And just a moment here. OK, we caught an exception in the routine antler.poster.match. If we look up the stack, we can see we're actually trying to match an identifier on the input. The one thing you'll notice about this is that there's no source code down below. The PC's not showing up there, and that's sort of irritating. Well, it turns out I have another project file that has the Antler source code in it, so let me open that up.
Just need to open it up, don't have to do anything with it. Now if I click on that line, it goes to the code in the other project. This is not a feature that's specific to the Java Debugger. I just wanted to sneak that in, because it's actually one of the cooler things about our debugging system. And you know what? That's actually about all I have here.
All the other standard stuff in the debugger is pretty much the same. You can actually set variables in this version of the debugger for Java. That's coming in the next version for the GDB debugger. But that's it. And with that, we can switch back over to the slides. I actually have the clicker here. I want to bring up Jim Ingham and Cleet Dines. And they're going to talk about the gory details of debugging with GDB. Thanks, Catlin.
So that's fast fingers Klee over there. I'm Jim. So we're going to tell you something about GDB, which is the underlying debugger that both Metrowerks and Project Builder use to run and control applications under debugging in Mac OS X. First of all, a couple of words. They said something about this in the Metrowerks session, too. It got some applause.
Our goals for Project Builder are that if you are a GUI type debugger person, you're used to Metrowerks, things like that, we're not going to make anything that you need to do as a user of debugging require the GDB command line. You'll always be able to get useful features from the Project Builder just straightforwardly. But the other thing is that GDB is actually a pretty powerful environment. And there are some things which, using GDB, you can actually get a lot of leverage of its capabilities. So we also want to make it possible, if you're a GDB user, that you can live in the project builder environment, get advantages of locating sources and stuff like that, and still be able to use the nice features in GDB that, if you've come accustomed to them, you love them or you might learn to love them at some point. We're not all the way there yet. There are some things in the GDB console window that don't work the way they do in the terminal, which is why in our demo we're actually going to be using terminal. But we'll get there. So why would you want to use GDB as opposed to Project Builder or in the context of Project Builder? It's actually a very good low level debugger. It knows everything about your application.
It's been hacked on by people for years and years, both in the embedded environment and in the Unix environment. So there are a lot of features for learning about your program poking around and various things like that. It's the low level debugger that everybody uses. So the features that are going to be in Project Builder will already be in GDB most likely. So if there's something we haven't gotten around to yet in Project Builder, it'll probably be available in GDB. So just if you need something that we haven't gotten around to yet. It's also scriptable. So if there are sort of actions that you want to attach to things that happen in the course of your debugging, you can do that within the GDB command language, which is a really nice feature. And finally, it's extensible. So if you have some special purpose things that you want to do-- and we'll show an example of that a little later on-- it's possible to extend the GDB debugger to do those things for you. It's an open source debugger, so the extensions, you just get the source. We have a plug-in architecture, and you can make commands, but also the sources available to you. You can look at it, see everything that's going on, and so on. So the first thing is what kind of things at this stage are there which we haven't yet gotten into PB, which you might want to use GDB for. We'll give you three examples of that. One is remote debugging, which we certainly intend to get PB able to do, but it doesn't do yet. The other is debugging PEPF binaries, which in all likelihood won't make it into PB, but we'll see. And then the third is kernel debugging.
Another thing that we'd like to do in Project Builder, but that's going to take more work because just the setup for kernel debugging is a little bit grody. So first of all, remote debugging. This is where you're controlling the debugger on one machine. You're actually running the debugger and the application on another machine. We don't have the situation like Metrowerks where they have a little nub. But in any case, the point is that you are not typing, you're not switching the debugger into the foreground or anything like that while you're running the application that you're debugging. So this is good for full screen applications like games or situations where you're trying to debug kind of mouse tracking and stuff like that, where having the debugger come to the foreground is obviously not going to help you figure out what's going on. So the way that you do this is that you would start the application on the remote machine. And you have to do that. You can't actually start an application remotely if it connects to the Windows server, because the Windows server doesn't allow you to do that from a Telnet or SSH session. So you have to go to the machine that's going to run the application, start it up. And then you go to the machine you're going to be debugging from, and you Telnet into the machine, running the application using Telnet, SSH, whatever your favorite thing is. And then you use GDB's attach command. So the way that that works is you say attach, And then you can give the PID, the process ID, of your application, which you can find through top or whatever. But actually, we've made it a little more convenient than that. If you type the application name of your application and then hit Tab, it'll actually go and look at the process list, find your process for you, and fill it in. If there's more than one process of the same name, it'll offer you the choice of-- and you can figure out which process it is. Again, processes are listed sequentially. So if it was launched-- the latest launched one is going to have the highest PID, so you can kind of figure it out that way. And as I say, we have a few more infrastructure things that we have to do in GDB to make this available in Project Builder, but it will be available in Project Builder in the future.
The second thing is using GDB with CarbonPath. So you know, Mako is the preferred format for Mac OS X. That's the one that our tools will support fully and enthusiastically. But there are some cases where, you know, if you're debugging a CarbonPath application, might want to get some features which aren't available in Code Warrior. For instance, up until Code Warrior 7, it didn't know about Mako binary. So if you wanted to set a breakpoint in a function in Mako binary that was called on your behalf, which wasn't, you know, exported to path through one of these vector libs, then the only way to do that was to run your application under GDB instead and set the breakpoint there. So that's kind of the main use for it. I haven't actually played with the new Code Warrior yet, so I don't know that is to do in Code Warrior. Maybe you can do that fine, and this is irrelevant. But also it's nice because you can connect remotely if that's important to you and other such things. The way that you do that is first, as Robert said, if you want to use really any of the Apple tools with Carbon path binaries, then you should build with inline trace back tables in Code Warrior because that records information about the functions such that the debuggers and all the other performance tools find the names of your functions. And then the next thing is that the loader itself is not the thing, the application loader on Mac OS X doesn't actually load CarbonPath binaries. It's actually an application, a Maco application, which is responsible for loading the CarbonPath binaries. So that's this thing called LaunchCFMApp. That's the thing that you have to run GDB on.
And I say long path because if I actually typed it out on the screen, it would actually fill this whole slide. But if you look at the little URL, it's in the URL. So just cut it and paste it. And then when you run, the way that the LaunchCFM application works is that it takes as its first argument the name of the CarbonPath application that you're actually attempting to run. So in GDB, you say run, and then you give it the name of the application that you want to run. And as I say, there's much more details about this in this tips and tricks URL.
So then the other place where you need to use GDB, and Project Builder doesn't support this yet, is kernel extension debugging. This is a two-machine debugging situation. In this case, GDB runs on the machine that you're sitting on, because of course you're debugging the kernel, and GDB is a user space program, so it's really not going to be able to run on the machine. The two machines talk to one another through a protocol called the KDP protocol. You don't need to worry too much about that. The only thing is kind of conceptually what what's going on is that the kernel has a little agent that lives in the kernel and GDB connects to that agent and talks to it with some protocol and that's how the GDB sitting on your desktop machine is able to control the kernel that you're connecting to. The only restriction is that the two machines have to be on the same subnet to connect. When you're running the kernel under GDB, it's just like you're running any other program. There's nothing particularly special going on. You can do backtraces. You can control execution. Everything works just like GB running on a normal application.
And it's kind of hidden to you that you're talking across this protocol. The only thing to keep in mind, though, is that you are talking to a little agent that's living in the kernel. So if you do something really heinous to the kernel in the course of your running your kernel extension or whatever, and that causes the kernel to lock up, then you could very likely kill the little agent, and then GDB is just dead because it has nothing to talk to anymore. What you'll see is it will start reporting timeouts talking to the kernel agent, and that means that you've completely hosed the machine and you don't have to reset. Sorry. And then there's much more information about this in the tutorial.
Okay, so now we're going to just show you a few of the neato reasons why you might want to use GDB even if you have a nice fancy IDE to control sort of the more common aspects of debugging like inspecting variables and stuff. So there are a few features that we'll talk about. One is the expression evaluator. We'll talk about how you can have commands which fire when breakpoints are hit. We'll talk about how to set up conditional breakpoints so that you can have breakpoints only hit when certain things happen. You can take a bunch of actions and can them up into your own user-defined commands to extend the GDB language. And finally, if you're really ambitious, you can write your own extensions to GDB and see and load them in. For all of this, the GDB manual is in the developer package. It's in developer documentation, developer tools, and it's a pretty nice manual. It gives you, you know, tells you about all the GDB commands, has some nice examples, things you might like to do. So if you get interested in this, that's a good place to go for information. So now, Klee is going to, this is Klee Dinas, he's my cohort in GDB things. He's going to show you some cool GDB stuff.
So the first thing is the expression evaluator. Basically, you can call any function that's in your target from GDB. And you just use the syntax of the language that's involved. So we'll use the appearance sample, our favorite little demo. And in this case, what we're going to do is call the print functions that are in Carbon, which help you get access to all of the opaque stuff that you're not supposed to look at anymore. So first of all, there are a bunch of print functions. I can never remember the names of things. And I refuse to remember the names of things if I can remember how to find them. So it's actually info function. Yeah. So there's a gdb command info function. You give it a regular expression. Don't worry about that. And just copy this down. And it'll tell you everything that's there. So there's all the lovely debug print functions you can poke around and use any of them. Let's print the window list. See what's there. Nothing yet. So if we step one step, or next. OK, now we've created a window. So if we look at the window list this time, hey, there's a window.
And so then we can look up and, hey, there's a debug print window. So let's see about that one. So we're going to call debug print window. The first entry is the actual window token. And since it's hex, we have to tell GDB it's hex. So there's all your information about the window. A couple of other things to remember is that the functions that you call do change target state. None of these debug functions do, obviously, unless the guys in the Carbon are pulling some kind of joke on you. But in general, a function will run in your program, which may be good. You may actually want to change target state. But if you call a function that crashes, you'll crash your program. Also, you can call these functions anywhere you use a GDB expression. So you can set variables to the value of a function called, and so on and so forth. So the next thing which is really useful is breakpoint commands. We're going to give a silly little example here where you might want to use it. For this, there are other tools like ObjectAlloc that probably do a better job of it. But so you've got a problem with retains and releases with CF with core foundations. Somebody is over-releasing something, and your program is crashing. What you'd really like to do is know all the times that an object gets retained and all the times that it gets released, some of these are not under your control because they're called on your behalf by various other CF functions. So you need some other way to get when that actually occurs. So what we're going to do is we're going to run-- we built this little string example. If you notice, there's a bug that we release the stir one twice, and that's down at the bottom. That's the bug. So what we want to do is we want to break on the CF retain, break on CF release every time it occurs.
And then when that occurs, we'd like to print out a little information for ourselves so that we don't have to go grub around every time. To do that, you set a command. So you say commands, and then if you don't give an argument, it's the last breakpoint you set is the one that the commands register for. You can also give a breakpoint number. So in this case, what we're doing is we're again calling inferior functions on our behalf. We're getting the retain count.
The little trick is that the arguments that are passed to a function in the PowerPC ABI are stored sequentially starting in register three, register four, five, and so on. So since we know that the cf retain takes one argument, which is the thing that you're retaining, that's got to be in R3. So that's -- we say cf get retain count of $R3. That's just the argument that's passed to cf retain. And then we're going to print some information about what's going on there. And we're going to print the backtrace. So Klee is also -- because he doesn't want to have to type all this stuff in, he's using the source command in GDB. He put this stuff in a file and is just using the source command to bring that file of gdb commands in. So it made a couple breakpoints.
So this is cool now. Every time he continues, we hit a cf retain, cf release call. We print a little information and we can step through our code this way, sort of automatically printing out the stuff that's interesting to us. But if you were in a big program and this was going to happen hundreds of times, you probably wouldn't want to step through each time because that would get really old very quickly. What you might want to do is sort of do an on the fly logging facility instead to log all the calls to retain and release. You can do that also. You can just put a continue command in your breakpoint command and that says hit this breakpoint, do a little bit of logging, print out some stuff and then keep running.
Now I have a little log of all our CF retains and releases. This also is a little bit overkill because, I mean, you know, there might be hundreds of these. This is going to just totally swamp the screen. It would be nice if you could be a little more discriminating about what you are watching. So that's the point of conditional breakpoints. You can tell GDB to stop only when certain conditions are true. The conditions can be basically any expression which is valid in the language that you stop at when you hit that breakpoint. And the condition gets reevaluated every time you hit the breakpoint. So you can basically write some C code, whatever you want, and GDB will evaluate that C code and then either stop or not, based on whether it's true or false. So in this case, what we're gonna do is, again, we're gonna--we want to watch the one that-- we have a suspicion that it's str1 that's the bad guy. So we want to only watch retains and releases of str1. Remember, since the condition's getting evaluated, every time the breakpoint's hit, str1 is no longer in scope, so we have to actually store away str1 so we can use it later on. And GDB allows you to use convenience variables. Any variable that begins with a dollar sign is actually a variable that GDB stashes away on your behalf. So we're storing str1 when we first create it in this watch variable, and then we do the regular stuff. We make our break, and we actually are also including the command in the little file he's going to source in. And then finally we set a condition on this breakpoint. $bpnum is another of these magical convenience variables. It's just the number of the last breakpoint that was set. So if you want to store away a breakpoint number for later use in your commands, you use the bpnum variable. And we're going to break when $R3, again, the argument to the cf retain is equal to this watch variable that we've stored away. So that's cool, nothing printed.
Now we only see the ones for the token that we were interested in. And finally, this would get really old to type after a while. And also, notice that he was able to call a start watch, stop watch, to actually watch just one particular thing. If you were going to do this over and over again, it would be nice to can the set of operations that did the watching and then just reuse them over and over again. For that, you can use what are called user-defined commands in GDB. The syntax-- this is not a particularly useful example. This is just to show the syntax-- is you say define, and then you give any name of the command that you want. If you name it the same as the GDB command, you'll override the GDB command, which may or may not be what you want, probably not.
And then you give any valid GDB commands, and finally the end statement. So the more complicated one that we're using is actually over on the screen there, the start watching command. The other thing about the GDB define commands, You can actually pass arguments to them, and they are stored in convenience variables, $arg0, $arg1, and so on and so forth. So we want to say, start watching in the string we're interested in watching. And that gets stored into this watch variable. And then we've set the breakpoints up in advance, so they're just sitting around for us. Our start watching command just enables those breakpoints. So you can enable and disable breakpoints in GDB just like you can in Project Builder. And finally, we set the conditions on those breakpoints. And then we, for convenience, defined a stopwatching command, so you can say, OK, I'm not interested in watching those things anymore. You just type stopwatching and you're done.
That's convenient. Yeah, that's this. So we define these two commands, start watching and stop watching. And finally, you can put all this in a file, and then there's a gdb source command. You can just say source and give a file name, and all these commands will be sourced into GDB. They'll be available to you. So if you have a bunch of special purpose tasks that you do in GDB, you can make a whole directory full of useful little files and source them in as you need them, and you don't actually have to type stuff.
So the last feature is this allows you to do things in the GDB command language. The one thing to be a little bit aware of is that GDB's command language is not designed to be a high performance interpreter by any means. So if you're doing something really, really complicated, it's probably going to get slow on you. And at that point, you might think about whether you wanted to write an extension to GDB in C, C++, Objective-C. The nice thing is you get access to all of GDB's internal functionality. On the other hand, GDB has a lot of internal functionality, not all of which is terribly well documented. So there's a fairly high learning curve. But if you have a job to do, you've got a job to do. This might help you. The other thing to keep in mind is that GDB is under the GPL. It's an open source piece of software. So if you are thinking about writing GDB plugins which you want to make commercial things, then realize that they're going to have to be distributed under the GPL license as well. If you want to just use it for in-house, It doesn't matter. The GPL allows you to do whatever you want for in-house use, and it only restricts what you do if you're trying to sell or distribute commercially or whatever your software. They're faster, and they have much more capability, much more flexible than GDB. So here's our example. Various people at Apple have MaxBugs wired into their mental processes, and their fingers just type MaxBugs commands. And so the people who are using GDB, a bunch of the guys in our group just couldn't type the GDB commands. They needed the max bug commands. They needed max bug. They needed it. So one of them actually went to the trouble of-- Making MaxBug. So it's got the complete MaxBug command sets. It updates properly. It has all the instruction listing and everything that you've come to love. It's got a nice little help so you can figure out what they all are.
Actually been a lifesaver for some of the people here. The one thing about it is that it's in this user lib exec, gdb, plugins, max bug, and you just source that file in. In the version that's on both GM and the CDs, there was a bug in the file that you source in. A path was set to be wrong. So you just need to go change that if you want to use this. And there's a tech note on the developer website. Please, typing in the address now. That tells you what you need to do. But actually, if you fire it up once, it'll say, can't find this file. And you'll look, and there's bogus stuff in the file, obviously. So it's pretty easy to fix. And with that, I'll bring Dave back up to tell us the last little bits about debugging on Mac OS X.
OK, so we've seen quite a lot of different powerful features here today. If you've been to WWDCs the past couple of years, you've seen me standing up here saying we're committed to delivering great developer tools. It's fun this year to actually be up here saying, well, you've got them now. And you've seen a lot of power here under the covers that there's a lot more we can do in terms of extending what we've got in Project Builder. What we've got there today is very powerful for C-based language debugging, for Java debugging.
But a lot of this capability of GDB, we just need to provide more user interface to it. And we can bring some of this more out into the Project Builder UI as well. So there's a lot of additional improvements that we plan to make in the product for debugging and across Project Builder as a whole. A lot of what we do is guided by the feedback that we get. All of my team monitors what's going on on the projectbuilder-users email list. That's the best way to broadly communicate with us and the entire rest of the community. If there's just feedback you want to send directly to us, actually I should move on to the lists here, there's ways to do that as well. So let me actually bring Godfrey back on stage. I was going to yank his chain this morning and have him introduced under the wrong name like we had done on Tuesday as well. My new name is Jonathan. Jonathan DiGiorgi. Here we are. Well, thank you very much. It's very good to see such a large crowd here on a Friday morning. I pointed to the information resources page. You have the clicker. I have the clicker.
uh... the first the first is our is our master page for all Mac OS ten tools uh... we try to put everything up there we have uh... number of additions to make both third party and internal and we continually develop that we put the the latest things on each of the pages for each for each specific technology uh... below that we have apples master left master mailing list page and the project builder users page we have the almost a thousand users i'm keep waiting like nine forty four last night come on i give me over a thousand So go sign up. We're reaching here.
Roadmap, we have a feedback forum for all the developer tools this afternoon at 3:30 in room J1. Not on the roadmap, but other important sessions that are still technically part of the tools track. Right after this session in A2, we have the Darwin documentation project and HeaderDoc. And also at 2:00, we have the Apple C++ framework session, which talks about the Apple class suites and Mac app. For those people interested in those areas, those are going to be very good sessions. My contact information, if you have any questions, any interests, any needs in tools, you can always contact me at [email protected].
We have the Mac OS X tools feedback mailing list, which actually distributes an incoming email to all of the engineering team. If you have feature enhancements, ideas, we love to hear this stuff. We really want your feedback. That's a great address if you don't necessarily want to send it to the whole community but just the 30 or so of us on that list. The interface builder folks are on there as well and I know everybody to distribute other requests to on that. And also send bug reports in as well. That's how we actually fully keep track of everything.