Custom ponyc Builds for Debugging¶
ponyc supports several build-time options that instrument the compiler and runtime for debugging. These require building ponyc from source with specific flags. All options described here use the Unix/Makefile build system.
Build Workflow¶
Every option follows the same pattern:
-
Clone ponyc and build its dependencies (one-time):
git clone https://github.com/ponylang/ponyc.git cd ponyc make libs -
Configure with the desired option:
make configure use=<option> -
Build:
make build
The resulting ponyc binary is in build/release/ — or, when you pass use= options, a suffixed directory like build/release-address_sanitizer that lists the enabled options. Use it in place of your system ponyc to compile programs with the instrumentation enabled.
To combine multiple options, separate them with commas:
make configure use=address_sanitizer,undefined_behavior_sanitizer
Not all combinations are valid — see the individual sections below for compatibility notes.
For full source build instructions (including platform-specific prerequisites), see Building ponyc from Source.
Valgrind¶
Annotates the Pony runtime so Valgrind tools like Memcheck and Helgrind can understand Pony’s custom memory allocator.
Valgrind development headers must be installed before building. On Debian/Ubuntu:
apt-get install valgrind
Build ponyc with Valgrind support:
make configure use=valgrind
make build
Compile your program with the instrumented ponyc, then run it under Valgrind:
valgrind ./my-program
Not available on OpenBSD
Valgrind has no OpenBSD port, so its development headers aren’t available and use=valgrind can’t be built there. gmake configure use=valgrind is rejected on OpenBSD with an error.
Doesn’t run on DragonFly BSD
use=valgrind builds on DragonFly and the Pony programs it compiles link, but DragonFly ships Valgrind 3.15, which is too old to run a Pony program; it hangs on the runtime’s memory arena. See ponyc#5435.
Address Sanitizer¶
AddressSanitizer (ASan) detects memory errors at runtime: buffer overflows, use-after-free, double-free, and stack buffer overflows.
make configure use=address_sanitizer
make build
Compile your program with the instrumented ponyc and run it normally. ASan reports errors to stderr as they occur.
Cannot be combined with thread_sanitizer.
To catch errors in the Pony runtime’s own allocations, pair it with pool_memalign, which routes every runtime allocation through posix_memalign/free so ASan can surround each one with redzones:
make configure use=pool_memalign,address_sanitizer
make build
The instrumented ponyc isn’t LeakSanitizer-clean, so running it — during its own build, and whenever you use it to compile a program — needs ASAN_OPTIONS set in the environment. Without it the compiler reports its own leaks at exit, and on libc++ platforms aborts on false container overflows:
- Linux:
ASAN_OPTIONS=detect_leaks=0 - FreeBSD and macOS:
ASAN_OPTIONS=detect_leaks=0:detect_container_overflow=0— the extra flag suppresses false positives from libc++ container annotations when the instrumented compiler sharesstd::vector/std::stringwith the non-instrumented vendored LLVM.
A use=pool_memalign,address_sanitizer build’s ponyc lands in build/release-address_sanitizer-pool_memalign, not build/release. For example, to compile a program on Linux:
ASAN_OPTIONS=detect_leaks=0 ./build/release-address_sanitizer-pool_memalign/ponyc path/to/your/program
Both options concern ponyc itself, not the programs it compiles. A correct Pony program is LeakSanitizer-clean at exit, so run a program you compiled with LeakSanitizer left on — it will report genuine leaks in your own code, such as memory you allocate through FFI and never free.
Not available on OpenBSD or DragonFly BSD
OpenBSD’s base toolchain ships no AddressSanitizer runtime, and DragonFly BSD’s gcc13 toolchain doesn’t build one either, so use=address_sanitizer can’t be built on either. gmake configure use=address_sanitizer is rejected on both OpenBSD and DragonFly with an error.
Thread Sanitizer¶
ThreadSanitizer (TSan) detects data races at runtime.
make configure use=thread_sanitizer
make build
Compile your program with the instrumented ponyc and run it normally. TSan reports data races to stderr.
Cannot be combined with address_sanitizer.
Not available on OpenBSD or DragonFly BSD
OpenBSD’s base toolchain ships no ThreadSanitizer runtime, and DragonFly BSD’s gcc13 toolchain doesn’t build one either, so use=thread_sanitizer can’t be built on either. gmake configure use=thread_sanitizer is rejected on both OpenBSD and DragonFly with an error.
Undefined Behavior Sanitizer¶
UndefinedBehaviorSanitizer (UBSan) detects undefined behavior at runtime: signed integer overflow, null pointer dereference, misaligned memory access, and other categories.
make configure use=undefined_behavior_sanitizer
make build
Compile your program with the instrumented ponyc and run it normally. UBSan reports violations to stderr.
Can be combined with address_sanitizer or thread_sanitizer.
Not available on OpenBSD or DragonFly BSD
OpenBSD ships only the minimal UndefinedBehaviorSanitizer runtime, not the standalone runtime ponyc links against; DragonFly BSD’s gcc13 toolchain doesn’t build the UBSan runtime at all. Either way, use=undefined_behavior_sanitizer can’t be built there. gmake configure use=undefined_behavior_sanitizer is rejected on both OpenBSD and DragonFly with an error.
Coverage¶
Instruments ponyc and the Pony runtime for source-level code coverage. It’s built for working on ponyc itself: run a coverage-instrumented ponyc and its test suite to see which parts of the compiler and runtime C code each run exercises. To measure coverage of your own Pony code instead, you don’t need a custom build — see Coverage Reports.
make configure use=coverage
make build
The instrumentation comes from the host toolchain’s coverage support — -fprofile-instr-generate -fcoverage-mapping under Clang, -fprofile-arcs -ftest-coverage under GCC. A Clang build writes an LLVM profile (default.profraw) that you turn into a report with llvm-profdata and llvm-cov; a GCC build writes .gcda files for gcov or lcov. These report tools aren’t part of the ponyc build — install them separately. For the full workflow, see Clang’s source-based coverage guide or the gcov documentation.
Not available on OpenBSD
OpenBSD’s base toolchain ships an incomplete profiling runtime, so a coverage-instrumented ponyc can’t be linked there. gmake configure use=coverage is rejected on OpenBSD with an error.
DTrace / SystemTap¶
The Pony runtime includes USDT (Userland Statically Defined Tracing) probes. They’re consumed by SystemTap on Linux and by DTrace on FreeBSD. No other platform is supported: macOS removed DTrace, and neither DragonFly BSD nor OpenBSD ships a DTrace-compatible probe-generation tool (OpenBSD’s btrace is a separate tracer with no USDT support). On DragonFly BSD and OpenBSD, gmake configure use=dtrace is rejected with an error rather than failing later in the build.
Building requires a dtrace-compatible tool on your PATH:
make configure use=dtrace
make build
The probes are defined under the pony provider and cover:
- Actor lifecycle — creation, scheduling, descheduling
- Message passing — actor-to-actor and thread message send/receive/push/pop
- Backpressure — overload, mute/unmute, pressure state changes
- Garbage collection — GC start/end, send/receive phases, threshold changes
- Memory — heap allocation
- Runtime lifecycle — init, start, end
- Work stealing — steal success/failure
- Thread state — suspend, resume, nanosleep
For the full list of probes with parameter types and descriptions, see src/common/dtrace_probes.d in the ponyc source.
Static linking on FreeBSD
Statically linked programs (ponyc --static) build and run but do not expose their probes to DTrace. FreeBSD registers USDT probes through the runtime linker, which a static binary doesn’t use; trace a dynamically linked build instead.
Runtime bitcode is incompatible with DTrace
A ponyc built with use=dtrace cannot compile programs with --runtimebc, and rejects the combination with an error. Probe generation operates on native object files, not LLVM bitcode, so the bitcode runtime carries no probes — a --runtimebc binary would have none. Use the default static runtime to trace your program.
Runtime Statistics¶
Runtime statistics tracking instruments the Pony runtime to report memory usage per actor and per scheduler thread. This is useful for understanding where memory is going in a running program.
Two options are available:
runtimestats— enables basic runtime statisticsruntimestats_messages— adds per-message tracking on top ofruntimestats
For most debugging scenarios, enable both:
make configure use=runtimestats,runtimestats_messages
make build
For details on the available tracking functions and how to call them from Pony code, see Tracking Memory Usage at Runtime.
Runtime Tracing¶
Runtime tracing records events from the Pony scheduler, actor lifecycle, and message passing. Events can be written to a trace file in the background, or stored in in-memory circular buffers that dump to stderr on abnormal termination (SIGILL, SIGSEGV, SIGBUS). Trace files use Chromium JSON format and can be viewed with Perfetto.
make configure use=runtime_tracing
make build
For details on tracing options and usage, see Tracing Pony Programs.
Systematic Testing¶
Systematic testing replaces the Pony scheduler with a deterministic, single-threaded scheduler that explores different actor interleaving orders. This is useful for reproducing concurrency bugs that are otherwise non-deterministic.
Systematic testing requires two options together:
make configure use=scheduler_scaling_pthreads,systematic_testing
make build
The scheduler_scaling_pthreads option is required because systematic testing needs pthread-based scheduler scaling rather than the default implementation.
Seed-Based Replay¶
When a systematic testing build runs, it uses a random seed to determine the interleaving order. If a run triggers a bug, the runtime prints the seed and a rerun command to stderr. You can replay the exact same interleaving:
./my-program --ponysystematictestingseed 12345
A typical workflow:
- Run the program repeatedly with different seeds until a bug manifests.
- Note the seed from the failing run.
- Replay with
--ponysystematictestingseedto reproduce the bug deterministically. - Fix the bug and verify with the same seed that the fix holds.