From 338c8538c92a1f3c4117fb920855a610cfaefc25 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Thu, 9 Jul 2020 18:52:31 -0700 Subject: Initial ObjectSpace.memsize_of implementation --- mrbgems/mruby-objectspace/mrbgem.rake | 4 + mrbgems/mruby-objectspace/src/mruby_objectspace.c | 201 ++++++++++++++++++++++ mrbgems/mruby-objectspace/test/objectspace.rb | 76 ++++++++ 3 files changed, 281 insertions(+) diff --git a/mrbgems/mruby-objectspace/mrbgem.rake b/mrbgems/mruby-objectspace/mrbgem.rake index fa35136a1..101e24275 100644 --- a/mrbgems/mruby-objectspace/mrbgem.rake +++ b/mrbgems/mruby-objectspace/mrbgem.rake @@ -2,4 +2,8 @@ MRuby::Gem::Specification.new('mruby-objectspace') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'ObjectSpace class' + + spec.add_test_dependency('mruby-metaprog') + spec.add_test_dependency('mruby-method') + spec.add_test_dependency('mruby-fiber') end diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index b89fb0580..fe7e929c8 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -2,6 +2,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include struct os_count_struct { mrb_int total; @@ -168,12 +176,205 @@ os_each_object(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(d.count); } +static void os_memsize_of_object(mrb_state*,mrb_value,mrb_bool,mrb_int*); + +static int +os_memsize_ivar_cb(mrb_state *mrb, mrb_sym _name, mrb_value obj, void *data) +{ + mrb_int *cb_data = (mrb_int *)data; + mrb_int recurse = *(&cb_data[0]); + mrb_int* total = &cb_data[1]; + + os_memsize_of_object(mrb, obj, (mrb_bool)(recurse), total); + return 0; +} + +static void +os_memsize_of_ivars(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int *t) +{ + /* need iv segment table size */ + if(recurse) { + mrb_int r = (mrb_int)recurse; + mrb_int *cb_data[2] = { &r, t }; + mrb_iv_foreach(mrb, obj, os_memsize_ivar_cb, t); + } +} + +static void +os_memsize_of_irep(mrb_state* state, struct mrb_irep *irep, mrb_int* t) +{ + mrb_int i; + (*t) += (irep->slen * sizeof(mrb_sym)) + + (irep->plen * sizeof(mrb_code)) + + (irep->ilen * sizeof(mrb_code)); + + for(i = 0; i < irep->rlen; i++) { + os_memsize_of_irep(state, irep->reps[i], t); + } +} + +static void +os_memsize_of_method(mrb_state* mrb, mrb_value method_obj, mrb_int* t) +{ + mrb_value proc_value = mrb_obj_iv_get(mrb, mrb_obj_ptr(method_obj), + mrb_intern_lit(mrb, "_proc")); + struct RProc *proc = mrb_proc_ptr(proc_value); + + (*t) += sizeof(struct RProc); + if(!MRB_PROC_CFUNC_P(proc)) os_memsize_of_irep(mrb, proc->body.irep, t); +} + +static void +os_memsize_of_methods(mrb_state* mrb, mrb_value obj, mrb_int* t) +{ + mrb_value method_list; + mrb_int i; + if(!mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "instance_methods"))) return; + method_list = mrb_funcall(mrb, obj, "instance_methods", 1, mrb_false_value()); + for(i = 0; i < RARRAY_LEN(method_list); i++) { + mrb_value method = mrb_funcall(mrb, obj, "instance_method", 1, + mrb_ary_ref(mrb, method_list, i)); + os_memsize_of_method(mrb, method, t); + } +} + +static void +os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t) +{ + switch(obj.tt) { + case MRB_TT_STRING: + (*t) += RSTRING_LEN(obj); + break; + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_EXCEPTION: + case MRB_TT_SCLASS: + case MRB_TT_ICLASS: + case MRB_TT_OBJECT: { + os_memsize_of_ivars(mrb, obj, recurse, t); + if(mrb_obj_is_kind_of(mrb, obj, mrb_class_get(mrb, "UnboundMethod"))) { + os_memsize_of_method(mrb, obj, t); + } + else { + os_memsize_of_methods(mrb, obj, t); + } + break; + } + case MRB_TT_HASH: { + struct htable* htable = RHASH_TBL(obj); + /* Need htable & segment struct defs */ + break; + } + case MRB_TT_ARRAY: { + mrb_int len, i; + len = RARRAY_LEN(obj); + /* Arrays that do not fit within an RArray perform a heap allocation + * storing an array of pointers to the original objects*/ + if(len > MRB_ARY_EMBED_LEN_MAX) (*t) += sizeof(mrb_value *) * len; + + if(recurse) { + for(i = 0; i < len; i++) { + os_memsize_of_object(mrb, ARY_PTR(mrb_ary_ptr(obj))[i], recurse, t); + } + } + break; + } + case MRB_TT_PROC: { + struct RProc* proc = mrb_proc_ptr(obj); + (*t) += MRB_ENV_LEN(proc->e.env) * sizeof(mrb_value); + if(!MRB_PROC_CFUNC_P(proc)) os_memsize_of_irep(mrb, proc->body.irep, t); + break; + } + case MRB_TT_DATA: + if(mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "memsize"))) { + (*t) += mrb_fixnum(mrb_funcall(mrb, obj, "memsize", 0)); + } + break; + #ifndef MRB_WITHOUT_FLOAT + case MRB_TT_FLOAT: + #ifdef MRB_WORD_BOXING + (*t) += sizeof(struct RFloat); + #endif + break; + #endif + case MRB_TT_RANGE: + #ifndef MRB_RANGE_EMBED + (*t) += sizeof(struct mrb_range_edges); + #endif + break; + case MRB_TT_FIBER: + struct RFiber* fiber = (struct RFiber*)mrb_ptr(obj); + (*t) += sizeof(struct mrb_context); + break; + /* zero heap size types. + * immediate VM stack values, contained within mrb_state, mrb_heap_page, + * or on C stack */ + case MRB_TT_TRUE: + case MRB_TT_FALSE: + case MRB_TT_FIXNUM: + case MRB_TT_BREAK: + case MRB_TT_CPTR: + case MRB_TT_SYMBOL: + case MRB_TT_FREE: + case MRB_TT_UNDEF: + case MRB_TT_ENV: + case MRB_TT_ISTRUCT: + /* never used, silences compiler warning + * not having a default: clause lets the compiler tell us when there is a new + * TT not accounted for */ + case MRB_TT_MAXDEFINE: + break; + } +} + +/* + * call-seq: + * ObjectSpace.memsize_of(obj, recurse: false) -> Numeric + * + * Returns the amount of heap memory allocated for object in size_t units. + * Not all objects cause additional heap allocations beyond their object pointer + * in the heap page and may return 0. + * + * The return value depends on the definition of size_t on that platform, + * therefore the value is not comparable across platform types. + * + * Immediate values such as integers, booleans, symbols and unboxed float numbers + * return 0. Additionally special objects which are small enough to fit inside an + * object * pointer, termed embedded objects, also return 0. Strings and arrays + * below a compile-time defined size may be embedded. + * + * Setting recurse: true descends into instance variables, array members, + * and hash values recursively, calculating the child objects and adding to + * the final sum. + * + */ + +static mrb_value +os_memsize_of(mrb_state *mrb, mrb_value self) +{ + mrb_int total; + mrb_value obj; + mrb_bool recurse; + const char *kw_names[1] = { "recurse" }; + mrb_value kw_values[1]; + const mrb_kwargs kwargs = { 1, kw_values, kw_names, 0, NULL }; + + mrb_get_args(mrb, "o:", &obj, &kwargs); + recurse = mrb_obj_eq(mrb, kw_values[0], mrb_true_value()) ? TRUE : FALSE; + + total = 0; + os_memsize_of_object(mrb, obj, recurse, &total); + + return mrb_fixnum_value(total); +} + void mrb_mruby_objectspace_gem_init(mrb_state *mrb) { struct RClass *os = mrb_define_module(mrb, "ObjectSpace"); mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1)); + mrb_define_class_method(mrb, os, "memsize_of", os_memsize_of, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); } void diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb index 9c44c2157..d26fd5a9e 100644 --- a/mrbgems/mruby-objectspace/test/objectspace.rb +++ b/mrbgems/mruby-objectspace/test/objectspace.rb @@ -1,3 +1,4 @@ +# coding: utf-8 assert('ObjectSpace.count_objects') do h = {} f = Fiber.new {} if Object.const_defined?(:Fiber) @@ -58,3 +59,78 @@ end assert 'Check class pointer of ObjectSpace.each_object.' do assert_nothing_raised { ObjectSpace.each_object { |obj| !obj } } end + +assert 'ObjectSpace.memsize_of' do + # immediate literals + int_size = ObjectSpace.memsize_of 1 + assert_equal int_size, 0, 'int zero' + + sym_size = ObjectSpace.memsize_of :foo + assert_equal sym_size, 0, 'sym zero' + + assert_equal ObjectSpace.memsize_of(true), int_size + assert_equal ObjectSpace.memsize_of(false), int_size + + float_size = if Object.const_defined? :Float + ObjectSpace.memsize_of 1.0 + else + nil + end + + # need some way of asking if floats are boxed + assert_equal float_size, 0 if float_size + + assert_not_equal ObjectSpace.memsize_of('a'), 0, 'memsize of str' + + if __ENCODING__ == "UTF-8" + assert_not_equal ObjectSpace.memsize_of("こんにちは世界"), 0, 'memsize of utf8 str' + end + + assert_not_equal ObjectSpace.memsize_of(0..1), 0, 'range not zero' + + # class defs + class_obj_size = ObjectSpace.memsize_of Class + assert_not_equal class_obj_size, 0, 'Class obj not zero' + + empty_class_def_size = ObjectSpace.memsize_of Class.new + + # need access to struct iv_tbl + # assert_not_equal empty_class_def_size, 0, 'Class def not zero' + + class_without_methods = Class.new do + @a = 1 + @b = 2 + end + class_total_size = empty_class_def_size + (int_size * 2) + assert_equal ObjectSpace.memsize_of(class_without_methods), class_total_size, 'class without methods size' + + module_without_methods = Module.new do + @a = 1 + @b = 2 + end + module_total_size = empty_class_def_size + (int_size * 2) + assert_equal ObjectSpace.memsize_of(module_without_methods), module_total_size, 'module without methods size' + + proc_size = ObjectSpace.memsize_of Proc.new { x = 1; x } + assert_not_equal proc_size, 0 + + class_with_methods = Class.new do + def foo + a = 0 + a + 1 + end + end + + m_size = ObjectSpace.memsize_of class_with_methods.instance_method(:foo) + assert_not_equal m_size, 0, 'method size not zero' + + # collections + assert_equal ObjectSpace.memsize_of([]), 0, 'empty array size zero' + assert_not_equal ObjectSpace.memsize_of(Array.new(16)), 0, 'array size non zero' + + # fiber + assert_not_equal ObjectSpace.memsize_of(Fiber.new {}), 0, 'fiber non zero' + + skip 'No hash table support yet' + assert_equal ObjectSpace.memsize_of({}), 0, 'empty hash size zero' +end -- cgit v1.2.3 From 37d662868807615a2b8f0f3e3939f592e2b43835 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Fri, 10 Jul 2020 00:57:45 -0700 Subject: fix case scopes and variable names --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index fe7e929c8..c0bc2b807 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -196,7 +196,7 @@ os_memsize_of_ivars(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int *t) if(recurse) { mrb_int r = (mrb_int)recurse; mrb_int *cb_data[2] = { &r, t }; - mrb_iv_foreach(mrb, obj, os_memsize_ivar_cb, t); + mrb_iv_foreach(mrb, obj, os_memsize_ivar_cb, cb_data); } } @@ -261,8 +261,8 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t break; } case MRB_TT_HASH: { - struct htable* htable = RHASH_TBL(obj); - /* Need htable & segment struct defs */ + /*struct htable* htable = RHASH_TBL(obj); + * Need htable & segment struct defs */ break; } case MRB_TT_ARRAY: { @@ -302,10 +302,11 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t (*t) += sizeof(struct mrb_range_edges); #endif break; - case MRB_TT_FIBER: - struct RFiber* fiber = (struct RFiber*)mrb_ptr(obj); + case MRB_TT_FIBER: { + /* struct RFiber* fiber = (struct RFiber*)mrb_ptr(obj); */ (*t) += sizeof(struct mrb_context); break; + } /* zero heap size types. * immediate VM stack values, contained within mrb_state, mrb_heap_page, * or on C stack */ -- cgit v1.2.3 From 41e3220539ff0150bb09968b243ce6ed96b6fe0e Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 15:56:27 -0700 Subject: All values use page slot size in calculation --- include/mruby/gc.h | 1 + mrbgems/mruby-objectspace/src/mruby_objectspace.c | 16 +++++++++++----- src/gc.c | 7 +++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/mruby/gc.h b/include/mruby/gc.h index 4d9fb60eb..3b2ded9d4 100644 --- a/include/mruby/gc.h +++ b/include/mruby/gc.h @@ -21,6 +21,7 @@ struct mrb_state; #define MRB_EACH_OBJ_BREAK 1 typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data); +const mrb_int mrb_objspace_page_slot_size(); MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c); #ifndef MRB_GC_ARENA_SIZE diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index c0bc2b807..791bf68fe 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -251,6 +251,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t case MRB_TT_SCLASS: case MRB_TT_ICLASS: case MRB_TT_OBJECT: { + (*t) += mrb_objspace_page_slot_size(); os_memsize_of_ivars(mrb, obj, recurse, t); if(mrb_obj_is_kind_of(mrb, obj, mrb_class_get(mrb, "UnboundMethod"))) { os_memsize_of_method(mrb, obj, t); @@ -270,6 +271,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t len = RARRAY_LEN(obj); /* Arrays that do not fit within an RArray perform a heap allocation * storing an array of pointers to the original objects*/ + (*t) += mrb_objspace_page_slot_size(); if(len > MRB_ARY_EMBED_LEN_MAX) (*t) += sizeof(mrb_value *) * len; if(recurse) { @@ -280,12 +282,14 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t break; } case MRB_TT_PROC: { + (*t) += mrb_objspace_page_slot_size(); struct RProc* proc = mrb_proc_ptr(obj); (*t) += MRB_ENV_LEN(proc->e.env) * sizeof(mrb_value); if(!MRB_PROC_CFUNC_P(proc)) os_memsize_of_irep(mrb, proc->body.irep, t); break; } case MRB_TT_DATA: + (*t) += mrb_objspace_page_slot_size(); if(mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "memsize"))) { (*t) += mrb_fixnum(mrb_funcall(mrb, obj, "memsize", 0)); } @@ -293,23 +297,25 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING - (*t) += sizeof(struct RFloat); + (*t) += mrb_objspace_page_slot_size() + + sizeof(struct RFloat); #endif break; #endif case MRB_TT_RANGE: #ifndef MRB_RANGE_EMBED - (*t) += sizeof(struct mrb_range_edges); + (*t) += mrb_objspace_page_slot_size() + + sizeof(struct mrb_range_edges); #endif break; case MRB_TT_FIBER: { /* struct RFiber* fiber = (struct RFiber*)mrb_ptr(obj); */ (*t) += sizeof(struct mrb_context); + case MRB_TT_ISTRUCT: + (*t) += mrb_objspace_page_slot_size(); break; - } /* zero heap size types. - * immediate VM stack values, contained within mrb_state, mrb_heap_page, - * or on C stack */ + * immediate VM stack values, contained within mrb_state, or on C stack */ case MRB_TT_TRUE: case MRB_TT_FALSE: case MRB_TT_FIXNUM: diff --git a/src/gc.c b/src/gc.c index e1892080f..fd4fb2406 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1599,6 +1599,13 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo } } +const mrb_int +mrb_objspace_page_slot_size() +{ + const mrb_int i = sizeof(RVALUE); + return i; +} + #ifdef GC_TEST #ifdef GC_DEBUG static mrb_value gc_test(mrb_state *, mrb_value); -- cgit v1.2.3 From e7bd7d0eaf677f62d86f27c2e9a917faa5a7d419 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 15:58:50 -0700 Subject: Use size of hash's table in calculation --- include/mruby/hash.h | 1 + mrbgems/mruby-objectspace/src/mruby_objectspace.c | 9 ++++++--- src/hash.c | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 0052a1105..cd53f6aeb 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -23,6 +23,7 @@ struct RHash { #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) +mrb_int os_memsize_of_hash_table(mrb_value obj); MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa); MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index 791bf68fe..b0a3e0d89 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -262,8 +262,12 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t break; } case MRB_TT_HASH: { - /*struct htable* htable = RHASH_TBL(obj); - * Need htable & segment struct defs */ + (*t) += mrb_objspace_page_slot_size() + + os_memsize_of_hash_table(obj); + if(recurse) { + os_memsize_of_object(mrb, mrb_hash_keys(mrb, obj), recurse, t); + os_memsize_of_object(mrb, mrb_hash_values(mrb, obj), recurse, t); + } break; } case MRB_TT_ARRAY: { @@ -325,7 +329,6 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t case MRB_TT_FREE: case MRB_TT_UNDEF: case MRB_TT_ENV: - case MRB_TT_ISTRUCT: /* never used, silences compiler warning * not having a default: clause lets the compiler tell us when there is a new * TT not accounted for */ diff --git a/src/hash.c b/src/hash.c index 4d5310903..7c90758c0 100644 --- a/src/hash.c +++ b/src/hash.c @@ -518,6 +518,20 @@ ht_foreach(mrb_state *mrb, htable *t, mrb_hash_foreach_func *func, void *p) } } +mrb_int +os_memsize_of_hash_table(mrb_value obj) +{ + struct htable *h = mrb_hash_ptr(obj)->ht; + mrb_int segkv_size = 0; + + if(h->index) segkv_size = (sizeof(struct segkv) * h->index->capa); + + return sizeof(htable) + + sizeof(segindex) + + (sizeof(segment) * h->size) + + segkv_size; +} + /* Iterates over the hash table. */ MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p) -- cgit v1.2.3 From 6f945a09b4a09828667da6d4bad85b8ef50baf9c Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 15:59:24 -0700 Subject: Use object iv table size in calculation --- include/mruby/variable.h | 1 + mrbgems/mruby-objectspace/src/mruby_objectspace.c | 2 +- src/variable.c | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/mruby/variable.h b/include/mruby/variable.h index 6e918cf57..5559f6606 100644 --- a/include/mruby/variable.h +++ b/include/mruby/variable.h @@ -35,6 +35,7 @@ mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym); void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_const_get(mrb_state*, mrb_sym); void mrb_vm_const_set(mrb_state*, mrb_sym, mrb_value); +mrb_int mrb_obj_iv_tbl_memsize(mrb_state*, mrb_value); MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value); MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym); diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index b0a3e0d89..e48ac5b11 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -192,7 +192,7 @@ os_memsize_ivar_cb(mrb_state *mrb, mrb_sym _name, mrb_value obj, void *data) static void os_memsize_of_ivars(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int *t) { - /* need iv segment table size */ + (*t) += mrb_obj_iv_tbl_memsize(mrb, obj); if(recurse) { mrb_int r = (mrb_int)recurse; mrb_int *cb_data[2] = { &r, t }; diff --git a/src/variable.c b/src/variable.c index 030aa7b00..0755f7d92 100644 --- a/src/variable.c +++ b/src/variable.c @@ -4,6 +4,7 @@ ** See Copyright Notice in mruby.h */ +#include #include #include #include @@ -1128,6 +1129,14 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c) return path; } +mrb_int +mrb_obj_iv_tbl_memsize(mrb_state* mrb, mrb_value obj) +{ + return sizeof(iv_tbl) + + (sizeof(segment) * ceil(iv_size(mrb, mrb_obj_ptr(obj)->iv)/ + MRB_IV_SEGMENT_SIZE)); +} + #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) mrb_bool -- cgit v1.2.3 From 5184263bdcdef9e467cf67b71be46368bc5409d2 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 16:00:14 -0700 Subject: Calculating sizes of VM components for a Fiber --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 27 ++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index e48ac5b11..0ffce2fbc 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -312,9 +312,30 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t sizeof(struct mrb_range_edges); #endif break; - case MRB_TT_FIBER: { - /* struct RFiber* fiber = (struct RFiber*)mrb_ptr(obj); */ - (*t) += sizeof(struct mrb_context); + case MRB_TT_FIBER: + struct RFiber* f = (struct RFiber *)mrb_ptr(obj); + mrb_callinfo *ci_p = f->cxt->cibase; + ptrdiff_t stack_size = f->cxt->stend - f->cxt->stbase; + ptrdiff_t ci_size = f->cxt->ciend - f->cxt->cibase; + mrb_int i = 0; + + while(ci_p < f->cxt->ciend) { + if(ci_p->proc) os_memsize_of_irep(mrb, ci_p->proc->body.irep, t); + ci_p++; + } + + for(i = 0; i <= f->cxt->esize; i++) { + os_memsize_of_irep(mrb, f->cxt->ensure[i]->body.irep, t); + } + + (*t) += mrb_objspace_page_slot_size() + + sizeof(struct RFiber) + + sizeof(struct mrb_context) + + sizeof(struct RProc *) * f->cxt->esize + + sizeof(uint16_t *) * f->cxt->rsize + + stack_size + + ci_size; + break; case MRB_TT_ISTRUCT: (*t) += mrb_objspace_page_slot_size(); break; -- cgit v1.2.3 From 5759256ff8b7edbaeefa50b37404453afdd86a0b Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 16:00:49 -0700 Subject: Update tests for new calculations --- mrbgems/mruby-objectspace/test/objectspace.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb index d26fd5a9e..60626e6bf 100644 --- a/mrbgems/mruby-objectspace/test/objectspace.rb +++ b/mrbgems/mruby-objectspace/test/objectspace.rb @@ -93,9 +93,7 @@ assert 'ObjectSpace.memsize_of' do assert_not_equal class_obj_size, 0, 'Class obj not zero' empty_class_def_size = ObjectSpace.memsize_of Class.new - - # need access to struct iv_tbl - # assert_not_equal empty_class_def_size, 0, 'Class def not zero' + assert_not_equal empty_class_def_size, 0, 'Class def not zero' class_without_methods = Class.new do @a = 1 @@ -125,12 +123,15 @@ assert 'ObjectSpace.memsize_of' do assert_not_equal m_size, 0, 'method size not zero' # collections - assert_equal ObjectSpace.memsize_of([]), 0, 'empty array size zero' - assert_not_equal ObjectSpace.memsize_of(Array.new(16)), 0, 'array size non zero' + assert_not_equal ObjectSpace.memsize_of([]), 0, 'empty array size not zero' + assert_not_equal ObjectSpace.memsize_of(Array.new(16)), 0, 'array size not zero' # fiber - assert_not_equal ObjectSpace.memsize_of(Fiber.new {}), 0, 'fiber non zero' + assert_not_equal ObjectSpace.memsize_of(Fiber.new {}), 0, 'fiber not zero' + + #hash + assert_not_equal ObjectSpace.memsize_of({}), 0, 'empty hash size not zero' + + # recursion - skip 'No hash table support yet' - assert_equal ObjectSpace.memsize_of({}), 0, 'empty hash size zero' end -- cgit v1.2.3 From ad4402159119d61d3a0b0a997b219bc1a0f4f196 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 16:43:59 -0700 Subject: C89 compiler mode fixes --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index 0ffce2fbc..f28336b95 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -286,8 +286,8 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t break; } case MRB_TT_PROC: { - (*t) += mrb_objspace_page_slot_size(); struct RProc* proc = mrb_proc_ptr(obj); + (*t) += mrb_objspace_page_slot_size(); (*t) += MRB_ENV_LEN(proc->e.env) * sizeof(mrb_value); if(!MRB_PROC_CFUNC_P(proc)) os_memsize_of_irep(mrb, proc->body.irep, t); break; @@ -312,7 +312,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t sizeof(struct mrb_range_edges); #endif break; - case MRB_TT_FIBER: + case MRB_TT_FIBER: { struct RFiber* f = (struct RFiber *)mrb_ptr(obj); mrb_callinfo *ci_p = f->cxt->cibase; ptrdiff_t stack_size = f->cxt->stend - f->cxt->stbase; @@ -336,6 +336,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t stack_size + ci_size; break; + } case MRB_TT_ISTRUCT: (*t) += mrb_objspace_page_slot_size(); break; -- cgit v1.2.3 From 0e5394638bd6f8fa8c52fa4f75c4c1799adb70e0 Mon Sep 17 00:00:00 2001 From: Rory OConnell <19547+RoryO@users.noreply.github.com> Date: Mon, 13 Jul 2020 17:05:03 -0700 Subject: Validate ensure stack presense before calculating --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index f28336b95..d5ffa83f1 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -324,8 +324,10 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t ci_p++; } - for(i = 0; i <= f->cxt->esize; i++) { - os_memsize_of_irep(mrb, f->cxt->ensure[i]->body.irep, t); + if(f->cxt->esize) { + for(i = 0; i <= f->cxt->esize; i++) { + os_memsize_of_irep(mrb, f->cxt->ensure[i]->body.irep, t); + } } (*t) += mrb_objspace_page_slot_size() + -- cgit v1.2.3 From 4d32c671a70cd066010cf502ab13ab8f78357d0e Mon Sep 17 00:00:00 2001 From: Rory O'Connell <19547+RoryO@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:30:35 -0700 Subject: Finishing out memsize_of recursion --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 39 +++++++----- mrbgems/mruby-objectspace/test/objectspace.rb | 73 ++++++++++++++++++++++- 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index d5ffa83f1..7088e166f 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -176,27 +176,28 @@ os_each_object(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(d.count); } -static void os_memsize_of_object(mrb_state*,mrb_value,mrb_bool,mrb_int*); +static void os_memsize_of_object(mrb_state*,mrb_value,mrb_value,mrb_int*); + +struct os_memsize_cb_data { + mrb_int *t; + mrb_value recurse; +}; static int os_memsize_ivar_cb(mrb_state *mrb, mrb_sym _name, mrb_value obj, void *data) { - mrb_int *cb_data = (mrb_int *)data; - mrb_int recurse = *(&cb_data[0]); - mrb_int* total = &cb_data[1]; - - os_memsize_of_object(mrb, obj, (mrb_bool)(recurse), total); + struct os_memsize_cb_data *cb_data = (struct os_memsize_cb_data *)(data); + os_memsize_of_object(mrb, obj, cb_data->recurse, cb_data->t); return 0; } static void -os_memsize_of_ivars(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int *t) +os_memsize_of_ivars(mrb_state* mrb, mrb_value obj, mrb_value recurse, mrb_int *t) { (*t) += mrb_obj_iv_tbl_memsize(mrb, obj); - if(recurse) { - mrb_int r = (mrb_int)recurse; - mrb_int *cb_data[2] = { &r, t }; - mrb_iv_foreach(mrb, obj, os_memsize_ivar_cb, cb_data); + if(!mrb_nil_p(recurse)) { + struct os_memsize_cb_data cb_data = {t, recurse}; + mrb_iv_foreach(mrb, obj, os_memsize_ivar_cb, &cb_data); } } @@ -239,8 +240,14 @@ os_memsize_of_methods(mrb_state* mrb, mrb_value obj, mrb_int* t) } static void -os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t) +os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_value recurse, mrb_int* t) { + if(!mrb_nil_p(recurse)) { + const mrb_value obj_id = mrb_fixnum_value(mrb_obj_id(obj)); + if(mrb_hash_key_p(mrb, recurse, obj_id)) return; + mrb_hash_set(mrb, recurse, obj_id, mrb_true_value()); + } + switch(obj.tt) { case MRB_TT_STRING: (*t) += RSTRING_LEN(obj); @@ -264,7 +271,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t case MRB_TT_HASH: { (*t) += mrb_objspace_page_slot_size() + os_memsize_of_hash_table(obj); - if(recurse) { + if(!mrb_nil_p(recurse)) { os_memsize_of_object(mrb, mrb_hash_keys(mrb, obj), recurse, t); os_memsize_of_object(mrb, mrb_hash_values(mrb, obj), recurse, t); } @@ -278,7 +285,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_bool recurse, mrb_int* t (*t) += mrb_objspace_page_slot_size(); if(len > MRB_ARY_EMBED_LEN_MAX) (*t) += sizeof(mrb_value *) * len; - if(recurse) { + if(!mrb_nil_p(recurse)) { for(i = 0; i < len; i++) { os_memsize_of_object(mrb, ARY_PTR(mrb_ary_ptr(obj))[i], recurse, t); } @@ -388,13 +395,13 @@ os_memsize_of(mrb_state *mrb, mrb_value self) { mrb_int total; mrb_value obj; - mrb_bool recurse; + mrb_value recurse; const char *kw_names[1] = { "recurse" }; mrb_value kw_values[1]; const mrb_kwargs kwargs = { 1, kw_values, kw_names, 0, NULL }; mrb_get_args(mrb, "o:", &obj, &kwargs); - recurse = mrb_obj_eq(mrb, kw_values[0], mrb_true_value()) ? TRUE : FALSE; + recurse = mrb_obj_eq(mrb, kw_values[0], mrb_true_value())? mrb_hash_new(mrb) : mrb_nil_value(); total = 0; os_memsize_of_object(mrb, obj, recurse, &total); diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb index 60626e6bf..610cdfbfa 100644 --- a/mrbgems/mruby-objectspace/test/objectspace.rb +++ b/mrbgems/mruby-objectspace/test/objectspace.rb @@ -123,15 +123,82 @@ assert 'ObjectSpace.memsize_of' do assert_not_equal m_size, 0, 'method size not zero' # collections - assert_not_equal ObjectSpace.memsize_of([]), 0, 'empty array size not zero' - assert_not_equal ObjectSpace.memsize_of(Array.new(16)), 0, 'array size not zero' + empty_array_size = ObjectSpace.memsize_of [] + assert_not_equal empty_array_size, 0, 'empty array size not zero' + assert_operator empty_array_size, :<, ObjectSpace.memsize_of(Array.new(16)), 'large array size greater than embed' # fiber - assert_not_equal ObjectSpace.memsize_of(Fiber.new {}), 0, 'fiber not zero' + empty_fiber_size = ObjectSpace.memsize_of(Fiber.new {}) + assert_not_equal empty_fiber_size, 0, 'empty fiber not zero' + assert_operator empty_fiber_size, :<, ObjectSpace.memsize_of(Fiber.new { yield; 1 }), 'Fiber code size growth' #hash assert_not_equal ObjectSpace.memsize_of({}), 0, 'empty hash size not zero' # recursion + foo_str = 'foo' * 10 + bar_str = 'bar' * 10 + caz_str = 'caz' * 10 + fbc_ary = [foo_str, bar_str, caz_str] + assert_operator ObjectSpace.memsize_of(fbc_ary), + :<, + ObjectSpace.memsize_of(fbc_ary, recurse: true), + 'basic array recursion' + + big_ary = [ 'a' * 10, + [ 'b' * 10, + [ 'c' * 10, + [ 'd' * 10, + [ 'e' * 10, + [ 'f' * 10, + ['g' * 10] + ] * 10, + ] * 10, + ] * 10, + ] * 10, + ] * 10, + ] * 10 + assert_operator ObjectSpace.memsize_of(big_ary), + :<, + ObjectSpace.memsize_of(big_ary, recurse: true), + 'large array recursion' + + assert_nothing_raised 'infinite array recursion' do + ObjectSpace.memsize_of(fbc_ary.push(fbc_ary)) + end + + basic_hsh = {a: [foo_str, bar_str], b: caz_str, 'c' => {}} + assert_operator ObjectSpace.memsize_of(basic_hsh), + :<, + ObjectSpace.memsize_of(basic_hsh, recurse: true), + 'hash recursion with basic keys' + + weird_keys = {big_ary => foo_str} + assert_operator ObjectSpace.memsize_of(weird_keys), + :<, + ObjectSpace.memsize_of(weird_keys, recurse: true), + 'hash recursion with collection as key' + + basic_hsh.store('d', basic_hsh) + assert_nothing_raised 'hash value recursion' do + ObjectSpace.memsize_of basic_hsh, recurse: true + end + + foo_klass = Class.new do + def bar= b + @bar = b + end + end + fk_one = foo_klass.new + fk_one.bar = fbc_ary + assert_operator ObjectSpace.memsize_of(fk_one), + :<, + ObjectSpace.memsize_of(fk_one, recurse: true), + 'basic ivar recursion' + + fk_one.bar = fk_one + assert_nothing_raised 'ivar infinite recursion' do + ObjectSpace.memsize_of(fk_one, recurse: true) + end end -- cgit v1.2.3 From 38b0759108882c4d791cc4cf1a5989fb8e5d533d Mon Sep 17 00:00:00 2001 From: Rory O'Connell <19547+RoryO@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:43:10 -0700 Subject: Clarify memsize_of documentation --- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index 7088e166f..9fbfd0d54 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -373,20 +373,24 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_value recurse, mrb_int* * ObjectSpace.memsize_of(obj, recurse: false) -> Numeric * * Returns the amount of heap memory allocated for object in size_t units. - * Not all objects cause additional heap allocations beyond their object pointer - * in the heap page and may return 0. * * The return value depends on the definition of size_t on that platform, * therefore the value is not comparable across platform types. * * Immediate values such as integers, booleans, symbols and unboxed float numbers * return 0. Additionally special objects which are small enough to fit inside an - * object * pointer, termed embedded objects, also return 0. Strings and arrays - * below a compile-time defined size may be embedded. + * object pointer, termed embedded objects, will return the size of the object pointer. + * Strings and arrays below a compile-time defined size may be embedded. * * Setting recurse: true descends into instance variables, array members, - * and hash values recursively, calculating the child objects and adding to - * the final sum. + * hash keys and hash values recursively, calculating the child objects and adding to + * the final sum. It avoids infinite recursion and over counting objects by + * internally tracking discovered object ids. + * + * MRB_TT_DATA objects aren't calculated beyond their original page slot. However, + * if the object implements a memsize method it will call that method and add the + * return value to the total. This provides an opportunity for C based data structures + * to report their memory usage. * */ -- cgit v1.2.3 From f74d370c1574fba53330c032ec6ac4716fee4a07 Mon Sep 17 00:00:00 2001 From: Rory O'Connell <19547+RoryO@users.noreply.github.com> Date: Wed, 15 Jul 2020 19:57:22 -0700 Subject: mrb_ prefix convention --- include/mruby/hash.h | 2 +- mrbgems/mruby-objectspace/src/mruby_objectspace.c | 2 +- src/hash.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mruby/hash.h b/include/mruby/hash.h index cd53f6aeb..04b265ec3 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -23,7 +23,7 @@ struct RHash { #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) -mrb_int os_memsize_of_hash_table(mrb_value obj); +mrb_int mrb_os_memsize_of_hash_table(mrb_value obj); MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa); MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index 9fbfd0d54..7892c6a1b 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -270,7 +270,7 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj, mrb_value recurse, mrb_int* } case MRB_TT_HASH: { (*t) += mrb_objspace_page_slot_size() + - os_memsize_of_hash_table(obj); + mrb_os_memsize_of_hash_table(obj); if(!mrb_nil_p(recurse)) { os_memsize_of_object(mrb, mrb_hash_keys(mrb, obj), recurse, t); os_memsize_of_object(mrb, mrb_hash_values(mrb, obj), recurse, t); diff --git a/src/hash.c b/src/hash.c index 7c90758c0..79b61d8b2 100644 --- a/src/hash.c +++ b/src/hash.c @@ -519,7 +519,7 @@ ht_foreach(mrb_state *mrb, htable *t, mrb_hash_foreach_func *func, void *p) } mrb_int -os_memsize_of_hash_table(mrb_value obj) +mrb_os_memsize_of_hash_table(mrb_value obj) { struct htable *h = mrb_hash_ptr(obj)->ht; mrb_int segkv_size = 0; -- cgit v1.2.3