• 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: 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 

Generated on Thu Sep 8 2011 03:50:34 for Ruby by  doxygen 1.7.1