Fat Cat Software

Using GDB’s ‘po’ command while a malloc lock is being held

Starting in a recent version of Xcode (version 3.2 I think, but I'm not totally sure) a change was made in GDB where, if you try to print out the description of an Objective-C object, either using the 'po' command from the console or the "Print Description to Console" contextual menu command, you will sometimes get an error message saying something like "Cancelling call as the malloc lock is held so it isn't safe to call the runtime" or "warning: Not safe to look up objc runtime data".The reason for this is that some other thread in your program is currently in the process of allocating some memory through malloc() or some other memory allocation routine. To print out your object, GDB will call the description method on the object being printed, which will return a plain NSString that it can easily print to the console. However, the description method almost always involves allocating some memory to create the new string containing the object's description. Calling this while malloc is still being executed on another thread can lead to all sorts of crashes, memory corruption, or other weirdness.However, while this change does prevent that sort of problem, it creates another problem in that when you hit a breakpoint, you can never be quite sure about whether you'll actually be able to examine the content of your objects. In my experience, this seems to be even more of a problem in garbage collected applications, since there is always a collector thread running in the background which can often be the thread responsible for allocating some memory at the time your breakpoint gets hit.This has become quite a pet peeve of mine, especially when I've spent quite a bit of time setting up a scenario to reproduce a bug, and I get to where I can break at the point in the code where the problem is, only to find that I can't actually look at anything! Needless to say, this puts a cramp in my style, and I've been looking for a workaround for this for quit some time. I've found that sometime, stepping over a few statements will run the program enough that the thread that's allocating memory will finish, but this is very hit-or-miss (more miss than hit) and I usually have to just restart my application and/or perform whatever setup steps were needed to get back to the same point, and hope that this time I can actually print out information.So, this being the case, I was very intrigued by a tidbit I learned while watching one of the WWDC session videos from last year. The tidbit is in the form of a GDB flag called "scheduler-locking". What this does, when enabled, is to cause GDB to only run the current thread when you click the "Continue" button or use the "continue" command from the GDB console, rather than running all threads at once. To enable this behavior, you can simply type "set scheduler-locking on" on the console.The way I thought of using this to get around this malloc problem is as follows. Let's say I get to this point where you've hit a breakpoint but can't print anything out. What I'll do is:First, I need to find out which thread is the one holding the malloc lock. The easiest way to do this is to type "info threads" in the GDB console. That will list all the threads running in your application, along with the topmost function call on the stack for each thread. Now, the exact function that's responsible can be a number of different things, including malloc obviously, but also any number of Cocoa, CoreFoundation, Core Graphics, or any number of other allocation related routines. So, you'll have to do a little judging to figure out exactly which thread is the culprit in your particular case.Switch to that thread to make it the current thread. You can do this either by typing "thread" followed by the thread number in the console, or by just selecting the appropriate thread from the pop-up menu in Xcode's debugger window.Once you've done that, go and type "set scheduler-locking on" in the console.Then, just click the "Step Out" button in Xcode to make the current thread finish whatever allocation function it was in. It's possible you'll need to step out two or more levels in order to make sure you're out of malloc-land. The trick here is that, since you enabled the scheduler-locking flag, only the thread that you selected will be run. This means that the thread your breakpoint is in (as well as all the other threads in your program) will not have budged at all, and once you're out of the allocation routine, you can then switch back to that thread and print things out!Finally, after you've finished your debugging, make sure to go back and do a "set scheduler-locking off" before continuing with the rest of your program. Leaving it on could cause other problems such as deadlocks in further debugging, so you should only leave it on as long as necessary to get the job done.I've only gotten the opportunity to try this technique a handful of times so far, but it's worked like a charm each time I've tried it. It is a little involved, but in situations where you have a hard to reproduce bug you're trying to figure out, it's far less work than rolling the dice over and over, hoping you'll hit your breakpoint in a state where you can actually examine your data. I'd be interested in hearing if anyone else tries using this technique and whether it works consistently, or if I've just been lucky so far.