Finding memory leaks in a Dashboard widget that uses an Objective-C widget plugin is a little tricky. All the malloc debugging features are enabled using special environment variables. The problem is that DashboardClient.app is launched automatically by Dock.app. So you can’t just set these environment variables in a terminal window and expect them to work. You also can’t start DashboardClient.app manually. There are some magic command line arguments that Dock.app passes in.

You could use ~/.MacOSX/environment.plist to set the environment variables, but that would require logging in and logging out anytime you wanted to set them. Also all applications would get these variables which is not at all desirable. You could try and reverse engineer the command line arguments passed to DashboardClient.app, but that could be difficult. The solution I found is a nice little shell script that replaces the DashboardClient.app binary. The first step is to rename the binary:

% cd /System/Library/CoreServices/Dock.app
% cd Contents/Resources/DashboardClient.app
% cd Contents/MacOS
% sudo mv DashboardClient DashboardClient.orig

You could change directories in one step. I just did this for ease of reading. Now create a shell script named DashboardClient with the following contents:

#!/bin/sh

# export MallocStackLogging=YES
# export DYLD_INSERT_LIBRARIES=/usr/lib/libMallocDebug.A.dylib
# export DYLD_FRAMEWORK_PATH="${HOME}/Library/Frameworks"

exec "${0}.orig" "$@"

Finally, make it executable:

% sudo chmod +x DashboardClient

The key is the last line of the shell script which launches the original binary with the command line arguments passed in. However, this now gives us the opportunity to set some environment variables before launching it. I’ve given three examples of how this may be used in the comments.

The first example turns on malloc stack logging, which is really nice for leaks (and the Leaks GUI). The second example loads libMallocDebug.A.dylib, which allows you to attach to it with MallocDebug. The final example has nothing to do with memory leaks, but it allows you to override system frameworks with your own. One possible use for this is to use a custom build of WebKit with Dashboard.

You can always go back to the original binary by removing the shell script and renaming the binary again. Also, keep in mind that a system update may very well overwrite these changes, so you may have to redo them. But all in all, this is a nice easy hack to enable malloc debugging on one instance of a Dashboard widget.

Update: Want per-widget and per-user control over environment variables? Read part 2 to see how to do this in Ruby instead of a shell script.