Apple Developer Tools • 48:09
This session is for developers new to Mac OS X and those who wish to learn the latest concepts and improvements to debugging in Mac OS X. Learn how to configure, run, and debug applications using source-level debugging within Apple's IDE. Advanced features of the GDB debugger are demonstrated, along with useful techniques for getting the most out of these powerful tools.
Speakers: Jeff Glasson, Scott Tooker, Klee Dienes
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
For those of you that are at the DevTools Feedback Forum, this time I will try to stay on the stage. Some people were there. That's me. So we're just going to do a little bit of an overview of debugging. And then we'll get into a little bit of detail. Some of the stuff you would have seen before if you'd seen the other Xcode sessions. But I want to go into how some of the features work a little bit.
So we'll go over how to get started. I'm going to go over a little bit of tools to show you how Crash Reporter works in Panther. And then we'll go through some UI with Scott Tucker will come back. We'll do some advanced GDB command line work. And then we'll have some demos.
So, what you guys probably, you know, if you have to use debuggers a lot, you know, maybe you shouldn't be doing what you're doing. So, this is kind of... I kind of look at debuggers as a crutch for my friend's program has a problem. How can I help my friend fix my problem? So what is debugging? Why does it crash? Why does it
[Transcript missing]
So what do we give you as part of Xcode? So in addition to what you think of as debugging, there's actually a lot of tools on the system that we've kind of talked about in other sessions as performance tools, like Sampler and malloc-debug, and even the Chud tools, but those are all actually very useful for debugging too.
And we will go into a little bit of detail of how to use malloc-debug later. No demos on that though, we're just going to get real nerdy and talk about it. But, you know, if your program is hanging or crashing, Sampler and malloc-debug can be very useful before you start dropping down into GDB or using the debugger UI inside Xcode.
So, Crash Logs, if you've got a program that crashes, you probably want to find out what happened and where it crashed. So there's a--most you guys should know this, but the console application under Applications Utilities has a preference to enable crash reporting. And that actually get set on by default, I believe, when you install the developer tools.
But there's also another preference you can set that lets you have the crash thing pop up as soon as your application crashes, you'll get a window up here. This has changed a little bit from Jaguar to Panther. So now the logs actually appear in your home directory library instead of the system library.
And instead of getting separate windows for each of the crash logs, you actually have a drawer on the side. If you played with the Panther preview, you'll be pretty obvious as to what's changed. So what's in the log? The log has a stack trace for all your-- It's got the register state for the thread that crashed. And if it crashed in an area that has debug information, it also has the file and line number.
Some of the things to be careful about is if you're writing a program that crashes a lot during your development cycle, these files get bigger and bigger as each crash gets appended to the end of the log and they stay until you delete them. They don't go away after you reboot the system. They are not considered temporary files. So be careful and manage your crash logs if you're crashing a lot. Okay. crashing a lot.
So when I say debugging, I generally mean GDB and debugging C-based programs. And the way that works inside Xcode is there's actually three separate distinct unit processes involved. There's the Xcode UI, there's the GDB debugger, and then there's your target application. And they all talk to each other via different IPC mechanisms to communicate. Xcode and GDB actually are very tightly coupled for a lot of the features, as you'll see in a little bit. And then GDB, of course, is going to issue low-level calls down to your application, mock-level calls, actually, to control and single-step and collect exceptions from your program.
So Xcode is really just the GDB user interface when regard to debugging. Of course, it's a lot more from other sessions, but we're talking about debugging here. So what does it let you do? It lets you view threads and stacks and local variables and you can type in expressions and it'll all be displayed. You can set breakpoints. You can basically not type GDB commands and debug your program and see results and interactively iterate over your program.
The important thing is all these cool new features that we've been talking about all week like fix and continue, zero link, which really isn't a debugging feature, but fix and continue mostly and these new data formatters. These are things that are only accessible via the Xcode UI. Do not try to access them from the GDB command line because they will not work. Xcode also has support for debugging non-C based languages like Apple script and Java, but we're not going to talk about that at all today.
So what do you need to do to make sure your program can be debugable? If you have a new project that you're creating with one of the Xcode templates, or if you are upgrading a project builder project, the development build style should set the correct preferences for you in your project.
And when you click build, it should just work. If you have an older or legacy project, such as you're bringing in an open source project that uses Makefiles or some other build system that you want to integrate into Xcode, you need to make sure that you pass the correct flags to the compiler to enable debug information generation, and on GCC that's dash G.
And also, you need to make sure you turn off optimization. We do not deal with optimized code right now very well. It may work, but you may see some weird things as you're debugging your program. You can see lines stepping back and forth as you say step, so you could get confused.
So one other thing is if you're debugging a framework, you need to make sure that you set the path to your debug framework and not the--if it's a system framework that's installed in system library frameworks, you want to make sure that you've set the path correctly to the debug library and that's in the inspector for the framework--or for the framework in the UI. And please use an absolute path there because otherwise it'll try to be a little too smart and you may get the wrong thing.
Oops, that didn't work. So what's new? So I kind of briefly touched this. Custom data formatters you've seen and heard about in previous sessions, if you've seen any of the tool sessions. Fix and continue we've been talking about all week. We've also added support for stopping on catching and throwing the C++ exceptions. And we've cleaned up the debugging UI in Xcode. And hopefully we are minimizing the amount of time you have to type at the GDB console prompt.
So data formatters, why are they good? What they do is they allow you to summarize data. If you've been using Project Builder and debugging and you have a complex data type, you've got all these turn downs and it can take up a lot of screen real estate to show your either class hierarchy or substructures or elements in your structure. So you can actually create a formatter to display that, what you care about on one line. We also support custom plug-ins for the data formatters.
The way it's architected right now is you need to have a function that returns a value, which is great for Cocoa, but Carbon APIs tend to pass things by reference, return values by reference, and return an error code as the return value of the function, which you really don't want to display the error code. You want to return, display the actual data.
And so we have a plug-in architecture that you can write your own for custom things. Or we actually supply a bunch of system types. And I will kind of encourage you to attend tomorrow morning's 9:00 a.m. session bright and early after the party tonight, where Chris Espinosa will go over that in detail.
You can also annotate things with text. So if you had--and I think you saw this in Ted's session earlier in the week, if you were there where you can actually say, you know, this is my X coordinate, Y coordinate, et cetera. So it's very easy to see at a glance what's going on. There's also some expression evaluation that can happen.
Important caveats or things to be careful or cognizant about is the formatter itself is actually bound to the type of the object that you're setting the formatter for. It's not just that instance of the type. So if you have a local variable that's an NSString or an integer or whatever, and you set a format string on it, that format will be applied to all displays of integers or NSStrings in your entire debug session. and David Maybe sometime we will expand that, but right now for this release it's going to be single formatted string for each type.
and we actually have 35 formatters for Carbon system types right now. That list can grow. That covers most of them. We're also shipping a bunch of base Cocoa foundation formatters. So out of the box, you will actually get a fairly good experience of these opaque types that Carbon tends to pass around. Hello. Not working. So, I wanted to bring up Scott Tooker, and he's going to give you a little overview of the UI and how it's changed and why you want to use it.
So what we're going to be looking at today is Sketch. It's a common, very well-known Objective-C example we ship on the system. And just to start, let me just bring up the breakpoints. I've actually set a breakpoint in STK graphics so that when we go to draw a rectangle, it'll break.
And so let me just go ahead and show you. One thing that we've had, we had in the old project builder, we continue to have an Xcode, but people ask from time to time, is how do I set up, you know, a symbolic breakpoint? I want to break on rays. I want to break on a function name where I don't necessarily have the code.
Well, if you go to the debug menu, show breakpoints, you can actually just click new breakpoint and then type in, and And then that's actually just passed directly down to GDB when they set a breakpoint. And it's easy as that. You can obviously just hit New Breakpoint, get additional ones. We also, in the breakpoint columns you see, we sort them by the file they're in and all that.
So let me go ahead and bring up-- build So first I just wanted to go through the interface real quickly here. This is the default layout. You'll notice, so here we have your threads, we have the summary, and we have the editor. And here let me actually just get it populated with data.
And so what you'll notice here is that for areas that we have symbols and we have access to source code information, we will show you in black. And so, for example, if I go to like NS application, well, I don't have AppKit source code on my machine right now, so that's in gray. And also you'll notice that because we don't have debug symbols, we don't have any pop--we're not populating over here.
But when you do, we'll show the variables over here and we'll show the current line we're on. This is the break point, is the gray part. The red point is actually the PC, and I'll get back to that in a little bit. So what I'd like to do here is show you the different--let's show you the other debugger layouts.
We got feedback that some people really didn't like this style of layout, so we actually have two that you can choose from. In this one, for those people who either you have, you know, long function names or you have a lot of variables, you can do it this way.
And either one, we actually have this little widget here that controls all three windows because we got the feedback that, "Oh, you know, it kind of sucks when I have to--I have to drag one way and then I have to find another widget to drag out the other way, and I really want one place to just control all three panes," and so we added that.
It really is the small stuff. It's amazing. So the other thing I wanted to show you is the console drawer. So in Project Builder, we had this little console drawer. Fortunately, small screens. Let me pull it up a little bit. And here it is. You can get access to GDB.
But we did get feedback that some people were like, oh, I don't really want it in a drawer. I'd rather just have a separate window. It's a lot easier for me if I'm going to do a lot of GDB stuff to make it really big. So we now have its own little window.
So let's go ahead and hit Continue. Let me just bring up a-- don't save. So let's bring up a new file. So what I want to show you now is I want to go into a little bit about how the data formatters work. So for this example, I'll go to my breakpoint again. And let me actually-- I want to shit-- switch back to the other debugger. I prefer the other debugger layout. Sorry.
So if I was really cool, I'd be using Demo Assistant. And if I was a really good typist, I'd just type it in. I am neither. So I'm just going to copy and paste. And so what you've got here is I'm going to go through these in order. This first one, what I'd like to do is a lot of times with Objective-C, you can run into retain release problems.
So I want a quick way in the debugger to see, OK, what's my retain count on this object? So what I'm going to paste in here basically is saying, OK, I want to get an integer back. And basically, I'm asking the variable self, what's your retain count? So if I retain that, return that, it comes up as one. Well, that's okay, but you know, it's kind of confusing, because one what? So I guess one thing I could do is I could just add the retain count.
Well, that's great too, but you know, the nasty thing with self is, well, what is self? I can't tell from here. I mean, I could go down and I could look for isa and all that kind of crap, but that's lame. I want to be able to do it right here.
So, let's go ahead and add this beginning part. And I'll just stop here for a second and just point out that what I'm doing is I'm basically asking the isa for the name property and then I'm saying, "Okay, I want to return this as a string." And I'm just prepending that to the front of what I've done here already.
So now you see I get SDK RangTangle, and you're like, oh, that's great, Scott, but does it really work? What if I do something different? So let's go ahead and hit Continue. There you go. So now I've got this nice little formatter that for self on all these SKT graphics, it's going to show me, oh, here's the class, and here's the retain count.
And we think this is going to be very useful for people when they need to get custom information quickly, when you're in a tight loop, or you just don't want to have to dig down deep and open up all the little twiddle triangles to see what you really want.
and at this point, I believe I am done. Take it away, Jeff. Thanks, Scott. So that actually was a pretty good, very simple demonstration of how powerful the custom data form writers can be. We're looking for feedback on how we could improve them, adding different format strings, what you think would be useful. So any input would be greatly appreciated.
So now I want to spend some time talking about fix and continue. So you've seen it. What is it? What does it really do? I mean, why do you want it? It really is debugging without stopping your program. And probably the most important use of this is you've got a program, you've been debugging it for hours, and you find a problem. It'll take hours to get back to that spot in your program run again. You don't want to take that time. So you can make a change, continue debugging, and you save that hour or two that it would take to get to the state that caused the problem.
So it's very important to note that this does depend on tight integration between Xcode and GDB. Xcode is telling GDB which files are changing, which binaries are being recompiled and how it--and which source file it's associated with. So, again, I will say it again and I will say this many times, this will not work from the command line. And also, this is not the panacea of changing. There are a lot of things that just won't work.
And I'm going to go through how this is implemented at kind of a high level and in the hope that it will help you understand the types of changes that won't be allowed. Other thing, this only works with GCC 3.3 because of some compile code generation options that we needed to have. And it does work with zero link enabled. So you don't have to worry about what your zero link setting is for this feature to work.
So actually I covered this out of order, but yes, it's--the other thing that this is useful for, in addition to taking a lot of time for long-running programs is for certain types of things you can do prototype style development. You know, if you're debugging, you can do quick changes, modify the behavior of a class or procedure, and just keep going until you get it right.
So how does it work? So here we have a simple, simple program with a main and two subroutines. I chose C just because it's easy to show on the screen, but fix and continue works with C, Objective C, and Objective C++, and C++. So here we have main making a call to foo. Let's see if I get these clicks right for all the animations. This is going to be fun. So foo runs and you find a problem in foo. You're in your debug session. There's a problem. What do you do? So you go and look at your source.
So then you're going to edit your file. You're going to recompile it and click on the little tape dispenser fix button. And what that does is it tells Xcode to tell GDB to reload that file. And what actually is happening is GDB is actually loading another little bundle, which I'm calling fixed foo, in addition to the old version of foo, which is in memory. And in addition to it loading that, what GDB is then going to do--oops, here we go--is it actually scribbles on the first few instructions of the old foo routine to point to the new foo.
Now, you might think, "Why should I do that? Why don't we just, you know, get rid of the old one and use the new one?" Well, you could have a function pointer to foo that you still want to have it be valid. You could have it stashed in a global variable somewhere. And so we actually keep all the old data--or the old pointers valid.
So, after that scribbling happens, actually I should point out before I go on that we also do fix up references to static global data also at this time too. So, if you're referencing a global that happens to be in that module, the right thing should happen. So, let's get back to the flow here.
So now, I'm later on in my program and Barr wants to call foo. So, it actually is going to jump to foo, the old version of foo, because we don't want to patch all calls to foo to point to the new one. That would take a lot of time. So, it's going to vector through that little jump thing that we scribbled at the beginning of the old version of foo and end up in fixed foo.
And then that's going to run--oops, come and it comes back and returns just like Foo would have. So that is kind of a very simple version of what's happening and I see the engineer that implemented this has got a big smile on his face saying, "I simplified it way too much." But we can deal with that during the questions later because that would take a long time to really explain all the details. So limitations.
So it doesn't support all types of changes. I mentioned that earlier. What we are hoping and what we will be doing over the process of releases of Xcode is that we want to have common unfixable changes flagged by the IDE. And again, do not try this from the command line. It just won't work.
So what doesn't work? What types of things aren't going to work? So if you're going to be changing structure or class layouts that actually is going to cause different code generations or references from other pieces of your code, that's going to run you into problems. Changing function signatures, again, because you're not recompiling the whole program, you're just recompiling pieces of the program. Adding global or static data in the module, since the rest of the program won't know about it, you won't be able to access it. Anything that needs an initialization, like static initializer, either C++ or C, because the initializers won't get run when this thing gets reloaded.
And again, virtual member functions similar to changing class layouts for the same reasons. And also in addition, if you're trying to modify a function that is at the top of the stack, currently active, changing local data is a bad thing to do, also, because you'll be changing the stack frame that's currently in memory and that would confuse a lot of things.
And also, if you are adding something that gets initialized, like, you know, even like I equals 5 earlier in the function than where you are and you want to reference I and expect that value to be set, that won't happen because we continue execution from where the PC was before.
So what does work? That sounds like a lot of things that don't work. But in fact, a lot of things do work, as you've seen all week. Common logic errors tend to be something that are easily patchable. Off by one errors, I forgot to multiply by two, et cetera. All those things should be fixable. You can also add new calls, add new logic to existing routines, no problem. You can delete code. You can change constants, string constants, integer constants, whatever. And pretty much any other common operation, you can try it and if it works, great.
If not, the hope is that Whoops, come on, come on. Flip, flip. You can always, if the fix fails and the IDE tells you the fix failed, you can always rerun your application from the beginning. And it's our goal that over time we actually increase the amount, the types of changes that are allowable to be fixed and we get a lot better doing error detection if it finds an unfixable patch. So, I bet you can find some things that crash your debug session today with the preview. But again, we appreciate feedback if you find those and have something that's reproducible and then we can fix it either for the GM or for the release after.
One caveat with the preview also is right now if you do a bunch of fix and continues and then want to rerun from the beginning, the build system isn't quite correctly hooked up, so you will actually lose those fixes if you rerun because it doesn't know it needs to recompile.
There's actually two different Dottos that get generated. It's kind of a bundle that gets fixed as opposed to your regular Dotto that would get linked in or zero linked into your application, and that'll be fixed for the GM. But right now, make sure you click build and run instead of just run after you do a fix and continue session.
So again, I'll bring Scott back up, and he's going to show some more fix and continue. So already you've probably seen this several times now, and you'll notice that one of the themes before was we always were showing you without showing you the debugger active at the time.
We kind of hit off the debugger and you were just editing and you do a fix and, ooh, you know, you're all excited. So what I'm going to do is now focus on, well, what if you're doing a debugging session, you're paused and you want to do a fix? You know, what's the experience like there? So what I'm going to do is I'm going to start debugging Sketch again.
And let's use that same breakpoint. And so what I'm going to do-- so we're stopped, and we're about to draw. And so what I'll do is I'm just going to comment out some lines and uncomment out some lines. So we're going to change the color to red. And let's see what else. And let's change the stroke line width to 8, for example. And so I can go ahead and do fix, and then I'll continue.
Huh. OK, so I got the stroke line with, but I didn't get the red. So as Jeff pointed out, you know, we remember where you are in the function. So when you fix the function, we'll take you back there and continue on. So one thing in the demos that we've done before is we've done things where it's always refreshing. But just something to remember when you're in the debugger, that we're just going to continue on from where you are.
So that brings up the next thing, which is, well, what if I wanted to have it actually get that fixed? Well, it would be really cool to be able to move the PC, wouldn't it? Well, guess what? So let me go ahead and let's just--just to show that something's actually going on here, we fixed it. So now I'm going to change the color to green. And I'm going to go ahead and I'm going to fix. But let me take this to say there. There. And let's continue. Oh! Continue. Yeah, this is what I get for being cocky.
Yeah, this is-- OK, well, we'll do one more fix. So let me go ahead and do the fix. Good. So let me-- oops. No, no, no. Continue. Continue. Continue. Continue. Continue. So what we're going to do-- let me just-- let's just catch up here. So let's close this file just so we can get rid of the clutter. Open up a new one.
Okay. So, for example, let's change it to, let's change it back to, let's change it to black, so we'll just get a black one. So, just to show you that I really, I'm not using all this to just divert the attention. But, so I'm going to fix, so I'll go ahead and fix this, and then I'm going to drag the PC back. And so then I'm going to say, and I'm also going to, just for, disable that, and let's continue. Thank you. There you go.
One more thing we wanted to show you at Jeff's request is there will be several times when you do something and it doesn't fix. So we'd like to show you a case of where that actually happens. So when this comes back as failing, it's supposed to fail. So I'm going to go ahead and I'm going to turn this breakpoint back on.
And then let me go up here, and I'm going to go up to the top. And so as Jeff pointed out, I really shouldn't do this. But I'm not listening to Jeff because I know what's best. And I'm going to fix it. In the future, we want to have a much more apparent-- well, for one thing, we want to show you the entire error and not just cut it off.
But we'd like to have a panel or a sheet come down telling you that a fix has not completed. And over time, like Jeff said, we want it to have more feedback that, OK, this isn't going to work, so you don't even get in these situations. And at that point, back to you, Jeff. All right, thanks, Scott.
So I seem to have been talking very quickly, so we're going to have a lot of time for questions at the end, which is good for this type of session. So another couple things that we have added, you've seen this I think in one of the sessions earlier in the week, is we've added support for stopping and catching and throwing of C++ exceptions, which is well, a often requested feature for C++ programmers. It's a very simple UI to show.
I don't know if you caught it when Scott had the debug menu pulled down, but there's two menu items under the debug menu, stop on C++ catch, stop on C++ throw, and then it just stops. You get a stack trace, you can see what's going on. You can also do a little bit more sophisticated things from the command line, which we'll talk after, or Klee might actually cover it when he's talking about the command line.
So I think I talked about a lot of these caveats already, but I'm going to go over them again. The first one I didn't mention, but yes, the custom formatters are really cool. If you notice, they actually have a lot of code in them or can cause a lot of code to be executed in your inferior debug process. So if you have, you know, hundreds of them on your screen, you're starting and stopping your process a hundred times and getting a value back to GDB.
This can cause single stepping to be slow. And you may want to disable them and--if you're stepping and then re-enable them when you care. Also, it's important that since you are actually executing code in the debug process that you do not take code that might cause a deadlock to happen.
So if, you know, you're debugging some graphics thing and a core graphics routine in the system has a lock held and you're waiting for that lock to--as part of your data formatter, that could be a problem. It could be a problem depending on when that data formatter is called. And also, for the preview release right now, and we hope to improve this over time, large zero link applications could slow down your debugging.
And what I mean by that is times to display the stack or times to single step as it's dynamically faulting in the modules as needed. And it could take a few seconds right now. You may see that and we're going to try to improve that for the final shipment for the GM. So with that, I want to bring up Klee Dienes, who's a senior GDB engineer, and he's going to talk about how to use this stuff from the command line.
Hi. So for the most part, the things that we do on the GDB command line are things that we hope to have be in the background. We want them to be things that you don't pay too much attention to. So for the next five or 10 minutes, I'm going to take those things into the foreground and hopefully tell you one really cool feature that we've added, and then a couple of neat things that you've always been able to do, but you might not have known you were able to do.
So our new feature is cache symbol file support. This is basically a feature that's designed to improve the startup time of GDB by letting you take large symbol files and have them get stored on disk so that when GDB goes to read them, instead of reading them, you'll see little dots go across your screen as the symbol files get loaded. The goal is to have it, instead of being dot, dot, dot, it should just be dot, dot, dot, dot, dot, dot, done.
A lot of people have said, hey, we really wish GDB startup could be faster. We'd love to make it so that it's always instant, but the least we can do is make it so that it's a lot faster the second time you do stuff. It's just a cache. It doesn't change anything about how it processes symbols.
It should do everything exactly the same. The only goal is to make it start up your program a lot quicker and get you debugging a lot quicker. The way it works right now is when you install new developer tools on your system, it builds a cache for all the system libraries on the system.
It'll go ahead and use those. It'll build those just once. You can optionally tell it to create a cache for your user binaries. I'll tell you how to do that in a few minutes. This is the first time we've shipped this. There's, as always, a couple of things that can go wrong.
The very first one is sometimes those caches can get out of date. The way you'll know that they're out of date is you'll see the big warning. It won't be red on your system, but it'll be there. It'll say, precompiled symbol file is out of date. Don't panic.
All it means is that it's going to take another couple of seconds to load the symbols. It should be exactly the same as if they weren't there at all. At any time, you can update them. There's a command. I don't know if you get the slides on this, but it's sudo or sudo, and there is a command, user lib exec gdb cache sim files. That'll regenerate the whole cache for the whole disk.
Similarly, if you think something's going wrong, you just don't trust them. For whatever reason, you can delete them whenever you feel like, and you can recreate them later if you discover that it wasn't actually that that was causing the problem or whatever else, or if you just want to update them.
So the reason that we didn't make this always the case for all binaries is that, A, it's a new feature and we want to get some use of it out in the field before we make it the default for everyone, but also that sometimes you don't want it.
So if you have, for example, a lot of binaries that don't change very often and you're just changing one thing and one part of it, like you have three frameworks in an application, it's great. You can cache the symbol files for the libraries, start up the application, everything's quick.
If you have one giant, massive binary, it might not be so quick necessarily because there's a little bit of overhead in creating the cache. So if you just had to regenerate that cache every single time, it might take longer to regenerate the cache than you'd say by starting up. So to get around this, for right now, we've made it something that you can set on a per-project basis. So basically in your per-project directory, there's a file called .gdb init that will control how gdb behaves.
And if you set the variable generatePrecompiledSymbolFiles to 1, it'll go ahead and create those cache symbol files for every framework that's loaded by gdb as gdb sees it. So the idea is that the first time you've debugged a framework that you've written or some other object that you're writing, it'll take another second or two to load it up. But after that, it'll just be dot, dot, dot, dot, dot, done, just like we're hoping. The cache files are stored, stored in a directory with your project called gdb_sim_file_cache.
If you want to disable it, you can just remove those files. If it's project builder, there's a cool thing. Since project builder stores in its build directory generally all in the same place, those will be shared among the project builder files, which I think is kind of neat.
So, you know, I'd love if you can try that out. You know, be sure, please let us know how well it works for you, if it's really affecting your performance, if it's really improving your performance. I really hope it does. So now on to a couple of neat features in GDB. These have been there for a long time, but they're still really neat. The first one is watchpoints, which is basically a way to have GDB stop when some expression in your program has changed.
So the most useful part of this is when you want to find out if a data value has changed. You know, typically it's for finding memory crashers or, you know, something in this library is changing some data variable of mine, and I don't know where it's getting changed.
I don't know how it's getting changed, but I sure know that it's bugging the hell out of me that it got changed, and I want to know where. So what you can do is at any point you can set a watchpoint on an expression. It can be any expression that's valid in your program. Ideally, it shouldn't be something that calls function. It can be function calls, but, you know, any valid C expression is a valid watchpoint expression. And what it'll do is GDB will protect the memory pages that are involved in that expression, and whenever they get modified, it'll stop.
It'll check the expression, check the old value, and it will see if it needs to report the value has changed. A couple of caveats is, you know, if you change that value all the time, GDB's going to be stopping your program all the time. That's going to be slow. That's going to be slow. If it's changing a code. Constantly. Particularly if it's something on your stack, because, you know, everything's on your stack.
That's going to be getting modified constantly. It's going to be stopping constantly. It's going to have to start the inferior over and over and over again. That's going to be a very unpleasant experience. But for things that are in global data, things that aren't accessed constantly, it's a tremendously valuable feature, particularly for, you know, I don't know what's trashing my memory. So sort of the classic example of that is to watch a buffer and say, you know, who changed this buffer? And you'll see.
In my example, there's this little cute little curly brackets around stuff, which some of you probably seen before. Some of you haven't. What that's saying is just take the variable buff and treat it as if it were an array of 1024 characters. So that's basically a shorthand for tell me when anything on this page has changed. I want GDB to stop. Tell me when on the page has changed and give me a back trace so I can figure out exactly how I got to what was doing that.
Another very useful feature is breakpoint conditions. It's basically the ability to add a conditional expression or a set of commands to any breakpoint or watchpoint. So what that lets you do, for example, in the case of watchpoints, is add extra conditions. So you can say, "I not just want to stop when this is changed, I want to stop when it's changed to 10." Or, "I want to stop when this pointer has become invalid." Or, you can use it for all kinds of crazy features. In the second example, you'll see what we've done is we've stopped on the open call.
So we said, "Whenever I open a file, no matter how, whatever reason, I want you to stop, and then I want you to tell me what file I opened." So since the argument's always going to be in the first argument register, R3, we can just have it print out the file that was opened, cast it to a string, and continue on the way. So what you'll see as you're running your program is just a big output dump of, you know, open this, open this, open this, open this, right down the line.
It's, you know, I'm sure there are other ways to do that exact same thing, but it's neat that you can do that without having to use any special features, just by setting breakpoints and putting logs. And some people have done some very impressive things with just breakpoint conditions, you know, to log their programs.
While we're on the topic of memory smashers, I want to talk a little bit about some other tools that are out there for debugging memory smashers, the best being, or one of the best being, malloc-debug, which is a tool that is designed to help badly behaved programs crash for you.
So what it'll do is it will overwrite freed memory after it's been freed, and it will add guard pages before and after malloc regions, so that when you free something, if you go to write to it later, it'll occasionally check to see, did you write on something that you shouldn't have, did you just overwrite the buffer, did you write before the beginning of the buffer, and it'll generate logging messages for you. The main reason I'm mentioning it here is that it actually works really well with GDB. If you run a program both under malloc-debug and GDB at the same time, you can set breakpoints at the malloc-debug output routines.
So if you set a breakpoint at, say, for example, malloc-printf, malloc-debug will stop in the debugger, let you get a backtrace of exactly what's doing the wrong thing, and let you do normal debugger operations on that. So you can look at the backtrace, find out how you got to the thing that's trashed in the memory, and go about fixing that. For more information on that, you can just check out the Help menu in the malloc-debug documentation, and there's actually a pretty sizable chunk of stuff there on how to do that.
The last one is on the topic of connecting up to things that you're running or something else is the GDB attach command. This is something that I know we'd love to have in Xcode. It isn't in there yet, so for right now, you can pretend it is by doing it from the GDB command line. It's a very simple syntax.
You just basically say attach and then the name of the program or the name of the program, follow the process ID, or just the process ID by itself. What GDB will do is it will connect up to the program you're debugging. It's pretty much just as if you ran the program under GDB, except that you could run it under something else. This is really great for programs that are hard to attach to. Say they might have a complicated startup routine. They might be, you know...
[Transcript missing]
Thanks, Klee.
So, just a couple more things to wrap up and then we'll go over to Q&A. We're not too short. So, you know, one of the things I want to get across is this is--we've done a lot to try to improve your debugging--your development productivity by improving debugging. And yeah, I made the joke at the beginning, but we all write bugs and the best way to fix the bugs is to find them as fast as possible and the tools are very important to do that. Fix and continue, it's great.
Play with it as much as you can. Feedback, definitely appreciated. We know there's problems with it. This is a first shot at this technology. We want to make it as robust as possible in time for our GM when Panther ships. Custom data formatters, you saw kind of just a real quick brief demonstration of how powerful that can be. But remember, lots of them can make your debugging experience slow.
And then Klee talked about cache sim files. This again, it's brand new. One thing I don't think Klee mentioned was that we actually at install time generate cache files for all the system libraries. So these are actually there by default. Klee was talking about how to generate the caches for your own private code.
So, how to learn more. There's documentation in Xcode from the Help menu. Matt Morris has shown that a number of times this week already, how to access that, and we've actually spent a lot of effort on that, too. Search, look, man pages for GDB are on the system. Release notes for the compiler actually are helpful for some of this stuff, so make sure you get the right settings for debugging. And standard documentation, which you don't need to go through.
Who to contact? Godfrey DiGiorgi. There's his email. Email should go to him. There's also the Xcode feedback mail list and bug reporter for bugs. Please, please file bugs. In the past, people have complained about GDB and the debugging experience and have not filed bugs. And we look at bugs, we fix bugs.
We don't fix bugs if we don't know about the bugs. Or if they just appear in a mailing list at some point, they will be forgotten when we're going through a bug list. So, please, please. Please file bugs and bug reporter. And the mailing list do provide lots of information and help for people.
So it's the end of the week. There still are a couple more sessions I need to plug for Chris in the morning, 9:00 AM, bright and early. He's going to go over debugging and tuning Carbon apps, and he will spend a significant amount of time talking about how to write custom data format or plug-ins for Carbon types. And please attend if you're interested.
And there's also a discussion on software test tools later in the day Friday. So with that, I would like to bring up Godfrey to keep me under control and everybody else and the GDB team and the rest of the QA panel. CHRISTOPHER COTTEN: If our QA panelist can please come up. Thank you very much, Jeff. That was a great job.