Runtime_events(3)

πŸ’– Runtime_events(3) β€” ring buffer-based runtime tracing πŸ’–

okay so this is the Runtime_events page from the OCaml library manual, and it's literally the group chat for the OCaml runtime's tea β˜• β€” ring-buffer tracing, bestie. Let's get into it.


🌸 NAME 🌸

Runtime_events β€” Runtime events - ring buffer-based runtime tracing


πŸŽ€ Module πŸŽ€

Module Runtime_events

Module Runtime_events : sig end

✨ Documentation ✨

okay so Runtime_events is the module that lets you enable and subscribe to tracing events from the Garbage Collector and other parts of the OCaml runtime. It's giving diagnostic/performance-monitoring main character energy πŸ’… β€” super useful for diagnostics or performance monitoring purposes.

You can use this module to subscribe to events for the current process or external processes asynchronously. No cap.

When enabled β€” either by setting the OCAML_RUNTIME_EVENTS_START environment variable or by calling Runtime_events.start β€” a file with the pid of the process and extension .events will be created. By default this is in the current directory, but it can be over-ridden by the OCAML_RUNTIME_EVENTS_DIR environment variable. Each domain maintains its own ring buffer in a section of the larger file into which it emits events. πŸ¦‹

There is additionally a set of C APIs in runtime_events.h that can enable zero-impact monitoring of the current process or bindings for other languages. iconic.

The runtime events system's behaviour can be controlled by the following environment variables:

  • -OCAML_RUNTIME_EVENTS_START β€” the manpage lists this env var with a leading dash; if set, it will cause the runtime events system to be started as part of the OCaml runtime initialization.
  • -OCAML_RUNTIME_EVENTS_DIR β€” sets the directory where the runtime events ring buffers will be located. If not present, the program's working directory will be used.
  • -OCAML_RUNTIME_EVENTS_PRESERVE β€” if set, this will prevent the OCaml runtime from removing its ring buffers when it terminates. This can help if you're monitoring very short running programs. lowkey a lifesaver for tiny programs 🫢

πŸ“ type runtime_counter πŸ“

type runtime_counter = β€” this is the type for counter events emitted by the runtime. Counter events are used to measure a quantity at a point in time or record the occurrence of an event. In the latter case their value will be one. periodt.

  • EV_C_FORCE_MINOR_ALLOC_SMALL β€” triggering of a minor collection due to a full minor heap. Since 5.0
  • EV_C_FORCE_MINOR_MAKE_VECT β€” triggering of a minor collection due to Array.make. Since 5.0
  • EV_C_FORCE_MINOR_SET_MINOR_HEAP_SIZE β€” triggering of a minor collection due to Gc.minor_heap_size. Since 5.0
  • EV_C_FORCE_MINOR_MEMPROF β€” triggering of a minor collection during memprof young sampling. Since 5.3
  • EV_C_MINOR_PROMOTED β€” total bytes promoted from the minor heap of this Domain to the major heap in the last minor collection. Since 5.0
  • EV_C_MINOR_ALLOCATED β€” total bytes allocated in the minor heap of this Domain in the last minor collection. Since 5.0
  • EV_C_REQUEST_MAJOR_ALLOC_SHR β€” major slice requested due to allocation in major heap. Since 5.0
  • EV_C_REQUEST_MAJOR_ADJUST_GC_SPEED β€” major slice requested by caml_adjust_gc_speed. Since 5.0
  • EV_C_REQUEST_MINOR_REALLOC_REF_TABLE β€” triggering of a minor collection due to ref table reallocation. Since 5.0
  • EV_C_REQUEST_MINOR_REALLOC_EPHE_REF_TABLE β€” triggering of a minor collection due to ephe_ref table reallocation. Since 5.0
  • EV_C_REQUEST_MINOR_REALLOC_CUSTOM_TABLE β€” triggering of a minor collection due to custom table reallocation. Since 5.0
  • EV_C_MAJOR_HEAP_POOL_WORDS β€” total words in a Domain's major heap pools. This is the sum of unallocated and live words in each pool. Since 5.1
  • EV_C_MAJOR_HEAP_POOL_LIVE_WORDS β€” current live words in a Domain's major heap pools. Since 5.1
  • EV_C_MAJOR_HEAP_LARGE_WORDS β€” total words of a Domain's major heap large allocations. A large allocation is an allocation larger than the largest sized pool. Since 5.1
  • EV_C_MAJOR_HEAP_POOL_FRAG_WORDS β€” words in a Domain's major heap pools lost to fragmentation. This is due to there not being a pool with the exact size of an allocation and a larger sized pool needing to be used. Since 5.1
  • EV_C_MAJOR_HEAP_POOL_LIVE_BLOCKS β€” live blocks of a Domain's major heap pools. Since 5.1
  • EV_C_MAJOR_HEAP_LARGE_BLOCKS β€” live blocks of a Domain's major heap large allocations. Since 5.1
  • EV_C_MAJOR_HEAP_WORDS β€” major heap size in words of a Domain. Since 5.3
  • EV_C_MAJOR_ALLOCATED_WORDS β€” allocations to the major heap of this Domain in words, since the last major slice. Since 5.3
  • EV_C_MAJOR_ALLOCATED_WORK β€” the amount of major GC 'work' needing to be done as a result of allocations to the major heap of this Domain in words, since the last major slice. Since 5.3
  • EV_C_MAJOR_DEPENDENT_WORK β€” the amount of major GC 'work' needing to be done as a result of dependent allocations to the major heap of this Domain in words, since the last major slice. Dependent memory is non-heap memory that depends on heap memory being collected in order to be freed. Since 5.3
  • EV_C_MAJOR_EXTRA_WORK β€” the amount of major GC 'work' needing to be done as a result of extra non-memory resources that are dependent on heap memory being collected in order to be freed. Since 5.3
  • EV_C_MAJOR_WORK_COUNTER β€” the global amount of major GC 'work' done by all domains since the program began. Since 5.3
  • EV_C_MAJOR_ALLOC_COUNTER β€” the global words of major GC allocations done by all domains since the program began. Since 5.3
  • EV_C_MAJOR_SLICE_TARGET β€” the target amount of global 'work' that should be done by all domains at the end of the major slice (see EV_C_MAJOR_SLICE_COUNTER). Since 5.3
  • EV_C_MAJOR_SLICE_BUDGET β€” the budget in 'work' that a domain has to do during the major slice. Since 5.3
  • EV_C_MINOR_ALLOCATED_WORDS β€” total words allocated in the minor heap of this Domain in the last minor collection. Since 5.4
  • EV_C_MINOR_PROMOTED_WORDS β€” total words promoted from the minor heap of this Domain to the major heap in the last minor collection. Since 5.4

πŸ¦‹ type runtime_phase πŸ¦‹

type runtime_phase = β€” phase events, aka the "what is the runtime doing right now" vibes. here are all the constructors, bestie:

  • EV_EXPLICIT_GC_SET β€” event spanning a call to Gc.set. Since 5.0
  • EV_EXPLICIT_GC_STAT β€” event spanning a call to Gc.stat. Since 5.0
  • EV_EXPLICIT_GC_MINOR β€” event spanning a call to Gc.minor, which forces a minor collection. Since 5.0
  • EV_EXPLICIT_GC_MAJOR β€” event spanning a call to Gc.major, which forces a major collection. Since 5.0
  • EV_EXPLICIT_GC_FULL_MAJOR β€” event spanning a call to Gc.full_major, which forces a full major collection. Since 5.0
  • EV_EXPLICIT_GC_COMPACT β€” event spanning a call to Gc.compact, which triggers a compaction. Since 5.0
  • EV_MAJOR β€” event spanning any major GC work. Since 5.0
  • EV_MAJOR_SWEEP β€” event spanning the sweeping work of a major GC. Since 5.0
  • EV_MAJOR_MARK_ROOTS β€” event spanning the marking of roots in a major GC. Since 5.0
  • EV_MAJOR_MEMPROF_ROOTS β€” event spanning the marking of memprof roots in a major GC. Since 5.3
  • EV_MAJOR_MARK β€” event spanning the marking of the heap in a major GC. Since 5.0
  • EV_MINOR β€” event spanning any minor GC work. Since 5.0
  • EV_MINOR_LOCAL_ROOTS β€” event spanning the scanning and major allocation of local roots during a minor GC. Since 5.0
  • EV_MINOR_MEMPROF_ROOTS β€” event spanning the scanning and promotion of memprof roots in a minor GC. Since 5.3
  • EV_MINOR_MEMPROF_CLEAN β€” event spanning cleaning and updating of memprof structures at the end of a minor GC. Since 5.3
  • EV_MINOR_FINALIZED β€” event spanning the running of finalisers for dead custom blocks at the end of a minor GC. Since 5.0
  • EV_EXPLICIT_GC_MAJOR_SLICE β€” event spanning a call to Gc.major_slice. Since 5.0
  • EV_FINALISE_UPDATE_FIRST β€” event spanning time spent in the first phase of finalisation at the end of a major GC cycle. Since 5.0
  • EV_FINALISE_UPDATE_LAST β€” event spanning time spent in the last phase of finalisation at the end of a major GC cycle. Since 5.0
  • EV_INTERRUPT_REMOTE β€” event spanning work triggered by an interrupt from another domain. This is usually a stop-the-world request. Since 5.0
  • EV_MAJOR_EPHE_MARK β€” event spanning the marking of ephemeron tables in a major GC. Since 5.0
  • EV_MAJOR_EPHE_SWEEP β€” event spanning the sweeping of ephemeron tables in a major GC. Since 5.0
  • EV_MAJOR_FINISH_MARKING β€” event spanning work done at the end of marking in a major GC. Since 5.0
  • EV_MAJOR_GC_CYCLE_DOMAINS β€” event spanning work done at the end of a major GC cycle. This includes a minor collection. Since 5.0
  • EV_MAJOR_GC_PHASE_CHANGE β€” event spanning the change of phase in the major GC which involves a global barrier. Since 5.0
  • EV_MAJOR_GC_STW β€” event spanning the stop-the-world phase done at the end of a major GC cycle. Since 5.0
  • EV_MAJOR_MARK_OPPORTUNISTIC β€” event spanning the work done during opportunistic marking in a major GC. Since 5.0
  • EV_MAJOR_SLICE β€” event spanning the work done during a major slice in a major GC. Since 5.0
  • EV_MAJOR_FINISH_CYCLE β€” event spanning attempts to drive all domains to the end of a major GC cycle. Since 5.0
  • EV_MINOR_CLEAR β€” event spanning the cleaning of the minor heap and supporting structures at the end of a minor GC. Since 5.0
  • EV_MINOR_FINALIZERS_OLDIFY β€” event spanning the promotion of finalisers during a minor GC. Since 5.0
  • EV_MINOR_GLOBAL_ROOTS β€” event spanning the scanning and major allocation of global roots during a minor GC. Since 5.0
  • EV_MINOR_LEAVE_BARRIER β€” event spanning the time spent in the barrier at the end of a minor GC, waiting for all domains to finish their work. Since 5.0
  • EV_STW_API_BARRIER β€” event spanning the time spent waiting for all other domains to reach the stop-the-world entry barrier. Since 5.0
  • EV_STW_HANDLER β€” event spanning the time spent in the stop-the-world handler, including time spent in the stop-the-world callback itself.

The span roster is still rolling, and that previous variant’s comment wraps up with Since 5.0 before we hit the next batch. Buckle up, bestie:

  • EV_STW_LEADER β€” the event spanning the time spent as the leader of a stop-the-world. Since 5.0
  • EV_MAJOR_FINISH_SWEEPING β€” spans the time spent finishing sweeping when forced to as part of domain termination. Since 5.0
  • EV_MAJOR_MEMPROF_CLEAN β€” spans the time spent cleaning memprof structures at the end of a major GC. Since 5.3
  • EV_MINOR_FINALIZERS_ADMIN β€” spans finalisers book-keeping at the end of a minor GC. Since 5.0
  • EV_MINOR_REMEMBERED_SET β€” spans the scanning and major allocation of remembered sets during a minor GC. Since 5.0
  • EV_MINOR_REMEMBERED_SET_PROMOTE β€” spans the promotion of blocks in the remembered set and global roots during a minor GC. Since 5.0
  • EV_MINOR_LOCAL_ROOTS_PROMOTE β€” spans the promotion of local roots during a minor GC. Since 5.0
  • EV_DOMAIN_CONDITION_WAIT β€” spans waiting in Condition.wait. Since 5.0
  • EV_DOMAIN_RESIZE_HEAP_RESERVATION β€” spans resizing the domain heap reservation, as a result of minor heap size changes. Since 5.0
  • EV_COMPACT β€” spans compaction of the heap during a call to Gc.compact. Since 5.2
  • EV_COMPACT_EVACUATE β€” spans evacuating major GC pools during a compaction. Since 5.2
  • EV_COMPACT_FORWARD β€” spans the walking of the heap to update changed pointers after an evacuation during a compaction. Since 5.2
  • EV_COMPACT_RELEASE β€” spans releasing the evacuated pools at the end of a compaction. Since 5.2
  • EV_EMPTY_MINOR β€” spans a domain needing to empty its minor heap for a new allocation. This includes time spent trying to become stop-the-world leader. Since 5.4

That whole list is the type for span events emitted by the runtime. ✨

Then there’s type lifecycle =, the lifecycle-event roster for Runtime_events and domains:

type lifecycle =
  • EV_RING_START β€” event indicating that the Runtime_events ring buffer has been started. Includes the PID of the process as an argument. Since 5.0
  • EV_RING_STOP β€” event indicating that the Runtime_events ring buffer has been stopped. Since 5.0
  • EV_RING_PAUSE β€” event indicating that the Runtime_events ring buffer has been paused. Since 5.0
  • EV_RING_RESUME β€” event indicating that the Runtime_events ring buffer has been resumed. Since 5.0
  • EV_FORK_PARENT β€” event indicating that a fork has occurred and the current domain is the parent. Includes the PID of the child as an argument. Since 5.0
  • EV_FORK_CHILD β€” event indicating that a fork has occurred and the current domain is the child. Since 5.0
  • EV_DOMAIN_SPAWN β€” event indicating that a new domain has been spawned. Includes the PID of the new domain as an argument. Since 5.0
  • EV_DOMAIN_TERMINATE β€” event indicating that a domain has terminated. Includes the PID of the domain as an argument. Since 5.0

Those are the lifecycle events for Runtime_events and domains. πŸ’…

  • val lifecycle_name : lifecycle -> string β€” returns a string representation of a given lifecycle event type. 🌸
  • val runtime_phase_name : runtime_phase -> string β€” returns a string representation of a given runtime phase event type. 🌸
  • val runtime_counter_name : runtime_counter -> string β€” returns a string representation of a given runtime counter type. 🌸
  • type cursor β€” the type of the cursor used when consuming. πŸŽ€
  • module Timestamp : sig end β€” yes, it’s literally shaped like that in the source.
  • module Type : sig end β€” same energy.
  • module User : sig end β€” same energy.
  • module Callbacks : sig end β€” same energy.
  • val start : unit -> unit β€” start () will start the collection of events in the runtime if not already started. Events can be consumed by creating a cursor with create_cursor and providing a set of callbacks to be called for each type of event. πŸ”₯
  • val path : unit -> string option β€” if runtime events are being collected, path () returns Some p where p is a path to the runtime events file. Otherwise, it returns None. πŸŽ€
  • val pause : unit -> unit β€” pause () will pause the collection of events in the runtime. Traces are collected if the program has called Runtime_events.start () or the OCAML_RUNTIME_EVENTS_START environment variable has been set. πŸ’…
  • val resume : unit -> unit β€” resume () will resume the collection of events in the runtime. Traces are collected if the program has called Runtime_events.start () or the OCAML_RUNTIME_EVENTS_START environment variable has been set. πŸ’…
  • val create_cursor : (string * int) option -> cursor β€” create_cursor path_pid creates a cursor to read from a runtime_events. Cursors can be created for runtime_events in and out of process. A runtime_events ring-buffer may have multiple cursors reading from it at any point in time, and a program may have multiple cursors open concurrently (for example if multiple consumers want different sets of events). If path_pid is None then a cursor is created for the current process. Otherwise the pair contains a string path to the directory that contains the pid .events file and int pid for the runtime_events of an external process to monitor. πŸ¦‹
  • val free_cursor : cursor -> unit β€” frees a previously created runtime_events cursor. 🧚
  • val read_poll : cursor -> Callbacks.t -> int option -> int β€” read_poll cursor callbacks max_option calls the corresponding functions on callbacks for up to max_option events read off the cursor’s runtime_events and returns the number of events read. πŸ‘‘

Source/version: OCamldoc; date: 2026-06-22; footer page: Runtime_events(3). That’s the whole part 2, bestie β€” go consume those events responsibly πŸ’–πŸ«Ά

πŸ“¦ where this page comes from