зеркало из https://github.com/github/ruby.git
[Feature #18364] Add GC.stat_heap to get stats for memory heaps
GC.stat_heap will return stats for memory heaps. This is used for the Variable Width Allocation feature.
This commit is contained in:
Родитель
e9a4cc02b4
Коммит
615e9b2865
122
gc.c
122
gc.c
|
@ -10643,6 +10643,128 @@ rb_gc_stat(VALUE key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum gc_stat_heap_sym {
|
||||||
|
gc_stat_heap_sym_slot_size,
|
||||||
|
gc_stat_heap_sym_heap_allocatable_pages,
|
||||||
|
gc_stat_heap_sym_heap_eden_pages,
|
||||||
|
gc_stat_heap_sym_heap_eden_slots,
|
||||||
|
gc_stat_heap_sym_heap_tomb_pages,
|
||||||
|
gc_stat_heap_sym_heap_tomb_slots,
|
||||||
|
gc_stat_heap_sym_last
|
||||||
|
};
|
||||||
|
|
||||||
|
static VALUE gc_stat_heap_symbols[gc_stat_heap_sym_last];
|
||||||
|
|
||||||
|
static void
|
||||||
|
setup_gc_stat_heap_symbols(void)
|
||||||
|
{
|
||||||
|
if (gc_stat_heap_symbols[0] == 0) {
|
||||||
|
#define S(s) gc_stat_heap_symbols[gc_stat_heap_sym_##s] = ID2SYM(rb_intern_const(#s))
|
||||||
|
S(slot_size);
|
||||||
|
S(heap_allocatable_pages);
|
||||||
|
S(heap_eden_pages);
|
||||||
|
S(heap_eden_slots);
|
||||||
|
S(heap_tomb_pages);
|
||||||
|
S(heap_tomb_slots);
|
||||||
|
#undef S
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
gc_stat_heap_internal(int size_pool_idx, VALUE hash_or_sym)
|
||||||
|
{
|
||||||
|
rb_objspace_t *objspace = &rb_objspace;
|
||||||
|
VALUE hash = Qnil, key = Qnil;
|
||||||
|
|
||||||
|
setup_gc_stat_heap_symbols();
|
||||||
|
|
||||||
|
if (RB_TYPE_P(hash_or_sym, T_HASH)) {
|
||||||
|
hash = hash_or_sym;
|
||||||
|
}
|
||||||
|
else if (SYMBOL_P(hash_or_sym)) {
|
||||||
|
key = hash_or_sym;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_raise(rb_eTypeError, "non-hash or symbol argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size_pool_idx < 0 || size_pool_idx >= SIZE_POOL_COUNT) {
|
||||||
|
rb_raise(rb_eArgError, "size pool index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_size_pool_t *size_pool = &size_pools[size_pool_idx];
|
||||||
|
|
||||||
|
#define SET(name, attr) \
|
||||||
|
if (key == gc_stat_heap_symbols[gc_stat_heap_sym_##name]) \
|
||||||
|
return attr; \
|
||||||
|
else if (hash != Qnil) \
|
||||||
|
rb_hash_aset(hash, gc_stat_heap_symbols[gc_stat_heap_sym_##name], SIZET2NUM(attr));
|
||||||
|
|
||||||
|
SET(slot_size, size_pool->slot_size);
|
||||||
|
SET(heap_allocatable_pages, size_pool->allocatable_pages);
|
||||||
|
SET(heap_eden_pages, SIZE_POOL_EDEN_HEAP(size_pool)->total_pages);
|
||||||
|
SET(heap_eden_slots, SIZE_POOL_EDEN_HEAP(size_pool)->total_slots);
|
||||||
|
SET(heap_tomb_pages, SIZE_POOL_TOMB_HEAP(size_pool)->total_pages);
|
||||||
|
SET(heap_tomb_slots, SIZE_POOL_TOMB_HEAP(size_pool)->total_slots);
|
||||||
|
#undef SET
|
||||||
|
|
||||||
|
if (!NIL_P(key)) { /* matched key should return above */
|
||||||
|
rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
gc_stat_heap(rb_execution_context_t *ec, VALUE self, VALUE heap_name, VALUE arg)
|
||||||
|
{
|
||||||
|
if (NIL_P(heap_name)) {
|
||||||
|
if (NIL_P(arg)) {
|
||||||
|
arg = rb_hash_new();
|
||||||
|
}
|
||||||
|
else if (RB_TYPE_P(arg, T_HASH)) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_raise(rb_eTypeError, "non-hash given");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
||||||
|
VALUE hash = rb_hash_aref(arg, INT2FIX(i));
|
||||||
|
if (NIL_P(hash)) {
|
||||||
|
hash = rb_hash_new();
|
||||||
|
rb_hash_aset(arg, INT2FIX(i), hash);
|
||||||
|
}
|
||||||
|
gc_stat_heap_internal(i, hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FIXNUM_P(heap_name)) {
|
||||||
|
int size_pool_idx = FIX2INT(heap_name);
|
||||||
|
|
||||||
|
if (NIL_P(arg)) {
|
||||||
|
arg = rb_hash_new();
|
||||||
|
}
|
||||||
|
else if (SYMBOL_P(arg)) {
|
||||||
|
size_t value = gc_stat_heap_internal(size_pool_idx, arg);
|
||||||
|
return SIZET2NUM(value);
|
||||||
|
}
|
||||||
|
else if (RB_TYPE_P(arg, T_HASH)) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_raise(rb_eTypeError, "non-hash or symbol given");
|
||||||
|
}
|
||||||
|
|
||||||
|
gc_stat_heap_internal(size_pool_idx, arg);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_raise(rb_eTypeError, "heap_name must be nil or an Integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
gc_stress_get(rb_execution_context_t *ec, VALUE self)
|
gc_stress_get(rb_execution_context_t *ec, VALUE self)
|
||||||
{
|
{
|
||||||
|
|
34
gc.rb
34
gc.rb
|
@ -205,6 +205,40 @@ module GC
|
||||||
Primitive.gc_stat hash_or_key
|
Primitive.gc_stat hash_or_key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# call-seq:
|
||||||
|
# GC.stat_heap -> Hash
|
||||||
|
# GC.stat_heap(nil, hash) -> Hash
|
||||||
|
# GC.stat_heap(heap_name) -> Hash
|
||||||
|
# GC.stat_heap(heap_name, hash) -> Hash
|
||||||
|
# GC.stat_heap(heap_name, :key) -> Numeric
|
||||||
|
#
|
||||||
|
# Returns information for memory pools in the GC.
|
||||||
|
#
|
||||||
|
# If the first optional argument, +heap_name+, is passed in and not +nil+, it
|
||||||
|
# returns a +Hash+ containing information about the particular memory pool.
|
||||||
|
# Otherwise, it will return a +Hash+ with memory pool names as keys and
|
||||||
|
# a +Hash+ containing information about the memory pool as values.
|
||||||
|
#
|
||||||
|
# If the second optional argument, +hash_or_key+, is given as +Hash+, it will
|
||||||
|
# be overwritten and returned. This is intended to avoid the probe effect.
|
||||||
|
#
|
||||||
|
# If both optional arguments are passed in and the second optional argument is
|
||||||
|
# a symbol, it will return a +Numeric+ of the value for the particular memory
|
||||||
|
# pool.
|
||||||
|
#
|
||||||
|
# On CRuby, +heap_name+ is of the type +Integer+ but may be of type +String+
|
||||||
|
# on other implementations.
|
||||||
|
#
|
||||||
|
# The contents of the hash are implementation specific and may change in
|
||||||
|
# the future without notice.
|
||||||
|
#
|
||||||
|
# If the optional argument, hash, is given, it is overwritten and returned.
|
||||||
|
#
|
||||||
|
# This method is only expected to work on CRuby.
|
||||||
|
def self.stat_heap heap_name = nil, hash_or_key = nil
|
||||||
|
Primitive.gc_stat_heap heap_name, hash_or_key
|
||||||
|
end
|
||||||
|
|
||||||
# call-seq:
|
# call-seq:
|
||||||
# GC.latest_gc_info -> {:gc_by=>:newobj}
|
# GC.latest_gc_info -> {:gc_by=>:newobj}
|
||||||
# GC.latest_gc_info(hash) -> hash
|
# GC.latest_gc_info(hash) -> hash
|
||||||
|
|
|
@ -139,6 +139,72 @@ class TestGc < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_stat_heap
|
||||||
|
skip 'stress' if GC.stress
|
||||||
|
|
||||||
|
stat_heap = {}
|
||||||
|
stat = {}
|
||||||
|
# Initialize to prevent GC in future calls
|
||||||
|
GC.stat_heap(0, stat_heap)
|
||||||
|
GC.stat(stat)
|
||||||
|
|
||||||
|
GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT].times do |i|
|
||||||
|
GC.stat_heap(i, stat_heap)
|
||||||
|
GC.stat(stat)
|
||||||
|
|
||||||
|
assert_equal GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] * (2**i), stat_heap[:slot_size]
|
||||||
|
assert_operator stat_heap[:heap_allocatable_pages], :<=, stat[:heap_allocatable_pages]
|
||||||
|
assert_operator stat_heap[:heap_eden_pages], :<=, stat[:heap_eden_pages]
|
||||||
|
assert_operator stat_heap[:heap_eden_slots], :>=, 0
|
||||||
|
assert_operator stat_heap[:heap_tomb_pages], :<=, stat[:heap_tomb_pages]
|
||||||
|
assert_operator stat_heap[:heap_tomb_slots], :>=, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
GC.stat_heap(0, stat_heap)
|
||||||
|
assert_equal stat_heap[:slot_size], GC.stat_heap(0, :slot_size)
|
||||||
|
assert_equal stat_heap[:slot_size], GC.stat_heap(0)[:slot_size]
|
||||||
|
|
||||||
|
assert_raise(ArgumentError) { GC.stat_heap(-1) }
|
||||||
|
assert_raise(ArgumentError) { GC.stat_heap(GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_stat_heap_all
|
||||||
|
stat_heap_all = {}
|
||||||
|
stat_heap = {}
|
||||||
|
|
||||||
|
2.times do
|
||||||
|
GC.stat_heap(0, stat_heap)
|
||||||
|
GC.stat_heap(nil, stat_heap_all)
|
||||||
|
end
|
||||||
|
|
||||||
|
GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT].times do |i|
|
||||||
|
GC.stat_heap(i, stat_heap)
|
||||||
|
|
||||||
|
assert_equal stat_heap, stat_heap_all[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raise(TypeError) { GC.stat_heap(nil, :slot_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_stat_heap_constraints
|
||||||
|
skip 'stress' if GC.stress
|
||||||
|
|
||||||
|
stat = GC.stat
|
||||||
|
stat_heap = GC.stat_heap
|
||||||
|
GC.stat(stat)
|
||||||
|
GC.stat_heap(nil, stat_heap)
|
||||||
|
|
||||||
|
stat_heap_sum = Hash.new(0)
|
||||||
|
stat_heap.values.each do |hash|
|
||||||
|
hash.each { |k, v| stat_heap_sum[k] += v }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal stat[:heap_allocatable_pages], stat_heap_sum[:heap_allocatable_pages]
|
||||||
|
assert_equal stat[:heap_eden_pages], stat_heap_sum[:heap_eden_pages]
|
||||||
|
assert_equal stat[:heap_tomb_pages], stat_heap_sum[:heap_tomb_pages]
|
||||||
|
assert_equal stat[:heap_available_slots], stat_heap_sum[:heap_eden_slots] + stat_heap_sum[:heap_tomb_slots]
|
||||||
|
end
|
||||||
|
|
||||||
def test_latest_gc_info
|
def test_latest_gc_info
|
||||||
omit 'stress' if GC.stress
|
omit 'stress' if GC.stress
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче