Ruby
2.1.3p242(2014-09-19revision47630)
Main Page
Modules
Data Structures
Files
File List
Globals
ext
objspace
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
25
static
VALUE
sym_output
,
sym_stdout
,
sym_string
,
sym_file
;
26
27
struct
dump_config
{
28
VALUE
type
;
29
FILE
*
stream
;
30
VALUE
string
;
31
int
roots
;
32
const
char
*
root_category
;
33
VALUE
cur_obj
;
34
VALUE
cur_obj_klass
;
35
size_t
cur_obj_references
;
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
55
dump_append_string_value
(
struct
dump_config
*dc,
VALUE
obj)
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
*
96
obj_type
(
VALUE
obj)
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
131
reachable_object_i
(
VALUE
ref,
void
*data)
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
147
dump_object
(
VALUE
obj,
struct
dump_config
*dc)
148
{
149
size_t
memsize;
150
struct
allocation_info
*ainfo;
151
rb_io_t
*fptr;
152
ID
flags
[
RB_OBJ_GC_FLAGS_MAX
];
153
size_t
n, i;
154
155
dc->
cur_obj
= obj;
156
dc->
cur_obj_references
= 0;
157
dc->
cur_obj_klass
=
BUILTIN_TYPE
(obj) ==
T_NODE
? 0 :
RBASIC_CLASS
(obj);
158
159
if
(dc->
cur_obj
== dc->
string
)
160
return
;
161
162
dump_append
(dc,
"{\"address\":\"%p\", \"type\":\"%s\""
, (
void
*)obj,
obj_type
(obj));
163
164
if
(dc->
cur_obj_klass
)
165
dump_append
(dc,
", \"class\":\"%p\""
, (
void
*)dc->
cur_obj_klass
);
166
if
(
rb_obj_frozen_p
(obj))
167
dump_append
(dc,
", \"frozen\":true"
);
168
169
switch
(
BUILTIN_TYPE
(obj)) {
170
case
T_NODE
:
171
dump_append
(dc,
", \"node_type\":\"%s\""
,
ruby_node_name
(
nd_type
(obj)));
172
break
;
173
174
case
T_STRING
:
175
if
(
STR_EMBED_P
(obj))
176
dump_append
(dc,
", \"embedded\":true"
);
177
if
(
STR_ASSOC_P
(obj))
178
dump_append
(dc,
", \"associated\":true"
);
179
if
(
is_broken_string
(obj))
180
dump_append
(dc,
", \"broken\":true"
);
181
if
(
FL_TEST
(obj,
RSTRING_FSTR
))
182
dump_append
(dc,
", \"fstring\":true"
);
183
if
(
STR_SHARED_P
(obj))
184
dump_append
(dc,
", \"shared\":true"
);
185
else
{
186
dump_append
(dc,
", \"bytesize\":%ld"
,
RSTRING_LEN
(obj));
187
if
(!
STR_EMBED_P
(obj) && !
STR_NOCAPA_P
(obj) && (
long
)
rb_str_capacity
(obj) !=
RSTRING_LEN
(obj))
188
dump_append
(dc,
", \"capacity\":%ld"
,
rb_str_capacity
(obj));
189
190
if
(
is_ascii_string
(obj)) {
191
dump_append
(dc,
", \"value\":"
);
192
dump_append_string_value
(dc, obj);
193
}
194
}
195
196
if
(!
ENCODING_IS_ASCII8BIT
(obj))
197
dump_append
(dc,
", \"encoding\":\"%s\""
,
rb_enc_name
(
rb_enc_from_index
(
ENCODING_GET
(obj))));
198
break
;
199
200
case
T_HASH
:
201
dump_append
(dc,
", \"size\":%ld"
,
RHASH_SIZE
(obj));
202
if
(
FL_TEST
(obj,
HASH_PROC_DEFAULT
))
203
dump_append
(dc,
", \"default\":\"%p\""
, (
void
*)
RHASH_IFNONE
(obj));
204
break
;
205
206
case
T_ARRAY
:
207
dump_append
(dc,
", \"length\":%ld"
,
RARRAY_LEN
(obj));
208
if
(
RARRAY_LEN
(obj) > 0 &&
FL_TEST
(obj,
ELTS_SHARED
))
209
dump_append
(dc,
", \"shared\":true"
);
210
if
(
RARRAY_LEN
(obj) > 0 &&
FL_TEST
(obj,
RARRAY_EMBED_FLAG
))
211
dump_append
(dc,
", \"embedded\":true"
);
212
break
;
213
214
case
T_CLASS
:
215
case
T_MODULE
:
216
if
(dc->
cur_obj_klass
)
217
dump_append
(dc,
", \"name\":\"%s\""
,
rb_class2name
(obj));
218
break
;
219
220
case
T_DATA
:
221
if
(
RTYPEDDATA_P
(obj))
222
dump_append
(dc,
", \"struct\":\"%s\""
,
RTYPEDDATA_TYPE
(obj)->wrap_struct_name);
223
break
;
224
225
case
T_FLOAT
:
226
dump_append
(dc,
", \"value\":\"%g\""
,
RFLOAT_VALUE
(obj));
227
break
;
228
229
case
T_OBJECT
:
230
dump_append
(dc,
", \"ivars\":%ld"
,
ROBJECT_NUMIV
(obj));
231
break
;
232
233
case
T_FILE
:
234
fptr =
RFILE
(obj)->fptr;
235
if
(fptr)
236
dump_append
(dc,
", \"fd\":%d"
, fptr->
fd
);
237
break
;
238
239
case
T_ZOMBIE
:
240
dump_append
(dc,
"}\n"
);
241
return
;
242
}
243
244
rb_objspace_reachable_objects_from
(obj,
reachable_object_i
, dc);
245
if
(dc->
cur_obj_references
> 0)
246
dump_append
(dc,
"]"
);
247
248
if
((ainfo =
objspace_lookup_allocation_info
(obj))) {
249
dump_append
(dc,
", \"file\":\"%s\", \"line\":%lu"
, ainfo->
path
, ainfo->
line
);
250
if
(
RTEST
(ainfo->
mid
))
251
dump_append
(dc,
", \"method\":\"%s\""
,
rb_id2name
(
SYM2ID
(ainfo->
mid
)));
252
dump_append
(dc,
", \"generation\":%"
PRIuSIZE
, ainfo->
generation
);
253
}
254
255
if
((memsize =
rb_obj_memsize_of
(obj)) > 0)
256
dump_append
(dc,
", \"memsize\":%"
PRIuSIZE
, memsize);
257
258
if
((n =
rb_obj_gc_flags
(obj, flags,
sizeof
(flags))) > 0) {
259
dump_append
(dc,
", \"flags\":{"
);
260
for
(i=0; i<n; i++) {
261
dump_append
(dc,
"\"%s\":true"
,
rb_id2name
(flags[i]));
262
if
(i != n-1)
dump_append
(dc,
", "
);
263
}
264
dump_append
(dc,
"}"
);
265
}
266
267
dump_append
(dc,
"}\n"
);
268
}
269
270
static
int
271
heap_i
(
void
*vstart,
void
*vend,
size_t
stride,
void
*data)
272
{
273
VALUE
v = (
VALUE
)vstart;
274
for
(; v != (
VALUE
)vend; v += stride) {
275
if
(
RBASIC
(v)->flags)
276
dump_object
(v, data);
277
}
278
return
0;
279
}
280
281
static
void
282
root_obj_i
(
const
char
*category,
VALUE
obj,
void
*data)
283
{
284
struct
dump_config
*dc = (
struct
dump_config
*)data;
285
286
if
(dc->
root_category
!=
NULL
&& category != dc->
root_category
)
287
dump_append
(dc,
"]}\n"
);
288
if
(dc->
root_category
==
NULL
|| category != dc->
root_category
)
289
dump_append
(dc,
"{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%p\""
, category, (
void
*)obj);
290
else
291
dump_append
(dc,
", \"%p\""
, (
void
*)obj);
292
293
dc->
root_category
= category;
294
dc->
roots
++;
295
}
296
297
static
VALUE
298
dump_output
(
struct
dump_config
*dc,
VALUE
opts,
VALUE
output
,
const
char
*filename)
299
{
300
VALUE
tmp;
301
302
if
(
RTEST
(opts))
303
output =
rb_hash_aref
(opts,
sym_output
);
304
305
if
(output ==
sym_stdout
) {
306
dc->
stream
= stdout;
307
dc->
string
=
Qnil
;
308
}
309
else
if
(output ==
sym_file
) {
310
rb_io_t
*fptr;
311
rb_require
(
"tempfile"
);
312
tmp =
rb_assoc_new
(
rb_str_new_cstr
(filename),
rb_str_new_cstr
(
".json"
));
313
tmp =
rb_funcallv
(
rb_path2class
(
"Tempfile"
),
rb_intern
(
"create"
), 1, &tmp);
314
io:
315
dc->
string
=
rb_io_get_write_io
(tmp);
316
rb_io_flush
(dc->
string
);
317
GetOpenFile
(dc->
string
, fptr);
318
dc->
stream
=
rb_io_stdio_file
(fptr);
319
}
320
else
if
(output ==
sym_string
) {
321
dc->
string
=
rb_str_new_cstr
(
""
);
322
}
323
else
if
(!
NIL_P
(tmp =
rb_io_check_io
(output))) {
324
output =
sym_file
;
325
goto
io;
326
}
327
else
{
328
rb_raise
(
rb_eArgError
,
"wrong output option: %"
PRIsVALUE
, output);
329
}
330
return
output
;
331
}
332
333
static
VALUE
334
dump_result
(
struct
dump_config
*dc,
VALUE
output
)
335
{
336
if
(output ==
sym_string
) {
337
return
dc->
string
;
338
}
339
else
if
(output ==
sym_file
) {
340
rb_io_flush
(dc->
string
);
341
return
dc->
string
;
342
}
343
else
{
344
return
Qnil
;
345
}
346
}
347
348
/*
349
* call-seq:
350
* ObjectSpace.dump(obj[, output: :string]) # => "{ ... }"
351
* ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
352
* ObjectSpace.dump(obj, output: :stdout) # => nil
353
*
354
* Dump the contents of a ruby object as JSON.
355
*
356
* This method is only expected to work with C Ruby.
357
* This is an experimental method and is subject to change.
358
* In particular, the function signature and output format are
359
* not guaranteed to be compatible in future versions of ruby.
360
*/
361
362
static
VALUE
363
objspace_dump
(
int
argc
,
VALUE
*
argv
,
VALUE
os)
364
{
365
static
const
char
filename[] =
"rubyobj"
;
366
VALUE
obj =
Qnil
, opts =
Qnil
,
output
;
367
struct
dump_config
dc = {0,};
368
369
rb_scan_args
(argc, argv,
"1:"
, &obj, &opts);
370
371
output =
dump_output
(&dc, opts,
sym_string
, filename);
372
373
dump_object
(obj, &dc);
374
375
return
dump_result
(&dc, output);
376
}
377
378
/*
379
* call-seq:
380
* ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
381
* ObjectSpace.dump_all(output: :stdout) # => nil
382
* ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..."
383
* ObjectSpace.dump_all(output:
384
* File.open('heap.json','w')) # => #<File:heap.json>
385
*
386
* Dump the contents of the ruby heap as JSON.
387
*
388
* This method is only expected to work with C Ruby.
389
* This is an experimental method and is subject to change.
390
* In particular, the function signature and output format are
391
* not guaranteed to be compatible in future versions of ruby.
392
*/
393
394
static
VALUE
395
objspace_dump_all
(
int
argc
,
VALUE
*
argv
,
VALUE
os)
396
{
397
static
const
char
filename[] =
"rubyheap"
;
398
VALUE
opts =
Qnil
,
output
;
399
struct
dump_config
dc = {0,};
400
401
rb_scan_args
(argc, argv,
"0:"
, &opts);
402
403
output =
dump_output
(&dc, opts,
sym_file
, filename);
404
405
/* dump roots */
406
rb_objspace_reachable_objects_from_root
(
root_obj_i
, &dc);
407
if
(dc.
roots
)
dump_append
(&dc,
"]}\n"
);
408
409
/* dump all objects */
410
rb_objspace_each_objects
(
heap_i
, &dc);
411
412
return
dump_result
(&dc, output);
413
}
414
415
void
416
Init_objspace_dump
(
VALUE
rb_mObjSpace)
417
{
418
#if 0
419
rb_mObjSpace =
rb_define_module
(
"ObjectSpace"
);
/* let rdoc know */
420
#endif
421
422
rb_define_module_function
(rb_mObjSpace,
"dump"
,
objspace_dump
, -1);
423
rb_define_module_function
(rb_mObjSpace,
"dump_all"
,
objspace_dump_all
, -1);
424
425
sym_output
=
ID2SYM
(
rb_intern
(
"output"
));
426
sym_stdout
=
ID2SYM
(
rb_intern
(
"stdout"
));
427
sym_string
=
ID2SYM
(
rb_intern
(
"string"
));
428
sym_file
=
ID2SYM
(
rb_intern
(
"file"
));
429
430
/* force create static IDs */
431
rb_obj_gc_flags
(rb_mObjSpace, 0, 0);
432
}
433
Generated on Mon Sep 22 2014 11:05:24 for Ruby by
1.8.2