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