mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
gc.c: expose GC.stat() to C-api via rb_gc_stat()
* include/ruby/intern.h: add rb_gc_stat() for access to GC.stat variables from c-api * gc.c (rb_gc_stat): new c-api method. accepts either VALUE hash like GC.stat, or VALUE symbol key and returns size_t directly. the second form is useful to avoid allocations, i.e. for usage inside INTERNAL_EVENT_GC tracepoints. * gc.c (gc_stat): add GC.stat(:key) to return single value instead of hash * gc.c (gc_stat_internal): helper method to retrieve single or all stat values * test/ruby/test_gc.rb (class TestGc): test for new behavior * NEWS: note about this new api git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44005 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
97ac1f6767
commit
241c1a5459
5 changed files with 126 additions and 57 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
||||||
|
Thu Dec 5 16:11:04 2013 Aman Gupta <ruby@tmm1.net>
|
||||||
|
|
||||||
|
* include/ruby/intern.h: add rb_gc_stat() for access to GC.stat
|
||||||
|
variables from c-api
|
||||||
|
* gc.c (rb_gc_stat): new c-api method. accepts either VALUE hash like
|
||||||
|
GC.stat, or VALUE symbol key and returns size_t directly. the second
|
||||||
|
form is useful to avoid allocations, i.e. for usage inside
|
||||||
|
INTERNAL_EVENT_GC tracepoints.
|
||||||
|
* gc.c (gc_stat): add GC.stat(:key) to return single value instead of hash
|
||||||
|
* gc.c (gc_stat_internal): helper method to retrieve single or all stat values
|
||||||
|
* test/ruby/test_gc.rb (class TestGc): test for new behavior
|
||||||
|
* NEWS: note about this new api
|
||||||
|
|
||||||
Thu Dec 5 14:40:41 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Thu Dec 5 14:40:41 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* hash.c (rb_hash): revert r43981 and bail out to the outermost frame
|
* hash.c (rb_hash): revert r43981 and bail out to the outermost frame
|
||||||
|
|
6
NEWS
6
NEWS
|
@ -319,6 +319,9 @@ with all sufficient information, see the ChangeLog file.
|
||||||
|
|
||||||
* rb_gc_count() added. This returns the number of times GC occurred.
|
* rb_gc_count() added. This returns the number of times GC occurred.
|
||||||
|
|
||||||
|
* rb_gc_stat() added. This allows access to specific GC.stat() values from C
|
||||||
|
without any allocation overhead.
|
||||||
|
|
||||||
* rb_postponed_job_register() added. Takes a function callback which is invoked
|
* rb_postponed_job_register() added. Takes a function callback which is invoked
|
||||||
when the VM is in a consistent state, i.e. to perform work from a C signal
|
when the VM is in a consistent state, i.e. to perform work from a C signal
|
||||||
handler.
|
handler.
|
||||||
|
@ -330,6 +333,7 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* RUBY_INTERNAL_EVENT_NEWOBJ
|
* RUBY_INTERNAL_EVENT_NEWOBJ
|
||||||
* RUBY_INTERNAL_EVENT_FREEOBJ
|
* RUBY_INTERNAL_EVENT_FREEOBJ
|
||||||
* RUBY_INTERNAL_EVENT_GC_START
|
* RUBY_INTERNAL_EVENT_GC_START
|
||||||
* RUBY_INTERNAL_EVENT_GC_END
|
* RUBY_INTERNAL_EVENT_GC_END_MARK
|
||||||
|
* RUBY_INTERNAL_EVENT_GC_END_SWEEP
|
||||||
* Note that you *can not* specify "internal events" with normal events
|
* Note that you *can not* specify "internal events" with normal events
|
||||||
(such as RUBY_EVENT_CALL, RUBY_EVENT_RETURN) simultaneously.
|
(such as RUBY_EVENT_CALL, RUBY_EVENT_RETURN) simultaneously.
|
||||||
|
|
157
gc.c
157
gc.c
|
@ -5056,49 +5056,9 @@ gc_count(VALUE self)
|
||||||
return SIZET2NUM(rb_gc_count());
|
return SIZET2NUM(rb_gc_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* call-seq:
|
|
||||||
* GC.stat -> Hash
|
|
||||||
*
|
|
||||||
* Returns a Hash containing information about the GC.
|
|
||||||
*
|
|
||||||
* The hash includes information about internal statistics about GC such as:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* :count=>2,
|
|
||||||
* :heap_used=>9,
|
|
||||||
* :heap_length=>11,
|
|
||||||
* :heap_increment=>2,
|
|
||||||
* :heap_live_slot=>6836,
|
|
||||||
* :heap_free_slot=>519,
|
|
||||||
* :heap_final_slot=>0,
|
|
||||||
* :heap_swept_slot=>818,
|
|
||||||
* :total_allocated_object=>7674,
|
|
||||||
* :total_freed_object=>838,
|
|
||||||
* :malloc_increase=>181034,
|
|
||||||
* :malloc_limit=>16777216,
|
|
||||||
* :minor_gc_count=>2,
|
|
||||||
* :major_gc_count=>0,
|
|
||||||
* :remembered_shady_object=>55,
|
|
||||||
* :remembered_shady_object_limit=>0,
|
|
||||||
* :old_object=>2422,
|
|
||||||
* :old_object_limit=>0,
|
|
||||||
* :oldmalloc_increase=>277386,
|
|
||||||
* :oldmalloc_limit=>16777216
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* The contents of the hash are implementation specific and may be changed in
|
|
||||||
* the future.
|
|
||||||
*
|
|
||||||
* This method is only expected to work on C Ruby.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
gc_stat(int argc, VALUE *argv, VALUE self)
|
gc_stat_internal(VALUE hash_or_sym, size_t *out)
|
||||||
{
|
{
|
||||||
rb_objspace_t *objspace = &rb_objspace;
|
|
||||||
VALUE hash;
|
|
||||||
static VALUE sym_count;
|
static VALUE sym_count;
|
||||||
static VALUE sym_heap_used, sym_heap_length, sym_heap_increment;
|
static VALUE sym_heap_used, sym_heap_length, sym_heap_increment;
|
||||||
static VALUE sym_heap_live_slot, sym_heap_free_slot, sym_heap_final_slot, sym_heap_swept_slot;
|
static VALUE sym_heap_live_slot, sym_heap_free_slot, sym_heap_final_slot, sym_heap_swept_slot;
|
||||||
|
@ -5120,6 +5080,16 @@ gc_stat(int argc, VALUE *argv, VALUE self)
|
||||||
#endif /* RGENGC_PROFILE */
|
#endif /* RGENGC_PROFILE */
|
||||||
#endif /* USE_RGENGC */
|
#endif /* USE_RGENGC */
|
||||||
|
|
||||||
|
rb_objspace_t *objspace = &rb_objspace;
|
||||||
|
VALUE hash = Qnil, key = Qnil;
|
||||||
|
|
||||||
|
if (RB_TYPE_P(hash_or_sym, T_HASH))
|
||||||
|
hash = hash_or_sym;
|
||||||
|
else if (SYMBOL_P(hash_or_sym) && out)
|
||||||
|
key = hash_or_sym;
|
||||||
|
else
|
||||||
|
rb_raise(rb_eArgError, "non-hash or symbol argument");
|
||||||
|
|
||||||
if (sym_count == 0) {
|
if (sym_count == 0) {
|
||||||
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
|
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
|
||||||
S(count);
|
S(count);
|
||||||
|
@ -5161,17 +5131,12 @@ gc_stat(int argc, VALUE *argv, VALUE self)
|
||||||
#undef S
|
#undef S
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rb_scan_args(argc, argv, "01", &hash) == 1) {
|
#define SET(name, attr) \
|
||||||
if (!RB_TYPE_P(hash, T_HASH)) {
|
if (key == sym_##name) \
|
||||||
rb_raise(rb_eTypeError, "non-hash given");
|
return (*out = attr, Qnil); \
|
||||||
}
|
else if (hash != Qnil) \
|
||||||
}
|
rb_hash_aset(hash, sym_##name, SIZET2NUM(attr));
|
||||||
|
|
||||||
if (hash == Qnil) {
|
|
||||||
hash = rb_hash_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SET(name, attr) rb_hash_aset(hash, sym_##name, SIZET2NUM(attr))
|
|
||||||
SET(count, objspace->profile.count);
|
SET(count, objspace->profile.count);
|
||||||
|
|
||||||
/* implementation dependent counters */
|
/* implementation dependent counters */
|
||||||
|
@ -5211,8 +5176,15 @@ gc_stat(int argc, VALUE *argv, VALUE self)
|
||||||
#endif
|
#endif
|
||||||
SET(remembered_normal_object_count, objspace->profile.remembered_normal_object_count);
|
SET(remembered_normal_object_count, objspace->profile.remembered_normal_object_count);
|
||||||
SET(remembered_shady_object_count, objspace->profile.remembered_shady_object_count);
|
SET(remembered_shady_object_count, objspace->profile.remembered_shady_object_count);
|
||||||
#if RGENGC_PROFILE >= 2
|
#endif /* RGENGC_PROFILE */
|
||||||
{
|
#endif /* USE_RGENGC */
|
||||||
|
#undef SET
|
||||||
|
|
||||||
|
if (key != Qnil) /* matched key should return above */
|
||||||
|
rb_raise(rb_eArgError, "unknown key: %s", RSTRING_PTR(rb_id2str(SYM2ID(key))));
|
||||||
|
|
||||||
|
#if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2
|
||||||
|
if (hash != Qnil) {
|
||||||
gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types);
|
gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types);
|
||||||
gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types);
|
gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types);
|
||||||
gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types);
|
gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types);
|
||||||
|
@ -5224,12 +5196,85 @@ gc_stat(int argc, VALUE *argv, VALUE self)
|
||||||
gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types);
|
gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif /* RGENGC_PROFILE */
|
|
||||||
#endif /* USE_RGENGC */
|
|
||||||
#undef SET
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* GC.stat -> Hash
|
||||||
|
* GC.stat(hash) -> hash
|
||||||
|
* GC.stat(:key) -> Numeric
|
||||||
|
*
|
||||||
|
* Returns a Hash containing information about the GC.
|
||||||
|
*
|
||||||
|
* The hash includes information about internal statistics about GC such as:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* :count=>2,
|
||||||
|
* :heap_used=>9,
|
||||||
|
* :heap_length=>11,
|
||||||
|
* :heap_increment=>2,
|
||||||
|
* :heap_live_slot=>6836,
|
||||||
|
* :heap_free_slot=>519,
|
||||||
|
* :heap_final_slot=>0,
|
||||||
|
* :heap_swept_slot=>818,
|
||||||
|
* :total_allocated_object=>7674,
|
||||||
|
* :total_freed_object=>838,
|
||||||
|
* :malloc_increase=>181034,
|
||||||
|
* :malloc_limit=>16777216,
|
||||||
|
* :minor_gc_count=>2,
|
||||||
|
* :major_gc_count=>0,
|
||||||
|
* :remembered_shady_object=>55,
|
||||||
|
* :remembered_shady_object_limit=>0,
|
||||||
|
* :old_object=>2422,
|
||||||
|
* :old_object_limit=>0,
|
||||||
|
* :oldmalloc_increase=>277386,
|
||||||
|
* :oldmalloc_limit=>16777216
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* The contents of the hash are implementation specific and may be changed in
|
||||||
|
* the future.
|
||||||
|
*
|
||||||
|
* This method is only expected to work on C Ruby.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
gc_stat(int argc, VALUE *argv, VALUE self)
|
||||||
|
{
|
||||||
|
VALUE arg = Qnil;
|
||||||
|
|
||||||
|
if (rb_scan_args(argc, argv, "01", &arg) == 1) {
|
||||||
|
if (SYMBOL_P(arg)) {
|
||||||
|
size_t value = 0;
|
||||||
|
gc_stat_internal(arg, &value);
|
||||||
|
return SIZET2NUM(value);
|
||||||
|
} else if (!RB_TYPE_P(arg, T_HASH)) {
|
||||||
|
rb_raise(rb_eTypeError, "non-hash given");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg == Qnil) {
|
||||||
|
arg = rb_hash_new();
|
||||||
|
}
|
||||||
|
gc_stat_internal(arg, 0);
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
rb_gc_stat(VALUE key)
|
||||||
|
{
|
||||||
|
if (SYMBOL_P(key)) {
|
||||||
|
size_t value = 0;
|
||||||
|
gc_stat_internal(key, &value);
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
gc_stat_internal(key, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* GC.stress -> fixnum, true or false
|
* GC.stress -> fixnum, true or false
|
||||||
|
|
|
@ -486,6 +486,7 @@ DEPRECATED(void rb_gc_set_params(void));
|
||||||
VALUE rb_define_finalizer(VALUE, VALUE);
|
VALUE rb_define_finalizer(VALUE, VALUE);
|
||||||
VALUE rb_undefine_finalizer(VALUE);
|
VALUE rb_undefine_finalizer(VALUE);
|
||||||
size_t rb_gc_count(void);
|
size_t rb_gc_count(void);
|
||||||
|
size_t rb_gc_stat(VALUE);
|
||||||
/* hash.c */
|
/* hash.c */
|
||||||
void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t);
|
void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t);
|
||||||
VALUE rb_check_hash_type(VALUE);
|
VALUE rb_check_hash_type(VALUE);
|
||||||
|
|
|
@ -79,6 +79,12 @@ class TestGc < Test::Unit::TestCase
|
||||||
assert_equal(count[:FREE], stat[:heap_free_slot])
|
assert_equal(count[:FREE], stat[:heap_free_slot])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_stat_single
|
||||||
|
stat = GC.stat
|
||||||
|
assert_equal stat[:count], GC.stat(:count)
|
||||||
|
assert_raise(ArgumentError){ GC.stat(:invalid) }
|
||||||
|
end
|
||||||
|
|
||||||
def test_gc_reason
|
def test_gc_reason
|
||||||
GC.start
|
GC.start
|
||||||
GC.stat[:heap_free_slot].times{ "a" + "b" }
|
GC.stat[:heap_free_slot].times{ "a" + "b" }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue