• Main Page
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

ext/objspace/objspace.c

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

Generated on Wed Sep 8 2010 21:53:56 for Ruby by  doxygen 1.7.1