Ruby  1.9.3p484(2013-11-22revision43786)
objspace.c
Go to the documentation of this file.
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 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 *);
40 
41 static size_t
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  size += st_memsize(RCLASS_M_TBL(obj));
64  if (RCLASS_IV_TBL(obj)) {
65  size += st_memsize(RCLASS_IV_TBL(obj));
66  }
67  if (RCLASS_IV_INDEX_TBL(obj)) {
68  size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
69  }
70  if (RCLASS(obj)->ptr->iv_tbl) {
71  size += st_memsize(RCLASS(obj)->ptr->iv_tbl);
72  }
73  if (RCLASS(obj)->ptr->const_tbl) {
74  size += st_memsize(RCLASS(obj)->ptr->const_tbl);
75  }
76  size += sizeof(rb_classext_t);
77  break;
78  case T_STRING:
79  size += rb_str_memsize(obj);
80  break;
81  case T_ARRAY:
82  size += rb_ary_memsize(obj);
83  break;
84  case T_HASH:
85  if (RHASH(obj)->ntbl) {
86  size += st_memsize(RHASH(obj)->ntbl);
87  }
88  break;
89  case T_REGEXP:
90  if (RREGEXP(obj)->ptr) {
91  size += onig_memsize(RREGEXP(obj)->ptr);
92  }
93  break;
94  case T_DATA:
95  size += rb_objspace_data_type_memsize(obj);
96  break;
97  case T_MATCH:
98  if (RMATCH(obj)->rmatch) {
99  struct rmatch *rm = RMATCH(obj)->rmatch;
100  size += sizeof(struct re_registers); /* TODO: onig_region_memsize(&rm->regs); */
101  size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
102  size += sizeof(struct rmatch);
103  }
104  break;
105  case T_FILE:
106  if (RFILE(obj)->fptr) {
107  size += rb_io_memsize(RFILE(obj)->fptr);
108  }
109  break;
110  case T_RATIONAL:
111  case T_COMPLEX:
112  break;
113  case T_ICLASS:
114  /* iClass shares table with the module */
115  break;
116 
117  case T_FLOAT:
118  break;
119 
120  case T_BIGNUM:
121  if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
122  size += RBIGNUM_LEN(obj) * sizeof(BDIGIT);
123  }
124  break;
125  case T_NODE:
126  switch (nd_type(obj)) {
127  case NODE_SCOPE:
128  if (RNODE(obj)->u1.tbl) {
129  /* TODO: xfree(RANY(obj)->as.node.u1.tbl); */
130  }
131  break;
132  case NODE_ALLOCA:
133  /* TODO: xfree(RANY(obj)->as.node.u1.node); */
134  ;
135  }
136  break; /* no need to free iv_tbl */
137 
138  case T_STRUCT:
139  if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
140  RSTRUCT(obj)->as.heap.ptr) {
141  size += sizeof(VALUE) * RSTRUCT_LEN(obj);
142  }
143  break;
144 
145  case T_ZOMBIE:
146  break;
147 
148  default:
149  rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
150  BUILTIN_TYPE(obj), (void*)obj);
151  }
152 
153  return size;
154 }
155 
156 /*
157  * call-seq:
158  * ObjectSpace.memsize_of(obj) -> Integer
159  *
160  * Return consuming memory size of obj.
161  *
162  * Note that the return size is incomplete. You need to deal with
163  * this information as only a *HINT*. Especially, the size of
164  * T_DATA may not be correct.
165  *
166  * This method is not expected to work except C Ruby.
167  */
168 
169 static VALUE
171 {
172  return SIZET2NUM(memsize_of(obj));
173 }
174 
175 struct total_data {
176  size_t total;
178 };
179 
180 static int
181 total_i(void *vstart, void *vend, size_t stride, void *ptr)
182 {
183  VALUE v;
184  struct total_data *data = (struct total_data *)ptr;
185 
186  for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
187  if (RBASIC(v)->flags) {
188  switch (BUILTIN_TYPE(v)) {
189  case T_NONE:
190  case T_ICLASS:
191  case T_NODE:
192  case T_ZOMBIE:
193  continue;
194  case T_CLASS:
195  if (FL_TEST(v, FL_SINGLETON))
196  continue;
197  default:
198  if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) {
199  data->total += memsize_of(v);
200  }
201  }
202  }
203  }
204 
205  return 0;
206 }
207 
208 /*
209  * call-seq:
210  * ObjectSpace.memsize_of_all([klass]) -> Integer
211  *
212  * Return consuming memory size of all living objects.
213  * If klass (should be Class object) is given, return the total
214  * memory size of instances of the given class.
215  *
216  * Note that the returned size is incomplete. You need to deal with
217  * this information as only a *HINT*. Especially, the size of
218  * T_DATA may not be correct.
219  *
220  * Note that this method does *NOT* return total malloc'ed memory size.
221  *
222  * This method can be defined by the following Ruby code:
223  *
224  * def memsize_of_all klass = false
225  * total = 0
226  * ObjectSpace.each_objects{|e|
227  * total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass)
228  * }
229  * total
230  * end
231  *
232  * This method is not expected to work except C Ruby.
233  */
234 
235 static VALUE
237 {
238  struct total_data data = {0, 0};
239 
240  if (argc > 0) {
241  rb_scan_args(argc, argv, "01", &data.klass);
242  }
243 
245  return SIZET2NUM(data.total);
246 }
247 
248 static int
250 {
251  VALUE k = (VALUE)key;
252  VALUE hash = (VALUE)arg;
253  rb_hash_aset(hash, k, INT2FIX(0));
254  return ST_CONTINUE;
255 }
256 
257 static int
258 cos_i(void *vstart, void *vend, size_t stride, void *data)
259 {
260  size_t *counts = (size_t *)data;
261  VALUE v = (VALUE)vstart;
262 
263  for (;v != (VALUE)vend; v += stride) {
264  if (RBASIC(v)->flags) {
265  counts[BUILTIN_TYPE(v)] += memsize_of(v);
266  }
267  }
268  return 0;
269 }
270 
271 /*
272  * call-seq:
273  * ObjectSpace.count_objects_size([result_hash]) -> hash
274  *
275  * Counts objects size (in bytes) for each type.
276  *
277  * Note that this information is incomplete. You need to deal with
278  * this information as only a *HINT*. Especially, total size of
279  * T_DATA may not right size.
280  *
281  * It returns a hash as:
282  * {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...}
283  *
284  * If the optional argument, result_hash, is given,
285  * it is overwritten and returned.
286  * This is intended to avoid probe effect.
287  *
288  * The contents of the returned hash is implementation defined.
289  * It may be changed in future.
290  *
291  * This method is not expected to work except C Ruby.
292  */
293 
294 static VALUE
296 {
297  size_t counts[T_MASK+1];
298  size_t total = 0;
299  size_t i;
300  VALUE hash;
301 
302  if (rb_scan_args(argc, argv, "01", &hash) == 1) {
303  if (TYPE(hash) != T_HASH)
304  rb_raise(rb_eTypeError, "non-hash given");
305  }
306 
307  for (i = 0; i <= T_MASK; i++) {
308  counts[i] = 0;
309  }
310 
311  rb_objspace_each_objects(cos_i, &counts[0]);
312 
313  if (hash == Qnil) {
314  hash = rb_hash_new();
315  }
316  else if (!RHASH_EMPTY_P(hash)) {
317  st_foreach(RHASH_TBL(hash), set_zero_i, hash);
318  }
319 
320  for (i = 0; i <= T_MASK; i++) {
321  if (counts[i]) {
322  VALUE type;
323  switch (i) {
324 #define COUNT_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break;
341  COUNT_TYPE(T_NIL);
350 #undef COUNT_TYPE
351  default: type = INT2NUM(i); break;
352  }
353  total += counts[i];
354  rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
355  }
356  }
357  rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
358  return hash;
359 }
360 
361 static int
362 cn_i(void *vstart, void *vend, size_t stride, void *n)
363 {
364  size_t *nodes = (size_t *)n;
365  VALUE v = (VALUE)vstart;
366 
367  for (; v != (VALUE)vend; v += stride) {
368  if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) {
369  size_t s = nd_type((NODE *)v);
370  nodes[s]++;
371  }
372  }
373 
374  return 0;
375 }
376 
377 /*
378  * call-seq:
379  * ObjectSpace.count_nodes([result_hash]) -> hash
380  *
381  * Counts nodes for each node type.
382  *
383  * This method is not for ordinary Ruby programmers, but for MRI developers
384  * who have interest in MRI performance and memory usage.
385  *
386  * It returns a hash as:
387  * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...}
388  *
389  * If the optional argument, result_hash, is given,
390  * it is overwritten and returned.
391  * This is intended to avoid probe effect.
392  *
393  * The contents of the returned hash is implementation defined.
394  * It may be changed in future.
395  *
396  * This method is not expected to work except C Ruby.
397  */
398 
399 static VALUE
401 {
402  size_t nodes[NODE_LAST+1];
403  size_t i;
404  VALUE hash;
405 
406  if (rb_scan_args(argc, argv, "01", &hash) == 1) {
407  if (TYPE(hash) != T_HASH)
408  rb_raise(rb_eTypeError, "non-hash given");
409  }
410 
411  for (i = 0; i <= NODE_LAST; i++) {
412  nodes[i] = 0;
413  }
414 
415  rb_objspace_each_objects(cn_i, &nodes[0]);
416 
417  if (hash == Qnil) {
418  hash = rb_hash_new();
419  }
420  else if (!RHASH_EMPTY_P(hash)) {
421  st_foreach(RHASH_TBL(hash), set_zero_i, hash);
422  }
423 
424  for (i=0; i<NODE_LAST; i++) {
425  if (nodes[i] != 0) {
426  VALUE node;
427  switch (i) {
428 #define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
533 #undef COUNT_NODE
534  default: node = INT2FIX(i);
535  }
536  rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
537  }
538  }
539  return hash;
540 }
541 
542 static int
543 cto_i(void *vstart, void *vend, size_t stride, void *data)
544 {
545  VALUE hash = (VALUE)data;
546  VALUE v = (VALUE)vstart;
547 
548  for (; v != (VALUE)vend; v += stride) {
549  if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) {
550  VALUE counter;
551  VALUE key = RBASIC(v)->klass;
552 
553  if (key == 0) {
554  const char *name = rb_objspace_data_type_name(v);
555  if (name == 0) name = "unknown";
556  key = ID2SYM(rb_intern(name));
557  }
558 
559  counter = rb_hash_aref(hash, key);
560  if (NIL_P(counter)) {
561  counter = INT2FIX(1);
562  }
563  else {
564  counter = INT2FIX(FIX2INT(counter) + 1);
565  }
566 
567  rb_hash_aset(hash, key, counter);
568  }
569  }
570 
571  return 0;
572 }
573 
574 /*
575  * call-seq:
576  * ObjectSpace.count_tdata_objects([result_hash]) -> hash
577  *
578  * Counts objects for each T_DATA type.
579  *
580  * This method is not for ordinary Ruby programmers, but for MRI developers
581  * who interest on MRI performance.
582  *
583  * It returns a hash as:
584  * {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6,
585  * :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99,
586  * ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1,
587  * Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2}
588  * # T_DATA objects existing at startup on r32276.
589  *
590  * If the optional argument, result_hash, is given,
591  * it is overwritten and returned.
592  * This is intended to avoid probe effect.
593  *
594  * The contents of the returned hash is implementation defined.
595  * It may be changed in future.
596  *
597  * In this version, keys are Class object or Symbol object.
598  * If object is kind of normal (accessible) object, the key is Class object.
599  * If object is not a kind of normal (internal) object, the key is symbol
600  * name, registered by rb_data_type_struct.
601  *
602  * This method is not expected to work except C Ruby.
603  *
604  */
605 
606 static VALUE
608 {
609  VALUE hash;
610 
611  if (rb_scan_args(argc, argv, "01", &hash) == 1) {
612  if (TYPE(hash) != T_HASH)
613  rb_raise(rb_eTypeError, "non-hash given");
614  }
615 
616  if (hash == Qnil) {
617  hash = rb_hash_new();
618  }
619  else if (!RHASH_EMPTY_P(hash)) {
620  st_foreach(RHASH_TBL(hash), set_zero_i, hash);
621  }
622 
623  rb_objspace_each_objects(cto_i, (void *)hash);
624 
625  return hash;
626 }
627 
628 /* objspace library extends ObjectSpace module and add several
629  * methods to get internal statistic information about
630  * object/memory management.
631  *
632  * Generally, you *SHOULD NOT*use this library if you do not know
633  * about the MRI implementation. Mainly, this library is for (memory)
634  * profiler developers and MRI developers who need to know how MRI
635  * memory usage.
636  */
637 
638 void
640 {
641  VALUE rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
642 
643  rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
644  rb_define_module_function(rb_mObjSpace, "memsize_of_all",
645  memsize_of_all_m, -1);
646 
647  rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
648  rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
649  rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
650 }
651