gProfiler: One Flamegraph Across Java, Python, Go and the Kernel
gProfiler is a system-wide profiler, combining multiple sampling profilers to produce unified visualization of what your CPU is spending time on.
At a glance
- What is it?
- gProfiler is an Apache-2.0 Python tool that runs several sampling profilers at once and merges their stack traces into a single flamegraph of the whole machine. It suits engineers debugging a slow service in production who need to see native, managed and kernel time in one picture.
- Who is it for?
- gProfiler earns its place on a Linux production host where a slow service mixes native, managed and kernel time and per-language profilers give you disconnected pictures. It is the wrong choice on non-Linux machines, on Aarch64 where runtime coverage is still incomplete, or when you need per-language profilers that never touch process internals.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One picture of native, managed and kernel time
The problem gProfiler addresses is not slow code in isolation but slow code in context. A service that spends its time in a JVM, in a Python interpreter, in Go code and in kernel calls produces four separate profiles under ordinary tooling, and merging them by hand is slow and error prone. gProfiler runs multiple sampling profilers and produces a unified visualization of what the CPU is spending time on, with stack traces for all processes on the system, covering native programs including Golang, the Java and Python runtimes, and kernel routines. The project describes itself as a system-wide profiler, and the distinction from a language profiler is the point: you get one flamegraph rather than four. It runs on Linux, on x86_64 and Aarch64, though the Aarch64 support is described as not complete, with not all runtime profilers supported.
Two output files and two symlinks that always point at the newest run
Output handling is unusually explicit, which makes the tool easy to script around. A run produces an aggregated, collapsed stack samples file named `profile_<timestamp>.col` and a flamegraph file named `profile_<timestamp>.html`. Two symbolic links, `last_profile.col` and `last_flamegraph.html`, always point to the most recent output files, so a monitoring agent can fetch a stable path instead of parsing timestamps. The output directory is set with the `--output-dir` option, and the `--rotating-output` option keeps only the last results, which is the setting to reach for on a long-lived agent where disk usage matters. That pairs well with uploading, since the historical record then lives on the server rather than on disk. When the flamegraph file is not wanted, `--no-flamegraph` suppresses it and leaves only the collapsed stack samples file, which is the compact artifact to ship elsewhere.
Sampling at 11 hertz and what a higher frequency costs you
Two flags shape the sampling itself. The `--profiling-frequency` option sets the sampling frequency in hertz, and `--profiling-duration` sets the length of each profiling session in seconds. The default frequency is 11 hertz. The documented trade-off is direct: a higher frequency produces more accurate results but creates greater overhead on the profiled system and its programs. That number is worth taking seriously on a production host, since a profiler that changes the behaviour of the service it measures can hand you a different bottleneck than the real one. Each duration counts as one session, and each session produces its own output, whether that output is written locally, uploaded, or both. On a busy machine, a short duration with the default frequency is the safer starting point, and the frequency is where you spend your overhead budget deliberately rather than by default.
Per-runtime profiler selection, including the Python eBPF path
Each runtime has its own switch, and the defaults differ in ways that matter. For Python, `--python-mode` takes `auto`, which tries PyPerf with eBPF and falls back to py-spy, `pyperf` for no fallback, `pyspy` or `py-spy` to force py-spy, and `disabled` to turn Python profiling off. The documentation notes that profiling using eBPF incurs lower overhead and also provides kernel and native stacks, which is a double benefit on the Python side of a mixed workload. Java is controlled with `--no-java` or `--java-mode disabled`, and `--java-collect-thread-names` records the thread name at sample time so that attribution stays accurate in thread pools where threads get renamed. PHP is enabled with `--php-mode phpspy` and narrowed with `--php-proc-filter`, which passes a process filter to phpspy. .NET uses `--dotnet-mode=dotnet-trace`, and Ruby is disabled with `--no-ruby`. NodeJS has its own modes, with `none` as the default.
attach-maps gives you JavaScript symbols without restarting Node
The NodeJS support has two modes, and the interesting one is `attach-maps`. In `perf` mode, gProfiler augments the system profiler results with jitdump files, which requires your `node` processes to have been started with `--perf-prof` and, for Node 10 and later, `--interpreted-frames-native-stack`. `attach-maps` removes that startup requirement. In this mode gProfiler loads a library based on the node-linux-perf module into all target NodeJS processes, which enables `perf-pid.map` file generation at runtime so that `perf` can symbolicate the compiled JavaScript functions and the profile shows proper JavaScript symbols. The mechanism is worth knowing: gProfiler uses the inspector protocol to connect, sends `SIGUSR1` to the process, and asks it to load the library matching its NodeJS version, using an arsenal of built-in libraries for common NodeJS versions. It applies to processes already running when gProfiler starts and to ones started during the run, and it stops the file writing when gProfiler stops.
Uploading to a studio and the service name that merges your fleet
Results can stay local or go to a self-hosted Granulate Performance Studio. The `--upload-results` flag sends them, and the `--token` option supplies the token generated after registering on the studio site. The `--service-name` option is the interesting one: profiles sent from numerous gProfiler instances using the same service name are aggregated together, which is how you get a view across an entire cluster rather than one host at a time. Both flags can be given together, in which case the local files are written and the results are uploaded, so the rotating-output mode and the upload mode are complementary rather than exclusive. Network access matters here. The studio has to be reachable over HTTPS, and if you downloaded gProfiler from GitHub releases then `https://github.com` must be reachable, or if you use the Docker image the registry must be reachable, `https://index.docker.io` by default. If you need an HTTPS proxy, those domains have to be whitelisted.
Running gProfiler and picking the profilers a session uses
A session is shaped by a handful of options, and the default behaviour is a sensible starting point. The profiling frequency and duration set the shape of each session, while the output and upload options decide where the results land.
Two of these deserve care on a live machine. A higher profiling frequency gives more accurate results at the cost of more overhead on the profiled system, and the default is 11 hertz, so the first run on a production host should keep that default. The runtime switches decide which profilers are active at all, and they differ per language. Python is controlled with `--python-mode`, which defaults to `auto`: PyPerf with eBPF, falling back to py-spy. Java can be turned off with `--no-java`, PHP is enabled with `--php-mode phpspy` and narrowed with `--php-proc-filter`, .NET uses `--dotnet-mode=dotnet-trace`, Ruby is disabled with `--no-ruby`, and NodeJS is off by default until you pass `--nodejs-mode`. A session that touches only the runtimes you care about produces a flamegraph you can read. The alternative to this tool is running a single profiler for a single runtime and reading its flamegraph on its own, which is what the per-language switches imply. py-spy alone covers Python processes and nothing else, and the system profiler alone gives you native and kernel frames but not the Java, PHP, .NET or JavaScript frames that a managed runtime would otherwise hide. gProfiler costs more overhead in exchange for one merged view, so a developer who only suspects one runtime may be better served by the narrow tool and a cheaper sample.
Installing gProfiler from pinned dependencies, a container or a binary
The packaging tells you how the tool is meant to land on a host, and the documentation points to a section called run as for the installation methods. The setup script builds a standard setuptools distribution named gprofiler, reads its version out of `gprofiler/__init__.py`, and pulls its install requirements from a requirements file that also includes a local subpackage. Those requirements are pinned to exact versions, which matters on a production machine because an unpinned upgrade of a profiler can change what it measures.
psutil==7.0.0
requests==2.33.0
ConfigArgParse==1.7
distro==1.9.0The version itself is not hardcoded in the packaging script but parsed from the package source.
version = re.search(r'__version__\s*=\s*"(.*?)"', Path("gprofiler/__init__.py").read_text())Beyond the Python package, the repository ships a container image definition, a separate executable definition built with pyinstaller, and shell scripts for enabling a hook and for linting. A container is the cleanest way to get a matching kernel and runtime environment onto a production host, while the executable path avoids managing a Python environment at all. Network access matters during setup too: a Docker image comes from the registry at `https://index.docker.io` by default, and a release download comes from `https://github.com`. Maintenance is recent, with the last push on 2026-10-22 and releases 1.57.1 on 2026-06-19, 1.56.2 on 2026-03-24 and 1.55.0 on 2025-12-18. The Apache-2.0 licence applies to the source, and the file headers credit Intel Corporation, so check the licence terms yourself if you plan to redistribute it inside a product.
Editorial conclusion
gProfiler earns its place on a Linux production host where a slow service mixes native, managed and kernel time and per-language profilers give you disconnected pictures. It is the wrong choice on non-Linux machines, on Aarch64 where runtime coverage is still incomplete, or when you need per-language profilers that never touch process internals. Before deploying it, run one short session with --profiling-duration and a frequency you can afford, then compare the merged flamegraph against a single language profiler to see which layer dominates.
Frequently asked questions
Which operating systems does gProfiler support?
The documentation states that gProfiler runs on Linux, on x86_64 and Aarch64. Aarch64 support is described as not complete, and not all runtime profilers are supported there.
What files does a gProfiler run produce?
A run writes an aggregated collapsed stack samples file named profile_<timestamp>.col and a flamegraph file named profile_<timestamp>.html, with the last_profile.col and last_flamegraph.html symbolic links pointing at the newest output. The --no-flamegraph option suppresses the HTML file.
How do I upload gProfiler results to a Granulate Performance Studio?
Use the --upload-results flag with the --token option for the token you generated on the studio site and the --service-name option as an identifier for the profiles. Profiles sharing a service name are aggregated together, and both flags can be combined with local output.
Can gProfiler profile NodeJS without restarting the application?
Yes, with the --nodejs-mode attach-maps setting. In that mode gProfiler uses the inspector protocol to load a matching library into running NodeJS processes so that perf can symbolicate JavaScript functions, without requiring the --perf-prof flag at startup.
What is the default sampling frequency of gProfiler and how does it affect overhead?
The default profiling frequency is 11 hertz. The documentation states that a higher frequency gives more accurate results but creates greater overhead on the profiled system and the programs running on it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/intel-gprofiler)Was this useful?