Tools • 1:01:35
Learn how to configure, run, and debug Carbon, Cocoa, and Java applications using the source-level debugging of Project Builder. This session demonstrates new and advanced features of the GDB debugger along with useful techniques for getting the most out of this powerful tool.
Speakers: Dave Payne, Rab Hagy, Dave Ewing, Ken Ryall, Jim Ingham
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Ladies and gentlemen, please welcome Technology Manager of Development Tools, Godfrey DiGiorgi. and the crowd goes wild. It's Friday afternoon. Been a long week. Been a good week? Excellent. Great to hear it. They said you might be a little tired at this time in the week and that perhaps we should start doing a dance up here, but I think you'd rather see develop how to debug your Mac OS X applications, right? And for that, let me introduce Dave Payne.
I actually really did want to leave the music playing the whole time this time. So I guess if you're still here, 5 o'clock on Friday afternoon to the last day after we've shown you all the APIs and the tools and everything else, you must occasionally have bugs. So I guess we have to deal with that.
So let's talk about what we'll learn here. There's a variety of different approaches to debugging on Mac OS X, standard debuggers and a few other tools as well. In addition, depending on what type of project you're working with, you'll be debugging in different ways potentially. We'll look at debugging with Project Builder. We'll bring our friends from MetroWorks up to talk about debugging with CodeWarrior Pro 8. And we'll get into some advanced debugging with GDB.
So most of you are probably familiar with this, just the overall debugging architecture on Mac OS X. So we've got the two primary IDEs, Project Builder and Code Warrior. Both of those work through GDB to talk to the OS and the target application. The interface to the OS changes over time. So, for example, the Core OS team is adding support for per-thread signals in Jaguar. So we're needing to make changes to GDB to accommodate that. So we provide GDB as an interface for Code Warrior as well.
But before we get into the two IDEs, I'd like to touch on a few other ways that you should be familiar with in addition to just the debuggers themselves. So in many situations, you'll be working along and the application will crash and you weren't running under the debugger.
Shucks! Why did it crash? Or your users don't want to run under the debugger, but it would be great if you could get some information from them as to why the application crashed. So it's really important that you understand how to turn on crash logging in the console application. So hopefully most of you run the console application on occasion.
It's best to not leave debug logs in your code. You can see those in console. You can see system log messages. Perhaps we've left debugging log messages in there that you might want to tell us about. But you can also turn on crash logging in console. So this is the console preferences. You go up to preferences and enable crash reporting.
So when you do that, then each application gets a separate crash log. My app has its own window. Mail.app if it ever crashed, for example. Projectbuilder.app, each gets its own. The latest crash is at the bottom of the window. So if you've got a lot of crash logs in there, you look at the top one, it's like, wait, I thought I fixed that crash. Oh yeah, right, right, the latest one is at the bottom. So you want to go down to the bottom.
Also, not necessarily intuitive, but when you reboot the system, it actually does retain all the previous crash logs, which is usually a good thing. But occasionally you might want to clear these windows to just get the things you care about now. And to be honest with you, I put a path up here for where the crash logs land. To be honest with you, I didn't verify that that's actually quite the correct path. So best of luck. Also, can we start the countdown timer here, please? Great, thanks.
So the crash logs do include a stack backtrace for all of the threads of your application. Most apps on Mac OS X are multi-threaded for you under the covers, at least, even if you don't have your own threads. They show the register state for the thread that did crash. And if you've got a binary that has debug symbols, I believe in Jaguar it should be showing the file and line number in these crash logs as well.
So there's other ways to look at things like what's stomping memory. So I want to point out that in Jaguar, we've added support for hardware watchpoints in GDB. So we use--
[Transcript missing]
So, okay, now I'm running along. My app isn't crashing, but it's hanging. Why is it hanging? A tool that you can use to take a look at this is the sample command line tool.
So you go into a terminal window, run sample with either the name or the process ID of the process and how long you'd like to sample it for, and that'll show the stack backtraces of what's going on in the app at that point. So you don't need to recompile the binary for any of this. It works just by sampling the application. So you can see some examples of how to call it.
It puts the output log into slash temp, and again, this is something that your users can run. You can also do this from a remote system. You can SSH into your system. Let's say that you've got a full-screen game that, you know, normally it's working. It's running really fast, but suddenly now it's hanging. You can do a sample from a remote terminal to take a look at that.
You can also do an attach with GDB and actually dynamically go into the debugger and take a look at what's going on with the application at that point. So that's sort of my overview of alternatives to the actual debuggers themselves. Now I'd like to bring on Rab Hagy from the Project Builder debugging team to talk about some of the new features of Project Builder debugging. Thank you, Dave.
Hello. In this section of our presentation today, I'll be talking about debugging with Project Builder, give you a brief overview, talk about how to set up your project to get the most out of your debugging, and then talk a bit about some of our work with displaying opaque data types, and also present some new features that are post your Jaguar CD.
Project Builder, the IDE, provides a generalized framework for debugging. It has a user interface that presents threads and stacks and variable views and so forth. It helps manage your breakpoints and has support for cross-project debugging. And within that framework, we have three plug-ins for specific languages. We have an AppleScript debugger, a Java debugger, which incidentally is not the same as JDB. It's our own debugger built from the ground up and uses the Java VM wire protocol. And for the C languages, the C, Objective-C, C++, Objective-C++, we use the GDB debugger.
Now, in the other sessions earlier in the week, the compiler session and the delivering with Project Builder session, we talked a lot about how to use Project Builder to control your compiler options, how to set up your project. With regards to debugging, there are two key areas I'd like to point out.
The first is if you're building or working with multiple projects that are related, an app project and another project that builds a shared library, you want to create a common folder for them to build into. This solves issues at both build time, so that everything's found, and at run time, so that we can run your newly built program. The way to set this, it's a... a setting on the project itself, and it's--you use the show info command, and we'll show you how to do that in our demo in a moment.
The second point is about compiler settings, especially for the C compiler. Of course, you need to enable the generation of symbolic information. And in the other sessions, we've talked about what is the appropriate optimization level to use for delivering your final project. But during development, when you're bringing up your project, we recommend using optimization level zero so that your debugging experience is the best. We present the most complete information. Now, here's a tip. If you're working with build settings, target settings, look both at the build style and the target settings to understand what--in this case, what optimization level is in use.
[Transcript missing]
Finally, you can create custom executables, which is a way to reference a program not built by Project Builder, or at least your particular project. And this is very useful for working with plug-ins, where you have your project builds the plug-in, and then you've got a program over here that's going to run and load your plug-in when you create a custom executable to reference that program. We also use custom executables within Project Builder to help run some of the Java programs. We use that to set up, for example, to run Applet Viewer for Java Applet Target.
Now I'd like to bring up Dave Ewing. Dave's our Java and indexing expert. And he'll be showing us some of these... Is that what I do? Yeah. He'll be showing us some of configuration issues that we talked about with a Java servlet program. But the points are applicable for C programs, too.
That's right. Well, the first thing I'm gonna show you is how you set up your build directory for your project. As Rab mentioned earlier, this is often used if you have more than one thing that you need to build into the same location so that they can build correctly together and run correctly together.
So I'm gonna bring up contextual menu here just to do the show info command. In this particular project, it's a Java servlet. And for the uninitiated, a Java servlet is like a CGI. It's something that runs in a web server, often called a servlet container in this world.
I'm gonna run this particular program inside the Tomcat servlet container. And in order to do that, I'm gonna actually build it into the Tomcat web apps directory here. And since I don't want to pollute that build directory, I'm gonna put all my intermediates inside the project folder in this other intermediates directory.
Okay, so, also as Rab said, When you're building for debugging versus building for deployment, you need to make sure your compiler options are set correctly for each of those. And the key thing to take home here is, so we have generate debugging symbols, pretend this is a native project here.
We have it turned on here, and we have the optimization level set here to 1. But we also have build styles, and if I go and edit the active build style... You can see we override the optimization setting there. It says -0, 0.
[Transcript missing]
Okay, the next thing I want to show you is how I set up this executable. So I'm going to edit the active executable here.
And Tomcat, when it's run, basically starts up with a shell script, so I'm going to execute that shell script. And it takes an argument. It's just the word "run." Tomcat can be run in a number of different modes, including in a debug mode where it launches JDB, the command line debugger that comes with the JDK, with the Java development kit.
And this particular application also needs some environment variables set so that it knows where to find things on the disk. So I have some of those set up as well. And the last thing down here to show is that I've got it set up to use the Java debugger in this case. So I'm going to do a quickie little demo showing that the Java debugger is actually hooked up in Project Builder too. So let's go ahead and say debug.
[Transcript missing]
All that sort of stuff. There aren't any local variables in this particular routine, which is a little boring. But if I just hit continue, we can go over here and it's a hello world sort of applet or servlet. All right. Thank you, Dave.
Let's talk a bit about opaque data types. If you've worked with Mac OS X at all, you'll no doubt run into the fact that most of the low-level system libraries or interfaces have opaque data types. That is, types without an exposed implementation structure. And you can't really tell how these types store information or... And not only can you not tell, but the debugger itself can't tell because there's no symbolic information. Fortunately, most of these types have a functional interface, an accessor method that will pull information out of the type.
Our plan and what we've started on, you'll see the beginnings of this in your project builder 2.0 beta CD or on the Jaguar one, is to use the functional interfaces to pull information out of these types and then to show that in the debugger. And the first step we've done is with the Objective-C NSString. It's probably the most complicated and helped-- helped pave the way for future work.
Now, if you know in a--in a debugging situation, when--when it's not always the case that your objects or your pointers are valid, and as a--as a result of showing NSStrings, we also developed some technology to actually validate the pointer to the string object so that we don't invoke these functions until we know that there's a very, very good chance that it is a true Objective-C object.
Now, this is in your, in the CDs that were handed out. Now I'd like to talk a bit about some future work that we want to show that we are going to try very hard to get into Jaguar. And it's, the first new feature is an expression window that allows you to enter an arbitrary C expression and have that displayed in a variable view.
This is useful for showing globals or dereferencing pointers. The expression is evaluated in the current context specified in the user interface, meaning the frame you're looking at, we use that scope information to evaluate the expression. Second new feature is the ability to take a single variable or a line in the variable view and put that out as a separate window. And then a few other new features that we'll show. So, let's go back to the demo.
So this is a simple Objective-C program. It just allocates a couple NSStrings. And you can see that the constant string, A string, we pull out its value. The... Yay! The next string, str, is a-- it's a random value. And I'm gonna assign it to even a known not-correct value. And you see we say invalid up there. Let you know that that pointer is not valid. We'll step along here.
Just one word on the interface. There's a new column called Summary. That's--the intent is where we'll put information to summarize a type or an object of a given type. And the first thing we're putting there is the strings. So we're about to allocate this new string. And... We got the name of our host. And then we'll step one more and release the string.
And that, Objective-C runtime, marks that as being an invalid object. So that could be handy. In the next line, we have a global called glob. And we'd like to see its value. So the easiest way is to bring up a new window expression, and you can type-- Ah.
Yes. Made it a simple name so that I could type it easily and I still can't type. So if we step now, we'll do an assignment to that. And yay, we updated its value. Let's take AP, Dave, and put that in the expression. AP--and let's show the types over here in the expression window.
We've added a few new things in the context menu you want to check out. So we have the type, and in this frame, AP is an integer. And when we step down, we see that AP changes to a pointer to this little structure. So expressions are evaluated in the context that you've selected over here. Let's see. Step some more.
All right, let's go back up to the context menu, Dave, in the main view there. And... You can--let's just pull that out. Oh, yeah, we've added formatting, finally, for variable types. Yay. And this is a pointer, so let's do the view as. And that takes the address--or the contents of that, and we'll change it to view it as an integer.
and that comes up there and since we know the first First, four bytes are an integer. Now when we step... and the rest of the team. So, um... I think that'll... man: Well, we've got one other little thing here. Oh, yeah, one more thing. So you can put an expression into the expression windows. Well, the expression can actually contain a function call.
And this function returns a C string, which is kind of your own way to summarize this type. So as we step... will see that A changes, and we've fetched and got a new string. So that's a--you know, we might find that useful to call functions each time you step. Okay. All right, we're done with our part, and now I'd like to bring up Ken Ryall from MetroWorks.
[Transcript missing]
Okay. Sorry about that. All right. We usually collaborate better than that. Yeah. Yeah. Yeah, we work better together on code than the projector. So I'm gonna talk a little bit about Code Warrior, the Code Warrior Mac tools for Pro 8, which we talked a bit about in our session on Tuesday, and we're going to be shipping around the end of this month.
They're in beta right now. When we were here last year, we had Code Warrior Pro 7, and that shipped, and we've had a lot of experience using that in the last year. And so we had a lot of customers carbonize their apps, bring their apps to Mac OS X with it, and we learned a lot by doing that.
We--you know, debugging worked fairly well, but once you run, like, the code from large apps like Microsoft Office or applications like Adobe's InDesign, which loads, like, 200 plug-ins on startup through there, and a lot of new things people were doing, like writing CFM applications that loaded, you know, Maco code and bundles, that sort of thing, we learned a lot about what our debugger needed to do better on Mac OS X.
So for Pro 8, we started by looking at performance improvements. And as Dave talked about earlier, the Code Warrior debugger on Mac OS X is based on top of GDB. GDB provides all the lower-level debug services that we use in our debugger. So we talked to Dave, and the GDB engineers, and worked together with them to figure out, you know, performance bottlenecks, to find better ways Code Warrior could talk to GDB and interact with it in different ways. So we collaborated on a lot of improvements.
So I'm happy to say the debugger in Pro 8 is quite a bit faster, both with application startup, for stepping, that sort of thing. So having said that, it's not as fast as we'd like for it to be. And we've heard that as well. So we're going to keep working on additional improvements and keep working with both the folks at Apple and all of our customers to make it even faster.
One of the things that does affect debugging quite a bit is that Pro 8 has a new Maco linker that generates smaller symbolics, and that helps our debugger start up faster and helps performance as well. So if you used our Maco tools in Pro 7, I'd really encourage you to use them in Pro 8. I think you'll have a better debugging experience.
There are some other things we added, because we were kind of frustrated with coming to WWDC last year and showing basically the same debugger we had running on 9, only, you know, basically with all the same features. So we've added things like smart variable formatting for helping you view additional data types and some types that are more opaque in Carbon on 10. And then we did something that-- people have asked for for a long time. CodeWare has a plug-in architecture that lets you add additional plug-in windows and things.
And we--you could create all these, but we didn't really have an interface to let you hook those up to the debugger or look at debugger data. So we've done that now so that you can do custom data types in the debugger and write your own plug-ins to view your data in specific ways for your application.
So the rest of the features are really focused on productivity and performance. We looked at things in the user interface that we needed to smooth out. So we have an improved memory window and register browser, especially for people doing debugging multiple-- apps that have a lot of threads and need to look at memory from cross-platform sources.
And the other thing we wanted to add were event points. And these are like breakpoints, except they do more than just stop. So I'll show those to you in a demo here. But we have five-- a log point, a pause point, a skip point, a script point, and a sound point. And they'll all make more sense if I just show them to you.
So here I've got a debugging session started with the Code Warrior Pro 8 tools. And this is just a simple Carbon Power Plan app. So I'll stop at a break point. And this function has a lot of variables. And the main thing they have in common is that you really can't tell very much about them. All you really see is hex addresses for things like STL strings and vectors and rectangles and that sort of thing. So one of the things we've added is a variable viewer formatter plug-in that lets you write XML descriptions of how you'd really like those variables displayed.
And if you turn on smart variable formatting, then you'll see the results of that. These all have any entries for things like, you know, pictures and rectangles. You can see for the string. I can see the value for the string, the size of the vector, the name of the file spec, that sort of thing. So that's helpful.
But we also wanted to provide a way to write custom viewers so that if I double-click on something, it doesn't just show me, you know, the hex data or the value of it. You can actually provide a custom user interface to show the actual data behind that variable and see what it really is. Thanks. I was actually there about a month and a half ago, so.
So we started looking at viewing some opaque data types as well. This is a Carbon window, which is normally completely opaque. And what we do is if your application links against a little library we provide, it can go out and run some code in your process and come back and give us information about the window. And then we can do the same thing with like FS specs and FS refs to where instead of just being a bunch of opaque data, you can actually see the path that the file reference actually refers to.
So now I'll clear this breakpoint and keep running and show you a couple of the event points. These are designed mostly to solve problems You have debugging things where you are trying to debug things in the user interface, and if you stop to make a context switch in the debugger, it would ruin the thing you're trying to debug.
So, for instance, in this case, I'd like to debug my menu-enabling code and have it tell me when I'm actually calling this routine to enable the cut or clear menu. But if I actually set a breakpoint there and stopped, it would ruin it because I would switch to the debugger and then... So what it's going to do is it's going to play... It's supposed to be playing sound.
Okay, sorry. It's been a long week. So now I can annoy you with sounds like this. And that verifies that the code in this is actually being called. Now, in another case, I want to resize this window and see what's actually going on behind the scenes. But again, if I stop the debugger each time, it's going to ruin it for me. So I'll set something called a log point. And this is sort of like an interactive printf statement that you can just sort of insert in your code. So in this case, I want to know the width of the window.
So this should calculate the width of the incoming rectangle for the window. I can tell it to treat it as an expression. And then in the debugger's log window, every time I resize the window, it'll tell me the new size. 433. and all that without going to the speech control panel. So I created a couple more windows and now it's throwing this assert because the old code said I couldn't have more than three windows.
Well, this is annoying, but I don't want to stop and take out the assert and recompile it. So what I'm going to do is find the assert and set a skip point. What a skip point does is it skips over that line of code and now Now I can continue working without running into the assert any longer.
So those are some of the event points in Pro 8. There are some other ones too. There's a pause point, and all it does is pause execution long enough for the debugger to update all the user interface. So if you just want to monitor something going on without actually stopping, you can do that.
And the other is a script point that will go out and execute an application or an Apple script whenever you hit that break point. So that's my demo of the debugger features in Code Warrior Pro 8. And I'd like to really thank Dave and his team, the GDB guys, of helping us provide a good debugger for you on Mac OS X.
Let's see if I can get anything else wrong. Jim? Jim Ingham is next, but I left the clicker over here. Oh, but you can tell me how to work it, because I have no idea either. All right. The key part is to point it straight into that thing. Okay, okay. Good. So we're not overwhelmed by technology.
Great, so that's really cool stuff. So, and I'm going to tell you about boring stuff now, because what we're going to tell you is the low-level stuff, which is sort of at the level of desperation, and then you want to go, and you don't have anything pretty to do anymore. You're going to have to get down at the lowest level of stuff.
So first though, we said this last year and just reiterate it because it's still true. I mean, in terms of the Project Builder and the whole Apple development environment that we work with, although we provide these really low-level, grody command-line GDB features, I mean, overall, we understand that you don't want to have to use those kinds of interfaces when you're worrying actually about your code and how it's working and how your application's working and so on.
So we want to bring as much as possible up into the UI and make it all available to you in some really nice way. On the other hand, there are certain classes of folks who really, really like typing and really, really like that interface, and we still want to maintain connection with all of that. And if you've been a Unix developer for years, you've got to love it. You know, it comes out of your fingers without you having to think about it. And we really do want to keep that experience for our folks as well.
So, you know, even if Project Builder reaches the stage where it has everything possible you could want, you still might want to use GDB. There are some reasons for that. And then in its current state, there are reasons. So, one, even with a beautiful UI, it's a good low-level debugger, and sometimes the kinds of queries you can make with a command interface can be more precise than you can make with a UI. So it's good to have it there for that.
In the current state of things, it's good to have it there because, you know, there's stuff that we haven't gotten into. We haven't gotten into the UI yet. But GDB's been around for ages and probably can do it in some hairy way or other. So it's there for that.
There are also certain things about having a command line that has some control constructs and stuff like that, which allow you to do sort of investigations on the fly. And so it's useful for that. And then finally, we have made the debugger extensible. There's the built-in features that come with the normal GDB user commands that you can do to kind of do ham-fisted, ways of doing things like the really neat formatted data stuff that they showed in CodeWarrior.
And then we also have a C-level plug-in, too, which allows you to get at all the information that GDB knows about your program. So you could do whatever horrible thing you can think of. And the documentation for all this is in the reference on your... the installation on your system, Jaguar.
Okay, so before that, just as a little plug, we have the net version of GDB, but then we've added a lot to it. So for those coming from Unix, there are things that you won't recognize, and maybe you like them or whatever. So this is the little list of that, and also toot our own horn or whatever. So the big one is Objective-C support, which is not in the net GDB, but that's all done by Apple. A lot of this stuff we're working its way slowly back into the net GDB, but that's a long negotiating process.
The other things we've added are some better shared library support. So there's a future break command so that you can say, you know, break if you ever see a symbol looking like this in any code that gets loaded at any point in time, which is useful. And then there's, this is like the propeller head command. There's a shared library, set shared library.
This is the command that you would use if you want to say, like, here's a library which I don't care about, please don't pay any attention to it. And this is mostly used by... both Code Warrior and by Project Builder, because they can have some smarts about what they should and should not pay attention to, but if you wanted to, you could use that.
There's a save breakpoints command that was added. This is mostly useful if you're not working in the Project Builder environment, because Project Builder actually already saves all your breakpoints. But if you're a real geek and you like GDB, it's cool to be able to save all your breakpoints and then restore them.
So that's there. And then the last thing is that we did add this plug-in mechanism, so you can actually write C code, and there's a GDB command line command to load that code into the inferior, and then it can, I mean into GDB rather, and it can have access to all of GDB's knowledge, so you can get any information about the inferior and do whatever you want with it.
So it's written in C, C++, Objective-C, whatever you want. Again, since it's a compiled code, you can do investigations into memory and stuff like that in very fast and flexible ways. Of course, you know it's C. You can do everything in C code, so you can crash GDB with it, but that's the risk you take. Also, we track NetGDB, and one of the things we're really trying to do is keep up with NetGDB.
So if somebody out on the Net decides that some structure should change in size, we're going to go, "Okay, it changes in size," and then your plug-in's going to break and you'll have to recompile it. But it's really important for us to keep up with the Net efforts, so we're going to continue doing that.
Sorry. Also, you're linking code into GDB, and you're going to have to do a lot of work to do that. So you're going to have to do a lot of work to do that. So you're linking code into GDB, so it has to have the same license that GDB has, or you'll be breaking the license agreement for using GDB, so that's a GPL.
So that won't matter if you write plug-ins to use in-house, because, you know, the GPL says whoever you give it to has to give the--I mean, has to have the source, too. If it's in-house, you know, that means that they have to be able to see the server that has the source on it, no other implications. But if you distribute it to other companies, you will have to give them the source. So whatever.
And the most psycho example of this is the MaxBug plug-in, which is available on your system, which is a really scary experience, but you should do it just for whatever fun's sake. If you load the MaxBug plug-in and then type mb, you'll see in a terminal window what looks exactly like MaxBug, except it's like a source line version of MaxBug, so it also has the source. It's psycho, anyway.
Okay, so just a couple of things about stuff that we haven't yet managed to hoist into Project Builder, but which might be interesting for you guys to do. That's the next section. So those sorts of things, the three main ones are, one is remote debugging, one is how to get to watch points, because we did that kind of late and we haven't got it into Project Builder yet, and one is kernel debugging.
So remote debugging is just running the, controlling the debugger from a window which is hosted by another computer so that if you have a game running or somewhere where switching foreground and background in order to run the debugging session is going to make the debugging session impossible, then it's nice to be able to type stuff at the debugger or whatever you're going to do on another machine which has a different way of debugging. Windows server.
The way that you do that is simple. You know, you start up the application that you want to target on the remote machine. Then you go over to the machine you're going to debug from, and in a terminal window, you SSH into the remote machine, start up GDB, and attach to the application.
One little tiny thing that the Apple guys have added-- they did this a while ago-- which is not in NetGDB, is when you want to attach to another application, you don't have to go through the bother of looking up its PID. If you actually type "application name" and hit a tab, there is a PID completer that'll look up all the PIDs with that application name. That's just a little convenience. And we want to do this in Project Builder, of course, 'cause it'll be much more convenient to see your sources.
So we do have a watchpoint support now. It's not the one, I mean, it's sort of hardware watchpoint support, right? It's not the hardware register, which is the memory register, because they won't let us have that in user space. But instead, it's a page protection watchpoint mechanism. In terms of speed, that's still pretty good, unless you happen to have a page which you're making, like, thousands of accesses per second, and your variable happens to be on that page. The performance will be fine. The command to access it is the gdb watch command.
You say watch, and then you give any expression or a raw address. If it's an expression, GDB will figure out what memory or memory-- multiple, you know, memory access. Like, if it's a pointer that points down to something, it'll watch both the pointer and the thing it points to. So if either of those change, it'll tell you, and so on and so forth. It's pretty smart about that.
There's one--or a couple big caveats. The first one is that it doesn't work well for stack objects. I think this is probably just bugs, but we haven't figured it out yet. But something gets really upset if you try to protect a page on the stack. So that doesn't work yet. And then the other thing is that it's unusual to actually want to watch, you know, a stack local variable.
So I haven't found that to be much of a problem. And then the other one, which is a little bit more serious, is that we use a page protection scheme, and at this point, there are no sort of special debugger page watch modes, so we just use, you know, the ordinary write protection.
And the problem with that is that the behavior of the system, when a buffer that's powered by a buffer is passed into a kernel call to be written on, is write protected, is that the kernel just fails the system call and immediately exits with E and val or something like that.
And there's--in the future, we'll fix that, because we have a bug on the kernel guys to give us a better interface to watching these pages. And they say, "Yeah, yeah, yeah, "but it's not gonna be done in time for Jaguar." So the symptom of the behavior to look out for is, you know, if you are running and watching a variable, and all of a sudden something like a read thing or a system call starts failing for no apparent reason, then it's probably because it's on the same page as the variable you're watching.
And there's not really much you can do about that unless you can manage to sort of move the variable off that page. And if it's a memory stomping bug, you're probably not in a good shape, 'cause, you know, you'll move it, and then the bug won't happen and so on and so on. That's the limitation.
So then the next topic is kernel debugging. So this is the way that you debug keks or, you know, if you're a Darwin developer and you want to--or you're just interested in poking around at what the kernel's doing, this is how you would do kernel debugging. It's a two-machine solution.
There is a stub, an agent in the kernel, which if you do command-power-button, or there's--and on the Darwin site, there's instructions also for how to write a little user-level program that'll drop you down into the kernel debugging stub. And so then the target machine is sitting there, and the little kernel monitor is waiting.
And then on another machine, you start up GDB, and you tell it to attach to the machine that has the kernel stub waiting for it. And then they carry on this conversation, and that's how you control the machine that you're kernel debugging. When you're sitting on the machine that you have GDB running on, you don't know that you're talking to the kernel that's controlling the whole machine. It just looks like an ordinary program, as far as you're concerned. You're just looking at the variables and looking at the stack.
There's no different experience there. There are a couple things, of course, you know. If you actually happen to really, really lock up the kernel, then you're gonna lock up the debugging agent, too, and then it won't talk to you anymore, and you'll get a KDP timeout, and things will go bad. And there's really nothing much you can do about that. And then your little piece of knowledge that you got from that was, "Wow, I really managed to mess the kernel up," so... I don't know.
The couple of things, you used to be able to have to specify the IP address only by number, but we fixed the kernel, the KDP protocol, so that it does ARP, or the kernel monitor stub, rather, so it does ARP requests now, so that you can actually go through routers and use names and so on and so forth.
So that's a little bit more convenient. And then the other thing which we've added to the KDP is the ability to detach from a session and then reattach from another machine. And that can be useful, you know, if you were the first person who attached when the machine was panicked or when your kex was doing something funny, and you were just completely confused, you had no idea what was going on, and you wanted Fred to look at it, but you didn't want Fred to come and take your machine and have to sit in front of it for hours, then you could detach and then Fred can attach from another machine. And that's a little bit more convenient. And then continue the debugging session. So that's sometimes convenient. And the good resource for the kernel debugging in kex is this, on your system, the HTML page that's there.
So kind of the past couple of years, and we say advanced features, this is really the little tidbit of something kind of cool and gross you can do in GDB. That's a better name for this section. Last year we showed using breakpoint commands to do kind of a ham-fisted version of the really great stuff that the event points that they were showing in Code Warrior. And so this year I'll do something a little different.
So I want to talk about the expression evaluator first of all, 'cause when using GDB that's the thing that you use most often to look at what's going on in your program. I want to talk about the fact that there is a teeny, teeny, teeny, tiny little interpretive language sitting in GDB.
Don't stress it too hard or it will fall over and kick its feet in the air, but it can be useful for some things. And then once you've gotten something really clever that you've managed to do, you can actually make user-defined commands to encapsulate that knowledge and not have to type it in again and get it wrong and so on.
So the first thing is the expression evaluator. When you're in GDB, pretty much any of the commands that take something that you can operate on, you know, print commands or call commands or the thing that you pass to a break point, to the break command or the thing you pass to the watch command, all that goes through an expression evaluator to figure out what thing you wanted to pay attention to.
The expression evaluator can call functions. If you call functions, the functions change state. It basically just evaluates whatever string you gave it as though it were an expression being compiled in the current source language, that is the source language of the currently selected frame. So, you know, as you go up and down the frame, you might change from C++ to C to Objective-C. And depending on the scope that you're currently focused on, it'll evaluate it there.
One thing that's kind of convenient is that you can store away the results of a given expression in a convenience variable and then refer to it later. So these are the evaluated results. So, like, if you're like me, you can never remember anything for more than five minutes.
You know, you can say, like, the second line there at the bottom set $oldval to, you know, some horrible expression, and then I don't have to remember that it was five. You know, and ten minutes later, I can say, "What was oldval again?" I can say, "Print $oldval," and it'll remind me.
So, and I don't have to scroll up and down in the terminal and stuff like that. So that's useful. And the first example is the example of just calling a function. The one thing about the call example that's worth noting is that because of some of the calling conventions and because basically calling a function with structure returns that you didn't expect to return a structure is gonna corrupt your program in ways that you'll find out kind of a long way down the line, we're a little bit fascistic about not calling functions on your behalf unless we know the return types. So if it's a system function, we actually don't have enough information to know the return types, so you're gonna have to provide them.
One thing that we fixed in the Jaguar GDB and also in the April tool CD is that if you're calling a function which is going to return-- If you write it one of these typical Objective-C expressions where, you know, you have some method call that returns some object, and then you're gonna call a method on that which returns an object, and then you're gonna call a method on that which returns an object, in that case, we parse it and we look and we say, "Okay, okay, okay, it would be really annoying if we didn't assume that this was an id, so we're gonna assume it's an ID for you and you won't have to put that in," which you actually used to have to do. That was quite annoying. So anyway, that one's better. But of course, you know, if you called one that returned a structure instead, well, we trusted you and you just crashed your program. So, sorry.
Okay, so as I say, there is this little-- actual sort of command interpreter that has more than just print and stuff. It actually has a couple of control constructs. And with these, you can get yourself into a lot of trouble. Sometimes you can do good. So the two that it has actually are-- there's a while command and there's an if command. So I just wanted to show you a little example that I use all the time for these. So this is the syntax. It's pretty straightforward.
So the example is that, you know, since I work on a kind of an old program, I have this program that's just full of linked lists. And so, for instance, there's linked lists that store every single .o file that there is record of in the entire executable that GDB happens to be looking at, which is hundreds and hundreds and hundreds.
And so paging through those is just a royal pain in the neck. So, you know, suppose you have something like this. You have this structure linked list and some data, and it always has a pointer to next, okay? And, you know, there are 2,000 of these, and you want to print them out. Even in a GUI debugger, this is a real pain because, you know, even if you have a cinema display, you end up tabbing so far over when you turn them out that-- and then you're scrolling back and-- there's no better way to print them out than that.
You can use the same technique that I'm going to show you for OO things. Basically, you just need to have a way to get next. And in GDB, you can call functions. So if you have some collection class that's a nice OO collection class or something like that, as long as you can call the next function, you can use the same little trick.
The only problem with this is that you can't create C++ objects from the debugger because the runtime knows the magic of how to do the malloc that's appropriate for what the real class is and all that stuff. And we haven't been able to do that in GDB yet. So if you have some class which is only iteratable by having an iterator object, well, you're out of luck.
[Transcript missing]
So then the next thing is what if you're hunting for a particular thing? So you can use the if construct to hunt for a particular thing. So in this case, I want the one where sumData is equal to 5. So I just changed this little example. So inside the while loop, I only do my little reporting if this dollar pointer goes to sumData is equal to 5.
So, you know, again, this is really the expressions that you pass to GDB really are the native expressions in your language. You can get at anything that you could have written in the C program and evaluate it and see its value, which is pretty powerful. Again, rummaging through more complex collections like NSDictionary's or arrays or NSArrays or whatever, as long as you have a way to do next, you know, you can use this trick to go through them.
And then finally, I'm not going to type this thing every time because I get it wrong. You do something really, really stupid like you forget set $pointer=$pointer goes to next and then it just goes off in the weeds spinning around forever and ever and ever and you look embarrassed and stuff. So in order to avoid having to do that, what I'd like to do is take this and actually put it into some script that I can reuse.
That's what the user-defined commands are for. Define is the GDB command that defines a user-defined command. You can even pass arguments into them. If you notice in this case, I took the exact example from before, and the only thing I changed was that instead of saying, you know, if $pointer goes to sum data is equal to 5, in the if test, I checked against $arg0. So that's the name for all the variables, $arg0, $arg1, and so on and so forth. So I can write a little general command which goes hunting through a list and finds a particular element for me.
Huh. It's useful to have around. And if you do these kind of things and keep them-- and find things that are useful to you, you can put them either in your .gdb init file, so that will get read in automatically when you start up GDB, or you can put it in some other file, and then GDB has a source command that you can use to bring in those.
So the Darwin guys, for instance, have a huge set of macros for kernel debugging which grab around and find, you know, all the-- I mean, decode all of the kernel data structures to show you what all the stacks are and what all the threads are and so on and so forth.
And if you want to figure out how you can really get yourself in trouble with this, I think that's in the Darwin repository. You can look it up, and there's just lots of gross stuff in there. Okay, so now let me bring Dave up, and he will close off.
So thanks very much, Jim. So you've seen a lot throughout the week about good tools from both Apple and MetroWorks and other vendors. On our side, we're doing a lot of work to try to improve the debugging support in Project Builder. Please send us your feedback. And there's a lot of documentation on the system. Jim mentioned the GDB documentation.
I'd also like to highlight the Maco runtime manual that's new in the current releases that you've got. It's a preliminary version that's going to get fleshed out more. And standard information for further info. And let's just go right on into bringing Godfrey back up so we can get to Q&A real fast. On my way, Dave. All right. So mailing list addresses, feedback addresses.