docs java javascript python c / c++ c# reading reports

How to profile C and C++

Native code has no runtime to ask, so profiling happens from the outside: the OS interrupts the process a few hundred times a second and records the call stack. On Linux that's perf, and it's the one to reach for — no recompile, no instrumentation, and it works on a binary that's already running in production. flamelens takes either the folded stacks every native profiler can export, or raw perf script output saved straight to a file.

Linux: perf, the short version

# sample the whole system at 99Hz with call graphs, for 60 seconds
sudo perf record -F 99 -a -g -- sleep 60

# ...or one process you already have running
sudo perf record -F 99 -p <pid> -g -- sleep 60

# ...or a command, start to finish
perf record -F 99 -g -- ./my_program

# then turn perf.data into text and upload THAT
perf script > profile.perf

Upload profile.perf as-is — we parse perf script output directly, so you don't need FlameGraph's Perl scripts. If you already use them, folded stacks work too and are a much smaller file:

perf script | stackcollapse-perf.pl > profile.folded

Why -F 99 rather than a round 100: sampling at a frequency that shares a factor with your workload's own periodicity (timers, frame rates, tick handlers) makes the profiler lock in step with it and over-count whatever runs on that beat. 99 is the conventional choice for exactly that reason.

Symbols or it's useless. A profile full of hex addresses tells nobody anything. Compile with -g (and keep it — -g -O2 is fine and normal for profiling; optimized-with-symbols is what you want, since profiling a -O0 build measures a program you don't ship). Add -fno-omit-frame-pointer so the stack walker can actually unwind; without it, call graphs on x86-64 collapse to a single frame. If your distro strips binaries, install the matching -dbg/-debuginfo packages, or use perf record --call-graph dwarf (slower, bigger, but it doesn't need frame pointers).

C++ specifically: the name mangling

perf script usually demangles for you. When it doesn't, you get _ZN3foo3barEv instead of foo::bar() — pipe it through c++filt before uploading, since a report that names mangled symbols is a report you have to decode by hand:

perf script | c++filt > profile.perf

Template-heavy code produces enormous symbol names (std::vector<std::pair<…>>::_M_realloc_insert). That's fine — it's exactly the signal that tells you which instantiation is hot.

macOS: Instruments

  1. Launch Instruments (it ships with Xcode) and choose the Time Profiler template — or from a terminal:
    xctrace record --template 'Time Profiler' --launch -- ./my_program
  2. Record while the slow thing happens, then stop.
  3. In the call tree, right-click and turn Invert Call Tree off, then File → Export the call tree as text.

perf doesn't exist on macOS (no perf_events), and Instruments' .trace bundle is a directory, not a file we can take. The practical path is to export a call tree and collapse it, or — simpler — use samply, which works on macOS and Linux and can write a profile flamelens reads.

In JetBrains CLion

  1. CLion has a built-in profiler on Linux (it drives perf) and macOS (it drives DTrace): Run → Profile '<target>', or the profile button in the run widget.
  2. First run on Linux, CLion will tell you if kernel.perf_event_paranoid blocks it — set it as it suggests (sudo sysctl -w kernel.perf_event_paranoid=1), which is the same setting the command-line flow needs.
  3. Exercise the slow path, stop, and read the flame graph in the profiler tool window.
  4. For a file to upload, run the perf record / perf script pair from CLion's built-in terminal against the binary CLion just built (it's in cmake-build-debug/ or your configured output directory).

In VS Code

  1. There's no native profiler in the C/C++ extension, so profiling is a task. Add one to .vscode/tasks.json so it's one keystroke:
    {
      "version": "2.0.0",
      "tasks": [{
        "label": "profile with perf",
        "type": "shell",
        "command": "perf record -F 99 -g -- ${workspaceFolder}/build/my_program && perf script > profile.perf"
      }]
    }
  2. Run it with Ctrl+Shift+P → Tasks: Run Task → profile with perf.
  3. profile.perf lands in your workspace folder.

Windows and Intel VTune

On Windows, use Windows Performance Recorder (wpr -start CPU -filemode, then wpr -stop trace.etl) and collapse the ETL with FlameGraph's stackcollapse-wpa.pl. With Intel VTune on either platform, vtune -collect hotspots -r result -- ./my_program then vtune -report top-down -format csv gives you something collapsible. Either way, folded stacks are the destination — it's the format everything can reach.

Now analyze it. Take profile.perf or profile.folded to the flamelens terminalauth <email>verify <code>scan init. The same format covers Rust and Go binaries, since by the time perf sees them they're all just symbols.

Why there's no “scan live” for native code

Java exposes JMX and Node exposes the inspector, so flamelens can attach to those over the network. A compiled binary exposes nothing — there's no runtime listening, and perf works by asking the kernel on that machine for sample interrupts. There is no remote equivalent and no port to open, so the honest answer is the same one Python gets: run an agent next to the process.

Continuous sampling with an agent ENTERPRISE

The agent runs on the host, connects out over HTTPS, and samples on a cadence — so you get a profile every N minutes, each diffed against the last:

agent new prod-encoder-1
# → prints a token, ONCE. store it like a password.

schedule new encoder 1234 native 30 60
# → sample pid 1234 for 60s, every 30 minutes

Like Python, the target is a pid (or whatever your script resolves to one), since that's what perf takes. The recorder step is two lines:

perf record -F 99 -p "$TARGET" -g -o /tmp/perf.data -- sleep "$secs"
perf script -i /tmp/perf.data | c++filt > "$out"

The agent needs permission to sample: kernel.perf_event_paranoid at 1 or lower, or CAP_PERFMON (CAP_SYS_ADMIN on kernels before 5.8). In Kubernetes that's securityContext.capabilities.add: ["PERFMON"] plus shareProcessNamespace: true for a sidecar to see the app container's pids. The full polling loop is on the JavaScript page — only the recorder line differs per runtime.

What makes a good profile