00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <ruby/ruby.h>
00016 #include <ruby/st.h>
00017 #include <ruby/io.h>
00018 #include <ruby/re.h>
00019 #include "node.h"
00020 #include "gc.h"
00021 #include "internal.h"
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 static VALUE
00037 memsize_of_m(VALUE self, VALUE obj)
00038 {
00039 return SIZET2NUM(rb_obj_memsize_of(obj));
00040 }
00041
00042 struct total_data {
00043 size_t total;
00044 VALUE klass;
00045 };
00046
00047 static int
00048 total_i(void *vstart, void *vend, size_t stride, void *ptr)
00049 {
00050 VALUE v;
00051 struct total_data *data = (struct total_data *)ptr;
00052
00053 for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
00054 if (RBASIC(v)->flags) {
00055 switch (BUILTIN_TYPE(v)) {
00056 case T_NONE:
00057 case T_ICLASS:
00058 case T_NODE:
00059 case T_ZOMBIE:
00060 continue;
00061 case T_CLASS:
00062 if (FL_TEST(v, FL_SINGLETON))
00063 continue;
00064 default:
00065 if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
00066 data->total += rb_obj_memsize_of(v);
00067 }
00068 }
00069 }
00070 }
00071
00072 return 0;
00073 }
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 static VALUE
00104 memsize_of_all_m(int argc, VALUE *argv, VALUE self)
00105 {
00106 struct total_data data = {0, 0};
00107
00108 if (argc > 0) {
00109 rb_scan_args(argc, argv, "01", &data.klass);
00110 }
00111
00112 rb_objspace_each_objects(total_i, &data);
00113 return SIZET2NUM(data.total);
00114 }
00115
00116 static int
00117 set_zero_i(st_data_t key, st_data_t val, st_data_t arg)
00118 {
00119 VALUE k = (VALUE)key;
00120 VALUE hash = (VALUE)arg;
00121 rb_hash_aset(hash, k, INT2FIX(0));
00122 return ST_CONTINUE;
00123 }
00124
00125 static int
00126 cos_i(void *vstart, void *vend, size_t stride, void *data)
00127 {
00128 size_t *counts = (size_t *)data;
00129 VALUE v = (VALUE)vstart;
00130
00131 for (;v != (VALUE)vend; v += stride) {
00132 if (RBASIC(v)->flags) {
00133 counts[BUILTIN_TYPE(v)] += rb_obj_memsize_of(v);
00134 }
00135 }
00136 return 0;
00137 }
00138
00139 static VALUE
00140 type2sym(enum ruby_value_type i)
00141 {
00142 VALUE type;
00143 switch (i) {
00144 #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
00145 CASE_TYPE(T_NONE);
00146 CASE_TYPE(T_OBJECT);
00147 CASE_TYPE(T_CLASS);
00148 CASE_TYPE(T_MODULE);
00149 CASE_TYPE(T_FLOAT);
00150 CASE_TYPE(T_STRING);
00151 CASE_TYPE(T_REGEXP);
00152 CASE_TYPE(T_ARRAY);
00153 CASE_TYPE(T_HASH);
00154 CASE_TYPE(T_STRUCT);
00155 CASE_TYPE(T_BIGNUM);
00156 CASE_TYPE(T_FILE);
00157 CASE_TYPE(T_DATA);
00158 CASE_TYPE(T_MATCH);
00159 CASE_TYPE(T_COMPLEX);
00160 CASE_TYPE(T_RATIONAL);
00161 CASE_TYPE(T_NIL);
00162 CASE_TYPE(T_TRUE);
00163 CASE_TYPE(T_FALSE);
00164 CASE_TYPE(T_SYMBOL);
00165 CASE_TYPE(T_FIXNUM);
00166 CASE_TYPE(T_UNDEF);
00167 CASE_TYPE(T_NODE);
00168 CASE_TYPE(T_ICLASS);
00169 CASE_TYPE(T_ZOMBIE);
00170 #undef CASE_TYPE
00171 default: rb_bug("type2sym: unknown type (%d)", i);
00172 }
00173 return type;
00174 }
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 static VALUE
00200 count_objects_size(int argc, VALUE *argv, VALUE os)
00201 {
00202 size_t counts[T_MASK+1];
00203 size_t total = 0;
00204 enum ruby_value_type i;
00205 VALUE hash;
00206
00207 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00208 if (!RB_TYPE_P(hash, T_HASH))
00209 rb_raise(rb_eTypeError, "non-hash given");
00210 }
00211
00212 for (i = 0; i <= T_MASK; i++) {
00213 counts[i] = 0;
00214 }
00215
00216 rb_objspace_each_objects(cos_i, &counts[0]);
00217
00218 if (hash == Qnil) {
00219 hash = rb_hash_new();
00220 }
00221 else if (!RHASH_EMPTY_P(hash)) {
00222 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00223 }
00224
00225 for (i = 0; i <= T_MASK; i++) {
00226 if (counts[i]) {
00227 VALUE type = type2sym(i);
00228 total += counts[i];
00229 rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
00230 }
00231 }
00232 rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
00233 return hash;
00234 }
00235
00236 static int
00237 cn_i(void *vstart, void *vend, size_t stride, void *n)
00238 {
00239 size_t *nodes = (size_t *)n;
00240 VALUE v = (VALUE)vstart;
00241
00242 for (; v != (VALUE)vend; v += stride) {
00243 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) {
00244 size_t s = nd_type((NODE *)v);
00245 nodes[s]++;
00246 }
00247 }
00248
00249 return 0;
00250 }
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275 static VALUE
00276 count_nodes(int argc, VALUE *argv, VALUE os)
00277 {
00278 size_t nodes[NODE_LAST+1];
00279 size_t i;
00280 VALUE hash;
00281
00282 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00283 if (!RB_TYPE_P(hash, T_HASH))
00284 rb_raise(rb_eTypeError, "non-hash given");
00285 }
00286
00287 for (i = 0; i <= NODE_LAST; i++) {
00288 nodes[i] = 0;
00289 }
00290
00291 rb_objspace_each_objects(cn_i, &nodes[0]);
00292
00293 if (hash == Qnil) {
00294 hash = rb_hash_new();
00295 }
00296 else if (!RHASH_EMPTY_P(hash)) {
00297 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00298 }
00299
00300 for (i=0; i<NODE_LAST; i++) {
00301 if (nodes[i] != 0) {
00302 VALUE node;
00303 switch (i) {
00304 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
00305 COUNT_NODE(NODE_SCOPE);
00306 COUNT_NODE(NODE_BLOCK);
00307 COUNT_NODE(NODE_IF);
00308 COUNT_NODE(NODE_CASE);
00309 COUNT_NODE(NODE_WHEN);
00310 COUNT_NODE(NODE_OPT_N);
00311 COUNT_NODE(NODE_WHILE);
00312 COUNT_NODE(NODE_UNTIL);
00313 COUNT_NODE(NODE_ITER);
00314 COUNT_NODE(NODE_FOR);
00315 COUNT_NODE(NODE_BREAK);
00316 COUNT_NODE(NODE_NEXT);
00317 COUNT_NODE(NODE_REDO);
00318 COUNT_NODE(NODE_RETRY);
00319 COUNT_NODE(NODE_BEGIN);
00320 COUNT_NODE(NODE_RESCUE);
00321 COUNT_NODE(NODE_RESBODY);
00322 COUNT_NODE(NODE_ENSURE);
00323 COUNT_NODE(NODE_AND);
00324 COUNT_NODE(NODE_OR);
00325 COUNT_NODE(NODE_MASGN);
00326 COUNT_NODE(NODE_LASGN);
00327 COUNT_NODE(NODE_DASGN);
00328 COUNT_NODE(NODE_DASGN_CURR);
00329 COUNT_NODE(NODE_GASGN);
00330 COUNT_NODE(NODE_IASGN);
00331 COUNT_NODE(NODE_IASGN2);
00332 COUNT_NODE(NODE_CDECL);
00333 COUNT_NODE(NODE_CVASGN);
00334 COUNT_NODE(NODE_CVDECL);
00335 COUNT_NODE(NODE_OP_ASGN1);
00336 COUNT_NODE(NODE_OP_ASGN2);
00337 COUNT_NODE(NODE_OP_ASGN_AND);
00338 COUNT_NODE(NODE_OP_ASGN_OR);
00339 COUNT_NODE(NODE_OP_CDECL);
00340 COUNT_NODE(NODE_CALL);
00341 COUNT_NODE(NODE_FCALL);
00342 COUNT_NODE(NODE_VCALL);
00343 COUNT_NODE(NODE_SUPER);
00344 COUNT_NODE(NODE_ZSUPER);
00345 COUNT_NODE(NODE_ARRAY);
00346 COUNT_NODE(NODE_ZARRAY);
00347 COUNT_NODE(NODE_VALUES);
00348 COUNT_NODE(NODE_HASH);
00349 COUNT_NODE(NODE_RETURN);
00350 COUNT_NODE(NODE_YIELD);
00351 COUNT_NODE(NODE_LVAR);
00352 COUNT_NODE(NODE_DVAR);
00353 COUNT_NODE(NODE_GVAR);
00354 COUNT_NODE(NODE_IVAR);
00355 COUNT_NODE(NODE_CONST);
00356 COUNT_NODE(NODE_CVAR);
00357 COUNT_NODE(NODE_NTH_REF);
00358 COUNT_NODE(NODE_BACK_REF);
00359 COUNT_NODE(NODE_MATCH);
00360 COUNT_NODE(NODE_MATCH2);
00361 COUNT_NODE(NODE_MATCH3);
00362 COUNT_NODE(NODE_LIT);
00363 COUNT_NODE(NODE_STR);
00364 COUNT_NODE(NODE_DSTR);
00365 COUNT_NODE(NODE_XSTR);
00366 COUNT_NODE(NODE_DXSTR);
00367 COUNT_NODE(NODE_EVSTR);
00368 COUNT_NODE(NODE_DREGX);
00369 COUNT_NODE(NODE_DREGX_ONCE);
00370 COUNT_NODE(NODE_ARGS);
00371 COUNT_NODE(NODE_ARGS_AUX);
00372 COUNT_NODE(NODE_OPT_ARG);
00373 COUNT_NODE(NODE_KW_ARG);
00374 COUNT_NODE(NODE_POSTARG);
00375 COUNT_NODE(NODE_ARGSCAT);
00376 COUNT_NODE(NODE_ARGSPUSH);
00377 COUNT_NODE(NODE_SPLAT);
00378 COUNT_NODE(NODE_TO_ARY);
00379 COUNT_NODE(NODE_BLOCK_ARG);
00380 COUNT_NODE(NODE_BLOCK_PASS);
00381 COUNT_NODE(NODE_DEFN);
00382 COUNT_NODE(NODE_DEFS);
00383 COUNT_NODE(NODE_ALIAS);
00384 COUNT_NODE(NODE_VALIAS);
00385 COUNT_NODE(NODE_UNDEF);
00386 COUNT_NODE(NODE_CLASS);
00387 COUNT_NODE(NODE_MODULE);
00388 COUNT_NODE(NODE_SCLASS);
00389 COUNT_NODE(NODE_COLON2);
00390 COUNT_NODE(NODE_COLON3);
00391 COUNT_NODE(NODE_CREF);
00392 COUNT_NODE(NODE_DOT2);
00393 COUNT_NODE(NODE_DOT3);
00394 COUNT_NODE(NODE_FLIP2);
00395 COUNT_NODE(NODE_FLIP3);
00396 COUNT_NODE(NODE_SELF);
00397 COUNT_NODE(NODE_NIL);
00398 COUNT_NODE(NODE_TRUE);
00399 COUNT_NODE(NODE_FALSE);
00400 COUNT_NODE(NODE_ERRINFO);
00401 COUNT_NODE(NODE_DEFINED);
00402 COUNT_NODE(NODE_POSTEXE);
00403 COUNT_NODE(NODE_ALLOCA);
00404 COUNT_NODE(NODE_BMETHOD);
00405 COUNT_NODE(NODE_MEMO);
00406 COUNT_NODE(NODE_IFUNC);
00407 COUNT_NODE(NODE_DSYM);
00408 COUNT_NODE(NODE_ATTRASGN);
00409 COUNT_NODE(NODE_PRELUDE);
00410 COUNT_NODE(NODE_LAMBDA);
00411 #undef COUNT_NODE
00412 default: node = INT2FIX(i);
00413 }
00414 rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
00415 }
00416 }
00417 return hash;
00418 }
00419
00420 static int
00421 cto_i(void *vstart, void *vend, size_t stride, void *data)
00422 {
00423 VALUE hash = (VALUE)data;
00424 VALUE v = (VALUE)vstart;
00425
00426 for (; v != (VALUE)vend; v += stride) {
00427 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) {
00428 VALUE counter;
00429 VALUE key = RBASIC(v)->klass;
00430
00431 if (key == 0) {
00432 const char *name = rb_objspace_data_type_name(v);
00433 if (name == 0) name = "unknown";
00434 key = ID2SYM(rb_intern(name));
00435 }
00436
00437 counter = rb_hash_aref(hash, key);
00438 if (NIL_P(counter)) {
00439 counter = INT2FIX(1);
00440 }
00441 else {
00442 counter = INT2FIX(FIX2INT(counter) + 1);
00443 }
00444
00445 rb_hash_aset(hash, key, counter);
00446 }
00447 }
00448
00449 return 0;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484 static VALUE
00485 count_tdata_objects(int argc, VALUE *argv, VALUE self)
00486 {
00487 VALUE hash;
00488
00489 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
00490 if (!RB_TYPE_P(hash, T_HASH))
00491 rb_raise(rb_eTypeError, "non-hash given");
00492 }
00493
00494 if (hash == Qnil) {
00495 hash = rb_hash_new();
00496 }
00497 else if (!RHASH_EMPTY_P(hash)) {
00498 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
00499 }
00500
00501 rb_objspace_each_objects(cto_i, (void *)hash);
00502
00503 return hash;
00504 }
00505
00506 static void
00507 iow_mark(void *ptr)
00508 {
00509 rb_gc_mark((VALUE)ptr);
00510 }
00511
00512 static size_t
00513 iow_size(const void *ptr)
00514 {
00515 VALUE obj = (VALUE)ptr;
00516 return rb_obj_memsize_of(obj);
00517 }
00518
00519 static const rb_data_type_t iow_data_type = {
00520 "ObjectSpace::InternalObjectWrapper",
00521 {iow_mark, 0, iow_size,},
00522 NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
00523 };
00524
00525 static VALUE rb_mInternalObjectWrapper;
00526
00527 static VALUE
00528 iow_newobj(VALUE obj)
00529 {
00530 return rb_data_typed_object_alloc(rb_mInternalObjectWrapper, (void *)obj, &iow_data_type);
00531 }
00532
00533
00534 static VALUE
00535 iow_type(VALUE self)
00536 {
00537 VALUE obj = (VALUE)DATA_PTR(self);
00538 return type2sym(BUILTIN_TYPE(obj));
00539 }
00540
00541
00542 static VALUE
00543 iow_inspect(VALUE self)
00544 {
00545 VALUE obj = (VALUE)DATA_PTR(self);
00546 VALUE type = type2sym(BUILTIN_TYPE(obj));
00547
00548 return rb_sprintf("#<InternalObject:%p %s>", (void *)obj, rb_id2name(SYM2ID(type)));
00549 }
00550
00551
00552 static VALUE
00553 iow_internal_object_id(VALUE self)
00554 {
00555 VALUE obj = (VALUE)DATA_PTR(self);
00556 return rb_obj_id(obj);
00557 }
00558
00559 struct rof_data {
00560 st_table *refs;
00561 VALUE internals;
00562 };
00563
00564 static void
00565 reachable_object_from_i(VALUE obj, void *data_ptr)
00566 {
00567 struct rof_data *data = (struct rof_data *)data_ptr;
00568 VALUE key = obj;
00569 VALUE val = obj;
00570
00571 if (rb_objspace_markable_object_p(obj)) {
00572 if (rb_objspace_internal_object_p(obj)) {
00573 val = iow_newobj(obj);
00574 rb_ary_push(data->internals, val);
00575 }
00576 st_insert(data->refs, key, val);
00577 }
00578 }
00579
00580 static int
00581 collect_values(st_data_t key, st_data_t value, st_data_t data)
00582 {
00583 VALUE ary = (VALUE)data;
00584 rb_ary_push(ary, (VALUE)value);
00585 return ST_CONTINUE;
00586 }
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630 static VALUE
00631 reachable_objects_from(VALUE self, VALUE obj)
00632 {
00633 if (rb_objspace_markable_object_p(obj)) {
00634 VALUE ret = rb_ary_new();
00635 struct rof_data data;
00636
00637 if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
00638 obj = (VALUE)DATA_PTR(obj);
00639 }
00640
00641 data.refs = st_init_numtable();
00642 data.internals = rb_ary_new();
00643
00644 rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
00645
00646 st_foreach(data.refs, collect_values, (st_data_t)ret);
00647 return ret;
00648 }
00649 else {
00650 return Qnil;
00651 }
00652 }
00653
00654 struct rofr_data {
00655 VALUE categories;
00656 const char *last_category;
00657 VALUE last_category_str;
00658 VALUE last_category_objects;
00659 };
00660
00661 static void
00662 reachable_object_from_root_i(const char *category, VALUE obj, void *ptr)
00663 {
00664 struct rofr_data *data = (struct rofr_data *)ptr;
00665 VALUE category_str;
00666 VALUE category_objects;
00667
00668 if (category == data->last_category) {
00669 category_str = data->last_category_str;
00670 category_objects = data->last_category_objects;
00671 }
00672 else {
00673 data->last_category = category;
00674 category_str = data->last_category_str = rb_str_new2(category);
00675 category_objects = data->last_category_objects = rb_hash_new();
00676 rb_funcall(category_objects, rb_intern("compare_by_identity"), 0);
00677 if (!NIL_P(rb_hash_lookup(data->categories, category_str))) {
00678 rb_bug("reachable_object_from_root_i: category should insert at once");
00679 }
00680 rb_hash_aset(data->categories, category_str, category_objects);
00681 }
00682
00683 if (rb_objspace_markable_object_p(obj) &&
00684 obj != data->categories &&
00685 obj != data->last_category_objects) {
00686 if (rb_objspace_internal_object_p(obj)) {
00687 obj = iow_newobj(obj);
00688 }
00689 rb_hash_aset(category_objects, obj, obj);
00690 }
00691 }
00692
00693 static int
00694 collect_values_of_values(VALUE category, VALUE category_objects, VALUE categories)
00695 {
00696 VALUE ary = rb_ary_new();
00697 st_foreach(rb_hash_tbl(category_objects), collect_values, ary);
00698 rb_hash_aset(categories, category, ary);
00699 return ST_CONTINUE;
00700 }
00701
00702
00703
00704
00705
00706
00707
00708 static VALUE
00709 reachable_objects_from_root(VALUE self)
00710 {
00711 struct rofr_data data;
00712 VALUE hash = data.categories = rb_hash_new();
00713 data.last_category = 0;
00714
00715 rb_funcall(hash, rb_intern("compare_by_identity"), 0);
00716 rb_objspace_reachable_objects_from_root(reachable_object_from_root_i, &data);
00717 rb_hash_foreach(hash, collect_values_of_values, hash);
00718
00719 return hash;
00720 }
00721
00722 void Init_object_tracing(VALUE rb_mObjSpace);
00723 void Init_objspace_dump(VALUE rb_mObjSpace);
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740 void
00741 Init_objspace(void)
00742 {
00743 VALUE rb_mObjSpace;
00744 #if 0
00745 rb_mObjSpace = rb_define_module("ObjectSpace");
00746 #endif
00747 rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
00748
00749 rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
00750 rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1);
00751
00752 rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
00753 rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
00754 rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
00755
00756 rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1);
00757 rb_define_module_function(rb_mObjSpace, "reachable_objects_from_root", reachable_objects_from_root, 0);
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768 rb_mInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject);
00769 rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0);
00770 rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0);
00771 rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
00772
00773 Init_object_tracing(rb_mObjSpace);
00774 Init_objspace_dump(rb_mObjSpace);
00775 }
00776