diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2020-07-17 07:10:58 +0900 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-07-17 07:10:58 +0900 |
| commit | b5bf9510ee5bf733eb258c8cde30e7026888c82d (patch) | |
| tree | 911a66ba0bb78872fbd3f737b32886e1b6959230 /mrbgems/mruby-objectspace/src/mruby_objectspace.c | |
| parent | bfd58a3fb3a374ba9db4badf95649001b0ca33eb (diff) | |
| parent | f74d370c1574fba53330c032ec6ac4716fee4a07 (diff) | |
| download | mruby-b5bf9510ee5bf733eb258c8cde30e7026888c82d.tar.gz mruby-b5bf9510ee5bf733eb258c8cde30e7026888c82d.zip | |
Merge pull request #5032 from RoryO/add-objspace-memsize-of
Add ObjectSpace.memsize_of
Diffstat (limited to 'mrbgems/mruby-objectspace/src/mruby_objectspace.c')
| -rw-r--r-- | mrbgems/mruby-objectspace/src/mruby_objectspace.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index b89fb0580..7892c6a1b 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -2,6 +2,14 @@ #include <mruby/gc.h> #include <mruby/hash.h> #include <mruby/class.h> +#include <mruby/object.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include <mruby/array.h> +#include <mruby/variable.h> +#include <mruby/proc.h> +#include <mruby/value.h> +#include <mruby/range.h> struct os_count_struct { mrb_int total; @@ -168,12 +176,250 @@ 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_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) +{ + 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_value recurse, mrb_int *t) +{ + (*t) += mrb_obj_iv_tbl_memsize(mrb, obj); + 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); + } +} + +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_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); + 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: { + (*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); + } + else { + os_memsize_of_methods(mrb, obj, t); + } + break; + } + case MRB_TT_HASH: { + (*t) += mrb_objspace_page_slot_size() + + 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); + } + 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*/ + (*t) += mrb_objspace_page_slot_size(); + if(len > MRB_ARY_EMBED_LEN_MAX) (*t) += sizeof(mrb_value *) * len; + + 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); + } + } + break; + } + case MRB_TT_PROC: { + 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; + } + 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)); + } + break; + #ifndef MRB_WITHOUT_FLOAT + case MRB_TT_FLOAT: + #ifdef MRB_WORD_BOXING + (*t) += mrb_objspace_page_slot_size() + + sizeof(struct RFloat); + #endif + break; + #endif + case MRB_TT_RANGE: + #ifndef MRB_RANGE_EMBED + (*t) += mrb_objspace_page_slot_size() + + sizeof(struct mrb_range_edges); + #endif + break; + 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++; + } + + 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() + + 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; + /* zero heap size types. + * immediate VM stack values, contained within mrb_state, 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: + /* 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. + * + * 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, 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, + * 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. + * + */ + +static mrb_value +os_memsize_of(mrb_state *mrb, mrb_value self) +{ + mrb_int total; + mrb_value obj; + 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())? mrb_hash_new(mrb) : mrb_nil_value(); + + 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 |
