|
Ruby 1.9.2p290(2011-07-09revision32553)
|
00001 /********************************************************************** 00002 00003 objspace.c - ObjectSpace extender for MRI. 00004 00005 $Author: yugui $ 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 00032 size_t rb_str_memsize(VALUE); 00033 size_t rb_ary_memsize(VALUE); 00034 size_t rb_io_memsize(rb_io_t *); 00035 size_t onig_memsize(regex_t *); 00036 size_t rb_generic_ivar_memsize(VALUE); 00037 size_t rb_objspace_data_type_memsize(VALUE obj); 00038 00039 void rb_objspace_each_objects( 00040 int (*callback)(void *start, void *end, size_t stride, void *data), 00041 void *data); 00042 00043 static size_t 00044 memsize_of(VALUE obj) 00045 { 00046 size_t size = 0; 00047 00048 if (SPECIAL_CONST_P(obj)) { 00049 return 0; 00050 } 00051 00052 if (FL_TEST(obj, FL_EXIVAR)) { 00053 size += rb_generic_ivar_memsize(obj); 00054 } 00055 00056 switch (BUILTIN_TYPE(obj)) { 00057 case T_OBJECT: 00058 if (!(RBASIC(obj)->flags & ROBJECT_EMBED) && 00059 ROBJECT(obj)->as.heap.ivptr) { 00060 size += ROBJECT(obj)->as.heap.numiv * sizeof(VALUE); 00061 } 00062 break; 00063 case T_MODULE: 00064 case T_CLASS: 00065 size += st_memsize(RCLASS_M_TBL(obj)); 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 size += sizeof(rb_classext_t); 00076 break; 00077 case T_STRING: 00078 size += rb_str_memsize(obj); 00079 break; 00080 case T_ARRAY: 00081 size += rb_ary_memsize(obj); 00082 break; 00083 case T_HASH: 00084 if (RHASH(obj)->ntbl) { 00085 size += st_memsize(RHASH(obj)->ntbl); 00086 } 00087 break; 00088 case T_REGEXP: 00089 if (RREGEXP(obj)->ptr) { 00090 size += onig_memsize(RREGEXP(obj)->ptr); 00091 } 00092 break; 00093 case T_DATA: 00094 size += rb_objspace_data_type_memsize(obj); 00095 break; 00096 case T_MATCH: 00097 if (RMATCH(obj)->rmatch) { 00098 struct rmatch *rm = RMATCH(obj)->rmatch; 00099 size += sizeof(struct re_registers); /* TODO: onig_region_memsize(&rm->regs); */ 00100 size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated; 00101 size += sizeof(struct rmatch); 00102 } 00103 break; 00104 case T_FILE: 00105 if (RFILE(obj)->fptr) { 00106 size += rb_io_memsize(RFILE(obj)->fptr); 00107 } 00108 break; 00109 case T_RATIONAL: 00110 case T_COMPLEX: 00111 break; 00112 case T_ICLASS: 00113 /* iClass shares table with the module */ 00114 break; 00115 00116 case T_FLOAT: 00117 break; 00118 00119 case T_BIGNUM: 00120 if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) { 00121 size += RBIGNUM_LEN(obj) * sizeof(BDIGIT); 00122 } 00123 break; 00124 case T_NODE: 00125 switch (nd_type(obj)) { 00126 case NODE_SCOPE: 00127 if (RNODE(obj)->u1.tbl) { 00128 /* TODO: xfree(RANY(obj)->as.node.u1.tbl); */ 00129 } 00130 break; 00131 case NODE_ALLOCA: 00132 /* TODO: xfree(RANY(obj)->as.node.u1.node); */ 00133 ; 00134 } 00135 break; /* no need to free iv_tbl */ 00136 00137 case T_STRUCT: 00138 if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 && 00139 RSTRUCT(obj)->as.heap.ptr) { 00140 size += sizeof(VALUE) * RSTRUCT_LEN(obj); 00141 } 00142 break; 00143 00144 case T_ZOMBIE: 00145 break; 00146 00147 default: 00148 rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)", 00149 BUILTIN_TYPE(obj), (void*)obj); 00150 } 00151 00152 return size; 00153 } 00154 00155 /* 00156 * call-seq: 00157 * ObjectSpace.memsize_of(obj) -> Integer 00158 * 00159 * Return consuming memory size of obj. 00160 * 00161 * Note that this information is incomplete. You need to deal with 00162 * this information as only a *HINT*. Especaially, the size of 00163 * T_DATA may not right size. 00164 * 00165 * This method is not expected to work except C Ruby. 00166 */ 00167 00168 static VALUE 00169 memsize_of_m(VALUE self, VALUE obj) 00170 { 00171 return SIZET2NUM(memsize_of(obj)); 00172 } 00173 00174 static int 00175 set_zero_i(st_data_t key, st_data_t val, st_data_t arg) 00176 { 00177 VALUE k = (VALUE)key; 00178 VALUE hash = (VALUE)arg; 00179 rb_hash_aset(hash, k, INT2FIX(0)); 00180 return ST_CONTINUE; 00181 } 00182 00183 static int 00184 cos_i(void *vstart, void *vend, size_t stride, void *data) 00185 { 00186 size_t *counts = (size_t *)data; 00187 VALUE v = (VALUE)vstart; 00188 00189 for (;v != (VALUE)vend; v += stride) { 00190 if (RBASIC(v)->flags) { 00191 counts[BUILTIN_TYPE(v)] += memsize_of(v); 00192 } 00193 } 00194 return 0; 00195 } 00196 00197 /* 00198 * call-seq: 00199 * ObjectSpace.count_objects_size([result_hash]) -> hash 00200 * 00201 * Counts objects size (in bytes) for each type. 00202 * 00203 * Note that this information is incomplete. You need to deal with 00204 * this information as only a *HINT*. Especaially, total size of 00205 * T_DATA may not right size. 00206 * 00207 * It returns a hash as: 00208 * {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...} 00209 * 00210 * If the optional argument, result_hash, is given, 00211 * it is overwritten and returned. 00212 * This is intended to avoid probe effect. 00213 * 00214 * The contents of the returned hash is implementation defined. 00215 * It may be changed in future. 00216 * 00217 * This method is not expected to work except C Ruby. 00218 */ 00219 00220 static VALUE 00221 count_objects_size(int argc, VALUE *argv, VALUE os) 00222 { 00223 size_t counts[T_MASK+1]; 00224 size_t total = 0; 00225 size_t i; 00226 VALUE hash; 00227 00228 if (rb_scan_args(argc, argv, "01", &hash) == 1) { 00229 if (TYPE(hash) != T_HASH) 00230 rb_raise(rb_eTypeError, "non-hash given"); 00231 } 00232 00233 for (i = 0; i <= T_MASK; i++) { 00234 counts[i] = 0; 00235 } 00236 00237 rb_objspace_each_objects(cos_i, &counts[0]); 00238 00239 if (hash == Qnil) { 00240 hash = rb_hash_new(); 00241 } 00242 else if (!RHASH_EMPTY_P(hash)) { 00243 st_foreach(RHASH_TBL(hash), set_zero_i, hash); 00244 } 00245 00246 for (i = 0; i <= T_MASK; i++) { 00247 if (counts[i]) { 00248 VALUE type; 00249 switch (i) { 00250 #define COUNT_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break; 00251 COUNT_TYPE(T_NONE); 00252 COUNT_TYPE(T_OBJECT); 00253 COUNT_TYPE(T_CLASS); 00254 COUNT_TYPE(T_MODULE); 00255 COUNT_TYPE(T_FLOAT); 00256 COUNT_TYPE(T_STRING); 00257 COUNT_TYPE(T_REGEXP); 00258 COUNT_TYPE(T_ARRAY); 00259 COUNT_TYPE(T_HASH); 00260 COUNT_TYPE(T_STRUCT); 00261 COUNT_TYPE(T_BIGNUM); 00262 COUNT_TYPE(T_FILE); 00263 COUNT_TYPE(T_DATA); 00264 COUNT_TYPE(T_MATCH); 00265 COUNT_TYPE(T_COMPLEX); 00266 COUNT_TYPE(T_RATIONAL); 00267 COUNT_TYPE(T_NIL); 00268 COUNT_TYPE(T_TRUE); 00269 COUNT_TYPE(T_FALSE); 00270 COUNT_TYPE(T_SYMBOL); 00271 COUNT_TYPE(T_FIXNUM); 00272 COUNT_TYPE(T_UNDEF); 00273 COUNT_TYPE(T_NODE); 00274 COUNT_TYPE(T_ICLASS); 00275 COUNT_TYPE(T_ZOMBIE); 00276 #undef COUNT_TYPE 00277 default: type = INT2NUM(i); break; 00278 } 00279 total += counts[i]; 00280 rb_hash_aset(hash, type, SIZET2NUM(counts[i])); 00281 } 00282 } 00283 rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total)); 00284 return hash; 00285 } 00286 00287 static int 00288 cn_i(void *vstart, void *vend, size_t stride, void *n) 00289 { 00290 size_t *nodes = (size_t *)n; 00291 VALUE v = (VALUE)vstart; 00292 00293 for (; v != (VALUE)vend; v += stride) { 00294 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) { 00295 size_t s = nd_type((NODE *)v); 00296 nodes[s]++; 00297 } 00298 } 00299 00300 return 0; 00301 } 00302 00303 /* 00304 * call-seq: 00305 * ObjectSpace.count_nodes([result_hash]) -> hash 00306 * 00307 * Counts nodes for each node type. 00308 * 00309 * This method is not for ordinary Ruby programmers, but for MRI developers 00310 * who have interest in MRI performance and memory usage. 00311 * 00312 * It returns a hash as: 00313 * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} 00314 * 00315 * If the optional argument, result_hash, is given, 00316 * it is overwritten and returned. 00317 * This is intended to avoid probe effect. 00318 * 00319 * The contents of the returned hash is implementation defined. 00320 * It may be changed in future. 00321 * 00322 * This method is not expected to work except C Ruby. 00323 */ 00324 00325 static VALUE 00326 count_nodes(int argc, VALUE *argv, VALUE os) 00327 { 00328 size_t nodes[NODE_LAST+1]; 00329 size_t i; 00330 VALUE hash; 00331 00332 if (rb_scan_args(argc, argv, "01", &hash) == 1) { 00333 if (TYPE(hash) != T_HASH) 00334 rb_raise(rb_eTypeError, "non-hash given"); 00335 } 00336 00337 for (i = 0; i <= NODE_LAST; i++) { 00338 nodes[i] = 0; 00339 } 00340 00341 rb_objspace_each_objects(cn_i, &nodes[0]); 00342 00343 if (hash == Qnil) { 00344 hash = rb_hash_new(); 00345 } 00346 else if (!RHASH_EMPTY_P(hash)) { 00347 st_foreach(RHASH_TBL(hash), set_zero_i, hash); 00348 } 00349 00350 for (i=0; i<NODE_LAST; i++) { 00351 if (nodes[i] != 0) { 00352 VALUE node; 00353 switch (i) { 00354 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break; 00355 COUNT_NODE(NODE_SCOPE); 00356 COUNT_NODE(NODE_BLOCK); 00357 COUNT_NODE(NODE_IF); 00358 COUNT_NODE(NODE_CASE); 00359 COUNT_NODE(NODE_WHEN); 00360 COUNT_NODE(NODE_OPT_N); 00361 COUNT_NODE(NODE_WHILE); 00362 COUNT_NODE(NODE_UNTIL); 00363 COUNT_NODE(NODE_ITER); 00364 COUNT_NODE(NODE_FOR); 00365 COUNT_NODE(NODE_BREAK); 00366 COUNT_NODE(NODE_NEXT); 00367 COUNT_NODE(NODE_REDO); 00368 COUNT_NODE(NODE_RETRY); 00369 COUNT_NODE(NODE_BEGIN); 00370 COUNT_NODE(NODE_RESCUE); 00371 COUNT_NODE(NODE_RESBODY); 00372 COUNT_NODE(NODE_ENSURE); 00373 COUNT_NODE(NODE_AND); 00374 COUNT_NODE(NODE_OR); 00375 COUNT_NODE(NODE_MASGN); 00376 COUNT_NODE(NODE_LASGN); 00377 COUNT_NODE(NODE_DASGN); 00378 COUNT_NODE(NODE_DASGN_CURR); 00379 COUNT_NODE(NODE_GASGN); 00380 COUNT_NODE(NODE_IASGN); 00381 COUNT_NODE(NODE_IASGN2); 00382 COUNT_NODE(NODE_CDECL); 00383 COUNT_NODE(NODE_CVASGN); 00384 COUNT_NODE(NODE_CVDECL); 00385 COUNT_NODE(NODE_OP_ASGN1); 00386 COUNT_NODE(NODE_OP_ASGN2); 00387 COUNT_NODE(NODE_OP_ASGN_AND); 00388 COUNT_NODE(NODE_OP_ASGN_OR); 00389 COUNT_NODE(NODE_CALL); 00390 COUNT_NODE(NODE_FCALL); 00391 COUNT_NODE(NODE_VCALL); 00392 COUNT_NODE(NODE_SUPER); 00393 COUNT_NODE(NODE_ZSUPER); 00394 COUNT_NODE(NODE_ARRAY); 00395 COUNT_NODE(NODE_ZARRAY); 00396 COUNT_NODE(NODE_VALUES); 00397 COUNT_NODE(NODE_HASH); 00398 COUNT_NODE(NODE_RETURN); 00399 COUNT_NODE(NODE_YIELD); 00400 COUNT_NODE(NODE_LVAR); 00401 COUNT_NODE(NODE_DVAR); 00402 COUNT_NODE(NODE_GVAR); 00403 COUNT_NODE(NODE_IVAR); 00404 COUNT_NODE(NODE_CONST); 00405 COUNT_NODE(NODE_CVAR); 00406 COUNT_NODE(NODE_NTH_REF); 00407 COUNT_NODE(NODE_BACK_REF); 00408 COUNT_NODE(NODE_MATCH); 00409 COUNT_NODE(NODE_MATCH2); 00410 COUNT_NODE(NODE_MATCH3); 00411 COUNT_NODE(NODE_LIT); 00412 COUNT_NODE(NODE_STR); 00413 COUNT_NODE(NODE_DSTR); 00414 COUNT_NODE(NODE_XSTR); 00415 COUNT_NODE(NODE_DXSTR); 00416 COUNT_NODE(NODE_EVSTR); 00417 COUNT_NODE(NODE_DREGX); 00418 COUNT_NODE(NODE_DREGX_ONCE); 00419 COUNT_NODE(NODE_ARGS); 00420 COUNT_NODE(NODE_ARGS_AUX); 00421 COUNT_NODE(NODE_OPT_ARG); 00422 COUNT_NODE(NODE_POSTARG); 00423 COUNT_NODE(NODE_ARGSCAT); 00424 COUNT_NODE(NODE_ARGSPUSH); 00425 COUNT_NODE(NODE_SPLAT); 00426 COUNT_NODE(NODE_TO_ARY); 00427 COUNT_NODE(NODE_BLOCK_ARG); 00428 COUNT_NODE(NODE_BLOCK_PASS); 00429 COUNT_NODE(NODE_DEFN); 00430 COUNT_NODE(NODE_DEFS); 00431 COUNT_NODE(NODE_ALIAS); 00432 COUNT_NODE(NODE_VALIAS); 00433 COUNT_NODE(NODE_UNDEF); 00434 COUNT_NODE(NODE_CLASS); 00435 COUNT_NODE(NODE_MODULE); 00436 COUNT_NODE(NODE_SCLASS); 00437 COUNT_NODE(NODE_COLON2); 00438 COUNT_NODE(NODE_COLON3); 00439 COUNT_NODE(NODE_DOT2); 00440 COUNT_NODE(NODE_DOT3); 00441 COUNT_NODE(NODE_FLIP2); 00442 COUNT_NODE(NODE_FLIP3); 00443 COUNT_NODE(NODE_SELF); 00444 COUNT_NODE(NODE_NIL); 00445 COUNT_NODE(NODE_TRUE); 00446 COUNT_NODE(NODE_FALSE); 00447 COUNT_NODE(NODE_ERRINFO); 00448 COUNT_NODE(NODE_DEFINED); 00449 COUNT_NODE(NODE_POSTEXE); 00450 COUNT_NODE(NODE_ALLOCA); 00451 COUNT_NODE(NODE_BMETHOD); 00452 COUNT_NODE(NODE_MEMO); 00453 COUNT_NODE(NODE_IFUNC); 00454 COUNT_NODE(NODE_DSYM); 00455 COUNT_NODE(NODE_ATTRASGN); 00456 COUNT_NODE(NODE_PRELUDE); 00457 COUNT_NODE(NODE_LAMBDA); 00458 COUNT_NODE(NODE_OPTBLOCK); 00459 #undef COUNT_NODE 00460 default: node = INT2FIX(nodes[i]); 00461 } 00462 rb_hash_aset(hash, node, SIZET2NUM(nodes[i])); 00463 } 00464 } 00465 return hash; 00466 } 00467 00468 static int 00469 cto_i(void *vstart, void *vend, size_t stride, void *data) 00470 { 00471 VALUE hash = (VALUE)data; 00472 VALUE v = (VALUE)vstart; 00473 00474 for (; v != (VALUE)vend; v += stride) { 00475 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) { 00476 VALUE counter = rb_hash_aref(hash, RBASIC(v)->klass); 00477 if (NIL_P(counter)) { 00478 counter = INT2FIX(1); 00479 } 00480 else { 00481 counter = INT2FIX(FIX2INT(counter) + 1); 00482 } 00483 rb_hash_aset(hash, RBASIC(v)->klass, counter); 00484 } 00485 } 00486 00487 return 0; 00488 } 00489 00490 /* 00491 * call-seq: 00492 * ObjectSpace.count_tdata_objects([result_hash]) -> hash 00493 * 00494 * Counts nodes for each node type. 00495 * 00496 * This method is not for ordinary Ruby programmers, but for MRI developers 00497 * who interest on MRI performance. 00498 * 00499 * It returns a hash as: 00500 * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} 00501 * 00502 * If the optional argument, result_hash, is given, 00503 * it is overwritten and returned. 00504 * This is intended to avoid probe effect. 00505 * 00506 * The contents of the returned hash is implementation defined. 00507 * It may be changed in future. 00508 * 00509 * This method is not expected to work except C Ruby. 00510 * 00511 */ 00512 00513 static VALUE 00514 count_tdata_objects(int argc, VALUE *argv, VALUE self) 00515 { 00516 VALUE hash; 00517 00518 if (rb_scan_args(argc, argv, "01", &hash) == 1) { 00519 if (TYPE(hash) != T_HASH) 00520 rb_raise(rb_eTypeError, "non-hash given"); 00521 } 00522 00523 if (hash == Qnil) { 00524 hash = rb_hash_new(); 00525 } 00526 else if (!RHASH_EMPTY_P(hash)) { 00527 st_foreach(RHASH_TBL(hash), set_zero_i, hash); 00528 } 00529 00530 rb_objspace_each_objects(cto_i, (void *)hash); 00531 00532 return hash; 00533 } 00534 00535 /* objspace library extends ObjectSpace module and add several 00536 * methods to get internal statistic information about 00537 * object/memory management. 00538 * 00539 * Generally, you *SHOULD NOT*use this library if you do not know 00540 * about the MRI implementation. Mainly, this library is for (memory) 00541 * profiler developers and MRI developers who need to know how MRI 00542 * memory usage. 00543 */ 00544 00545 void 00546 Init_objspace(void) 00547 { 00548 VALUE rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace")); 00549 00550 rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); 00551 rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1); 00552 rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); 00553 rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); 00554 } 00555
1.7.3