Ruby 2.0.0p247(2013-06-27revision41674)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

objspace.c
Go to the documentation of this file.
1 /**********************************************************************
2
3 objspace.c - ObjectSpace extender for MRI.
4
5 $Author: nagachika $
6 created at: Wed Jun 17 07:39:17 2009
7
8 NOTE: This extension library is not expected to exist except C Ruby.
9
10 All the files in this distribution are covered under the Ruby's
11 license (see the file COPYING).
12
13 **********************************************************************/
14
15 /* objspace library extends ObjectSpace module and add several
16 * methods to get internal statistic information about
17 * object/memory management.
18 *
19 * Generally, you *SHOULD NOT*use this library if you do not know
20 * about the MRI implementation. Mainly, this library is for (memory)
21 * profiler developers and MRI developers who need to know how MRI
22 * memory usage.
23 *
24 */
25
26 #include <ruby/ruby.h>
27 #include <ruby/st.h>
28 #include <ruby/io.h>
29 #include <ruby/re.h>
30 #include "node.h"
31 #include "gc.h"
32 #include "regint.h"
33 #include "internal.h"
34
35 size_t rb_str_memsize(VALUE);
36 size_t rb_ary_memsize(VALUE);
37 size_t rb_io_memsize(const rb_io_t *);
38 size_t rb_generic_ivar_memsize(VALUE);
39 size_t rb_objspace_data_type_memsize(VALUE obj);
40
41 static size_t
42 memsize_of(VALUE obj)
43 {
44 size_t size = 0;
45
46 if (SPECIAL_CONST_P(obj)) {
47 return 0;
48 }
49
50 if (FL_TEST(obj, FL_EXIVAR)) {
51 size += rb_generic_ivar_memsize(obj);
52 }
53
54 switch (BUILTIN_TYPE(obj)) {
55 case T_OBJECT:
56 if (!(RBASIC(obj)->flags & ROBJECT_EMBED) &&
57 ROBJECT(obj)->as.heap.ivptr) {
58 size += ROBJECT(obj)->as.heap.numiv * sizeof(VALUE);
59 }
60 break;
61 case T_MODULE:
62 case T_CLASS:
63 if (RCLASS_M_TBL(obj)) {
64 size += st_memsize(RCLASS_M_TBL(obj));
65 }
66 if (RCLASS_IV_TBL(obj)) {
67 size += st_memsize(RCLASS_IV_TBL(obj));
68 }
69 if (RCLASS_IV_INDEX_TBL(obj)) {
70 size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
71 }
72 if (RCLASS(obj)->ptr->iv_tbl) {
73 size += st_memsize(RCLASS(obj)->ptr->iv_tbl);
74 }
75 if (RCLASS(obj)->ptr->const_tbl) {
76 size += st_memsize(RCLASS(obj)->ptr->const_tbl);
77 }
78 size += sizeof(rb_classext_t);
79 break;
80 case T_STRING:
81 size += rb_str_memsize(obj);
82 break;
83 case T_ARRAY:
84 size += rb_ary_memsize(obj);
85 break;
86 case T_HASH:
87 if (RHASH(obj)->ntbl) {
88 size += st_memsize(RHASH(obj)->ntbl);
89 }
90 break;
91 case T_REGEXP:
92 if (RREGEXP(obj)->ptr) {
93 size += onig_memsize(RREGEXP(obj)->ptr);
94 }
95 break;
96 case T_DATA:
97 size += rb_objspace_data_type_memsize(obj);
98 break;
99 case T_MATCH:
100 if (RMATCH(obj)->rmatch) {
101 struct rmatch *rm = RMATCH(obj)->rmatch;
102 size += onig_region_memsize(&rm->regs);
103 size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
104 size += sizeof(struct rmatch);
105 }
106 break;
107 case T_FILE:
108 if (RFILE(obj)->fptr) {
109 size += rb_io_memsize(RFILE(obj)->fptr);
110 }
111 break;
112 case T_RATIONAL:
113 case T_COMPLEX:
114 break;
115 case T_ICLASS:
116 /* iClass shares table with the module */
117 break;
118
119 case T_FLOAT:
120 break;
121
122 case T_BIGNUM:
123 if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
124 size += RBIGNUM_LEN(obj) * sizeof(BDIGIT);
125 }
126 break;
127 case T_NODE:
128 switch (nd_type(obj)) {
129 case NODE_SCOPE:
130 if (RNODE(obj)->u1.tbl) {
131 /* TODO: xfree(RANY(obj)->as.node.u1.tbl); */
132 }
133 break;
134 case NODE_ALLOCA:
135 /* TODO: xfree(RANY(obj)->as.node.u1.node); */
136 ;
137 }
138 break; /* no need to free iv_tbl */
139
140 case T_STRUCT:
141 if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
142 RSTRUCT(obj)->as.heap.ptr) {
143 size += sizeof(VALUE) * RSTRUCT_LEN(obj);
144 }
145 break;
146
147 case T_ZOMBIE:
148 break;
149
150 default:
151 rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
152 BUILTIN_TYPE(obj), (void*)obj);
153 }
154
155 return size;
156 }
157
158 /*
159 * call-seq:
160 * ObjectSpace.memsize_of(obj) -> Integer
161 *
162 * Return consuming memory size of obj.
163 *
164 * Note that the return size is incomplete. You need to deal with
165 * this information as only a *HINT*. Especially, the size of
166 * T_DATA may not be correct.
167 *
168 * This method is not expected to work except C Ruby.
169 */
170
171 static VALUE
172 memsize_of_m(VALUE self, VALUE obj)
173 {
174 return SIZET2NUM(memsize_of(obj));
175 }
176
177 struct total_data {
178 size_t total;
179 VALUE klass;
180 };
181
182 static int
183 total_i(void *vstart, void *vend, size_t stride, void *ptr)
184 {
185 VALUE v;
186 struct total_data *data = (struct total_data *)ptr;
187
188 for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
189 if (RBASIC(v)->flags) {
190 switch (BUILTIN_TYPE(v)) {
191 case T_NONE:
192 case T_ICLASS:
193 case T_NODE:
194 case T_ZOMBIE:
195 continue;
196 case T_CLASS:
197 if (FL_TEST(v, FL_SINGLETON))
198 continue;
199 default:
200 if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
201 data->total += memsize_of(v);
202 }
203 }
204 }
205 }
206
207 return 0;
208 }
209
210 /*
211 * call-seq:
212 * ObjectSpace.memsize_of_all([klass]) -> Integer
213 *
214 * Return consuming memory size of all living objects.
215 * If klass (should be Class object) is given, return the total
216 * memory size of instances of the given class.
217 *
218 * Note that the returned size is incomplete. You need to deal with
219 * this information as only a *HINT*. Especially, the size of
220 * T_DATA may not be correct.
221 *
222 * Note that this method does *NOT* return total malloc'ed memory size.
223 *
224 * This method can be defined by the following Ruby code:
225 *
226 * def memsize_of_all klass = false
227 * total = 0
228 * ObjectSpace.each_object{|e|
229 * total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass)
230 * }
231 * total
232 * end
233 *
234 * This method is not expected to work except C Ruby.
235 */
236
237 static VALUE
238 memsize_of_all_m(int argc, VALUE *argv, VALUE self)
239 {
240 struct total_data data = {0, 0};
241
242 if (argc > 0) {
243 rb_scan_args(argc, argv, "01", &data.klass);
244 }
245
246 rb_objspace_each_objects(total_i, &data);
247 return SIZET2NUM(data.total);
248 }
249
250 static int
251 set_zero_i(st_data_t key, st_data_t val, st_data_t arg)
252 {
253 VALUE k = (VALUE)key;
254 VALUE hash = (VALUE)arg;
255 rb_hash_aset(hash, k, INT2FIX(0));
256 return ST_CONTINUE;
257 }
258
259 static int
260 cos_i(void *vstart, void *vend, size_t stride, void *data)
261 {
262 size_t *counts = (size_t *)data;
263 VALUE v = (VALUE)vstart;
264
265 for (;v != (VALUE)vend; v += stride) {
266 if (RBASIC(v)->flags) {
267 counts[BUILTIN_TYPE(v)] += memsize_of(v);
268 }
269 }
270 return 0;
271 }
272
273 static VALUE
274 type2sym(enum ruby_value_type i)
275 {
276 VALUE type;
277 switch (i) {
278 #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
279 CASE_TYPE(T_NONE);
280 CASE_TYPE(T_OBJECT);
281 CASE_TYPE(T_CLASS);
282 CASE_TYPE(T_MODULE);
283 CASE_TYPE(T_FLOAT);
284 CASE_TYPE(T_STRING);
285 CASE_TYPE(T_REGEXP);
286 CASE_TYPE(T_ARRAY);
287 CASE_TYPE(T_HASH);
288 CASE_TYPE(T_STRUCT);
289 CASE_TYPE(T_BIGNUM);
290 CASE_TYPE(T_FILE);
291 CASE_TYPE(T_DATA);
292 CASE_TYPE(T_MATCH);
293 CASE_TYPE(T_COMPLEX);
294 CASE_TYPE(T_RATIONAL);
295 CASE_TYPE(T_NIL);
296 CASE_TYPE(T_TRUE);
297 CASE_TYPE(T_FALSE);
298 CASE_TYPE(T_SYMBOL);
299 CASE_TYPE(T_FIXNUM);
300 CASE_TYPE(T_UNDEF);
301 CASE_TYPE(T_NODE);
302 CASE_TYPE(T_ICLASS);
303 CASE_TYPE(T_ZOMBIE);
304 #undef CASE_TYPE
305 default: rb_bug("type2sym: unknown type (%d)", i);
306 }
307 return type;
308 }
309
310 /*
311 * call-seq:
312 * ObjectSpace.count_objects_size([result_hash]) -> hash
313 *
314 * Counts objects size (in bytes) for each type.
315 *
316 * Note that this information is incomplete. You need to deal with
317 * this information as only a *HINT*. Especially, total size of
318 * T_DATA may not right size.
319 *
320 * It returns a hash as:
321 * {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...}
322 *
323 * If the optional argument, result_hash, is given,
324 * it is overwritten and returned.
325 * This is intended to avoid probe effect.
326 *
327 * The contents of the returned hash is implementation defined.
328 * It may be changed in future.
329 *
330 * This method is not expected to work except C Ruby.
331 */
332
333 static VALUE
334 count_objects_size(int argc, VALUE *argv, VALUE os)
335 {
336 size_t counts[T_MASK+1];
337 size_t total = 0;
338 enum ruby_value_type i;
339 VALUE hash;
340
341 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
342 if (!RB_TYPE_P(hash, T_HASH))
343 rb_raise(rb_eTypeError, "non-hash given");
344 }
345
346 for (i = 0; i <= T_MASK; i++) {
347 counts[i] = 0;
348 }
349
350 rb_objspace_each_objects(cos_i, &counts[0]);
351
352 if (hash == Qnil) {
353 hash = rb_hash_new();
354 }
355 else if (!RHASH_EMPTY_P(hash)) {
356 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
357 }
358
359 for (i = 0; i <= T_MASK; i++) {
360 if (counts[i]) {
361 VALUE type = type2sym(i);
362 total += counts[i];
363 rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
364 }
365 }
366 rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
367 return hash;
368 }
369
370 static int
371 cn_i(void *vstart, void *vend, size_t stride, void *n)
372 {
373 size_t *nodes = (size_t *)n;
374 VALUE v = (VALUE)vstart;
375
376 for (; v != (VALUE)vend; v += stride) {
377 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) {
378 size_t s = nd_type((NODE *)v);
379 nodes[s]++;
380 }
381 }
382
383 return 0;
384 }
385
386 /*
387 * call-seq:
388 * ObjectSpace.count_nodes([result_hash]) -> hash
389 *
390 * Counts nodes for each node type.
391 *
392 * This method is not for ordinary Ruby programmers, but for MRI developers
393 * who have interest in MRI performance and memory usage.
394 *
395 * It returns a hash as:
396 * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...}
397 *
398 * If the optional argument, result_hash, is given,
399 * it is overwritten and returned.
400 * This is intended to avoid probe effect.
401 *
402 * The contents of the returned hash is implementation defined.
403 * It may be changed in future.
404 *
405 * This method is not expected to work except C Ruby.
406 */
407
408 static VALUE
409 count_nodes(int argc, VALUE *argv, VALUE os)
410 {
411 size_t nodes[NODE_LAST+1];
412 size_t i;
413 VALUE hash;
414
415 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
416 if (!RB_TYPE_P(hash, T_HASH))
417 rb_raise(rb_eTypeError, "non-hash given");
418 }
419
420 for (i = 0; i <= NODE_LAST; i++) {
421 nodes[i] = 0;
422 }
423
424 rb_objspace_each_objects(cn_i, &nodes[0]);
425
426 if (hash == Qnil) {
427 hash = rb_hash_new();
428 }
429 else if (!RHASH_EMPTY_P(hash)) {
430 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
431 }
432
433 for (i=0; i<NODE_LAST; i++) {
434 if (nodes[i] != 0) {
435 VALUE node;
436 switch (i) {
437 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
438 COUNT_NODE(NODE_SCOPE);
439 COUNT_NODE(NODE_BLOCK);
440 COUNT_NODE(NODE_IF);
441 COUNT_NODE(NODE_CASE);
442 COUNT_NODE(NODE_WHEN);
443 COUNT_NODE(NODE_OPT_N);
444 COUNT_NODE(NODE_WHILE);
445 COUNT_NODE(NODE_UNTIL);
446 COUNT_NODE(NODE_ITER);
447 COUNT_NODE(NODE_FOR);
448 COUNT_NODE(NODE_BREAK);
449 COUNT_NODE(NODE_NEXT);
450 COUNT_NODE(NODE_REDO);
451 COUNT_NODE(NODE_RETRY);
452 COUNT_NODE(NODE_BEGIN);
453 COUNT_NODE(NODE_RESCUE);
454 COUNT_NODE(NODE_RESBODY);
455 COUNT_NODE(NODE_ENSURE);
456 COUNT_NODE(NODE_AND);
457 COUNT_NODE(NODE_OR);
458 COUNT_NODE(NODE_MASGN);
459 COUNT_NODE(NODE_LASGN);
460 COUNT_NODE(NODE_DASGN);
461 COUNT_NODE(NODE_DASGN_CURR);
462 COUNT_NODE(NODE_GASGN);
463 COUNT_NODE(NODE_IASGN);
464 COUNT_NODE(NODE_IASGN2);
465 COUNT_NODE(NODE_CDECL);
466 COUNT_NODE(NODE_CVASGN);
467 COUNT_NODE(NODE_CVDECL);
468 COUNT_NODE(NODE_OP_ASGN1);
469 COUNT_NODE(NODE_OP_ASGN2);
470 COUNT_NODE(NODE_OP_ASGN_AND);
471 COUNT_NODE(NODE_OP_ASGN_OR);
472 COUNT_NODE(NODE_OP_CDECL);
473 COUNT_NODE(NODE_CALL);
474 COUNT_NODE(NODE_FCALL);
475 COUNT_NODE(NODE_VCALL);
476 COUNT_NODE(NODE_SUPER);
477 COUNT_NODE(NODE_ZSUPER);
478 COUNT_NODE(NODE_ARRAY);
479 COUNT_NODE(NODE_ZARRAY);
480 COUNT_NODE(NODE_VALUES);
481 COUNT_NODE(NODE_HASH);
482 COUNT_NODE(NODE_RETURN);
483 COUNT_NODE(NODE_YIELD);
484 COUNT_NODE(NODE_LVAR);
485 COUNT_NODE(NODE_DVAR);
486 COUNT_NODE(NODE_GVAR);
487 COUNT_NODE(NODE_IVAR);
488 COUNT_NODE(NODE_CONST);
489 COUNT_NODE(NODE_CVAR);
490 COUNT_NODE(NODE_NTH_REF);
491 COUNT_NODE(NODE_BACK_REF);
492 COUNT_NODE(NODE_MATCH);
493 COUNT_NODE(NODE_MATCH2);
494 COUNT_NODE(NODE_MATCH3);
495 COUNT_NODE(NODE_LIT);
496 COUNT_NODE(NODE_STR);
497 COUNT_NODE(NODE_DSTR);
498 COUNT_NODE(NODE_XSTR);
499 COUNT_NODE(NODE_DXSTR);
500 COUNT_NODE(NODE_EVSTR);
501 COUNT_NODE(NODE_DREGX);
502 COUNT_NODE(NODE_DREGX_ONCE);
503 COUNT_NODE(NODE_ARGS);
504 COUNT_NODE(NODE_ARGS_AUX);
505 COUNT_NODE(NODE_OPT_ARG);
506 COUNT_NODE(NODE_KW_ARG);
507 COUNT_NODE(NODE_POSTARG);
508 COUNT_NODE(NODE_ARGSCAT);
509 COUNT_NODE(NODE_ARGSPUSH);
510 COUNT_NODE(NODE_SPLAT);
511 COUNT_NODE(NODE_TO_ARY);
512 COUNT_NODE(NODE_BLOCK_ARG);
513 COUNT_NODE(NODE_BLOCK_PASS);
514 COUNT_NODE(NODE_DEFN);
515 COUNT_NODE(NODE_DEFS);
516 COUNT_NODE(NODE_ALIAS);
517 COUNT_NODE(NODE_VALIAS);
518 COUNT_NODE(NODE_UNDEF);
519 COUNT_NODE(NODE_CLASS);
520 COUNT_NODE(NODE_MODULE);
521 COUNT_NODE(NODE_SCLASS);
522 COUNT_NODE(NODE_COLON2);
523 COUNT_NODE(NODE_COLON3);
524 COUNT_NODE(NODE_CREF);
525 COUNT_NODE(NODE_DOT2);
526 COUNT_NODE(NODE_DOT3);
527 COUNT_NODE(NODE_FLIP2);
528 COUNT_NODE(NODE_FLIP3);
529 COUNT_NODE(NODE_SELF);
530 COUNT_NODE(NODE_NIL);
531 COUNT_NODE(NODE_TRUE);
532 COUNT_NODE(NODE_FALSE);
533 COUNT_NODE(NODE_ERRINFO);
534 COUNT_NODE(NODE_DEFINED);
535 COUNT_NODE(NODE_POSTEXE);
536 COUNT_NODE(NODE_ALLOCA);
537 COUNT_NODE(NODE_BMETHOD);
538 COUNT_NODE(NODE_MEMO);
539 COUNT_NODE(NODE_IFUNC);
540 COUNT_NODE(NODE_DSYM);
541 COUNT_NODE(NODE_ATTRASGN);
542 COUNT_NODE(NODE_PRELUDE);
543 COUNT_NODE(NODE_LAMBDA);
544 #undef COUNT_NODE
545 default: node = INT2FIX(i);
546 }
547 rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
548 }
549 }
550 return hash;
551 }
552
553 static int
554 cto_i(void *vstart, void *vend, size_t stride, void *data)
555 {
556 VALUE hash = (VALUE)data;
557 VALUE v = (VALUE)vstart;
558
559 for (; v != (VALUE)vend; v += stride) {
560 if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) {
561 VALUE counter;
562 VALUE key = RBASIC(v)->klass;
563
564 if (key == 0) {
565 const char *name = rb_objspace_data_type_name(v);
566 if (name == 0) name = "unknown";
567 key = ID2SYM(rb_intern(name));
568 }
569
570 counter = rb_hash_aref(hash, key);
571 if (NIL_P(counter)) {
572 counter = INT2FIX(1);
573 }
574 else {
575 counter = INT2FIX(FIX2INT(counter) + 1);
576 }
577
578 rb_hash_aset(hash, key, counter);
579 }
580 }
581
582 return 0;
583 }
584
585 /*
586 * call-seq:
587 * ObjectSpace.count_tdata_objects([result_hash]) -> hash
588 *
589 * Counts objects for each T_DATA type.
590 *
591 * This method is not for ordinary Ruby programmers, but for MRI developers
592 * who interest on MRI performance.
593 *
594 * It returns a hash as:
595 * {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6,
596 * :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99,
597 * ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1,
598 * Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2}
599 * # T_DATA objects existing at startup on r32276.
600 *
601 * If the optional argument, result_hash, is given,
602 * it is overwritten and returned.
603 * This is intended to avoid probe effect.
604 *
605 * The contents of the returned hash is implementation defined.
606 * It may be changed in future.
607 *
608 * In this version, keys are Class object or Symbol object.
609 * If object is kind of normal (accessible) object, the key is Class object.
610 * If object is not a kind of normal (internal) object, the key is symbol
611 * name, registered by rb_data_type_struct.
612 *
613 * This method is not expected to work except C Ruby.
614 *
615 */
616
617 static VALUE
618 count_tdata_objects(int argc, VALUE *argv, VALUE self)
619 {
620 VALUE hash;
621
622 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
623 if (!RB_TYPE_P(hash, T_HASH))
624 rb_raise(rb_eTypeError, "non-hash given");
625 }
626
627 if (hash == Qnil) {
628 hash = rb_hash_new();
629 }
630 else if (!RHASH_EMPTY_P(hash)) {
631 st_foreach(RHASH_TBL(hash), set_zero_i, hash);
632 }
633
634 rb_objspace_each_objects(cto_i, (void *)hash);
635
636 return hash;
637 }
638
639 static void
640 iow_mark(void *ptr)
641 {
642 rb_gc_mark((VALUE)ptr);
643 }
644
645 static size_t
646 iow_size(const void *ptr)
647 {
648 VALUE obj = (VALUE)ptr;
649 return memsize_of(obj);
650 }
651
652 static const rb_data_type_t iow_data_type = {
653 "ObjectSpace::InternalObjectWrapper",
654 {iow_mark, 0, iow_size,},
655 };
656
657 static VALUE rb_mInternalObjectWrapper;
658
659 static VALUE
660 iow_newobj(VALUE obj)
661 {
662 return rb_data_typed_object_alloc(rb_mInternalObjectWrapper, (void *)obj, &iow_data_type);
663 }
664
665 static VALUE
666 iow_type(VALUE self)
667 {
668 VALUE obj = (VALUE)DATA_PTR(self);
669 return type2sym(BUILTIN_TYPE(obj));
670 }
671
672 static VALUE
673 iow_inspect(VALUE self)
674 {
675 VALUE obj = (VALUE)DATA_PTR(self);
676 VALUE type = type2sym(BUILTIN_TYPE(obj));
677
678 return rb_sprintf("#<InternalObject:%p %s>", (void *)obj, rb_id2name(SYM2ID(type)));
679 }
680
681 static VALUE
682 iow_internal_object_id(VALUE self)
683 {
684 VALUE obj = (VALUE)DATA_PTR(self);
685 return rb_obj_id(obj);
686 }
687
688 struct rof_data {
689 st_table *refs;
690 VALUE internals;
691 };
692
693 static void
694 reachable_object_from_i(VALUE obj, void *data_ptr)
695 {
696 struct rof_data *data = (struct rof_data *)data_ptr;
697 VALUE key = obj;
698 VALUE val = obj;
699
700 if (rb_objspace_markable_object_p(obj)) {
701 if (rb_objspace_internal_object_p(obj)) {
702 val = iow_newobj(obj);
703 rb_ary_push(data->internals, val);
704 }
705 st_insert(data->refs, key, val);
706 }
707 }
708
709 static int
710 collect_values(st_data_t key, st_data_t value, st_data_t data)
711 {
712 VALUE ary = (VALUE)data;
713 rb_ary_push(ary, (VALUE)value);
714 return ST_CONTINUE;
715 }
716
717 /*
718 * call-seq:
719 * ObjectSpace.reachable_objects_from(obj) -> array or nil
720 *
721 * [MRI specific feature] Return all reachable objects from `obj'.
722 *
723 * This method returns all reachable objects from `obj'.
724 * If `obj' has references two or more references to same object `x',
725 * them returned array only include one `x' object.
726 * If `obj' is non-markable (non-heap management) object such as
727 * true, false, nil, symbols and Fixnums (and Flonum) them it simply
728 * returns nil.
729 *
730 * If `obj' has references to internal object, then it returns
731 * instances of `ObjectSpace::InternalObjectWrapper' class.
732 * This object contains a reference to an internal object and
733 * you can check the type of internal object with `type' method.
734 *
735 * If `obj' is instance of `ObjectSpace::InternalObjectWrapper'
736 * class, then this method returns all reachable object from
737 * an internal object, which is pointed by `obj'.
738 *
739 * With this method, you can find memory leaks.
740 *
741 * This method is not expected to work except C Ruby.
742 *
743 * Example:
744 * ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
745 * #=> [Array, 'a', 'b', 'c']
746 *
747 * ObjectSpace.reachable_objects_from(['a', 'a', 'a'])
748 * #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id
749 *
750 * ObjectSpace.reachable_objects_from([v = 'a', v, v])
751 * #=> [Array, 'a']
752 *
753 * ObjectSpace.reachable_objects_from(1)
754 * #=> nil # 1 is not markable (heap managed) object
755 *
756 */
757
758 static VALUE
759 reachable_objects_from(VALUE self, VALUE obj)
760 {
761 if (rb_objspace_markable_object_p(obj)) {
762 VALUE ret = rb_ary_new();
763 struct rof_data data;
764
765 if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
766 obj = (VALUE)DATA_PTR(obj);
767 }
768
769 data.refs = st_init_numtable();
770 data.internals = rb_ary_new();
771
772 rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
773
774 st_foreach(data.refs, collect_values, (st_data_t)ret);
775 return ret;
776 }
777 else {
778 return Qnil;
779 }
780 }
781
782 /* objspace library extends ObjectSpace module and add several
783 * methods to get internal statistic information about
784 * object/memory management.
785 *
786 * Generally, you *SHOULD NOT*use this library if you do not know
787 * about the MRI implementation. Mainly, this library is for (memory)
788 * profiler developers and MRI developers who need to know how MRI
789 * memory usage.
790 */
791
792 void
793 Init_objspace(void)
794 {
795 VALUE rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
796
797 rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
798 rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1);
799
800 rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
801 rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
802 rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
803
804 rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1);
805
806 rb_mInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject);
807 rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0);
808 rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0);
809 rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
810 }
811