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