ext/objspace/objspace.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   objspace.c - ObjectSpace extender for MRI.
00004 
00005   $Author: ko1 $
00006   created at: Wed Jun 17 07:39:17 2009
00007 
00008   NOTE: This extension library is only expected to exist with C Ruby.
00009 
00010   All the files in this distribution are covered under the Ruby's
00011   license (see the file COPYING).
00012 
00013 **********************************************************************/
00014 
00015 #include <ruby/ruby.h>
00016 #include <ruby/st.h>
00017 #include <ruby/io.h>
00018 #include <ruby/re.h>
00019 #include "node.h"
00020 #include "gc.h"
00021 #include "internal.h"
00022 
00023 /*
00024  *  call-seq:
00025  *    ObjectSpace.memsize_of(obj) -> Integer
00026  *
00027  *  Return consuming memory size of obj.
00028  *
00029  *  Note that the return size is incomplete.  You need to deal with this
00030  *  information as only a *HINT*. Especially, the size of +T_DATA+ may not be
00031  *  correct.
00032  *
00033  *  This method is only expected to work with C Ruby.
00034  */
00035 
00036 static VALUE
00037 memsize_of_m(VALUE self, VALUE obj)
00038 {
00039     return SIZET2NUM(rb_obj_memsize_of(obj));
00040 }
00041 
00042 struct total_data {
00043     size_t total;
00044     VALUE klass;
00045 };
00046 
00047 static int
00048 total_i(void *vstart, void *vend, size_t stride, void *ptr)
00049 {
00050     VALUE v;
00051     struct total_data *data = (struct total_data *)ptr;
00052 
00053     for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
00054         if (RBASIC(v)->flags) {
00055             switch (BUILTIN_TYPE(v)) {
00056               case T_NONE:
00057               case T_ICLASS:
00058               case T_NODE:
00059               case T_ZOMBIE:
00060                 continue;
00061               case T_CLASS:
00062                 if (FL_TEST(v, FL_SINGLETON))
00063                   continue;
00064               default:
00065                 if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
00066                     data->total += rb_obj_memsize_of(v);
00067                 }
00068             }
00069         }
00070     }
00071 
00072     return 0;
00073 }
00074 
00075 /*
00076  *  call-seq:
00077  *    ObjectSpace.memsize_of_all([klass]) -> Integer
00078  *
00079  *  Return consuming memory size of all living objects.
00080  *
00081  *  If +klass+ (should be Class object) is given, return the total memory size
00082  *  of instances of the given class.
00083  *
00084  *  Note that the returned size is incomplete. You need to deal with this
00085  *  information as only a *HINT*. Especially, the size of +T_DATA+ may not be
00086  *  correct.
00087  *
00088  *  Note that this method does *NOT* return total malloc'ed memory size.
00089  *
00090  *  This method can be defined by the following Ruby code:
00091  *
00092  *      def memsize_of_all klass = false
00093  *        total = 0
00094  *        ObjectSpace.each_object{|e|
00095  *          total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass)
00096  *        }
00097  *        total
00098  *      end
00099  *
00100  *  This method is only expected to work with C Ruby.
00101  */
00102 
00103 static VALUE
00104 memsize_of_all_m(int argc, VALUE *argv, VALUE self)
00105 {
00106     struct total_data data = {0, 0};
00107 
00108     if (argc > 0) {
00109         rb_scan_args(argc, argv, "01", &data.klass);
00110     }
00111 
00112     rb_objspace_each_objects(total_i, &data);
00113     return SIZET2NUM(data.total);
00114 }
00115 
00116 static int
00117 set_zero_i(st_data_t key, st_data_t val, st_data_t arg)
00118 {
00119     VALUE k = (VALUE)key;
00120     VALUE hash = (VALUE)arg;
00121     rb_hash_aset(hash, k, INT2FIX(0));
00122     return ST_CONTINUE;
00123 }
00124 
00125 static int
00126 cos_i(void *vstart, void *vend, size_t stride, void *data)
00127 {
00128     size_t *counts = (size_t *)data;
00129     VALUE v = (VALUE)vstart;
00130 
00131     for (;v != (VALUE)vend; v += stride) {
00132         if (RBASIC(v)->flags) {
00133             counts[BUILTIN_TYPE(v)] += rb_obj_memsize_of(v);
00134         }
00135     }
00136     return 0;
00137 }
00138 
00139 static VALUE
00140 type2sym(enum ruby_value_type i)
00141 {
00142     VALUE type;
00143     switch (i) {
00144 #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
00145         CASE_TYPE(T_NONE);
00146         CASE_TYPE(T_OBJECT);
00147         CASE_TYPE(T_CLASS);
00148         CASE_TYPE(T_MODULE);
00149         CASE_TYPE(T_FLOAT);
00150         CASE_TYPE(T_STRING);
00151         CASE_TYPE(T_REGEXP);
00152         CASE_TYPE(T_ARRAY);
00153         CASE_TYPE(T_HASH);
00154         CASE_TYPE(T_STRUCT);
00155         CASE_TYPE(T_BIGNUM);
00156         CASE_TYPE(T_FILE);
00157         CASE_TYPE(T_DATA);
00158         CASE_TYPE(T_MATCH);
00159         CASE_TYPE(T_COMPLEX);
00160         CASE_TYPE(T_RATIONAL);
00161         CASE_TYPE(T_NIL);
00162         CASE_TYPE(T_TRUE);
00163         CASE_TYPE(T_FALSE);
00164         CASE_TYPE(T_SYMBOL);
00165         CASE_TYPE(T_FIXNUM);
00166         CASE_TYPE(T_UNDEF);
00167         CASE_TYPE(T_NODE);
00168         CASE_TYPE(T_ICLASS);
00169         CASE_TYPE(T_ZOMBIE);
00170 #undef CASE_TYPE
00171       default: rb_bug("type2sym: unknown type (%d)", i);
00172     }
00173     return type;
00174 }
00175 
00176 /*
00177  *  call-seq:
00178  *    ObjectSpace.count_objects_size([result_hash]) -> hash
00179  *
00180  *  Counts objects size (in bytes) for each type.
00181  *
00182  *  Note that this information is incomplete.  You need to deal with
00183  *  this information as only a *HINT*.  Especially, total size of
00184  *  T_DATA may not right size.
00185  *
00186  *  It returns a hash as:
00187  *    {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...}
00188  *
00189  *  If the optional argument, result_hash, is given,
00190  *  it is overwritten and returned.
00191  *  This is intended to avoid probe effect.
00192  *
00193  *  The contents of the returned hash is implementation defined.
00194  *  It may be changed in future.
00195  *
00196  *  This method is only expected to work with C Ruby.
00197  */
00198 
00199 static VALUE
00200 count_objects_size(int argc, VALUE *argv, VALUE os)
00201 {
00202     size_t counts[T_MASK+1];
00203     size_t total = 0;
00204     enum ruby_value_type i;
00205     VALUE hash;
00206 
00207     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00208         if (!RB_TYPE_P(hash, T_HASH))
00209             rb_raise(rb_eTypeError, "non-hash given");
00210     }
00211 
00212     for (i = 0; i <= T_MASK; i++) {
00213         counts[i] = 0;
00214     }
00215 
00216     rb_objspace_each_objects(cos_i, &counts[0]);
00217 
00218     if (hash == Qnil) {
00219         hash = rb_hash_new();
00220     }
00221     else if (!RHASH_EMPTY_P(hash)) {
00222         st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00223     }
00224 
00225     for (i = 0; i <= T_MASK; i++) {
00226         if (counts[i]) {
00227             VALUE type = type2sym(i);
00228             total += counts[i];
00229             rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
00230         }
00231     }
00232     rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
00233     return hash;
00234 }
00235 
00236 static int
00237 cn_i(void *vstart, void *vend, size_t stride, void *n)
00238 {
00239     size_t *nodes = (size_t *)n;
00240     VALUE v = (VALUE)vstart;
00241 
00242     for (; v != (VALUE)vend; v += stride) {
00243         if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) {
00244             size_t s = nd_type((NODE *)v);
00245             nodes[s]++;
00246         }
00247     }
00248 
00249     return 0;
00250 }
00251 
00252 /*
00253  *  call-seq:
00254  *     ObjectSpace.count_nodes([result_hash]) -> hash
00255  *
00256  *  Counts nodes for each node type.
00257  *
00258  *  This method is only for MRI developers interested in performance and memory
00259  *  usage of Ruby programs.
00260  *
00261  *  It returns a hash as:
00262  *
00263  *      {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...}
00264  *
00265  *  If the optional argument, result_hash, is given, it is overwritten and
00266  *  returned. This is intended to avoid probe effect.
00267  *
00268  *  Note:
00269  *  The contents of the returned hash is implementation defined.
00270  *  It may be changed in future.
00271  *
00272  *  This method is only expected to work with C Ruby.
00273  */
00274 
00275 static VALUE
00276 count_nodes(int argc, VALUE *argv, VALUE os)
00277 {
00278     size_t nodes[NODE_LAST+1];
00279     size_t i;
00280     VALUE hash;
00281 
00282     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00283         if (!RB_TYPE_P(hash, T_HASH))
00284             rb_raise(rb_eTypeError, "non-hash given");
00285     }
00286 
00287     for (i = 0; i <= NODE_LAST; i++) {
00288         nodes[i] = 0;
00289     }
00290 
00291     rb_objspace_each_objects(cn_i, &nodes[0]);
00292 
00293     if (hash == Qnil) {
00294         hash = rb_hash_new();
00295     }
00296     else if (!RHASH_EMPTY_P(hash)) {
00297         st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00298     }
00299 
00300     for (i=0; i<NODE_LAST; i++) {
00301         if (nodes[i] != 0) {
00302             VALUE node;
00303             switch (i) {
00304 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
00305                 COUNT_NODE(NODE_SCOPE);
00306                 COUNT_NODE(NODE_BLOCK);
00307                 COUNT_NODE(NODE_IF);
00308                 COUNT_NODE(NODE_CASE);
00309                 COUNT_NODE(NODE_WHEN);
00310                 COUNT_NODE(NODE_OPT_N);
00311                 COUNT_NODE(NODE_WHILE);
00312                 COUNT_NODE(NODE_UNTIL);
00313                 COUNT_NODE(NODE_ITER);
00314                 COUNT_NODE(NODE_FOR);
00315                 COUNT_NODE(NODE_BREAK);
00316                 COUNT_NODE(NODE_NEXT);
00317                 COUNT_NODE(NODE_REDO);
00318                 COUNT_NODE(NODE_RETRY);
00319                 COUNT_NODE(NODE_BEGIN);
00320                 COUNT_NODE(NODE_RESCUE);
00321                 COUNT_NODE(NODE_RESBODY);
00322                 COUNT_NODE(NODE_ENSURE);
00323                 COUNT_NODE(NODE_AND);
00324                 COUNT_NODE(NODE_OR);
00325                 COUNT_NODE(NODE_MASGN);
00326                 COUNT_NODE(NODE_LASGN);
00327                 COUNT_NODE(NODE_DASGN);
00328                 COUNT_NODE(NODE_DASGN_CURR);
00329                 COUNT_NODE(NODE_GASGN);
00330                 COUNT_NODE(NODE_IASGN);
00331                 COUNT_NODE(NODE_IASGN2);
00332                 COUNT_NODE(NODE_CDECL);
00333                 COUNT_NODE(NODE_CVASGN);
00334                 COUNT_NODE(NODE_CVDECL);
00335                 COUNT_NODE(NODE_OP_ASGN1);
00336                 COUNT_NODE(NODE_OP_ASGN2);
00337                 COUNT_NODE(NODE_OP_ASGN_AND);
00338                 COUNT_NODE(NODE_OP_ASGN_OR);
00339                 COUNT_NODE(NODE_OP_CDECL);
00340                 COUNT_NODE(NODE_CALL);
00341                 COUNT_NODE(NODE_FCALL);
00342                 COUNT_NODE(NODE_VCALL);
00343                 COUNT_NODE(NODE_SUPER);
00344                 COUNT_NODE(NODE_ZSUPER);
00345                 COUNT_NODE(NODE_ARRAY);
00346                 COUNT_NODE(NODE_ZARRAY);
00347                 COUNT_NODE(NODE_VALUES);
00348                 COUNT_NODE(NODE_HASH);
00349                 COUNT_NODE(NODE_RETURN);
00350                 COUNT_NODE(NODE_YIELD);
00351                 COUNT_NODE(NODE_LVAR);
00352                 COUNT_NODE(NODE_DVAR);
00353                 COUNT_NODE(NODE_GVAR);
00354                 COUNT_NODE(NODE_IVAR);
00355                 COUNT_NODE(NODE_CONST);
00356                 COUNT_NODE(NODE_CVAR);
00357                 COUNT_NODE(NODE_NTH_REF);
00358                 COUNT_NODE(NODE_BACK_REF);
00359                 COUNT_NODE(NODE_MATCH);
00360                 COUNT_NODE(NODE_MATCH2);
00361                 COUNT_NODE(NODE_MATCH3);
00362                 COUNT_NODE(NODE_LIT);
00363                 COUNT_NODE(NODE_STR);
00364                 COUNT_NODE(NODE_DSTR);
00365                 COUNT_NODE(NODE_XSTR);
00366                 COUNT_NODE(NODE_DXSTR);
00367                 COUNT_NODE(NODE_EVSTR);
00368                 COUNT_NODE(NODE_DREGX);
00369                 COUNT_NODE(NODE_DREGX_ONCE);
00370                 COUNT_NODE(NODE_ARGS);
00371                 COUNT_NODE(NODE_ARGS_AUX);
00372                 COUNT_NODE(NODE_OPT_ARG);
00373                 COUNT_NODE(NODE_KW_ARG);
00374                 COUNT_NODE(NODE_POSTARG);
00375                 COUNT_NODE(NODE_ARGSCAT);
00376                 COUNT_NODE(NODE_ARGSPUSH);
00377                 COUNT_NODE(NODE_SPLAT);
00378                 COUNT_NODE(NODE_TO_ARY);
00379                 COUNT_NODE(NODE_BLOCK_ARG);
00380                 COUNT_NODE(NODE_BLOCK_PASS);
00381                 COUNT_NODE(NODE_DEFN);
00382                 COUNT_NODE(NODE_DEFS);
00383                 COUNT_NODE(NODE_ALIAS);
00384                 COUNT_NODE(NODE_VALIAS);
00385                 COUNT_NODE(NODE_UNDEF);
00386                 COUNT_NODE(NODE_CLASS);
00387                 COUNT_NODE(NODE_MODULE);
00388                 COUNT_NODE(NODE_SCLASS);
00389                 COUNT_NODE(NODE_COLON2);
00390                 COUNT_NODE(NODE_COLON3);
00391                 COUNT_NODE(NODE_CREF);
00392                 COUNT_NODE(NODE_DOT2);
00393                 COUNT_NODE(NODE_DOT3);
00394                 COUNT_NODE(NODE_FLIP2);
00395                 COUNT_NODE(NODE_FLIP3);
00396                 COUNT_NODE(NODE_SELF);
00397                 COUNT_NODE(NODE_NIL);
00398                 COUNT_NODE(NODE_TRUE);
00399                 COUNT_NODE(NODE_FALSE);
00400                 COUNT_NODE(NODE_ERRINFO);
00401                 COUNT_NODE(NODE_DEFINED);
00402                 COUNT_NODE(NODE_POSTEXE);
00403                 COUNT_NODE(NODE_ALLOCA);
00404                 COUNT_NODE(NODE_BMETHOD);
00405                 COUNT_NODE(NODE_MEMO);
00406                 COUNT_NODE(NODE_IFUNC);
00407                 COUNT_NODE(NODE_DSYM);
00408                 COUNT_NODE(NODE_ATTRASGN);
00409                 COUNT_NODE(NODE_PRELUDE);
00410                 COUNT_NODE(NODE_LAMBDA);
00411 #undef COUNT_NODE
00412               default: node = INT2FIX(i);
00413             }
00414             rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
00415         }
00416     }
00417     return hash;
00418 }
00419 
00420 static int
00421 cto_i(void *vstart, void *vend, size_t stride, void *data)
00422 {
00423     VALUE hash = (VALUE)data;
00424     VALUE v = (VALUE)vstart;
00425 
00426     for (; v != (VALUE)vend; v += stride) {
00427         if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) {
00428             VALUE counter;
00429             VALUE key = RBASIC(v)->klass;
00430 
00431             if (key == 0) {
00432                 const char *name = rb_objspace_data_type_name(v);
00433                 if (name == 0) name = "unknown";
00434                 key = ID2SYM(rb_intern(name));
00435             }
00436 
00437             counter = rb_hash_aref(hash, key);
00438             if (NIL_P(counter)) {
00439                 counter = INT2FIX(1);
00440             }
00441             else {
00442                 counter = INT2FIX(FIX2INT(counter) + 1);
00443             }
00444 
00445             rb_hash_aset(hash, key, counter);
00446         }
00447     }
00448 
00449     return 0;
00450 }
00451 
00452 /*
00453  *  call-seq:
00454  *     ObjectSpace.count_tdata_objects([result_hash]) -> hash
00455  *
00456  *  Counts objects for each +T_DATA+ type.
00457  *
00458  *  This method is only for MRI developers interested in performance and memory
00459  *  usage of Ruby programs.
00460  *
00461  *  It returns a hash as:
00462  *
00463  *      {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6,
00464  *       :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99,
00465  *       ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1,
00466  *       Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2}
00467  *      # T_DATA objects existing at startup on r32276.
00468  *
00469  *  If the optional argument, result_hash, is given, it is overwritten and
00470  *  returned. This is intended to avoid probe effect.
00471  *
00472  *  The contents of the returned hash is implementation specific and may change
00473  *  in the future.
00474  *
00475  *  In this version, keys are Class object or Symbol object.
00476  *
00477  *  If object is kind of normal (accessible) object, the key is Class object.
00478  *  If object is not a kind of normal (internal) object, the key is symbol
00479  *  name, registered by rb_data_type_struct.
00480  *
00481  *  This method is only expected to work with C Ruby.
00482  */
00483 
00484 static VALUE
00485 count_tdata_objects(int argc, VALUE *argv, VALUE self)
00486 {
00487     VALUE hash;
00488 
00489     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00490         if (!RB_TYPE_P(hash, T_HASH))
00491             rb_raise(rb_eTypeError, "non-hash given");
00492     }
00493 
00494     if (hash == Qnil) {
00495         hash = rb_hash_new();
00496     }
00497     else if (!RHASH_EMPTY_P(hash)) {
00498         st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00499     }
00500 
00501     rb_objspace_each_objects(cto_i, (void *)hash);
00502 
00503     return hash;
00504 }
00505 
00506 static void
00507 iow_mark(void *ptr)
00508 {
00509     rb_gc_mark((VALUE)ptr);
00510 }
00511 
00512 static size_t
00513 iow_size(const void *ptr)
00514 {
00515     VALUE obj = (VALUE)ptr;
00516     return rb_obj_memsize_of(obj);
00517 }
00518 
00519 static const rb_data_type_t iow_data_type = {
00520     "ObjectSpace::InternalObjectWrapper",
00521     {iow_mark, 0, iow_size,},
00522     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
00523 };
00524 
00525 static VALUE rb_mInternalObjectWrapper;
00526 
00527 static VALUE
00528 iow_newobj(VALUE obj)
00529 {
00530     return rb_data_typed_object_alloc(rb_mInternalObjectWrapper, (void *)obj, &iow_data_type);
00531 }
00532 
00533 /* Returns the type of the internal object. */
00534 static VALUE
00535 iow_type(VALUE self)
00536 {
00537     VALUE obj = (VALUE)DATA_PTR(self);
00538     return type2sym(BUILTIN_TYPE(obj));
00539 }
00540 
00541 /* See Object#inspect. */
00542 static VALUE
00543 iow_inspect(VALUE self)
00544 {
00545     VALUE obj = (VALUE)DATA_PTR(self);
00546     VALUE type = type2sym(BUILTIN_TYPE(obj));
00547 
00548     return rb_sprintf("#<InternalObject:%p %s>", (void *)obj, rb_id2name(SYM2ID(type)));
00549 }
00550 
00551 /* Returns the Object#object_id of the internal object. */
00552 static VALUE
00553 iow_internal_object_id(VALUE self)
00554 {
00555     VALUE obj = (VALUE)DATA_PTR(self);
00556     return rb_obj_id(obj);
00557 }
00558 
00559 struct rof_data {
00560     st_table *refs;
00561     VALUE internals;
00562 };
00563 
00564 static void
00565 reachable_object_from_i(VALUE obj, void *data_ptr)
00566 {
00567     struct rof_data *data = (struct rof_data *)data_ptr;
00568     VALUE key = obj;
00569     VALUE val = obj;
00570 
00571     if (rb_objspace_markable_object_p(obj)) {
00572         if (rb_objspace_internal_object_p(obj)) {
00573             val = iow_newobj(obj);
00574             rb_ary_push(data->internals, val);
00575         }
00576         st_insert(data->refs, key, val);
00577     }
00578 }
00579 
00580 static int
00581 collect_values(st_data_t key, st_data_t value, st_data_t data)
00582 {
00583     VALUE ary = (VALUE)data;
00584     rb_ary_push(ary, (VALUE)value);
00585     return ST_CONTINUE;
00586 }
00587 
00588 /*
00589  *  call-seq:
00590  *     ObjectSpace.reachable_objects_from(obj) -> array or nil
00591  *
00592  *  [MRI specific feature] Return all reachable objects from `obj'.
00593  *
00594  *  This method returns all reachable objects from `obj'.
00595  *
00596  *  If `obj' has two or more references to the same object `x', then returned
00597  *  array only includes one `x' object.
00598  *
00599  *  If `obj' is a non-markable (non-heap management) object such as true,
00600  *  false, nil, symbols and Fixnums (and Flonum) then it simply returns nil.
00601  *
00602  *  If `obj' has references to an internal object, then it returns instances of
00603  *  ObjectSpace::InternalObjectWrapper class. This object contains a reference
00604  *  to an internal object and you can check the type of internal object with
00605  *  `type' method.
00606  *
00607  *  If `obj' is instance of ObjectSpace::InternalObjectWrapper class, then this
00608  *  method returns all reachable object from an internal object, which is
00609  *  pointed by `obj'.
00610  *
00611  *  With this method, you can find memory leaks.
00612  *
00613  *  This method is only expected to work except with C Ruby.
00614  *
00615  *  Example:
00616  *    ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
00617  *    #=> [Array, 'a', 'b', 'c']
00618  *
00619  *    ObjectSpace.reachable_objects_from(['a', 'a', 'a'])
00620  *    #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id
00621  *
00622  *    ObjectSpace.reachable_objects_from([v = 'a', v, v])
00623  *    #=> [Array, 'a']
00624  *
00625  *    ObjectSpace.reachable_objects_from(1)
00626  *    #=> nil # 1 is not markable (heap managed) object
00627  *
00628  */
00629 
00630 static VALUE
00631 reachable_objects_from(VALUE self, VALUE obj)
00632 {
00633     if (rb_objspace_markable_object_p(obj)) {
00634         VALUE ret = rb_ary_new();
00635         struct rof_data data;
00636 
00637         if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
00638             obj = (VALUE)DATA_PTR(obj);
00639         }
00640 
00641         data.refs = st_init_numtable();
00642         data.internals = rb_ary_new();
00643 
00644         rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
00645 
00646         st_foreach(data.refs, collect_values, (st_data_t)ret);
00647         return ret;
00648     }
00649     else {
00650         return Qnil;
00651     }
00652 }
00653 
00654 struct rofr_data {
00655     VALUE categories;
00656     const char *last_category;
00657     VALUE last_category_str;
00658     VALUE last_category_objects;
00659 };
00660 
00661 static void
00662 reachable_object_from_root_i(const char *category, VALUE obj, void *ptr)
00663 {
00664     struct rofr_data *data = (struct rofr_data *)ptr;
00665     VALUE category_str;
00666     VALUE category_objects;
00667 
00668     if (category == data->last_category) {
00669         category_str = data->last_category_str;
00670         category_objects = data->last_category_objects;
00671     }
00672     else {
00673         data->last_category = category;
00674         category_str = data->last_category_str = rb_str_new2(category);
00675         category_objects = data->last_category_objects = rb_hash_new();
00676         rb_funcall(category_objects, rb_intern("compare_by_identity"), 0);
00677         if (!NIL_P(rb_hash_lookup(data->categories, category_str))) {
00678             rb_bug("reachable_object_from_root_i: category should insert at once");
00679         }
00680         rb_hash_aset(data->categories, category_str, category_objects);
00681     }
00682 
00683     if (rb_objspace_markable_object_p(obj) &&
00684         obj != data->categories &&
00685         obj != data->last_category_objects) {
00686         if (rb_objspace_internal_object_p(obj)) {
00687             obj = iow_newobj(obj);
00688         }
00689         rb_hash_aset(category_objects, obj, obj);
00690     }
00691 }
00692 
00693 static int
00694 collect_values_of_values(VALUE category, VALUE category_objects, VALUE categories)
00695 {
00696     VALUE ary = rb_ary_new();
00697     st_foreach(rb_hash_tbl(category_objects), collect_values, ary);
00698     rb_hash_aset(categories, category, ary);
00699     return ST_CONTINUE;
00700 }
00701 
00702 /*
00703  *  call-seq:
00704  *     ObjectSpace.reachable_objects_from_root -> hash
00705  *
00706  *  [MRI specific feature] Return all reachable objects from root.
00707  */
00708 static VALUE
00709 reachable_objects_from_root(VALUE self)
00710 {
00711     struct rofr_data data;
00712     VALUE hash = data.categories = rb_hash_new();
00713     data.last_category = 0;
00714 
00715     rb_funcall(hash, rb_intern("compare_by_identity"), 0);
00716     rb_objspace_reachable_objects_from_root(reachable_object_from_root_i, &data);
00717     rb_hash_foreach(hash, collect_values_of_values, hash);
00718 
00719     return hash;
00720 }
00721 
00722 void Init_object_tracing(VALUE rb_mObjSpace);
00723 void Init_objspace_dump(VALUE rb_mObjSpace);
00724 
00725 /*
00726  * Document-module: ObjectSpace
00727  *
00728  * The objspace library extends the ObjectSpace module and adds several
00729  * methods to get internal statistic information about
00730  * object/memory management.
00731  *
00732  * You need to <code>require 'objspace'</code> to use this extension module.
00733  *
00734  * Generally, you *SHOULD NOT* use this library if you do not know
00735  * about the MRI implementation.  Mainly, this library is for (memory)
00736  * profiler developers and MRI developers who need to know about MRI
00737  * memory usage.
00738  */
00739 
00740 void
00741 Init_objspace(void)
00742 {
00743     VALUE rb_mObjSpace;
00744 #if 0
00745     rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
00746 #endif
00747     rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
00748 
00749     rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
00750     rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1);
00751 
00752     rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
00753     rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
00754     rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
00755 
00756     rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1);
00757     rb_define_module_function(rb_mObjSpace, "reachable_objects_from_root", reachable_objects_from_root, 0);
00758 
00759     /*
00760      * This class is used as a return value from
00761      * ObjectSpace::reachable_objects_from.
00762      *
00763      * When ObjectSpace::reachable_objects_from returns an object with
00764      * references to an internal object, an instance of this class is returned.
00765      *
00766      * You can use the #type method to check the type of the internal object.
00767      */
00768     rb_mInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject);
00769     rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0);
00770     rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0);
00771     rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
00772 
00773     Init_object_tracing(rb_mObjSpace);
00774     Init_objspace_dump(rb_mObjSpace);
00775 }
00776 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7