Wednesday, November 16, 2005

Finding Preferences

Finding all the preferences a given application uses can be done very easily for a Cocoa-based application. All it takes is a bit of debuggerry (with gdb in this case).

This allows one to find all the various preferences, but not how they are used. Use can be found either by inferring from the preference name, or when it is unhelpful, actually changing the value for the given preference and watching what happens.

Read on for more detail

Finding Boolean Preferences
The NSUserDefaults class has several methods allowing an application to save and retrieve preference values. One of these is boolForKey: when the preference is a simple on/off type setting.

Here's how to find all the boolean preferences in Safari (chosen for examples since it's pretty ubiquitous these days...):

  • First run Safari from within gdb (in Terminal):

    $ gdb /Applications/

  • Now we set a breakpoint at NSUserDefaults' boolForKey::

    (gdb) break [NSUserDefaults boolForKey:]

    Sometimes when setting a breakpoint prior to running the app, you'll be asked about "pending on future shared library load"; as long as you entered the class and method names fine, you can answer yes to the question.

  • Next, since we only care about the names of preferences and don't need to do more serious debugging, we set a couple of commands to run when the breakpoint is hit. These commands print out the name of the preference, then tell gdb to continue running the program.

    (gdb) commands 1
    >print-object $r5

    (gdb's helpful help output is elided from the code bits). The important bit is print-object $r5 which prints out the string which was passed to boolForKey:, namely, the name of the preference; $r5 is actually general register 5, r3 has self (the instance of NSUserDefaults), and r4 the selector (basically, a representation of boolForKey:.

  • Now, run Safari:

    (gdb) run

    At this point, Safari will start up (more slowly than usual), and you'll see a bunch of output in Terminal from gdb like:

    Breakpoint 1, 0x928c4074 in -[NSUserDefaults boolForKey:] ()

    The second line is the name of a preference (in this case TabbedBrowsing). From here you can either guess at the functionality affected by the preference (TabbedBrowsing should be somewhat obvious), or set/unset the preference to see what happens.

  • Note that quite a few of the preferences are manipulated from the application's Preferences menu item, so be sure to try and figure out which, as those are less interesting. The more interesting ones are those which aren't settable by the normal means.

Finding Other Preferences
NSUserDefaults has other methods besides boolForKey:; most are specialized like boolForKey: in that they return a specific type. However, to truly find everything, look to objectForKey: which is used by the others to actually read the data from the preferences. Of course, you have to do more work just to find out the type (integer, string, boolean), but it'll give all of them. It'll also further slow the running of Safari.