F-Script Anywhere With LLDB

Ever since the start of my career at Apple, working with the venerable Macsbug, I have prided myself on making the most of whatever debugging facilities are at my disposal. I came to be quite capable with Macsbug, adding custom commands and data templates that helped me speed through crucial debugging sessions that would have otherwise taken much longer.

When I moved from the classic Mac OS team to Mac OS X, I was forced, only slightly earlier than every other Mac developer, to adapt to gdb. I did so with modest aplomb, adding custom commands that made it easy to, for example, continue until the next branch instruction (as it happens, also the first real post on the Red Sweater Blog).

When Apple started shifting away from gdb to lldb a few years ago, I realized I would have to throw out all my old tricks and start building new ones. To my great shame, progress on this front has been slower than I wished. The shame is made greater by the fact that lldb is so delightfully extensible, it practically begs for a nerd like me to go town adding every manner of finessing to suit my needs.

The nut of lldb’s extensibility is that much of its functionality is actually implemented in Python, and developers such as ourselves are invited to extend that functionality by providing Python modules of our own.

I finally decided to break the ice with lldb’s extensibility by adding a shortcut command for something I often want to do, but frequently put off because it’s too cumbersome: injecting F-Script into an arbitrary application running on my Mac. F-Script is a novel dynamic programming interface that lets you query the runtime of a Cocoa app using a custom language. It also features a handy tool for drilling down into an app’s view hierarchy and then navigating the various superviews and subviews, along with all their attributes. In some respects it’s very similar to a “web inspector,” only for native Objective-C applications on the Mac (and sadly, with far fewer features).

There are Automator workflows that aim to automate the process of injecting F-Script into a target app, by running the required commands, via gdb or lldb, to make the injection work seamlessly. For some reason, these workflows have never worked so seamlessly for me, so I’m always reduced to attaching to the process with lldb, and running the required commands manually to get the framework loaded.

Fortunately for me, “having to run lldb” is not such a big deal. Usually when I want to poke around at an app, I’m in lldb anyway, trying to break on a specific function or method, or examining the application’s windows and views via the command line. Once I’m attached to a process with lldb, getting F-Script to inject itself is as easy as running these two commands:

expr (void) [[NSBundle bundleWithPath:@"/Library/Frameworks/FScript.framework"] load]
expr (void) [FScriptMenuItem insertInMainMenu]

That’s all well and good but to do that I always have to find the memo I took with the specific commands, then copy and paste them individually into lldb. Far too often, I wind up imagining the struggle of this work and put it off until I’ve spent minutes if not hours doing things “the harder way” until I finally relent and load F-Script.

Today I decided that I need to stop manually copying and pasting these commands, and I need to finally learn the slightest bit about lldb’s Python-based script commands. The fruit of this effort is summarized in this GitHub gist. Follow the directions on that link, and you’ll be no further away than typing “fsa” in lldb from having all of its utility instantly injected into the attached app from lldb.

Even if you’re not interested in F-Script Anywhere, the gist is a concise example of what it takes to install a simple, Python-backed macro command into lldb. Enjoy!