Ruby
2.0.0p247(2013-06-27revision41674)
|
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