Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →
GC::Profiler.raw_data
static VALUE
gc_profile_record_get(int argc, VALUE *argv, VALUE _)
{
static ID keywords[2] = {0};
VALUE prof, options, limit_value, since_value;
VALUE gc_profile = rb_ary_new();
size_t i, count, matching = 0, skip = 0, limit = SIZE_MAX, since = 0;
bool use_since = false;
rb_objspace_t *objspace = rb_gc_get_objspace();
if (!keywords[0]) {
keywords[0] = rb_intern("limit");
keywords[1] = rb_intern("since");
}
rb_scan_args_kw(rb_keyword_given_p(), argc, argv, ":", &options);
VALUE values[2] = {Qundef, Qundef};
rb_get_kwargs(options, keywords, 0, 2, values);
limit_value = values[0];
since_value = values[1];
if (limit_value != Qundef && !NIL_P(limit_value)) {
long value = NUM2LONG(limit_value);
if (value < 0) {
rb_raise(rb_eArgError, "limit must be non-negative");
}
limit = (size_t)value;
}
if (since_value != Qundef && !NIL_P(since_value)) {
long value = NUM2LONG(since_value);
if (value < 0) {
rb_raise(rb_eArgError, "since must be non-negative");
}
since = (size_t)value;
use_since = true;
}
if (!objspace->profile.run) {
return Qnil;
}
count = gc_profile_record_count(objspace);
for (i = 0; i < count; i++) {
gc_profile_record *record = &objspace->profile.records[gc_profile_record_index(objspace, i)];
if (!use_since || record->sequence > since) {
matching++;
}
}
if (limit < matching) {
skip = matching - limit;
}
for (i = 0; i < count; i++) {
gc_profile_record *record = &objspace->profile.records[gc_profile_record_index(objspace, i)];
if (use_since && record->sequence <= since) {
continue;
}
if (skip > 0) {
skip--;
continue;
}
prof = rb_hash_new();
rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(objspace, rb_hash_new(), record->flags));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_SEQUENCE")), SIZET2NUM(record->sequence));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_WALL_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_wall_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_WALL_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_invoke_wall_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_PAUSE_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_pause_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_STOP_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_stop_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_STW_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_stw_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_WALL_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_mark_wall_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_WALL_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_sweep_wall_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_COMPACT_WALL_TIME")),
DBL2NUM(hrtime_to_sec(record->gc_compact_wall_time)));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(record->heap_total_size));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(record->heap_total_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("MOVED_OBJECTS")), SIZET2NUM(record->moved_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), Qtrue);
#if GC_PROFILE_MORE_DETAIL
rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(record->gc_mark_time));
rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(record->gc_sweep_time));
rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(record->allocate_increase));
rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(record->allocate_limit));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_PAGES")), SIZET2NUM(record->heap_use_pages));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(record->heap_live_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(record->heap_free_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), (record->flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse);
#endif
#if RGENGC_PROFILE > 0
rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects));
rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBERED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects));
#endif
rb_ary_push(gc_profile, prof);
}
return gc_profile;
}
Returns an Array of retained raw profile data Hashes ordered from earliest to latest by :GC_INVOKE_TIME. limit: returns at most the newest retained records. since: returns records with :GC_SEQUENCE greater than the given sequence.
For example:
[
{
:GC_TIME=>1.3000000000000858e-05,
:GC_INVOKE_TIME=>0.010634999999999999,
:GC_WALL_TIME=>1.4000000000000001e-05,
:GC_INVOKE_WALL_TIME=>0.010640000000000000,
:GC_PAUSE_TIME=>1.5000000000000000e-05,
:GC_STOP_TIME=>1.0000000000000000e-06,
:GC_STW_TIME=>1.4000000000000001e-05,
:GC_MARK_WALL_TIME=>9.0000000000000002e-06,
:GC_SWEEP_WALL_TIME=>5.0000000000000004e-06,
:GC_COMPACT_WALL_TIME=>0.0000000000000000e+00,
:HEAP_USE_SIZE=>289640,
:HEAP_TOTAL_SIZE=>588960,
:HEAP_TOTAL_OBJECTS=>14724,
:GC_IS_MARKED=>false
},
# ...
]
The keys mean:
:GC_SEQUENCE-
Monotonically increasing sequence number for this profiler record.
:GC_TIME-
CPU time elapsed in seconds for this
GCrun. This is process CPU time, not elapsed wall-clock time. :GC_INVOKE_TIME-
CPU time elapsed in seconds from startup to when the
GCwas invoked. :GC_WALL_TIME-
Monotonic wall-clock counterpart to
:GC_TIMEfor thisGCrecord. This does not include time spent stopping other ractors before the VM entersGC. Use the phase wall-clock fields below for mark, sweep, and compaction attribution. :GC_INVOKE_WALL_TIME-
Monotonic wall-clock time elapsed in seconds from startup to when the
GCwas invoked. :GC_PAUSE_TIME-
Monotonic wall-clock time elapsed in seconds while user execution was blocked by this
GCentry, including time to stop other ractors. This may include time from incremental marking or lazy sweeping continuation charged to this record. :GC_STOP_TIME-
Monotonic wall-clock time elapsed in seconds stopping other ractors.
:GC_STW_TIME-
Monotonic wall-clock time elapsed in seconds after other ractors have stopped and before the VM exits
GC. :GC_MARK_WALL_TIME-
Monotonic wall-clock time elapsed in seconds spent marking for this
GCrecord, accumulated across incremental marking continuations. :GC_SWEEP_WALL_TIME-
Monotonic wall-clock time elapsed in seconds spent sweeping for this
GCrecord, accumulated across lazy sweeping continuations. This does not include compaction time, which is reported separately as:GC_COMPACT_WALL_TIME. :GC_COMPACT_WALL_TIME-
Monotonic wall-clock time elapsed in seconds spent compacting for this
GCrecord, or0.0if thisGCdid not compact. :HEAP_USE_SIZE-
Total bytes of heap used
:HEAP_TOTAL_SIZE-
Total size of heap in bytes
:HEAP_TOTAL_OBJECTS-
Total number of objects
:GC_IS_MARKED-
Returns
trueif theGCis in mark phase
The wall-clock timing fields relate to each other as follows:
GC_PAUSE_TIME == GC_STOP_TIME + GC_STW_TIME
:GC_MARK_WALL_TIME, :GC_SWEEP_WALL_TIME, and :GC_COMPACT_WALL_TIME report separate phase timings and must not be added to :GC_WALL_TIME.
:GC_WALL_TIME is the wall-clock counterpart to :GC_TIME and is nested inside :GC_STW_TIME, so it must not be added to :GC_STW_TIME. The difference +GC_STW_TIME - GC_WALL_TIME+ is VM overhead inside the stopped interval (GC event hooks, bookkeeping, consistency checks, and continuation work).
If ruby was built with GC_PROFILE_MORE_DETAIL, you will also have access to the following hash keys:
:GC_MARK_TIME:GC_SWEEP_TIME:ALLOCATE_INCREASE:ALLOCATE_LIMIT:HEAP_USE_PAGES:HEAP_LIVE_OBJECTS:HEAP_FREE_OBJECTS:HAVE_FINALIZE