1 /**********************************************************************
2 
3   objspace.c - ObjectSpace extender for MRI.
4 
5   $Author: usa $
6   created at: Wed Jun 17 07:39:17 2009
7 
8   NOTE: This extension library is only expected to exist with 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 #include <ruby/io.h>
16 #include "internal.h"
17 #include <ruby/st.h>
18 #include <ruby/re.h>
19 #include "node.h"
20 #include "gc.h"
21 #include "symbol.h"
22 
23 /*
24  *  call-seq:
25  *    ObjectSpace.memsize_of(obj) -> Integer
26  *
27  *  Return consuming memory size of obj.
28  *
29  *  Note that the return size is incomplete.  You need to deal with this
30  *  information as only a *HINT*. Especially, the size of +T_DATA+ may not be
31  *  correct.
32  *
33  *  This method is only expected to work with C Ruby.
34  *
35  *  From Ruby 2.2, memsize_of(obj) returns a memory size includes
36  *  sizeof(RVALUE).
37  */
38 
39 static VALUE
memsize_of_m(VALUE self,VALUE obj)40 memsize_of_m(VALUE self, VALUE obj)
41 {
42     return SIZET2NUM(rb_obj_memsize_of(obj));
43 }
44 
45 struct total_data {
46     size_t total;
47     VALUE klass;
48 };
49 
50 static int
total_i(void * vstart,void * vend,size_t stride,void * ptr)51 total_i(void *vstart, void *vend, size_t stride, void *ptr)
52 {
53     VALUE v;
54     struct total_data *data = (struct total_data *)ptr;
55 
56     for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
57 	if (RBASIC(v)->flags) {
58 	    switch (BUILTIN_TYPE(v)) {
59 	      case T_NONE:
60 	      case T_IMEMO:
61 	      case T_ICLASS:
62 	      case T_NODE:
63 	      case T_ZOMBIE:
64 		continue;
65 	      default:
66 		if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
67 		    data->total += rb_obj_memsize_of(v);
68 		}
69 	    }
70 	}
71     }
72 
73     return 0;
74 }
75 
76 /*
77  *  call-seq:
78  *    ObjectSpace.memsize_of_all([klass]) -> Integer
79  *
80  *  Return consuming memory size of all living objects.
81  *
82  *  If +klass+ (should be Class object) is given, return the total memory size
83  *  of instances of the given class.
84  *
85  *  Note that the returned size is incomplete. You need to deal with this
86  *  information as only a *HINT*. Especially, the size of +T_DATA+ may not be
87  *  correct.
88  *
89  *  Note that this method does *NOT* return total malloc'ed memory size.
90  *
91  *  This method can be defined by the following Ruby code:
92  *
93  *	def memsize_of_all klass = false
94  *  	  total = 0
95  *  	  ObjectSpace.each_object{|e|
96  *  	    total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass)
97  *  	  }
98  *  	  total
99  *  	end
100  *
101  *  This method is only expected to work with C Ruby.
102  */
103 
104 static VALUE
memsize_of_all_m(int argc,VALUE * argv,VALUE self)105 memsize_of_all_m(int argc, VALUE *argv, VALUE self)
106 {
107     struct total_data data = {0, 0};
108 
109     if (argc > 0) {
110 	rb_scan_args(argc, argv, "01", &data.klass);
111     }
112 
113     rb_objspace_each_objects(total_i, &data);
114     return SIZET2NUM(data.total);
115 }
116 
117 static int
set_zero_i(st_data_t key,st_data_t val,st_data_t arg)118 set_zero_i(st_data_t key, st_data_t val, st_data_t arg)
119 {
120     VALUE k = (VALUE)key;
121     VALUE hash = (VALUE)arg;
122     rb_hash_aset(hash, k, INT2FIX(0));
123     return ST_CONTINUE;
124 }
125 
126 static VALUE
setup_hash(int argc,VALUE * argv)127 setup_hash(int argc, VALUE *argv)
128 {
129     VALUE hash;
130 
131     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
132         if (!RB_TYPE_P(hash, T_HASH))
133             rb_raise(rb_eTypeError, "non-hash given");
134     }
135 
136     if (hash == Qnil) {
137         hash = rb_hash_new();
138     }
139     else if (!RHASH_EMPTY_P(hash)) {
140         st_foreach(RHASH_TBL(hash), set_zero_i, hash);
141     }
142 
143     return hash;
144 }
145 
146 static int
cos_i(void * vstart,void * vend,size_t stride,void * data)147 cos_i(void *vstart, void *vend, size_t stride, void *data)
148 {
149     size_t *counts = (size_t *)data;
150     VALUE v = (VALUE)vstart;
151 
152     for (;v != (VALUE)vend; v += stride) {
153 	if (RBASIC(v)->flags) {
154 	    counts[BUILTIN_TYPE(v)] += rb_obj_memsize_of(v);
155 	}
156     }
157     return 0;
158 }
159 
160 static VALUE
type2sym(enum ruby_value_type i)161 type2sym(enum ruby_value_type i)
162 {
163     VALUE type;
164     switch (i) {
165 #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
166 	CASE_TYPE(T_NONE);
167 	CASE_TYPE(T_OBJECT);
168 	CASE_TYPE(T_CLASS);
169 	CASE_TYPE(T_MODULE);
170 	CASE_TYPE(T_FLOAT);
171 	CASE_TYPE(T_STRING);
172 	CASE_TYPE(T_REGEXP);
173 	CASE_TYPE(T_ARRAY);
174 	CASE_TYPE(T_HASH);
175 	CASE_TYPE(T_STRUCT);
176 	CASE_TYPE(T_BIGNUM);
177 	CASE_TYPE(T_FILE);
178 	CASE_TYPE(T_DATA);
179 	CASE_TYPE(T_MATCH);
180 	CASE_TYPE(T_COMPLEX);
181 	CASE_TYPE(T_RATIONAL);
182 	CASE_TYPE(T_NIL);
183 	CASE_TYPE(T_TRUE);
184 	CASE_TYPE(T_FALSE);
185 	CASE_TYPE(T_SYMBOL);
186 	CASE_TYPE(T_FIXNUM);
187 	CASE_TYPE(T_UNDEF);
188 	CASE_TYPE(T_IMEMO);
189 	CASE_TYPE(T_NODE);
190 	CASE_TYPE(T_ICLASS);
191 	CASE_TYPE(T_ZOMBIE);
192 #undef CASE_TYPE
193       default: rb_bug("type2sym: unknown type (%d)", i);
194     }
195     return type;
196 }
197 
198 /*
199  *  call-seq:
200  *    ObjectSpace.count_objects_size([result_hash]) -> hash
201  *
202  *  Counts objects size (in bytes) for each type.
203  *
204  *  Note that this information is incomplete.  You need to deal with
205  *  this information as only a *HINT*.  Especially, total size of
206  *  T_DATA may be wrong.
207  *
208  *  It returns a hash as:
209  *    {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...}
210  *
211  *  If the optional argument, result_hash, is given,
212  *  it is overwritten and returned.
213  *  This is intended to avoid probe effect.
214  *
215  *  The contents of the returned hash is implementation defined.
216  *  It may be changed in future.
217  *
218  *  This method is only expected to work with C Ruby.
219  */
220 
221 static VALUE
count_objects_size(int argc,VALUE * argv,VALUE os)222 count_objects_size(int argc, VALUE *argv, VALUE os)
223 {
224     size_t counts[T_MASK+1];
225     size_t total = 0;
226     enum ruby_value_type i;
227     VALUE hash = setup_hash(argc, argv);
228 
229     for (i = 0; i <= T_MASK; i++) {
230 	counts[i] = 0;
231     }
232 
233     rb_objspace_each_objects(cos_i, &counts[0]);
234 
235     for (i = 0; i <= T_MASK; i++) {
236 	if (counts[i]) {
237 	    VALUE type = type2sym(i);
238 	    total += counts[i];
239 	    rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
240 	}
241     }
242     rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
243     return hash;
244 }
245 
246 struct dynamic_symbol_counts {
247     size_t mortal;
248     size_t immortal;
249 };
250 
251 static int
cs_i(void * vstart,void * vend,size_t stride,void * n)252 cs_i(void *vstart, void *vend, size_t stride, void *n)
253 {
254     struct dynamic_symbol_counts *counts = (struct dynamic_symbol_counts *)n;
255     VALUE v = (VALUE)vstart;
256 
257     for (; v != (VALUE)vend; v += stride) {
258 	if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_SYMBOL) {
259 	    ID id = RSYMBOL(v)->id;
260 	    if ((id & ~ID_SCOPE_MASK) == 0) {
261 		counts->mortal++;
262 	    }
263 	    else {
264 		counts->immortal++;
265 	    }
266 	}
267     }
268 
269     return 0;
270 }
271 
272 size_t rb_sym_immortal_count(void);
273 
274 /*
275  *  call-seq:
276  *     ObjectSpace.count_symbols([result_hash]) -> hash
277  *
278  *  Counts symbols for each Symbol type.
279  *
280  *  This method is only for MRI developers interested in performance and memory
281  *  usage of Ruby programs.
282  *
283  *  If the optional argument, result_hash, is given, it is overwritten and
284  *  returned. This is intended to avoid probe effect.
285  *
286  *  Note:
287  *  The contents of the returned hash is implementation defined.
288  *  It may be changed in future.
289  *
290  *  This method is only expected to work with C Ruby.
291  *
292  *  On this version of MRI, they have 3 types of Symbols (and 1 total counts).
293  *
294  *   * mortal_dynamic_symbol: GC target symbols (collected by GC)
295  *   * immortal_dynamic_symbol: Immortal symbols promoted from dynamic symbols (do not collected by GC)
296  *   * immortal_static_symbol: Immortal symbols (do not collected by GC)
297  *   * immortal_symbol: total immortal symbols (immortal_dynamic_symbol+immortal_static_symbol)
298  */
299 
300 static VALUE
count_symbols(int argc,VALUE * argv,VALUE os)301 count_symbols(int argc, VALUE *argv, VALUE os)
302 {
303     struct dynamic_symbol_counts dynamic_counts = {0, 0};
304     VALUE hash = setup_hash(argc, argv);
305 
306     size_t immortal_symbols = rb_sym_immortal_count();
307     rb_objspace_each_objects(cs_i, &dynamic_counts);
308 
309     rb_hash_aset(hash, ID2SYM(rb_intern("mortal_dynamic_symbol")),   SIZET2NUM(dynamic_counts.mortal));
310     rb_hash_aset(hash, ID2SYM(rb_intern("immortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.immortal));
311     rb_hash_aset(hash, ID2SYM(rb_intern("immortal_static_symbol")),  SIZET2NUM(immortal_symbols - dynamic_counts.immortal));
312     rb_hash_aset(hash, ID2SYM(rb_intern("immortal_symbol")),         SIZET2NUM(immortal_symbols));
313 
314     return hash;
315 }
316 
317 static int
cn_i(void * vstart,void * vend,size_t stride,void * n)318 cn_i(void *vstart, void *vend, size_t stride, void *n)
319 {
320     size_t *nodes = (size_t *)n;
321     VALUE v = (VALUE)vstart;
322 
323     for (; v != (VALUE)vend; v += stride) {
324 	if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) {
325 	    size_t s = nd_type((NODE *)v);
326 	    nodes[s]++;
327 	}
328     }
329 
330     return 0;
331 }
332 
333 /*
334  *  call-seq:
335  *     ObjectSpace.count_nodes([result_hash]) -> hash
336  *
337  *  Counts nodes for each node type.
338  *
339  *  This method is only for MRI developers interested in performance and memory
340  *  usage of Ruby programs.
341  *
342  *  It returns a hash as:
343  *
344  *	{:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...}
345  *
346  *  If the optional argument, result_hash, is given, it is overwritten and
347  *  returned. This is intended to avoid probe effect.
348  *
349  *  Note:
350  *  The contents of the returned hash is implementation defined.
351  *  It may be changed in future.
352  *
353  *  This method is only expected to work with C Ruby.
354  */
355 
356 static VALUE
count_nodes(int argc,VALUE * argv,VALUE os)357 count_nodes(int argc, VALUE *argv, VALUE os)
358 {
359     size_t nodes[NODE_LAST+1];
360     enum node_type i;
361     VALUE hash = setup_hash(argc, argv);
362 
363     for (i = 0; i <= NODE_LAST; i++) {
364 	nodes[i] = 0;
365     }
366 
367     rb_objspace_each_objects(cn_i, &nodes[0]);
368 
369     for (i=0; i<NODE_LAST; i++) {
370 	if (nodes[i] != 0) {
371 	    VALUE node;
372 	    switch (i) {
373 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); goto set
374 		COUNT_NODE(NODE_SCOPE);
375 		COUNT_NODE(NODE_BLOCK);
376 		COUNT_NODE(NODE_IF);
377 		COUNT_NODE(NODE_UNLESS);
378 		COUNT_NODE(NODE_CASE);
379 		COUNT_NODE(NODE_CASE2);
380 		COUNT_NODE(NODE_WHEN);
381 		COUNT_NODE(NODE_WHILE);
382 		COUNT_NODE(NODE_UNTIL);
383 		COUNT_NODE(NODE_ITER);
384 		COUNT_NODE(NODE_FOR);
385 		COUNT_NODE(NODE_FOR_MASGN);
386 		COUNT_NODE(NODE_BREAK);
387 		COUNT_NODE(NODE_NEXT);
388 		COUNT_NODE(NODE_REDO);
389 		COUNT_NODE(NODE_RETRY);
390 		COUNT_NODE(NODE_BEGIN);
391 		COUNT_NODE(NODE_RESCUE);
392 		COUNT_NODE(NODE_RESBODY);
393 		COUNT_NODE(NODE_ENSURE);
394 		COUNT_NODE(NODE_AND);
395 		COUNT_NODE(NODE_OR);
396 		COUNT_NODE(NODE_MASGN);
397 		COUNT_NODE(NODE_LASGN);
398 		COUNT_NODE(NODE_DASGN);
399 		COUNT_NODE(NODE_DASGN_CURR);
400 		COUNT_NODE(NODE_GASGN);
401 		COUNT_NODE(NODE_IASGN);
402 		COUNT_NODE(NODE_CDECL);
403 		COUNT_NODE(NODE_CVASGN);
404 		COUNT_NODE(NODE_OP_ASGN1);
405 		COUNT_NODE(NODE_OP_ASGN2);
406 		COUNT_NODE(NODE_OP_ASGN_AND);
407 		COUNT_NODE(NODE_OP_ASGN_OR);
408 		COUNT_NODE(NODE_OP_CDECL);
409 		COUNT_NODE(NODE_CALL);
410 		COUNT_NODE(NODE_OPCALL);
411 		COUNT_NODE(NODE_FCALL);
412 		COUNT_NODE(NODE_VCALL);
413 		COUNT_NODE(NODE_QCALL);
414 		COUNT_NODE(NODE_SUPER);
415 		COUNT_NODE(NODE_ZSUPER);
416 		COUNT_NODE(NODE_ARRAY);
417 		COUNT_NODE(NODE_ZARRAY);
418 		COUNT_NODE(NODE_VALUES);
419 		COUNT_NODE(NODE_HASH);
420 		COUNT_NODE(NODE_RETURN);
421 		COUNT_NODE(NODE_YIELD);
422 		COUNT_NODE(NODE_LVAR);
423 		COUNT_NODE(NODE_DVAR);
424 		COUNT_NODE(NODE_GVAR);
425 		COUNT_NODE(NODE_IVAR);
426 		COUNT_NODE(NODE_CONST);
427 		COUNT_NODE(NODE_CVAR);
428 		COUNT_NODE(NODE_NTH_REF);
429 		COUNT_NODE(NODE_BACK_REF);
430 		COUNT_NODE(NODE_MATCH);
431 		COUNT_NODE(NODE_MATCH2);
432 		COUNT_NODE(NODE_MATCH3);
433 		COUNT_NODE(NODE_LIT);
434 		COUNT_NODE(NODE_STR);
435 		COUNT_NODE(NODE_DSTR);
436 		COUNT_NODE(NODE_XSTR);
437 		COUNT_NODE(NODE_DXSTR);
438 		COUNT_NODE(NODE_EVSTR);
439 		COUNT_NODE(NODE_DREGX);
440 		COUNT_NODE(NODE_ONCE);
441 		COUNT_NODE(NODE_ARGS);
442 		COUNT_NODE(NODE_ARGS_AUX);
443 		COUNT_NODE(NODE_OPT_ARG);
444 		COUNT_NODE(NODE_KW_ARG);
445 		COUNT_NODE(NODE_POSTARG);
446 		COUNT_NODE(NODE_ARGSCAT);
447 		COUNT_NODE(NODE_ARGSPUSH);
448 		COUNT_NODE(NODE_SPLAT);
449 		COUNT_NODE(NODE_BLOCK_PASS);
450 		COUNT_NODE(NODE_DEFN);
451 		COUNT_NODE(NODE_DEFS);
452 		COUNT_NODE(NODE_ALIAS);
453 		COUNT_NODE(NODE_VALIAS);
454 		COUNT_NODE(NODE_UNDEF);
455 		COUNT_NODE(NODE_CLASS);
456 		COUNT_NODE(NODE_MODULE);
457 		COUNT_NODE(NODE_SCLASS);
458 		COUNT_NODE(NODE_COLON2);
459 		COUNT_NODE(NODE_COLON3);
460 		COUNT_NODE(NODE_DOT2);
461 		COUNT_NODE(NODE_DOT3);
462 		COUNT_NODE(NODE_FLIP2);
463 		COUNT_NODE(NODE_FLIP3);
464 		COUNT_NODE(NODE_SELF);
465 		COUNT_NODE(NODE_NIL);
466 		COUNT_NODE(NODE_TRUE);
467 		COUNT_NODE(NODE_FALSE);
468 		COUNT_NODE(NODE_ERRINFO);
469 		COUNT_NODE(NODE_DEFINED);
470 		COUNT_NODE(NODE_POSTEXE);
471 		COUNT_NODE(NODE_DSYM);
472 		COUNT_NODE(NODE_ATTRASGN);
473 		COUNT_NODE(NODE_LAMBDA);
474 #undef COUNT_NODE
475 	      case NODE_LAST: break;
476 	    }
477 	    UNREACHABLE;
478 	  set:
479 	    rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
480 	}
481     }
482     return hash;
483 }
484 
485 static int
cto_i(void * vstart,void * vend,size_t stride,void * data)486 cto_i(void *vstart, void *vend, size_t stride, void *data)
487 {
488     VALUE hash = (VALUE)data;
489     VALUE v = (VALUE)vstart;
490 
491     for (; v != (VALUE)vend; v += stride) {
492 	if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) {
493 	    VALUE counter;
494 	    VALUE key = RBASIC(v)->klass;
495 
496 	    if (key == 0) {
497 		const char *name = rb_objspace_data_type_name(v);
498 		if (name == 0) name = "unknown";
499 		key = ID2SYM(rb_intern(name));
500 	    }
501 
502 	    counter = rb_hash_aref(hash, key);
503 	    if (NIL_P(counter)) {
504 		counter = INT2FIX(1);
505 	    }
506 	    else {
507 		counter = INT2FIX(FIX2INT(counter) + 1);
508 	    }
509 
510 	    rb_hash_aset(hash, key, counter);
511 	}
512     }
513 
514     return 0;
515 }
516 
517 /*
518  *  call-seq:
519  *     ObjectSpace.count_tdata_objects([result_hash]) -> hash
520  *
521  *  Counts objects for each +T_DATA+ type.
522  *
523  *  This method is only for MRI developers interested in performance and memory
524  *  usage of Ruby programs.
525  *
526  *  It returns a hash as:
527  *
528  *	{RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6,
529  *  	 :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99,
530  *  	 ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1,
531  *  	 Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2}
532  *  	# T_DATA objects existing at startup on r32276.
533  *
534  *  If the optional argument, result_hash, is given, it is overwritten and
535  *  returned. This is intended to avoid probe effect.
536  *
537  *  The contents of the returned hash is implementation specific and may change
538  *  in the future.
539  *
540  *  In this version, keys are Class object or Symbol object.
541  *
542  *  If object is kind of normal (accessible) object, the key is Class object.
543  *  If object is not a kind of normal (internal) object, the key is symbol
544  *  name, registered by rb_data_type_struct.
545  *
546  *  This method is only expected to work with C Ruby.
547  */
548 
549 static VALUE
count_tdata_objects(int argc,VALUE * argv,VALUE self)550 count_tdata_objects(int argc, VALUE *argv, VALUE self)
551 {
552     VALUE hash = setup_hash(argc, argv);
553     rb_objspace_each_objects(cto_i, (void *)hash);
554     return hash;
555 }
556 
557 static ID imemo_type_ids[IMEMO_MASK+1];
558 
559 static int
count_imemo_objects_i(void * vstart,void * vend,size_t stride,void * data)560 count_imemo_objects_i(void *vstart, void *vend, size_t stride, void *data)
561 {
562     VALUE hash = (VALUE)data;
563     VALUE v = (VALUE)vstart;
564 
565     for (; v != (VALUE)vend; v += stride) {
566 	if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_IMEMO) {
567 	    VALUE counter;
568 	    VALUE key = ID2SYM(imemo_type_ids[imemo_type(v)]);
569 
570 	    counter = rb_hash_aref(hash, key);
571 
572 	    if (NIL_P(counter)) {
573 		counter = INT2FIX(1);
574 	    }
575 	    else {
576 		counter = INT2FIX(FIX2INT(counter) + 1);
577 	    }
578 
579 	    rb_hash_aset(hash, key, counter);
580 	}
581     }
582 
583     return 0;
584 }
585 
586 /*
587  *  call-seq:
588  *     ObjectSpace.count_imemo_objects([result_hash]) -> hash
589  *
590  *  Counts objects for each +T_IMEMO+ type.
591  *
592  *  This method is only for MRI developers interested in performance and memory
593  *  usage of Ruby programs.
594  *
595  *  It returns a hash as:
596  *
597  *       {:imemo_ifunc=>8,
598  *        :imemo_svar=>7,
599  *        :imemo_cref=>509,
600  *        :imemo_memo=>1,
601  *        :imemo_throw_data=>1}
602  *
603  *  If the optional argument, result_hash, is given, it is overwritten and
604  *  returned. This is intended to avoid probe effect.
605  *
606  *  The contents of the returned hash is implementation specific and may change
607  *  in the future.
608  *
609  *  In this version, keys are symbol objects.
610  *
611  *  This method is only expected to work with C Ruby.
612  */
613 
614 static VALUE
count_imemo_objects(int argc,VALUE * argv,VALUE self)615 count_imemo_objects(int argc, VALUE *argv, VALUE self)
616 {
617     VALUE hash = setup_hash(argc, argv);
618 
619     if (imemo_type_ids[0] == 0) {
620 	imemo_type_ids[0] = rb_intern("imemo_env");
621 	imemo_type_ids[1] = rb_intern("imemo_cref");
622 	imemo_type_ids[2] = rb_intern("imemo_svar");
623 	imemo_type_ids[3] = rb_intern("imemo_throw_data");
624 	imemo_type_ids[4] = rb_intern("imemo_ifunc");
625 	imemo_type_ids[5] = rb_intern("imemo_memo");
626 	imemo_type_ids[6] = rb_intern("imemo_ment");
627 	imemo_type_ids[7] = rb_intern("imemo_iseq");
628 	imemo_type_ids[8] = rb_intern("imemo_tmpbuf");
629         imemo_type_ids[9] = rb_intern("imemo_ast");
630 	imemo_type_ids[10] = rb_intern("imemo_parser_strterm");
631     }
632 
633     rb_objspace_each_objects(count_imemo_objects_i, (void *)hash);
634 
635     return hash;
636 }
637 
638 static void
iow_mark(void * ptr)639 iow_mark(void *ptr)
640 {
641     rb_gc_mark((VALUE)ptr);
642 }
643 
644 static size_t
iow_size(const void * ptr)645 iow_size(const void *ptr)
646 {
647     VALUE obj = (VALUE)ptr;
648     return rb_obj_memsize_of(obj);
649 }
650 
651 static const rb_data_type_t iow_data_type = {
652     "ObjectSpace::InternalObjectWrapper",
653     {iow_mark, 0, iow_size,},
654     0, 0, RUBY_TYPED_FREE_IMMEDIATELY
655 };
656 
657 static VALUE rb_mInternalObjectWrapper;
658 
659 static VALUE
iow_newobj(VALUE obj)660 iow_newobj(VALUE obj)
661 {
662     return TypedData_Wrap_Struct(rb_mInternalObjectWrapper, &iow_data_type, (void *)obj);
663 }
664 
665 /* Returns the type of the internal object. */
666 static VALUE
iow_type(VALUE self)667 iow_type(VALUE self)
668 {
669     VALUE obj = (VALUE)DATA_PTR(self);
670     return type2sym(BUILTIN_TYPE(obj));
671 }
672 
673 /* See Object#inspect. */
674 static VALUE
iow_inspect(VALUE self)675 iow_inspect(VALUE self)
676 {
677     VALUE obj = (VALUE)DATA_PTR(self);
678     VALUE type = type2sym(BUILTIN_TYPE(obj));
679 
680     return rb_sprintf("#<InternalObject:%p %"PRIsVALUE">", (void *)obj, rb_sym2str(type));
681 }
682 
683 /* Returns the Object#object_id of the internal object. */
684 static VALUE
iow_internal_object_id(VALUE self)685 iow_internal_object_id(VALUE self)
686 {
687     VALUE obj = (VALUE)DATA_PTR(self);
688     return rb_obj_id(obj);
689 }
690 
691 struct rof_data {
692     st_table *refs;
693     VALUE internals;
694 };
695 
696 static void
reachable_object_from_i(VALUE obj,void * data_ptr)697 reachable_object_from_i(VALUE obj, void *data_ptr)
698 {
699     struct rof_data *data = (struct rof_data *)data_ptr;
700     VALUE key = obj;
701     VALUE val = obj;
702 
703     if (rb_objspace_markable_object_p(obj)) {
704 	if (rb_objspace_internal_object_p(obj)) {
705 	    val = iow_newobj(obj);
706 	    rb_ary_push(data->internals, val);
707 	}
708 	st_insert(data->refs, key, val);
709     }
710 }
711 
712 static int
collect_values(st_data_t key,st_data_t value,st_data_t data)713 collect_values(st_data_t key, st_data_t value, st_data_t data)
714 {
715     VALUE ary = (VALUE)data;
716     rb_ary_push(ary, (VALUE)value);
717     return ST_CONTINUE;
718 }
719 
720 /*
721  *  call-seq:
722  *     ObjectSpace.reachable_objects_from(obj) -> array or nil
723  *
724  *  [MRI specific feature] Return all reachable objects from `obj'.
725  *
726  *  This method returns all reachable objects from `obj'.
727  *
728  *  If `obj' has two or more references to the same object `x', then returned
729  *  array only includes one `x' object.
730  *
731  *  If `obj' is a non-markable (non-heap management) object such as true,
732  *  false, nil, symbols and Fixnums (and Flonum) then it simply returns nil.
733  *
734  *  If `obj' has references to an internal object, then it returns instances of
735  *  ObjectSpace::InternalObjectWrapper class. This object contains a reference
736  *  to an internal object and you can check the type of internal object with
737  *  `type' method.
738  *
739  *  If `obj' is instance of ObjectSpace::InternalObjectWrapper class, then this
740  *  method returns all reachable object from an internal object, which is
741  *  pointed by `obj'.
742  *
743  *  With this method, you can find memory leaks.
744  *
745  *  This method is only expected to work except with C Ruby.
746  *
747  *  Example:
748  *    ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
749  *    #=> [Array, 'a', 'b', 'c']
750  *
751  *    ObjectSpace.reachable_objects_from(['a', 'a', 'a'])
752  *    #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id
753  *
754  *    ObjectSpace.reachable_objects_from([v = 'a', v, v])
755  *    #=> [Array, 'a']
756  *
757  *    ObjectSpace.reachable_objects_from(1)
758  *    #=> nil # 1 is not markable (heap managed) object
759  *
760  */
761 
762 static VALUE
reachable_objects_from(VALUE self,VALUE obj)763 reachable_objects_from(VALUE self, VALUE obj)
764 {
765     if (rb_objspace_markable_object_p(obj)) {
766 	VALUE ret = rb_ary_new();
767 	struct rof_data data;
768 
769 	if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
770 	    obj = (VALUE)DATA_PTR(obj);
771 	}
772 
773 	data.refs = st_init_numtable();
774 	data.internals = rb_ary_new();
775 
776 	rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
777 
778 	st_foreach(data.refs, collect_values, (st_data_t)ret);
779 	return ret;
780     }
781     else {
782 	return Qnil;
783     }
784 }
785 
786 struct rofr_data {
787     VALUE categories;
788     const char *last_category;
789     VALUE last_category_str;
790     VALUE last_category_objects;
791 };
792 
793 static void
reachable_object_from_root_i(const char * category,VALUE obj,void * ptr)794 reachable_object_from_root_i(const char *category, VALUE obj, void *ptr)
795 {
796     struct rofr_data *data = (struct rofr_data *)ptr;
797     VALUE category_str;
798     VALUE category_objects;
799 
800     if (category == data->last_category) {
801 	category_str = data->last_category_str;
802 	category_objects = data->last_category_objects;
803     }
804     else {
805 	data->last_category = category;
806 	category_str = data->last_category_str = rb_str_new2(category);
807 	category_objects = data->last_category_objects = rb_ident_hash_new();
808 	if (!NIL_P(rb_hash_lookup(data->categories, category_str))) {
809 	    rb_bug("reachable_object_from_root_i: category should insert at once");
810 	}
811 	rb_hash_aset(data->categories, category_str, category_objects);
812     }
813 
814     if (rb_objspace_markable_object_p(obj) &&
815 	obj != data->categories &&
816 	obj != data->last_category_objects) {
817 	if (rb_objspace_internal_object_p(obj)) {
818 	    obj = iow_newobj(obj);
819 	}
820 	rb_hash_aset(category_objects, obj, obj);
821     }
822 }
823 
824 static int
collect_values_of_values(VALUE category,VALUE category_objects,VALUE categories)825 collect_values_of_values(VALUE category, VALUE category_objects, VALUE categories)
826 {
827     VALUE ary = rb_ary_new();
828     rb_hash_foreach(category_objects, collect_values, ary);
829     rb_hash_aset(categories, category, ary);
830     return ST_CONTINUE;
831 }
832 
833 /*
834  *  call-seq:
835  *     ObjectSpace.reachable_objects_from_root -> hash
836  *
837  *  [MRI specific feature] Return all reachable objects from root.
838  */
839 static VALUE
reachable_objects_from_root(VALUE self)840 reachable_objects_from_root(VALUE self)
841 {
842     struct rofr_data data;
843     VALUE hash = data.categories = rb_ident_hash_new();
844     data.last_category = 0;
845 
846     rb_objspace_reachable_objects_from_root(reachable_object_from_root_i, &data);
847     rb_hash_foreach(hash, collect_values_of_values, hash);
848 
849     return hash;
850 }
851 
852 static VALUE
wrap_klass_iow(VALUE klass)853 wrap_klass_iow(VALUE klass)
854 {
855     if (!RTEST(klass)) {
856 	return Qnil;
857     }
858     else if (RB_TYPE_P(klass, T_ICLASS)) {
859 	return iow_newobj(klass);
860     }
861     else {
862 	return klass;
863     }
864 }
865 
866 /*
867  *  call-seq:
868  *     ObjectSpace.internal_class_of(obj) -> Class or Module
869  *
870  *  [MRI specific feature] Return internal class of obj.
871  *  obj can be an instance of InternalObjectWrapper.
872  *
873  *  Note that you should not use this method in your application.
874  */
875 static VALUE
objspace_internal_class_of(VALUE self,VALUE obj)876 objspace_internal_class_of(VALUE self, VALUE obj)
877 {
878     VALUE klass;
879 
880     if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
881 	obj = (VALUE)DATA_PTR(obj);
882     }
883 
884     klass = CLASS_OF(obj);
885     return wrap_klass_iow(klass);
886 }
887 
888 /*
889  *  call-seq:
890  *     ObjectSpace.internal_super_of(cls) -> Class or Module
891  *
892  *  [MRI specific feature] Return internal super class of cls (Class or Module).
893  *  obj can be an instance of InternalObjectWrapper.
894  *
895  *  Note that you should not use this method in your application.
896  */
897 static VALUE
objspace_internal_super_of(VALUE self,VALUE obj)898 objspace_internal_super_of(VALUE self, VALUE obj)
899 {
900     VALUE super;
901 
902     if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
903 	obj = (VALUE)DATA_PTR(obj);
904     }
905 
906     switch (OBJ_BUILTIN_TYPE(obj)) {
907       case T_MODULE:
908       case T_CLASS:
909       case T_ICLASS:
910 	super = RCLASS_SUPER(obj);
911 	break;
912       default:
913 	rb_raise(rb_eArgError, "class or module is expected");
914     }
915 
916     return wrap_klass_iow(super);
917 }
918 
919 void Init_object_tracing(VALUE rb_mObjSpace);
920 void Init_objspace_dump(VALUE rb_mObjSpace);
921 
922 /*
923  * Document-module: ObjectSpace
924  *
925  * The objspace library extends the ObjectSpace module and adds several
926  * methods to get internal statistic information about
927  * object/memory management.
928  *
929  * You need to <code>require 'objspace'</code> to use this extension module.
930  *
931  * Generally, you *SHOULD NOT* use this library if you do not know
932  * about the MRI implementation.  Mainly, this library is for (memory)
933  * profiler developers and MRI developers who need to know about MRI
934  * memory usage.
935  */
936 
937 void
Init_objspace(void)938 Init_objspace(void)
939 {
940 #undef rb_intern
941     VALUE rb_mObjSpace;
942 #if 0
943     rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
944 #endif
945     rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
946 
947     rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
948     rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1);
949 
950     rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
951     rb_define_module_function(rb_mObjSpace, "count_symbols", count_symbols, -1);
952     rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
953     rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
954     rb_define_module_function(rb_mObjSpace, "count_imemo_objects", count_imemo_objects, -1);
955 
956     rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1);
957     rb_define_module_function(rb_mObjSpace, "reachable_objects_from_root", reachable_objects_from_root, 0);
958 
959     rb_define_module_function(rb_mObjSpace, "internal_class_of", objspace_internal_class_of, 1);
960     rb_define_module_function(rb_mObjSpace, "internal_super_of", objspace_internal_super_of, 1);
961 
962     /*
963      * This class is used as a return value from
964      * ObjectSpace::reachable_objects_from.
965      *
966      * When ObjectSpace::reachable_objects_from returns an object with
967      * references to an internal object, an instance of this class is returned.
968      *
969      * You can use the #type method to check the type of the internal object.
970      */
971     rb_mInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject);
972     rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0);
973     rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0);
974     rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
975 
976     Init_object_tracing(rb_mObjSpace);
977     Init_objspace_dump(rb_mObjSpace);
978 }
979