Skip to content

Repository files navigation

Markdown flame logo

profiler-md

Converts performance profiles to human and LLM friendly Markdown.

Note

This package is in beta and I'm excited for you to try it! Share suggestions, bug reports, and feature requests by filing an issue.

Demo

Converting and diffing CPU profiles in the terminal
# CPU profile

Took 2.49s over 2,659 samples (939.9µs per sample).

| Category           |     % |    Time | Samples |
| ------------------ | ----: | ------: | ------: |
| Third-party        | 87.4% |   2.18s |   2,410 |
| Garbage collector  |  7.6% | 191.0ms |     156 |
| Standard library   |  3.9% |  96.8ms |      70 |
| Native             |  0.9% |  22.7ms |      20 |
| Regular expression |  0.1% |   2.5ms |       2 |
| Ours               |  0.1% |   1.3ms |       1 |

## Hottest functions

### Self time

Functions ranked by time spent directly in the function body, excluding callees.

|    % |    Time | Samples | Function                        | Location                                             |
| ---: | ------: | ------: | ------------------------------- | ---------------------------------------------------- |
| 7.6% | 191.0ms |     156 | `(garbage collector)`           | `<unknown>`                                          |
| 2.9% |  71.5ms |      57 | `wrapSafe`                      | `node:internal/modules/cjs/loader:1671:18`           |
| 2.8% |  69.0ms |      66 | `recursiveTypeRelatedTo`        | `node_modules/typescript/lib/typescript.js:64383:38` |
| 2.5% |  63.0ms |      66 | `isRelatedTo`                   | `node_modules/typescript/lib/typescript.js:63813:27` |
| 2.1% |  51.7ms |      43 | `checkTypeRelatedTo`            | `node_modules/typescript/lib/typescript.js:63505:32` |

The output continues with the hottest functions by total time, the hottest call stacks, and per-line, caller, and callee detail. See the full output, or examples/output/ for heap snapshots and diffs.

Features

  • Polyglot: profile, call graph, and heap snapshot formats across many languages
  • Profile analysis: sampling rates, category breakdowns, and the hottest functions, call stacks, lines, callers, and callees
  • Heap analysis: self and dominator-based retained sizes, retainer paths, and the largest constructors and strings
  • Multi-profile inputs: one all JFR recording becomes CPU, allocation, and lock contention profiles
  • Diffing: ranked regressions and improvements between two profiles or two heap snapshots, across formats and profilers
  • Source maps: resolves minified and transpiled locations back to original sources
  • Zero config: auto-detects the format and profiler
  • Configurable: top entry counts, base URLs, categorization, filtering, and diff matching
  • CLI and API: a command line and a fully-typed API with sync and async variants (API docs)
  • Self-documenting: --help <language> and --help <format> explain how to generate and read each profile type
  • Agent-ready: ships a skill that guides an agent through profiling and optimizing your code

Install

# npm
$ npm i -g profiler-md

# Homebrew
$ brew install tomeraberbach/tap/profiler-md
Shell completions (optional)
# Bash (Linux)
$ profiler-md --completion bash > ~/.local/share/bash-completion/completions/profiler-md

# Bash (macOS/Homebrew)
$ profiler-md --completion bash > $(brew --prefix)/etc/bash_completion.d/profiler-md

# Fish
$ profiler-md --completion fish > ~/.config/fish/completions/profiler-md.fish

# Zsh
$ profiler-md --completion zsh > ~/.zfunc/_profiler-md

# PowerShell
$ profiler-md --completion pwsh >> $PROFILE.CurrentUserCurrentHost

Usage

CLI

# Convert a profile, paged and syntax highlighted
$ profiler-md profile.cpuprofile

# Diff two profiles or two heap snapshots
$ profiler-md base.cpuprofile current.cpuprofile

# Write the Markdown to a file
$ profiler-md profile.pb.gz -o profile.md

# Read a profile from stdin
$ node --cpu-prof app.js && cat *.cpuprofile | profiler-md

# Show how to profile a language
$ profiler-md --help python
All flags
$ profiler-md --help
Converts performance profiles to human and LLM friendly Markdown.

Usage: profiler-md [OPTIONS] [FILE]
       profiler-md [OPTIONS] BASE CURRENT
       profiler-md --help [TOPIC]

Examples:
  # Convert a profile, paged and syntax highlighted
  $ profiler-md profile.cpuprofile

  # Diff two profiles or two heap snapshots
  $ profiler-md base.cpuprofile current.cpuprofile

  # Write the Markdown to a file
  $ profiler-md profile.pb.gz -o profile.md

  # Read a profile from stdin
  $ node --cpu-prof app.js && cat *.cpuprofile | profiler-md

  # Show how to profile a language
  $ profiler-md --help python

  FILE                                Profile to convert (default: stdin)
  BASE                                Base profile to diff
  CURRENT                             Current profile to diff against the base

Output:
  -o, --output FILE                   Output file (default: - for stdout)
  --no-pager                          Disable stdout paging (default: auto)
  --color, --no-color                 Enable or disable ANSI syntax 
                                      highlighting (default: auto)

Input:
  -f, --format FORMAT                 Input profile format (default: auto)
  -r, --origin ORIGIN                 Input profile origin (default: auto)
  --source-maps GLOB                  Source maps (JSON or inline) to apply to 
                                      locations (repeatable)
  --base-url STRING                   Base URL or path to show paths relative 
                                      to, or auto for their common ancestor 
                                      (default: cwd)

Ranking:
  --top-n N                           Entries to show per ranking, including 
                                      category subsections (default: 20)
  --min-category-share FRACTION       Share of a profile a category needs for 
                                      its own subsection, from 0 to 1 (default: 
                                      0.01)

Filtering:
  --category REGEX=CATEGORY           Categorize functions whose name or 
                                      location matches REGEX as CATEGORY, first 
                                      rule winning (repeatable)
  --hide REGEX                        Hide entries whose name or location 
                                      matches REGEX, still counting hidden 
                                      entries in totals (repeatable)
  --show REGEX                        Show only entries whose name or location 
                                      matches REGEX, still counting hidden 
                                      entries in totals (repeatable)
  --hide-category CATEGORY            Hide entries of CATEGORY, still counting 
                                      hidden entries in totals (repeatable)
  --show-category CATEGORY            Show only entries of CATEGORY, still 
                                      counting hidden entries in totals 
                                      (repeatable)

Diffing:
  --match-name REGEX=REPLACEMENT      Rewrite names matching REGEX to 
                                      REPLACEMENT when pairing diffed entries 
                                      (repeatable)
  --match-location REGEX=REPLACEMENT  Rewrite locations (URL, path, or logical 
                                      name) matching REGEX to REPLACEMENT when 
                                      pairing diffed entries (repeatable)

Help:
  -h, --help [TOPIC]                  Show this help message or topic docs
  --version                           Show the version
  --completion SHELL                  Print a completion script for SHELL 
                                      (bash, fish, nu, pwsh, or zsh)

Formats:
  callgrind, collapsed, ghc-eventlog, ghc-json-profile, hprof, jfr,
  jsc-heap-snapshot, memray, perf, pprof, speedscope, systing, v8-cpu-profile,
  v8-heap-profile, v8-heap-snapshot, webkit-timeline-recording

Origins:
  async-profiler, bun, chrome, deno, dotnet-trace, eflambe, excimer, ghc, go,
  gperftools, jdk, memray, node, node-pprof, perf, pprof-jl, pprof-rs,
  profile-jl, py-spy, pyinstrument, rbspy, safari, simpleperf, systing, tachyon,
  unknown, valgrind

Function categories:
  ours, third-party, stdlib, native, unknown, garbage-collector, compiler, jit,
  regexp, kernel, idle

Heap snapshot categories:
  object, array, string, concatenated-string, sliced-string, function, code,
  regexp, number, big-number, symbol, native, object-shape, internal, synthetic,
  unknown

Languages:
  c/cpp, csharp/fsharp, elixir/erlang, fortran, go, haskell, java/kotlin/groovy,
  javascript/typescript, julia, php, python, ruby, rust, swift, zig

Docs: https://github.com/TomerAberbach/profiler-md
Bugs: https://github.com/TomerAberbach/profiler-md/issues

API

import { openAsBlob } from 'node:fs'
import { diffProfilesAsync, profileToMdAsync } from 'profiler-md'

// Convert a profile or heap snapshot. The format and origin are auto-detected
console.log(await profileToMdAsync(await openAsBlob(`example.cpuprofile`)))

// Diff two profiles or two heap snapshots
console.log(
  await diffProfilesAsync(
    await openAsBlob(`base.cpuprofile`),
    await openAsBlob(`current.cpuprofile`),
  ),
)

See the API docs for sync variants, explicit formats and origins, and configuration callbacks.

Skill

Use the profiler-md skill to have an agent profile and optimize your code:

$ npx skills add TomerAberbach/profiler-md --skill profile-optimize

See skills.sh for more info.

Fun fact: the skill has profiled and optimized profiler-md itself!

Languages and formats

The table lists only the formats each language's own tools generate. Third-party tools convert others. Each format's docs explain how to generate and read it.

LanguageFormats
C⁠/⁠C++
Callgrind
perf.data
pprof
systing
C#⁠/⁠F#
Speedscope
Elixir⁠/⁠Erlang
Collapsed stacks
Fortran
pprof
Go
pprof
Haskell
GHC eventlog
GHC JSON profile
Java⁠/⁠Kotlin⁠/⁠Groovy
Collapsed stacks
HPROF
JFR
JavaScript⁠/⁠TypeScript
JSC heap snapshot
pprof
V8 CPU profile
V8 heap profile
V8 heap snapshot
WebKit timeline recording
Julia
pprof
V8 heap snapshot
PHP
Collapsed stacks
Speedscope
Python
Collapsed stacks
memray
Speedscope
Ruby
Callgrind
Collapsed stacks
pprof
Speedscope
Rust
pprof
Swift
pprof
Zig
perf.data
pprof

Contributing

Stars are always welcome!

For bugs and feature requests, create an issue.

License

MIT © Tomer Aberbach

About

🔥 Converts performance profiles to human and LLM friendly Markdown.

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages