summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--NEWS6
-rw-r--r--gc.c157
-rw-r--r--include/ruby/intern.h1
-rw-r--r--test/ruby/test_gc.rb6
5 files changed, 126 insertions, 57 deletions
diff --git a/ChangeLog b/ChangeLog
index ac1852865c..495aeab0cf 100644
--- a/ChangeLog
+++ b/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>
* hash.c (rb_hash): revert r43981 and bail out to the outermost frame
diff --git a/NEWS b/NEWS
index 8dcc979975..22a6b4735c 100644
--- a/NEWS
+++ b/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_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
when the VM is in a consistent state, i.e. to perform work from a C signal
handler.
@@ -330,6 +333,7 @@ with all sufficient information, see the ChangeLog file.
* RUBY_INTERNAL_EVENT_NEWOBJ
* RUBY_INTERNAL_EVENT_FREEOBJ
* 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
(such as RUBY_EVENT_CALL, RUBY_EVENT_RETURN) simultaneously.
diff --git a/gc.c b/gc.c
index aec6828289..ac41c013a8 100644
--- a/gc.c
+++ b/gc.c
@@ -5056,49 +5056,9 @@ gc_count(VALUE self)
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
-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_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;
@@ -5120,6 +5080,16 @@ gc_stat(int argc, VALUE *argv, VALUE self)
#endif /* RGENGC_PROFILE */
#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) {
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
S(count);
@@ -5161,17 +5131,12 @@ gc_stat(int argc, VALUE *argv, VALUE self)
#undef S
}
- if (rb_scan_args(argc, argv, "01", &hash) == 1) {
- if (!RB_TYPE_P(hash, T_HASH)) {
- rb_raise(rb_eTypeError, "non-hash given");
- }
- }
-
- if (hash == Qnil) {
- hash = rb_hash_new();
- }
+#define SET(name, attr) \
+ if (key == sym_##name) \
+ return (*out = attr, Qnil); \
+ else if (hash != Qnil) \
+ rb_hash_aset(hash, sym_##name, SIZET2NUM(attr));
-#define SET(name, attr) rb_hash_aset(hash, sym_##name, SIZET2NUM(attr))
SET(count, objspace->profile.count);
/* implementation dependent counters */
@@ -5211,8 +5176,15 @@ gc_stat(int argc, VALUE *argv, VALUE self)
#endif
SET(remembered_normal_object_count, objspace->profile.remembered_normal_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_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);
@@ -5224,14 +5196,87 @@ 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);
}
#endif
-#endif /* RGENGC_PROFILE */
-#endif /* USE_RGENGC */
-#undef SET
+
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:
* GC.stress -> fixnum, true or false
*
* Returns current status of GC stress mode.
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index c1114ae875..cc13557c4e 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -486,6 +486,7 @@ DEPRECATED(void rb_gc_set_params(void));
VALUE rb_define_finalizer(VALUE, VALUE);
VALUE rb_undefine_finalizer(VALUE);
size_t rb_gc_count(void);
+size_t rb_gc_stat(VALUE);
/* hash.c */
void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t);
VALUE rb_check_hash_type(VALUE);
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 7410516c7a..d43f8b8693 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -79,6 +79,12 @@ class TestGc < Test::Unit::TestCase
assert_equal(count[:FREE], stat[:heap_free_slot])
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
GC.start
GC.stat[:heap_free_slot].times{ "a" + "b" }