home

Emacs: Profiling on MacOS

Profiling Emacs on MacOS is a child’s play…

…with a playbook stashed away.


It’s easy in the end; however, there are numerous pieces of misleading information out there that have to be found.

What’s needed?

  • XCode + CommandLineTools
  • Instruments.app
  • Emacs with source and debugging symbols (optional, but recommended)
  • GNU Make (for the lazy) - scroll to the bottom

Getting XCode and the CommandLineTools are straightforward; Instruments.app should come inside the package.

If you decide to use an attached Makefile, remember to use GNU Make in the newest version as it’s not necessarily compatible with the non-GNU one that comes with the system.

Note on Debug Symbols

This is especially related to Homebrew installed flavor. Install from source - i.e. use -s or --HEAD flag during installation Keep sources after installation - --debug flag - by default Homebrew will delete them If possible - use extra debug flag - e.g. emacs-plus has --with-debug which e.g. installation for emacs-plus could look like:

brew install emacs-plus --debug --with-debug --HEAD

Enable profiling

Create a debug.plist file (or any other .plist file; it probably won’t make any difference).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.get-task-allow</key>
    <true/>
  </dict>
</plist>

Set permissions using that debug.plist For attaching to Emacs.app

codesign -s - -v -f --entitlements debug.plist /path/to/Emacs.app

For attaching directly to the binary

codesign -s - -v -f --entitlements \
  debug.plist \
  /path/to/Emacs.app/Contents/MacOS/emacs

In emacs-plus sometimes there’s an error about FinderInfo attribute. It can be removed using xattr -d com.apple.FinderInfo $EMACS_PATH.

Testing it

At that point you can open Instruments.app with - for example - “Allocations” template. Set binary to emacs binary (or app, but I’m not sure how .app reacts to the flags) Set argument to -Q - to get clean instance - it won’t interfere with running Emacs if you have one It should start gathering trace without any complains or extra dialogs. If permission dialog fails it means that entitlements command failed. Lazy Version Or just use prepared Makefile :)

#!/usr/bin/env gmake -f
DEBUG_PLIST_FILE := $(shell mktemp -d)/debug.plist

define DEBUG_PLIST
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>com.apple.security.get-task-allow</key><true/></dict></plist>
endef

.INTERMEDIATE: ${DEBUG_PLIST_FILE}

${DEBUG_PLIST_FILE}:
	@echo '$(DEBUG_PLIST)' > $(DEBUG_PLIST_FILE)

%: ${DEBUG_PLIST_FILE} FORCE
	-@xattr -d com.apple.FinderInfo "$@" 2>/dev/null
	@codesign -s - -v -f --entitlements ${DEBUG_PLIST_FILE} "$@"

FORCE: ;

Download, paste to Sign.mk do chmod +x Sign.mk and use as normal script. This is not an ordinary shellscript as I’m using it for a full build (and a nice bonus is that it takes care of cleanup of plist file for me).

Extra Tip no. 1.

In Emacs source directory there’s .lldbinit file. It can be used to debug using lldb. Because of safety in order to run it you need:

cd /path/to/emacs/source/src
lldb --local-lldbinit . # ... rest of params, either -p and PID or executable

Extra Tip no. 2

When modifying source code don’t bother with rebuilding. It’s much better to craft own build script and use source directly (recompilation after change of MacOS specific code takes seconds).

Extra Tip no. 3

With CommandLineTools comes xctrace - CLI utility allowing to start trace that can be opened using Instruments.app. Example usage looks like:

xctrace record --template Allocations  --launch -- ./src/emacs

I personally prefer using Instruments.app even though it tends to ocassionally crash when it ingest too much data.

Przemysław Alexander Kamiński
vel xlii vel exlee

github | li

Powered by hugo and hugo-theme-nostyleplease.