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
Powered by hugo and hugo-theme-nostyleplease.