Ruby
2.1.3p242(2014-09-19revision47630)
Main Page
Modules
Data Structures
Files
File List
Globals
ext
objspace
object_tracing.c
Go to the documentation of this file.
1
/**********************************************************************
2
3
object_tracing.c - Object Tracing mechanism/ObjectSpace extender for MRI.
4
5
$Author$
6
created at: Mon May 27 16:27:44 2013
7
8
NOTE: This extension library is not expected to exist except C Ruby.
9
NOTE: This feature is an example usage of internal event tracing APIs.
10
11
All the files in this distribution are covered under the Ruby's
12
license (see the file COPYING).
13
14
**********************************************************************/
15
16
#include "
ruby/ruby.h
"
17
#include "
ruby/debug.h
"
18
#include "
objspace.h
"
19
#include "
internal.h
"
20
21
struct
traceobj_arg
{
22
int
running
;
23
int
keep_remains
;
24
VALUE
newobj_trace
;
25
VALUE
freeobj_trace
;
26
st_table
*
object_table
;
/* obj (VALUE) -> allocation_info */
27
st_table
*
str_table
;
/* cstr -> refcount */
28
struct
traceobj_arg
*
prev_traceobj_arg
;
29
};
30
31
static
const
char
*
32
make_unique_str
(
st_table
*tbl,
const
char
*str,
long
len)
33
{
34
if
(!str) {
35
return
NULL
;
36
}
37
else
{
38
st_data_t
n;
39
char
*
result
;
40
41
if
(
st_lookup
(tbl, (
st_data_t
)str, &n)) {
42
st_insert
(tbl, (
st_data_t
)str, n+1);
43
st_get_key
(tbl, (
st_data_t
)str, (
st_data_t
*)&result);
44
}
45
else
{
46
result = (
char
*)
ruby_xmalloc
(len+1);
47
strncpy(result, str, len);
48
result[len] = 0;
49
st_add_direct
(tbl, (
st_data_t
)result, 1);
50
}
51
return
result
;
52
}
53
}
54
55
static
void
56
delete_unique_str
(
st_table
*tbl,
const
char
*str)
57
{
58
if
(str) {
59
st_data_t
n;
60
61
st_lookup
(tbl, (
st_data_t
)str, &n);
62
if
(n == 1) {
63
st_delete
(tbl, (
st_data_t
*)&str, 0);
64
ruby_xfree
((
char
*)str);
65
}
66
else
{
67
st_insert
(tbl, (
st_data_t
)str, n-1);
68
}
69
}
70
}
71
72
static
void
73
newobj_i
(
VALUE
tpval,
void
*data)
74
{
75
struct
traceobj_arg
*
arg
= (
struct
traceobj_arg
*)data;
76
rb_trace_arg_t
*tparg =
rb_tracearg_from_tracepoint
(tpval);
77
VALUE
obj =
rb_tracearg_object
(tparg);
78
VALUE
path =
rb_tracearg_path
(tparg);
79
VALUE
line =
rb_tracearg_lineno
(tparg);
80
VALUE
mid =
rb_tracearg_method_id
(tparg);
81
VALUE
klass =
rb_tracearg_defined_class
(tparg);
82
struct
allocation_info
*info;
83
const
char
*path_cstr =
RTEST
(path) ?
make_unique_str
(arg->
str_table
,
RSTRING_PTR
(path),
RSTRING_LEN
(path)) : 0;
84
VALUE
class_path
= (
RTEST
(klass) && !
OBJ_FROZEN
(klass)) ?
rb_class_path_cached
(klass) :
Qnil
;
85
const
char
*class_path_cstr =
RTEST
(class_path) ?
make_unique_str
(arg->
str_table
,
RSTRING_PTR
(class_path),
RSTRING_LEN
(class_path)) : 0;
86
87
if
(
st_lookup
(arg->
object_table
, (
st_data_t
)obj, (
st_data_t
*)&info)) {
88
if
(arg->
keep_remains
) {
89
if
(info->
living
) {
90
/* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */
91
}
92
}
93
/* reuse info */
94
delete_unique_str
(arg->
str_table
, info->
path
);
95
delete_unique_str
(arg->
str_table
, info->
class_path
);
96
}
97
else
{
98
info = (
struct
allocation_info
*)
ruby_xmalloc
(
sizeof
(
struct
allocation_info
));
99
}
100
info->
living
= 1;
101
info->
flags
=
RBASIC
(obj)->flags;
102
info->
klass
=
RBASIC_CLASS
(obj);
103
104
info->
path
= path_cstr;
105
info->
line
=
NUM2INT
(line);
106
info->
mid
=
mid
;
107
info->
class_path
= class_path_cstr;
108
info->
generation
=
rb_gc_count
();
109
st_insert
(arg->
object_table
, (
st_data_t
)obj, (
st_data_t
)info);
110
}
111
112
static
void
113
freeobj_i
(
VALUE
tpval,
void
*data)
114
{
115
struct
traceobj_arg
*
arg
= (
struct
traceobj_arg
*)data;
116
rb_trace_arg_t
*tparg =
rb_tracearg_from_tracepoint
(tpval);
117
VALUE
obj =
rb_tracearg_object
(tparg);
118
struct
allocation_info
*info;
119
120
if
(
st_lookup
(arg->
object_table
, (
st_data_t
)obj, (
st_data_t
*)&info)) {
121
if
(arg->
keep_remains
) {
122
info->
living
= 0;
123
}
124
else
{
125
st_delete
(arg->
object_table
, (
st_data_t
*)&obj, (
st_data_t
*)&info);
126
delete_unique_str
(arg->
str_table
, info->
path
);
127
delete_unique_str
(arg->
str_table
, info->
class_path
);
128
ruby_xfree
(info);
129
}
130
}
131
}
132
133
static
int
134
free_keys_i
(
st_data_t
key
,
st_data_t
value,
void
*data)
135
{
136
ruby_xfree
((
void
*)key);
137
return
ST_CONTINUE
;
138
}
139
140
static
int
141
free_values_i
(
st_data_t
key
,
st_data_t
value,
void
*data)
142
{
143
ruby_xfree
((
void
*)value);
144
return
ST_CONTINUE
;
145
}
146
147
static
struct
traceobj_arg
*
tmp_trace_arg
;
/* TODO: Do not use global variables */
148
static
int
tmp_keep_remains
;
/* TODO: Do not use global variables */
149
150
static
struct
traceobj_arg
*
151
get_traceobj_arg
(
void
)
152
{
153
if
(tmp_trace_arg == 0) {
154
tmp_trace_arg =
ALLOC_N
(
struct
traceobj_arg
, 1);
155
tmp_trace_arg->
running
= 0;
156
tmp_trace_arg->
keep_remains
=
tmp_keep_remains
;
157
tmp_trace_arg->
newobj_trace
= 0;
158
tmp_trace_arg->
freeobj_trace
= 0;
159
tmp_trace_arg->
object_table
=
st_init_numtable
();
160
tmp_trace_arg->
str_table
=
st_init_strtable
();
161
}
162
return
tmp_trace_arg
;
163
}
164
165
/*
166
* call-seq: trace_object_allocations_start
167
*
168
* Starts tracing object allocations.
169
*
170
*/
171
static
VALUE
172
trace_object_allocations_start
(
VALUE
self
)
173
{
174
struct
traceobj_arg
*
arg
=
get_traceobj_arg
();
175
176
if
(arg->
running
++ > 0) {
177
/* do nothing */
178
}
179
else
{
180
if
(arg->
newobj_trace
== 0) {
181
arg->
newobj_trace
=
rb_tracepoint_new
(0,
RUBY_INTERNAL_EVENT_NEWOBJ
,
newobj_i
, arg);
182
arg->
freeobj_trace
=
rb_tracepoint_new
(0,
RUBY_INTERNAL_EVENT_FREEOBJ
,
freeobj_i
, arg);
183
}
184
rb_tracepoint_enable
(arg->
newobj_trace
);
185
rb_tracepoint_enable
(arg->
freeobj_trace
);
186
}
187
188
return
Qnil
;
189
}
190
191
/*
192
* call-seq: trace_object_allocations_stop
193
*
194
* Stop tracing object allocations.
195
*
196
* Note that if ::trace_object_allocations_start is called n-times, then
197
* tracing will stop after calling ::trace_object_allocations_stop n-times.
198
*
199
*/
200
static
VALUE
201
trace_object_allocations_stop
(
VALUE
self
)
202
{
203
struct
traceobj_arg
*
arg
=
get_traceobj_arg
();
204
205
if
(arg->
running
> 0) {
206
arg->
running
--;
207
}
208
209
if
(arg->
running
== 0) {
210
rb_tracepoint_disable
(arg->
newobj_trace
);
211
rb_tracepoint_disable
(arg->
freeobj_trace
);
212
arg->
newobj_trace
= 0;
213
arg->
freeobj_trace
= 0;
214
}
215
216
return
Qnil
;
217
}
218
219
/*
220
* call-seq: trace_object_allocations_clear
221
*
222
* Clear recorded tracing information.
223
*
224
*/
225
static
VALUE
226
trace_object_allocations_clear
(
VALUE
self
)
227
{
228
struct
traceobj_arg
*
arg
=
get_traceobj_arg
();
229
230
/* clear tables */
231
st_foreach
(arg->
object_table
,
free_values_i
, 0);
232
st_clear
(arg->
object_table
);
233
st_foreach
(arg->
str_table
,
free_keys_i
, 0);
234
st_clear
(arg->
str_table
);
235
236
/* do not touch TracePoints */
237
238
return
Qnil
;
239
}
240
241
/*
242
* call-seq: trace_object_allocations { block }
243
*
244
* Starts tracing object allocations from the ObjectSpace extension module.
245
*
246
* For example:
247
*
248
* require 'objspace'
249
*
250
* class C
251
* include ObjectSpace
252
*
253
* def foo
254
* trace_object_allocations do
255
* obj = Object.new
256
* p "#{allocation_sourcefile(obj)}:#{allocation_sourceline(obj)}"
257
* end
258
* end
259
* end
260
*
261
* C.new.foo #=> "objtrace.rb:8"
262
*
263
* This example has included the ObjectSpace module to make it easier to read,
264
* but you can also use the ::trace_object_allocations notation (recommended).
265
*
266
* Note that this feature introduces a huge performance decrease and huge
267
* memory consumption.
268
*/
269
static
VALUE
270
trace_object_allocations
(
VALUE
self
)
271
{
272
trace_object_allocations_start
(
self
);
273
return
rb_ensure
(
rb_yield
,
Qnil
,
trace_object_allocations_stop
,
self
);
274
}
275
276
int
rb_bug_reporter_add
(
void
(*
func
)(
FILE
*,
void
*),
void
*data);
277
static
int
object_allocations_reporter_registered
= 0;
278
279
static
int
280
object_allocations_reporter_i
(
st_data_t
key
,
st_data_t
val,
st_data_t
ptr)
281
{
282
FILE
*out = (
FILE
*)ptr;
283
VALUE
obj = (
VALUE
)key;
284
struct
allocation_info
*info = (
struct
allocation_info
*)val;
285
286
fprintf(out,
"-- %p (%s F: %p, "
, (
void
*)obj, info->
living
?
"live"
:
"dead"
, (
void
*)info->
flags
);
287
if
(info->
class_path
) fprintf(out,
"C: %s"
, info->
class_path
);
288
else
fprintf(out,
"C: %p"
, (
void
*)info->
klass
);
289
fprintf(out,
"@%s:%lu"
, info->
path
? info->
path
:
""
, info->
line
);
290
if
(!
NIL_P
(info->
mid
)) fprintf(out,
" (%s)"
,
rb_id2name
(
SYM2ID
(info->
mid
)));
291
fprintf(out,
")\n"
);
292
293
return
ST_CONTINUE
;
294
}
295
296
static
void
297
object_allocations_reporter
(
FILE
*out,
void
*ptr)
298
{
299
fprintf(out,
"== object_allocations_reporter: START\n"
);
300
if
(tmp_trace_arg) {
301
st_foreach
(tmp_trace_arg->
object_table
,
object_allocations_reporter_i
, (
st_data_t
)out);
302
}
303
fprintf(out,
"== object_allocations_reporter: END\n"
);
304
}
305
306
static
VALUE
307
trace_object_allocations_debug_start
(
VALUE
self
)
308
{
309
tmp_keep_remains
= 1;
310
if
(object_allocations_reporter_registered == 0) {
311
object_allocations_reporter_registered = 1;
312
rb_bug_reporter_add
(
object_allocations_reporter
, 0);
313
}
314
315
return
trace_object_allocations_start
(
self
);
316
}
317
318
static
struct
allocation_info
*
319
lookup_allocation_info
(
VALUE
obj)
320
{
321
if
(tmp_trace_arg) {
322
struct
allocation_info
*info;
323
if
(
st_lookup
(tmp_trace_arg->
object_table
, obj, (
st_data_t
*)&info)) {
324
return
info;
325
}
326
}
327
return
NULL
;
328
}
329
330
struct
allocation_info
*
331
objspace_lookup_allocation_info
(
VALUE
obj)
332
{
333
return
lookup_allocation_info
(obj);
334
}
335
336
/*
337
* call-seq: allocation_sourcefile(object) -> string
338
*
339
* Returns the source file origin from the given +object+.
340
*
341
* See ::trace_object_allocations for more information and examples.
342
*/
343
static
VALUE
344
allocation_sourcefile
(
VALUE
self
,
VALUE
obj)
345
{
346
struct
allocation_info
*info =
lookup_allocation_info
(obj);
347
348
if
(info && info->
path
) {
349
return
rb_str_new2
(info->
path
);
350
}
351
else
{
352
return
Qnil
;
353
}
354
}
355
356
/*
357
* call-seq: allocation_sourceline(object) -> string
358
*
359
* Returns the original line from source for from the given +object+.
360
*
361
* See ::trace_object_allocations for more information and examples.
362
*/
363
static
VALUE
364
allocation_sourceline
(
VALUE
self
,
VALUE
obj)
365
{
366
struct
allocation_info
*info =
lookup_allocation_info
(obj);
367
368
if
(info) {
369
return
INT2FIX
(info->
line
);
370
}
371
else
{
372
return
Qnil
;
373
}
374
}
375
376
/*
377
* call-seq: allocation_class_path(object) -> string
378
*
379
* Returns the class for the given +object+.
380
*
381
* class A
382
* def foo
383
* ObjectSpace::trace_object_allocations do
384
* obj = Object.new
385
* p "#{ObjectSpace::allocation_class_path(obj)}"
386
* end
387
* end
388
* end
389
*
390
* A.new.foo #=> "Class"
391
*
392
* See ::trace_object_allocations for more information and examples.
393
*/
394
static
VALUE
395
allocation_class_path
(
VALUE
self
,
VALUE
obj)
396
{
397
struct
allocation_info
*info =
lookup_allocation_info
(obj);
398
399
if
(info && info->
class_path
) {
400
return
rb_str_new2
(info->
class_path
);
401
}
402
else
{
403
return
Qnil
;
404
}
405
}
406
407
/*
408
* call-seq: allocation_method_id(object) -> string
409
*
410
* Returns the method identifier for the given +object+.
411
*
412
* class A
413
* include ObjectSpace
414
*
415
* def foo
416
* trace_object_allocations do
417
* obj = Object.new
418
* p "#{allocation_class_path(obj)}##{allocation_method_id(obj)}"
419
* end
420
* end
421
* end
422
*
423
* A.new.foo #=> "Class#new"
424
*
425
* See ::trace_object_allocations for more information and examples.
426
*/
427
static
VALUE
428
allocation_method_id
(
VALUE
self
,
VALUE
obj)
429
{
430
struct
allocation_info
*info =
lookup_allocation_info
(obj);
431
if
(info) {
432
return
info->
mid
;
433
}
434
else
{
435
return
Qnil
;
436
}
437
}
438
439
/*
440
* call-seq: allocation_generation(object) -> Fixnum
441
*
442
* Returns garbage collector generation for the given +object+.
443
*
444
* class B
445
* include ObjectSpace
446
*
447
* def foo
448
* trace_object_allocations do
449
* obj = Object.new
450
* p "Generation is #{allocation_generation(obj)}"
451
* end
452
* end
453
* end
454
*
455
* B.new.foo #=> "Generation is 3"
456
*
457
* See ::trace_object_allocations for more information and examples.
458
*/
459
static
VALUE
460
allocation_generation
(
VALUE
self
,
VALUE
obj)
461
{
462
struct
allocation_info
*info =
lookup_allocation_info
(obj);
463
if
(info) {
464
return
SIZET2NUM
(info->
generation
);
465
}
466
else
{
467
return
Qnil
;
468
}
469
}
470
471
void
472
Init_object_tracing
(
VALUE
rb_mObjSpace)
473
{
474
#if 0
475
rb_mObjSpace =
rb_define_module
(
"ObjectSpace"
);
/* let rdoc know */
476
#endif
477
478
rb_define_module_function
(rb_mObjSpace,
"trace_object_allocations"
,
trace_object_allocations
, 0);
479
rb_define_module_function
(rb_mObjSpace,
"trace_object_allocations_start"
,
trace_object_allocations_start
, 0);
480
rb_define_module_function
(rb_mObjSpace,
"trace_object_allocations_stop"
,
trace_object_allocations_stop
, 0);
481
rb_define_module_function
(rb_mObjSpace,
"trace_object_allocations_clear"
,
trace_object_allocations_clear
, 0);
482
483
rb_define_module_function
(rb_mObjSpace,
"trace_object_allocations_debug_start"
,
trace_object_allocations_debug_start
, 0);
484
485
rb_define_module_function
(rb_mObjSpace,
"allocation_sourcefile"
,
allocation_sourcefile
, 1);
486
rb_define_module_function
(rb_mObjSpace,
"allocation_sourceline"
,
allocation_sourceline
, 1);
487
rb_define_module_function
(rb_mObjSpace,
"allocation_class_path"
,
allocation_class_path
, 1);
488
rb_define_module_function
(rb_mObjSpace,
"allocation_method_id"
,
allocation_method_id
, 1);
489
rb_define_module_function
(rb_mObjSpace,
"allocation_generation"
,
allocation_generation
, 1);
490
}
491
Generated on Mon Sep 22 2014 11:05:24 for Ruby by
1.8.2