Palanteer: an event recorder you drive from a Python script, not a sampling profiler
Visual Python and C++ nanosecond profiler, logger, tests enabler
At a glance
- What is it?
- A header-only C++ instrumentation library plus a Python package that record atomic events, stream them to a viewer, and expose a remote CLI so a script can configure and freeze the program under test. Suited to deterministic debugging of long runs and embedded targets, and a poor fit when you only need periodic CPU samples.
- Who is it for?
- Adopt Palanteer if your debugging need is to record a specific sequence of events and replay it under script control, particularly in C++ or on an embedded target where sampling profilers are awkward. Do not adopt it as a substitute for a sampling profiler, because what it records is explicit instrumentation rather than periodic stack samples.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly C++, 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
The unit of observation is an atomic event, not a sample
Palanteer is described as a set of tools for C++ and Python programs whose central idea is the collection of meaningful atomic events: timings, memory, lock wait and usage, context switches and data values.
That framing is the whole design. A sampling profiler answers which function was on the stack at an arbitrary moment, which is exactly the wrong question when the bug is a specific ordering, a lock convoy, or a value that went wrong once in ten million iterations. An event recorder answers what actually happened, in order, provided you instrumented it.
The recorded stream is then viewed rather than summarised. The viewer offers hierarchical logs, a timeline, a plot, a histogram and a flame graph, and logging is exposed through a printf-compatible interface. The C++ side adds printf-like nanosecond logging with level, category and graphable arguments.
Up to 8 streams can be recorded simultaneously, meaning data from different processes can be correlated in one session, which is the case where a single process profiler is structurally blind.
The C++ side is one header and a compile-time switch
The C++ library is an ultralight single-header cross-platform instrumentation library. Integration has three parts: define the implementation macro exactly once, include the header, and start it.
#include <stdlib.h> // For "rand"
#define PL_IMPLEMENTATION 1 // The instrumentation library shall be "implemented" once
#include "palanteer.h"
plInitAndStart("example");
plDeclareThread("Main");
plRegisterCli(setBoundsCliHandler, "config:setRange", "min=int max=int", "Sets the value bounds of the random generator");
plFreezePoint();
plBegin("Generate some random values");
for(int i=0; i<100000; ++i) {
int value = globalMinValue + rand()%(globalMaxValue+1-globalMinValue);
plData("random data", value);
}
plEnd("");
plStopAndUninit();plBegin and plEnd delimit a scope event, and the plEnd("") form is documented as a shortcut for repeating the same name. plData attaches a named value to the enclosing scope, which is how the recorded data values end up in the viewer rather than just the timings.
Three compile-time features reduce the cost of instrumentation in release-adjacent builds. Groups of instrumentation are selected at compile time, so you can compile out whole categories. Static strings are hashed at compile time, which is what makes nanosecond logging affordable, and there is compile-time striping of all instrumentation static strings for projects with a very large number of them. The instrumentation itself is switched on at build time, as the example comment shows on Linux with g++ -DUSE_PL=1.
What the instrumentation costs, according to the README
The README publishes three figures for the C++ side and they are the basis for judging whether the approach fits your budget.
Nanosecond resolution at roughly 25 nanoseconds cost per event on a standard x64 machine. Up to about 5 million events per second when recording, with the bottleneck stated as the server processing side rather than the instrumented program. And up to about 150 000 events per second when the flow is processed through a Python script, with the bottleneck stated as the Python script side.
Read together, these say something specific about the architecture. The instrumented program is not the limit at any of these rates, so instrumentation cost is not what should stop you. The script processing rate is the number to plan against, because a scripted session that replays a large recording will be bounded by the Python side rather than by the capture.
The README links to its documentation for the details behind these figures.
The remote CLI is the feature that changes how you debug
The example program registers a CLI handler and then calls plFreezePoint(). Together they let an external script reconfigure a running process at a controlled moment.
The handler receives a communication helper, and the parameters are declared in the registration string rather than parsed by hand:
void setBoundsCliHandler(plCliIo& cio)
{
int minValue = cio.getParamInt(0);
int maxValue = cio.getParamInt(1);
if(minValue>maxValue) {
cio.setErrorState("Minimum value (%d) shall be lower than the maximum value (%d)", minValue, maxValue);
return;
}
globalMinValue = minValue;
globalMaxValue = maxValue;
}Calling cio.setErrorState reports failure with a formatted message; returning without calling it means the call succeeded. The example comment notes that no care is taken about thread safety here, to keep the example simple, which is a reminder that the handler runs in your program's thread.
The Python scripting module can call that handler, stop the program at the freeze point, and then read back the recorded data values and the timing of the scope event. That is the deep testing claim the README makes, and it is a real capability rather than a slogan: the program under test becomes a device you drive.
The repository layout matches the three parts: a c++/ directory for the library, a python/ directory for the scripting side, server/ for the server that receives streams, tools/ for tooling, plus CMakeLists.txt, cmake/ and INSTALL.md.
Python gets instrumentation without touching the code
The Python side runs unmodified programs with a syntax similar to cProfile, because most instrumentation is automatic by default. The automatically captured events are functions enter and leave, interpreter memory allocations, all raised exceptions, garbage collection runs, and coroutines. Multithreading, asyncio and gevent are supported.
A memory allocator and GC trace gathered without modifying the program is the strongest argument for the Python path. You get allocation and garbage collection events alongside the function timeline rather than as a separate run.
Manual instrumentation is still available when the automatic one should be enhanced or replaced. The README's Python example is described as the equivalent of the C++ program above, with the same CLI handler concept, where a handler returning a non-null status signals failure and a text answer carries the explanation:
def setBoundsCliHandler(minValue, maxValue):
global globalMinValue, globalMaxValue
if minValue>maxValue:
return 1, "Minimum value (%d) shall be lower than the maximum value (%d)" % (minValue, maxValue)The features list also mentions enhanced assertions and a stack trace dump for C++, and automatic code instrumentation of C++ functions enter and leave, which the README restricts to Linux GCC only.
Where Palanteer is the wrong tool
The first boundary is that you must decide in advance what to record. The instrumentation is explicit in both languages. An event you did not declare does not appear, so a performance question phrased as why is this function slow is answerable only if you instrumented it or if the scope timing happens to line up. For a cold profile of unfamiliar code, a sampling profiler answers faster and needs no edits.
The second is platform coverage for the automatic C++ path. The manual instrumentation is a single header and cross-platform, but automatic function instrumentation is documented as Linux GCC only. A team on Windows or clang gets the library without that automation.
The third is scale of the recorded stream. A recording is a file, and the server processing side and Python script side both have documented throughput ceilings. A program that emits millions of events per second for hours is a case where you want sampling and aggregation rather than a complete ordered trace.
On licensing, the repository has a LICENSE.md rather than a standard identifier and GitHub classifies it as a custom licence it cannot categorise, so read that file before redistributing anything you build with it.
Editorial conclusion
Adopt Palanteer if your debugging need is to record a specific sequence of events and replay it under script control, particularly in C++ or on an embedded target where sampling profilers are awkward. Do not adopt it as a substitute for a sampling profiler, because what it records is explicit instrumentation rather than periodic stack samples. Verify first that the viewer and server build step from the repository root, since the README does not document installation and the details sit in INSTALL.md, and note that the Python path instruments unmodified programs while automatic C++ instrumentation is documented as Linux GCC only.
Frequently asked questions
How do I install Palanteer?
The README does not contain installation commands; it points at INSTALL.md in the repository. The project builds with CMake, since the repository root carries CMakeLists.txt and a cmake/ directory.
How is the C++ instrumentation library linked into a program?
It is a single header. You define PL_IMPLEMENTATION once, include palanteer.h, and call plInitAndStart with the program name. Instrumentation itself is selected at build time, shown in the README example as g++ -DUSE_PL=1.
What does Palanteer instrument automatically in Python?
Unmodified Python programs are analysed with a cProfile-like syntax covering functions enter and leave, interpreter memory allocations, all raised exceptions, garbage collection runs and coroutines. Multithreading, asyncio and gevent are supported.
What is a freeze point for in Palanteer?
plFreezePoint marks a place in the program where the Python scripting module can temporarily stop execution. In the README example the script stops there, calls the registered CLI handler to change configuration, then inspects the recorded data.
How many events per second can Palanteer record?
The README states up to about 5 million events per second when recording, with the bottleneck on the server processing side, and up to about 150 000 events per second when processing the flow through a Python script.
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/dfeneyrou-palanteer)Was this useful?