Add ability to trace runtime events in chromium json format #4649
We have added the ability to trace runtime events for pony applications. Runtime tracing is helpful in understanding how the runtime works and for debugging issues with the runtime.
The tracing can either write to file in the background or work in a "flight recorder" mode where events are stored into in memory circular buffers and written to stderr in case of abnormal program behavior (SIGILL, SIGSEGV, SIGBUS, etc).
Traces are written in chromium json format and trace files can be viewed with the perfetto trace viewer.
Links:
Chromium json format
perfetto trace viewer
Prevent memory explosion when using ponynoblock in some programs
We have enhanced the runtime to prevent memory explosion in some programs when --ponynnoblock
is used.
The following is an example of such a program:
use "collections"
use "random"
use "time"
class Notify is TimerNotify
let _a: A
var _m: Main
new iso create(a: A, m: Main) =>
_a = a
_m = m
fun ref apply(timer: Timer, count: U64): Bool =>
_a.done(_m)
// returning false from apply in a TimerNotify
// instructs the runtime that the timer should be
// cancelled which allows for the owning Timers
// actor to potentially be reaped if there are no
// references to it and/or if the cycle detector
// decides it is safe to be reaped
false
actor A
// acquires a reference to the Main actor via an ORCA
// message to prevent the Main actor from being reaped
// until after this actor is done with the reference to
// it. This reference will be released the next time GC
// runs for this actor since this actor doesn't keep a
// reference to it
new create(m: Main, n: USize) =>
// acquires a reference to the Timers actor implicitly
// because this actor creates it. The reference will
// be freed after the next GC of this actor since this
// actor doesn't keep a reference to it
let timers = Timers
// passing a reference to the Main and A actors via the
// Notify class delays/prevents them from being reaped
// until after the references are no longer used either
// because the Notify object is destroyed or the Timers
// actor is reaped
let timer = Timer(Notify(this, m), n.u64() * 1_000_000)
timers(consume timer)
// acquires a reference to the Main actor via an ORCA
// message (if the previous reference has already been
// released) to prevent the Main actor from being reaped
// until after this actor is done with the reference to
// it. This reference will be released the next time GC
// runs for this actor since this actor doesn't keep a
// reference to it
// After this behavior completes, this actor can
// potentially be reaped if there are no references to it
// and/or if the cycle detector decides it is safe to be
// reaped
be done(m: Main) =>
m.done(this)
actor Main
let n: USize = 100000
var e: USize = 0
let max_num_oustanding: USize = 13
var num_outstanding: USize = 0
let s: SetIs[A] = s.create()
let rand: Random
new create(env: Env) =>
let t = Time.seconds()
rand = Rand(t.u64())
spawn()
be spawn() =>
if e < n then
while num_outstanding < max_num_oustanding do
let i = rand.int[USize](2)
// passing a reference to the Main actor
// delays/prevents it from being reaped
// until after the reference is no longer
// used
let a = A(this, i)
// saving references to A actors in the set
// delays/prevents them from being reaped
// until after the reference has been deleted
s.set(a)
e = e + 1
num_outstanding = num_outstanding + 1
end
end
be done(a: A) =>
// deletes a reference to an A actor but the actor
// can only be reaped after GC is run for the Main
// actor as that is when the ORCA message releasing
// the reference is sent
s.unset(a)
num_outstanding = num_outstanding - 1
spawn()
before:
root@5babe01f566f:/workspaces/ponyc# /usr/bin/time ./before --ponynoblock
35.91user 7.08system 0:05.91elapsed 727%CPU (0avgtext+0avgdata 1716480maxresident)k
0inputs+0outputs (3major+428831minor)pagefaults 0swaps
after:
root@5babe01f566f:/workspaces/ponyc# /usr/bin/time ./after --ponynoblock
41.16user 2.73system 0:05.89elapsed 744%CPU (0avgtext+0avgdata 13440maxresident)k
0inputs+0outputs (3major+3137minor)pagefaults 0swaps
Make GC for an actor more aggressive if an actor GC frees many actor references
We have enhanced the runtime to make actor GC more aggressive if an actor GC cycle deleted many references to other actors (> 100). This could be due to a program design where an actor creates many other actors and keeps references to them for a bit before replacing those references with ones for other newly created actors. This change will make GC for these actors more aggressive in order to try and limit memory explosion that could potentially occur otherwise.
Update to LLVM 17.0.1
We've updated the LLVM version used to build Pony to 17.0.1.
[0.59.0] - 2025-04-26
Fixed
- Prevent memory explosion when using ponynoblock in some programs (PR #4666)
- Make GC for an actor more aggressive if an actor GC frees many refs (PR #4662)
Added
- Add ability to trace runtime events in chromium json format (PR #4649)