How to use perf on MacOS for code profiling
This is a quick post that details how to run perf (also known as perf_events) on an OSX machine. Perf is a powerful Linux tool - it can instrument CPU performance and is capable of lightweight profiling. You can get more context about what perf can do here.
It is usually included in the Linux kernel, but there’s no way install it on a Mac. This is a pain because you’d usually want to profile (and generate flame-graphs 1) for your applications locally and not on a prod/staging server because that has a suitable Linux distribution running.
On OSX you can use Docker containers to create such an environment and install perf on it:
Create a Dockerfile with the base image of the distribution you intend to use. e.g. if you’re targeting a Node app for profiling, you can use a Node base image which is debian-based.
FROM node:14.17.0 WORKDIR /usr/src/app
In the same Dockerfile, download the linux-tools source for the Linux version you are using 2, and compile using make.
RUN HOME=$(pwd) && \ # Gets the Linux version and strips out the 'linuxkit' part LINUX_VER=$(uname -r | cut -d'.' -f1-3 | cut -d'-' -f1) && \ # Downloads compressed linux-tools for the version wget "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-$LINUX_VER.tar.xz" RUN HOME=$(pwd) && \ LINUX_VER=$(uname -r | cut -d'.' -f1-3 | cut -d'-' -f1) && \ # Un-compress the source and compile with make tar -xf "./linux-$LINUX_VER.tar.xz" && cd "linux-$LINUX_VER/tools/perf/" && \ apt-get update && apt -y install flex bison && \ make -C . && make install && \ cd $HOME
Since this a Node app, do the normal Node things like install packages and copy in source code to the image.
COPY package*.json ./ RUN npm install COPY app.js ./ EXPOSE 3000 ENTRYPOINT ["node", "app.js"]
Build the image.
docker build -t example-perf .
Run the container in privileged mode and open a bash shell in it.
docker run --name example-perf --privileged -d example-perf docker container exec -it example-perf bash
Use perf in it’s installed directory.
# Enables you run perf without some kernel errors echo 0 > /proc/sys/kernel/kptr_restrict cd ./linux-$(uname -r | cut -d'.' -f1-3 | cut -d'-' -f1)/tools/perf ./perf record -F99 -p "$(pgrep -n node)" -g -- sleep 30
As a bonus, you can copy perf to the
/bin directory so you can access it anywhere, but I haven’t tested that.
If you don’t like/use Docker, you can try replicating this in Vagrant. With Vagrant I don’t think you’d even need to download the source, because Linux headers are available. The
apt-get command should suffice.
You can use this repository to get started with creating a Vagrant environment.
I talk about generating flame-graphs for Node applications in a post coming out soon. ↩
In a sane world, you should be able to install perf in the container using
apt-get install linux-tools-common linux-tools-generic linux-tools-uname -rand be done with it, but you can’t because there are no Linux headers in Docker for Mac. I discussed this briefly in this post. You end up with errors like this:
E: Unable to locate package linux-tools-common E: Unable to locate package linux-tools-generic E: Unable to locate package linux-tools-4.9.125-linuxkit E: Couldn't find any package by glob 'linux-tools-4.9.125-linuxkit' E: Couldn't find any package by regex 'linux-tools-4.9.125-linuxkit'