Ruby  2.1.10p492(2016-04-01revision54464)
objspace_dump.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  objspace_dump.c - Heap dumping ObjectSpace extender for MRI.
4 
5  $Author$
6  created at: Sat Oct 11 10:11:00 2013
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 #include "ruby/ruby.h"
16 #include "ruby/debug.h"
17 #include "ruby/encoding.h"
18 #include "ruby/io.h"
19 #include "gc.h"
20 #include "node.h"
21 #include "vm_core.h"
22 #include "objspace.h"
23 #include "internal.h"
24 
26 
27 struct dump_config {
31  int roots;
32  const char *root_category;
36 };
37 
38 static void
39 dump_append(struct dump_config *dc, const char *format, ...)
40 {
41  va_list vl;
42  va_start(vl, format);
43 
44  if (dc->stream) {
45  vfprintf(dc->stream, format, vl);
46  fflush(dc->stream);
47  }
48  else if (dc->string)
49  rb_str_vcatf(dc->string, format, vl);
50 
51  va_end(vl);
52 }
53 
54 static void
56 {
57  int i;
58  char c, *value;
59 
60  dump_append(dc, "\"");
61  for (i = 0, value = RSTRING_PTR(obj); i < RSTRING_LEN(obj); i++) {
62  switch ((c = value[i])) {
63  case '\\':
64  case '"':
65  dump_append(dc, "\\%c", c);
66  break;
67  case '\0':
68  dump_append(dc, "\\u0000");
69  break;
70  case '\b':
71  dump_append(dc, "\\b");
72  break;
73  case '\t':
74  dump_append(dc, "\\t");
75  break;
76  case '\f':
77  dump_append(dc, "\\f");
78  break;
79  case '\n':
80  dump_append(dc, "\\n");
81  break;
82  case '\r':
83  dump_append(dc, "\\r");
84  break;
85  default:
86  if (c <= 0x1f)
87  dump_append(dc, "\\u%04d", c);
88  else
89  dump_append(dc, "%c", c);
90  }
91  }
92  dump_append(dc, "\"");
93 }
94 
95 static inline const char *
97 {
98  switch (BUILTIN_TYPE(obj)) {
99 #define CASE_TYPE(type) case T_##type: return #type; break
100  CASE_TYPE(NONE);
101  CASE_TYPE(NIL);
102  CASE_TYPE(OBJECT);
103  CASE_TYPE(CLASS);
104  CASE_TYPE(ICLASS);
105  CASE_TYPE(MODULE);
106  CASE_TYPE(FLOAT);
107  CASE_TYPE(STRING);
108  CASE_TYPE(REGEXP);
109  CASE_TYPE(ARRAY);
110  CASE_TYPE(HASH);
111  CASE_TYPE(STRUCT);
112  CASE_TYPE(BIGNUM);
113  CASE_TYPE(FILE);
114  CASE_TYPE(FIXNUM);
115  CASE_TYPE(TRUE);
116  CASE_TYPE(FALSE);
117  CASE_TYPE(DATA);
118  CASE_TYPE(MATCH);
119  CASE_TYPE(SYMBOL);
120  CASE_TYPE(RATIONAL);
121  CASE_TYPE(COMPLEX);
122  CASE_TYPE(UNDEF);
123  CASE_TYPE(NODE);
124  CASE_TYPE(ZOMBIE);
125 #undef CASE_TYPE
126  }
127  return "UNKNOWN";
128 }
129 
130 static void
132 {
133  struct dump_config *dc = (struct dump_config *)data;
134 
135  if (dc->cur_obj_klass == ref)
136  return;
137 
138  if (dc->cur_obj_references == 0)
139  dump_append(dc, ", \"references\":[\"%p\"", (void *)ref);
140  else
141  dump_append(dc, ", \"%p\"", (void *)ref);
142 
143  dc->cur_obj_references++;
144 }
145 
146 static void
148 {
149  size_t memsize;
150  struct allocation_info *ainfo;
151  rb_io_t *fptr;
153  size_t n, i;
154 
155  if (SPECIAL_CONST_P(obj)) {
156  dump_append(dc, "{}");
157  return;
158  }
159 
160  dc->cur_obj = obj;
161  dc->cur_obj_references = 0;
162  dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj);
163 
164  if (dc->cur_obj == dc->string)
165  return;
166 
167  dump_append(dc, "{\"address\":\"%p\", \"type\":\"%s\"", (void *)obj, obj_type(obj));
168 
169  if (dc->cur_obj_klass)
170  dump_append(dc, ", \"class\":\"%p\"", (void *)dc->cur_obj_klass);
171  if (rb_obj_frozen_p(obj))
172  dump_append(dc, ", \"frozen\":true");
173 
174  switch (BUILTIN_TYPE(obj)) {
175  case T_NODE:
176  dump_append(dc, ", \"node_type\":\"%s\"", ruby_node_name(nd_type(obj)));
177  break;
178 
179  case T_STRING:
180  if (STR_EMBED_P(obj))
181  dump_append(dc, ", \"embedded\":true");
182  if (STR_ASSOC_P(obj))
183  dump_append(dc, ", \"associated\":true");
184  if (is_broken_string(obj))
185  dump_append(dc, ", \"broken\":true");
186  if (FL_TEST(obj, RSTRING_FSTR))
187  dump_append(dc, ", \"fstring\":true");
188  if (STR_SHARED_P(obj))
189  dump_append(dc, ", \"shared\":true");
190  else {
191  dump_append(dc, ", \"bytesize\":%ld", RSTRING_LEN(obj));
192  if (!STR_EMBED_P(obj) && !STR_NOCAPA_P(obj) && (long)rb_str_capacity(obj) != RSTRING_LEN(obj))
193  dump_append(dc, ", \"capacity\":%ld", rb_str_capacity(obj));
194 
195  if (is_ascii_string(obj)) {
196  dump_append(dc, ", \"value\":");
197  dump_append_string_value(dc, obj);
198  }
199  }
200 
201  if (!ENCODING_IS_ASCII8BIT(obj))
202  dump_append(dc, ", \"encoding\":\"%s\"", rb_enc_name(rb_enc_from_index(ENCODING_GET(obj))));
203  break;
204 
205  case T_HASH:
206  dump_append(dc, ", \"size\":%ld", RHASH_SIZE(obj));
207  if (FL_TEST(obj, HASH_PROC_DEFAULT))
208  dump_append(dc, ", \"default\":\"%p\"", (void *)RHASH_IFNONE(obj));
209  break;
210 
211  case T_ARRAY:
212  dump_append(dc, ", \"length\":%ld", RARRAY_LEN(obj));
213  if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED))
214  dump_append(dc, ", \"shared\":true");
215  if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_EMBED_FLAG))
216  dump_append(dc, ", \"embedded\":true");
217  break;
218 
219  case T_CLASS:
220  case T_MODULE:
221  if (dc->cur_obj_klass)
222  dump_append(dc, ", \"name\":\"%s\"", rb_class2name(obj));
223  break;
224 
225  case T_DATA:
226  if (RTYPEDDATA_P(obj))
227  dump_append(dc, ", \"struct\":\"%s\"", RTYPEDDATA_TYPE(obj)->wrap_struct_name);
228  break;
229 
230  case T_FLOAT:
231  dump_append(dc, ", \"value\":\"%g\"", RFLOAT_VALUE(obj));
232  break;
233 
234  case T_OBJECT:
235  dump_append(dc, ", \"ivars\":%ld", ROBJECT_NUMIV(obj));
236  break;
237 
238  case T_FILE:
239  fptr = RFILE(obj)->fptr;
240  if (fptr)
241  dump_append(dc, ", \"fd\":%d", fptr->fd);
242  break;
243 
244  case T_ZOMBIE:
245  dump_append(dc, "}\n");
246  return;
247  }
248 
250  if (dc->cur_obj_references > 0)
251  dump_append(dc, "]");
252 
253  if ((ainfo = objspace_lookup_allocation_info(obj))) {
254  dump_append(dc, ", \"file\":\"%s\", \"line\":%lu", ainfo->path, ainfo->line);
255  if (RTEST(ainfo->mid))
256  dump_append(dc, ", \"method\":\"%s\"", rb_id2name(SYM2ID(ainfo->mid)));
257  dump_append(dc, ", \"generation\":%"PRIuSIZE, ainfo->generation);
258  }
259 
260  if ((memsize = rb_obj_memsize_of(obj)) > 0)
261  dump_append(dc, ", \"memsize\":%"PRIuSIZE, memsize);
262 
263  if ((n = rb_obj_gc_flags(obj, flags, sizeof(flags))) > 0) {
264  dump_append(dc, ", \"flags\":{");
265  for (i=0; i<n; i++) {
266  dump_append(dc, "\"%s\":true", rb_id2name(flags[i]));
267  if (i != n-1) dump_append(dc, ", ");
268  }
269  dump_append(dc, "}");
270  }
271 
272  dump_append(dc, "}\n");
273 }
274 
275 static int
276 heap_i(void *vstart, void *vend, size_t stride, void *data)
277 {
278  VALUE v = (VALUE)vstart;
279  for (; v != (VALUE)vend; v += stride) {
280  if (RBASIC(v)->flags)
281  dump_object(v, data);
282  }
283  return 0;
284 }
285 
286 static void
287 root_obj_i(const char *category, VALUE obj, void *data)
288 {
289  struct dump_config *dc = (struct dump_config *)data;
290 
291  if (dc->root_category != NULL && category != dc->root_category)
292  dump_append(dc, "]}\n");
293  if (dc->root_category == NULL || category != dc->root_category)
294  dump_append(dc, "{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%p\"", category, (void *)obj);
295  else
296  dump_append(dc, ", \"%p\"", (void *)obj);
297 
298  dc->root_category = category;
299  dc->roots++;
300 }
301 
302 static VALUE
303 dump_output(struct dump_config *dc, VALUE opts, VALUE output, const char *filename)
304 {
305  VALUE tmp;
306 
307  if (RTEST(opts))
308  output = rb_hash_aref(opts, sym_output);
309 
310  if (output == sym_stdout) {
311  dc->stream = stdout;
312  dc->string = Qnil;
313  }
314  else if (output == sym_file) {
315  rb_io_t *fptr;
316  rb_require("tempfile");
317  tmp = rb_assoc_new(rb_str_new_cstr(filename), rb_str_new_cstr(".json"));
318  tmp = rb_funcallv(rb_path2class("Tempfile"), rb_intern("create"), 1, &tmp);
319  io:
320  dc->string = rb_io_get_write_io(tmp);
321  rb_io_flush(dc->string);
322  GetOpenFile(dc->string, fptr);
323  dc->stream = rb_io_stdio_file(fptr);
324  }
325  else if (output == sym_string) {
326  dc->string = rb_str_new_cstr("");
327  }
328  else if (!NIL_P(tmp = rb_io_check_io(output))) {
329  output = sym_file;
330  goto io;
331  }
332  else {
333  rb_raise(rb_eArgError, "wrong output option: %"PRIsVALUE, output);
334  }
335  return output;
336 }
337 
338 static VALUE
340 {
341  if (output == sym_string) {
342  return dc->string;
343  }
344  else if (output == sym_file) {
345  rb_io_flush(dc->string);
346  return dc->string;
347  }
348  else {
349  return Qnil;
350  }
351 }
352 
353 /*
354  * call-seq:
355  * ObjectSpace.dump(obj[, output: :string]) # => "{ ... }"
356  * ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
357  * ObjectSpace.dump(obj, output: :stdout) # => nil
358  *
359  * Dump the contents of a ruby object as JSON.
360  *
361  * This method is only expected to work with C Ruby.
362  * This is an experimental method and is subject to change.
363  * In particular, the function signature and output format are
364  * not guaranteed to be compatible in future versions of ruby.
365  */
366 
367 static VALUE
369 {
370  static const char filename[] = "rubyobj";
371  VALUE obj = Qnil, opts = Qnil, output;
372  struct dump_config dc = {0,};
373 
374  rb_scan_args(argc, argv, "1:", &obj, &opts);
375 
376  output = dump_output(&dc, opts, sym_string, filename);
377 
378  dump_object(obj, &dc);
379 
380  return dump_result(&dc, output);
381 }
382 
383 /*
384  * call-seq:
385  * ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
386  * ObjectSpace.dump_all(output: :stdout) # => nil
387  * ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..."
388  * ObjectSpace.dump_all(output:
389  * File.open('heap.json','w')) # => #<File:heap.json>
390  *
391  * Dump the contents of the ruby heap as JSON.
392  *
393  * This method is only expected to work with C Ruby.
394  * This is an experimental method and is subject to change.
395  * In particular, the function signature and output format are
396  * not guaranteed to be compatible in future versions of ruby.
397  */
398 
399 static VALUE
401 {
402  static const char filename[] = "rubyheap";
403  VALUE opts = Qnil, output;
404  struct dump_config dc = {0,};
405 
406  rb_scan_args(argc, argv, "0:", &opts);
407 
408  output = dump_output(&dc, opts, sym_file, filename);
409 
410  /* dump roots */
412  if (dc.roots) dump_append(&dc, "]}\n");
413 
414  /* dump all objects */
416 
417  return dump_result(&dc, output);
418 }
419 
420 void
422 {
423 #if 0
424  rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
425 #endif
426 
427  rb_define_module_function(rb_mObjSpace, "dump", objspace_dump, -1);
428  rb_define_module_function(rb_mObjSpace, "dump_all", objspace_dump_all, -1);
429 
430  sym_output = ID2SYM(rb_intern("output"));
431  sym_stdout = ID2SYM(rb_intern("stdout"));
432  sym_string = ID2SYM(rb_intern("string"));
433  sym_file = ID2SYM(rb_intern("file"));
434 
435  /* force create static IDs */
436  rb_obj_gc_flags(rb_mObjSpace, 0, 0);
437 }
VALUE data
Definition: tcltklib.c:3360
static void root_obj_i(const char *category, VALUE obj, void *data)
#define nd_type(n)
FILE * stream
Definition: objspace_dump.c:29
static VALUE sym_stdout
Definition: objspace_dump.c:25
static VALUE objspace_dump(int argc, VALUE *argv, VALUE os)
#define FALSE
Definition: nkf.h:174
memo u1 value
Definition: enum.c:587
#define STR_SHARED_P(s)
#define FL_TEST(x, f)
static void dump_object(VALUE obj, struct dump_config *dc)
static void dump_append(struct dump_config *dc, const char *format,...)
Definition: objspace_dump.c:39
Definition: io.h:61
#define T_NODE
#define rb_enc_name(enc)
#define STR_EMBED_P(str)
#define RFLOAT_VALUE(v)
VALUE rb_str_new_cstr(const char *)
Definition: string.c:560
#define STR_NOCAPA_P(s)
VALUE rb_require(const char *)
Definition: load.c:1036
#define RSTRING_PTR(str)
#define UNDEF
NIL_P(eventloop_thread)
Definition: tcltklib.c:4056
#define RFILE(obj)
#define T_ARRAY
void rb_objspace_reachable_objects_from_root(void(func)(const char *category, VALUE, void *), void *passing_data)
Definition: gc.c:5810
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:1857
#define T_HASH
#define T_FILE
const char * root_category
Definition: objspace_dump.c:32
void rb_objspace_each_objects(each_obj_callback *callback, void *data)
Definition: gc.c:1761
#define RB_OBJ_GC_FLAGS_MAX
tmp
Definition: enum.c:447
size_t rb_obj_memsize_of(VALUE)
Definition: gc.c:2551
#define ENCODING_IS_ASCII8BIT(obj)
VALUE rb_path2class(const char *)
Definition: variable.c:379
#define ID2SYM(x)
static VALUE sym_string
Definition: objspace_dump.c:25
#define T_FLOAT
#define T_OBJECT
#define RHASH_SIZE(h)
#define GetOpenFile(obj, fp)
Definition: io.h:118
#define RSTRING_FSTR
static VALUE sym_output
Definition: objspace_dump.c:25
i
Definition: enum.c:446
#define ENCODING_GET(obj)
Definition: ripper.y:240
static unsigned char * output
Definition: nkf.c:32
size_t generation
Definition: objspace.h:16
VALUE cur_obj_klass
Definition: objspace_dump.c:34
static void reachable_object_i(VALUE ref, void *data)
VALUE rb_io_get_write_io(VALUE io)
Definition: io.c:639
static void dump_append_string_value(struct dump_config *dc, VALUE obj)
Definition: objspace_dump.c:55
#define RARRAY_LEN(a)
size_t rb_str_capacity(VALUE)
Definition: string.c:468
#define ROBJECT_NUMIV(o)
#define Qnil
Definition: enum.c:67
VALUE rb_funcallv(VALUE, ID, int, const VALUE *)
Calls a method.
Definition: vm_eval.c:812
VALUE rb_io_check_io(VALUE io)
Definition: io.c:633
int flags
Definition: tcltklib.c:3015
#define PRIuSIZE
unsigned long ID
Definition: ripper.y:89
va_end(args)
static VALUE VALUE obj
Definition: tcltklib.c:3150
#define RSTRING_LEN(str)
int fd
Definition: io.h:62
unsigned long line
Definition: objspace.h:13
#define MATCH(s, p, c)
Definition: date_parse.c:267
#define RBASIC_CLASS(obj)
#define T_STRING
static VALUE dump_output(struct dump_config *dc, VALUE opts, VALUE output, const char *filename)
VALUE * argv
Definition: tcltklib.c:1969
#define STR_ASSOC_P(s)
static VALUE objspace_dump_all(int argc, VALUE *argv, VALUE os)
#define RTEST(v)
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1661
#define TRUE
Definition: nkf.h:175
VALUE v
Definition: enum.c:845
#define RTYPEDDATA_P(v)
#define HASH_PROC_DEFAULT
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1719
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:620
#define STRING(string, length)
Definition: yaml_private.h:128
int argc
Definition: tcltklib.c:1968
#define RHASH_IFNONE(h)
#define ELTS_SHARED
VALUE rb_obj_frozen_p(VALUE)
Definition: object.c:1096
size_t rb_obj_gc_flags(VALUE, ID[], size_t)
static int heap_i(void *vstart, void *vend, size_t stride, void *data)
static VALUE dump_result(struct dump_config *dc, VALUE output)
VpDivd * c
Definition: bigdecimal.c:1223
static const char * obj_type(VALUE obj)
Definition: objspace_dump.c:96
const char * rb_class2name(VALUE)
Definition: variable.c:397
struct allocation_info * objspace_lookup_allocation_info(VALUE obj)
#define T_CLASS
void rb_objspace_reachable_objects_from(VALUE obj, void(func)(VALUE, void *), void *data)
Definition: gc.c:5782
static VALUE sym_file
Definition: objspace_dump.c:25
const char * ruby_node_name(int node)
Definition: iseq.c:1604
VALUE rb_io_flush(VALUE)
Definition: io.c:1510
#define RBASIC(obj)
VALUE rb_str_vcatf(VALUE, const char *, va_list)
Definition: sprintf.c:1263
#define RARRAY_EMBED_FLAG
gz io
Definition: zlib.c:2263
FILE * rb_io_stdio_file(rb_io_t *fptr)
Definition: io.c:7275
data n
Definition: enum.c:860
#define T_MODULE
const char * rb_id2name(ID id)
Definition: ripper.c:17271
#define BUILTIN_TYPE(x)
#define PRIsVALUE
VALUE rb_hash_aref(VALUE, VALUE)
Definition: hash.c:706
VALUE opts
Definition: tcltklib.c:6160
#define CASE_TYPE(type)
unsigned long VALUE
Definition: ripper.y:88
#define SPECIAL_CONST_P(x)
VALUE rb_define_module(const char *name)
Definition: class.c:727
#define rb_intern(str)
size_t cur_obj_references
Definition: objspace_dump.c:35
#define NULL
Definition: _sdbm.c:102
#define T_DATA
const char * path
Definition: objspace.h:12
void Init_objspace_dump(VALUE rb_mObjSpace)
#define SYM2ID(x)
#define is_broken_string(str)
VALUE rb_eArgError
Definition: error.c:549
#define RTYPEDDATA_TYPE(v)
#define is_ascii_string(str)
#define T_ZOMBIE
rb_encoding * rb_enc_from_index(int index)
Definition: encoding.c:590