ruby-prof 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +7 -0
- data/ext/ruby_prof/rp_aggregate_call_tree.c +19 -1
- data/ext/ruby_prof/rp_allocation.c +38 -10
- data/ext/ruby_prof/rp_allocation.h +1 -1
- data/ext/ruby_prof/rp_call_tree.c +28 -12
- data/ext/ruby_prof/rp_call_trees.c +33 -11
- data/ext/ruby_prof/rp_call_trees.h +0 -1
- data/ext/ruby_prof/rp_measurement.c +25 -9
- data/ext/ruby_prof/rp_measurement.h +1 -1
- data/ext/ruby_prof/rp_method.c +33 -12
- data/ext/ruby_prof/rp_method.h +23 -24
- data/ext/ruby_prof/rp_profile.c +49 -26
- data/ext/ruby_prof/rp_profile.h +1 -1
- data/ext/ruby_prof/rp_stack.h +2 -2
- data/ext/ruby_prof/rp_thread.c +30 -13
- data/ext/ruby_prof/rp_thread.h +3 -1
- data/lib/ruby-prof/version.rb +1 -1
- data/test/fiber_test.rb +24 -33
- data/test/gc_test.rb +17 -32
- data/test/inverse_call_tree_test.rb +1 -1
- data/test/line_number_test.rb +3 -3
- data/test/measure_wall_time_test.rb +5 -5
- data/test/unique_call_path_test.rb +1 -1
- metadata +3 -3
data/ext/ruby_prof/rp_method.c
CHANGED
@@ -138,7 +138,7 @@ prof_method_t* prof_get_method(VALUE self)
|
|
138
138
|
{
|
139
139
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
140
140
|
ending up in endless recursion. */
|
141
|
-
prof_method_t* result =
|
141
|
+
prof_method_t* result = RTYPEDDATA_DATA(self);
|
142
142
|
|
143
143
|
if (!result)
|
144
144
|
rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
|
@@ -146,9 +146,11 @@ prof_method_t* prof_get_method(VALUE self)
|
|
146
146
|
return result;
|
147
147
|
}
|
148
148
|
|
149
|
-
prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
149
|
+
prof_method_t* prof_method_create(VALUE profile, VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
150
150
|
{
|
151
151
|
prof_method_t* result = ALLOC(prof_method_t);
|
152
|
+
result->profile = profile;
|
153
|
+
|
152
154
|
result->key = method_key(klass, msym);
|
153
155
|
result->klass_flags = 0;
|
154
156
|
|
@@ -181,8 +183,11 @@ prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, in
|
|
181
183
|
out our Ruby object reference.*/
|
182
184
|
static void prof_method_ruby_gc_free(void* data)
|
183
185
|
{
|
184
|
-
|
185
|
-
|
186
|
+
if (data)
|
187
|
+
{
|
188
|
+
prof_method_t* method = (prof_method_t*)data;
|
189
|
+
method->object = Qnil;
|
190
|
+
}
|
186
191
|
}
|
187
192
|
|
188
193
|
static void prof_method_free(prof_method_t* method)
|
@@ -191,9 +196,7 @@ static void prof_method_free(prof_method_t* method)
|
|
191
196
|
yes clean it up so to avoid a segmentation fault. */
|
192
197
|
if (method->object != Qnil)
|
193
198
|
{
|
194
|
-
|
195
|
-
RDATA(method->object)->dfree = NULL;
|
196
|
-
RDATA(method->object)->data = NULL;
|
199
|
+
RTYPEDDATA(method->object)->data = NULL;
|
197
200
|
method->object = Qnil;
|
198
201
|
}
|
199
202
|
|
@@ -210,8 +213,13 @@ size_t prof_method_size(const void* data)
|
|
210
213
|
|
211
214
|
void prof_method_mark(void* data)
|
212
215
|
{
|
216
|
+
if (!data) return;
|
217
|
+
|
213
218
|
prof_method_t* method = (prof_method_t*)data;
|
214
219
|
|
220
|
+
if (method->profile != Qnil)
|
221
|
+
rb_gc_mark(method->profile);
|
222
|
+
|
215
223
|
if (method->object != Qnil)
|
216
224
|
rb_gc_mark(method->object);
|
217
225
|
|
@@ -229,16 +237,29 @@ void prof_method_mark(void* data)
|
|
229
237
|
|
230
238
|
static VALUE prof_method_allocate(VALUE klass)
|
231
239
|
{
|
232
|
-
prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, 0);
|
240
|
+
prof_method_t* method_data = prof_method_create(Qnil, Qnil, Qnil, Qnil, 0);
|
233
241
|
method_data->object = prof_method_wrap(method_data);
|
234
242
|
return method_data->object;
|
235
243
|
}
|
236
244
|
|
245
|
+
static const rb_data_type_t method_info_type =
|
246
|
+
{
|
247
|
+
.wrap_struct_name = "MethodInfo",
|
248
|
+
.function =
|
249
|
+
{
|
250
|
+
.dmark = prof_method_mark,
|
251
|
+
.dfree = prof_method_ruby_gc_free,
|
252
|
+
.dsize = prof_method_size,
|
253
|
+
},
|
254
|
+
.data = NULL,
|
255
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
256
|
+
};
|
257
|
+
|
237
258
|
VALUE prof_method_wrap(prof_method_t* method)
|
238
259
|
{
|
239
260
|
if (method->object == Qnil)
|
240
261
|
{
|
241
|
-
method->object =
|
262
|
+
method->object = TypedData_Wrap_Struct(cRpMethodInfo, &method_info_type, method);
|
242
263
|
}
|
243
264
|
return method->object;
|
244
265
|
}
|
@@ -392,7 +413,7 @@ static VALUE prof_method_call_trees(VALUE self)
|
|
392
413
|
/* :nodoc: */
|
393
414
|
static VALUE prof_method_dump(VALUE self)
|
394
415
|
{
|
395
|
-
prof_method_t* method_data =
|
416
|
+
prof_method_t* method_data = prof_get_method(self);
|
396
417
|
VALUE result = rb_hash_new();
|
397
418
|
|
398
419
|
rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_method_klass_name(self));
|
@@ -414,7 +435,7 @@ static VALUE prof_method_dump(VALUE self)
|
|
414
435
|
/* :nodoc: */
|
415
436
|
static VALUE prof_method_load(VALUE self, VALUE data)
|
416
437
|
{
|
417
|
-
prof_method_t* method_data =
|
438
|
+
prof_method_t* method_data = prof_get_method(self);
|
418
439
|
method_data->object = self;
|
419
440
|
|
420
441
|
method_data->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
|
@@ -447,7 +468,7 @@ static VALUE prof_method_load(VALUE self, VALUE data)
|
|
447
468
|
void rp_init_method_info()
|
448
469
|
{
|
449
470
|
/* MethodInfo */
|
450
|
-
cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo",
|
471
|
+
cRpMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
|
451
472
|
rb_undef_method(CLASS_OF(cRpMethodInfo), "new");
|
452
473
|
rb_define_alloc_func(cRpMethodInfo, prof_method_allocate);
|
453
474
|
|
data/ext/ruby_prof/rp_method.h
CHANGED
@@ -9,38 +9,37 @@
|
|
9
9
|
|
10
10
|
extern VALUE cRpMethodInfo;
|
11
11
|
|
12
|
-
|
12
|
+
// Source relation bit offsets.
|
13
13
|
enum {
|
14
|
-
kModuleIncludee = 0x1,
|
15
|
-
kClassSingleton = 0x2,
|
16
|
-
kModuleSingleton = 0x4,
|
17
|
-
kObjectSingleton = 0x8,
|
18
|
-
kOtherSingleton = 0x10
|
14
|
+
kModuleIncludee = 0x1, // Included in module
|
15
|
+
kClassSingleton = 0x2, // Singleton of a class
|
16
|
+
kModuleSingleton = 0x4, // Singleton of a module
|
17
|
+
kObjectSingleton = 0x8, // Singleton of an object
|
18
|
+
kOtherSingleton = 0x10 // Singleton of unkown object
|
19
19
|
};
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
typedef struct
|
21
|
+
// Profiling information for each method.
|
22
|
+
// Excluded methods have no call_trees, source_klass, or source_file.
|
23
|
+
typedef struct prof_method_t
|
24
24
|
{
|
25
|
-
|
25
|
+
VALUE profile; // Profile this method is associated with - needed for mark phase
|
26
|
+
struct prof_call_trees_t* call_trees; // Call infos that call this method
|
27
|
+
st_table* allocations_table; // Tracks object allocations
|
26
28
|
|
27
|
-
|
29
|
+
st_data_t key; // Table key
|
30
|
+
unsigned int klass_flags; // Information about the type of class
|
31
|
+
VALUE klass; // Resolved klass
|
32
|
+
VALUE klass_name; // Resolved klass name for this method
|
33
|
+
VALUE method_name; // Resolved method name for this method
|
28
34
|
|
29
|
-
|
30
|
-
st_table* allocations_table; /* Tracks object allocations */
|
31
|
-
|
32
|
-
unsigned int klass_flags; /* Information about the type of class */
|
33
|
-
VALUE klass; /* Resolved klass */
|
34
|
-
VALUE klass_name; /* Resolved klass name for this method */
|
35
|
-
VALUE method_name; /* Resolved method name for this method */
|
36
|
-
|
37
|
-
VALUE object; /* Cached ruby object */
|
35
|
+
VALUE object; // Cached ruby object
|
38
36
|
|
39
37
|
bool recursive;
|
40
|
-
|
41
|
-
|
38
|
+
int visits; // Current visits on the stack
|
39
|
+
VALUE source_file; // Source file
|
40
|
+
int source_line; // Line number
|
42
41
|
|
43
|
-
prof_measurement_t* measurement;
|
42
|
+
prof_measurement_t* measurement; // Stores measurement data for this method
|
44
43
|
} prof_method_t;
|
45
44
|
|
46
45
|
void rp_init_method_info(void);
|
@@ -51,7 +50,7 @@ st_table* method_table_create(void);
|
|
51
50
|
prof_method_t* method_table_lookup(st_table* table, st_data_t key);
|
52
51
|
size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val);
|
53
52
|
void method_table_free(st_table* table);
|
54
|
-
prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line);
|
53
|
+
prof_method_t* prof_method_create(VALUE profile, VALUE klass, VALUE msym, VALUE source_file, int source_line);
|
55
54
|
prof_method_t* prof_get_method(VALUE self);
|
56
55
|
|
57
56
|
VALUE prof_method_wrap(prof_method_t* result);
|
data/ext/ruby_prof/rp_profile.c
CHANGED
@@ -109,15 +109,17 @@ static int excludes_method(st_data_t key, prof_profile_t* profile)
|
|
109
109
|
method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
|
110
110
|
}
|
111
111
|
|
112
|
-
static prof_method_t* create_method(
|
112
|
+
static prof_method_t* create_method(VALUE profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
|
113
113
|
{
|
114
|
-
prof_method_t* result = prof_method_create(klass, msym, source_file, source_line);
|
115
|
-
|
114
|
+
prof_method_t* result = prof_method_create(profile, klass, msym, source_file, source_line);
|
115
|
+
|
116
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
117
|
+
method_table_insert(profile_t->last_thread_data->method_table, result->key, result);
|
116
118
|
|
117
119
|
return result;
|
118
120
|
}
|
119
121
|
|
120
|
-
static prof_method_t* check_parent_method(
|
122
|
+
static prof_method_t* check_parent_method(VALUE profile, thread_data_t* thread_data)
|
121
123
|
{
|
122
124
|
VALUE msym = ID2SYM(rb_intern("_inserted_parent_"));
|
123
125
|
st_data_t key = method_key(cProfile, msym);
|
@@ -132,7 +134,7 @@ static prof_method_t* check_parent_method(prof_profile_t* profile, thread_data_t
|
|
132
134
|
return result;
|
133
135
|
}
|
134
136
|
|
135
|
-
prof_method_t* check_method(
|
137
|
+
prof_method_t* check_method(VALUE profile, rb_trace_arg_t* trace_arg, rb_event_flag_t event, thread_data_t* thread_data)
|
136
138
|
{
|
137
139
|
VALUE klass = rb_tracearg_defined_class(trace_arg);
|
138
140
|
|
@@ -150,7 +152,8 @@ prof_method_t* check_method(prof_profile_t* profile, rb_trace_arg_t* trace_arg,
|
|
150
152
|
|
151
153
|
st_data_t key = method_key(klass, msym);
|
152
154
|
|
153
|
-
|
155
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
156
|
+
if (excludes_method(key, profile_t))
|
154
157
|
return NULL;
|
155
158
|
|
156
159
|
prof_method_t* result = method_table_lookup(thread_data->method_table, key);
|
@@ -208,15 +211,17 @@ static void prof_trace(prof_profile_t* profile, rb_trace_arg_t* trace_arg, doubl
|
|
208
211
|
|
209
212
|
static void prof_event_hook(VALUE trace_point, void* data)
|
210
213
|
{
|
211
|
-
|
214
|
+
VALUE profile = (VALUE)data;
|
215
|
+
prof_profile_t* profile_t = prof_get_profile(profile);
|
216
|
+
|
212
217
|
rb_trace_arg_t* trace_arg = rb_tracearg_from_tracepoint(trace_point);
|
213
|
-
double measurement = prof_measure(
|
218
|
+
double measurement = prof_measure(profile_t->measurer, trace_arg);
|
214
219
|
rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
|
215
220
|
VALUE self = rb_tracearg_self(trace_arg);
|
216
221
|
|
217
222
|
if (trace_file != NULL)
|
218
223
|
{
|
219
|
-
prof_trace(
|
224
|
+
prof_trace(profile_t, trace_arg, measurement);
|
220
225
|
}
|
221
226
|
|
222
227
|
/* Special case - skip any methods from the mProf
|
@@ -224,7 +229,7 @@ static void prof_event_hook(VALUE trace_point, void* data)
|
|
224
229
|
if (self == mProf)
|
225
230
|
return;
|
226
231
|
|
227
|
-
thread_data_t* thread_data = check_fiber(
|
232
|
+
thread_data_t* thread_data = check_fiber(profile_t, measurement);
|
228
233
|
|
229
234
|
if (!thread_data->trace)
|
230
235
|
return;
|
@@ -252,7 +257,7 @@ static void prof_event_hook(VALUE trace_point, void* data)
|
|
252
257
|
}
|
253
258
|
else
|
254
259
|
{
|
255
|
-
frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(
|
260
|
+
frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
|
256
261
|
}
|
257
262
|
|
258
263
|
thread_data->call_tree = call_tree;
|
@@ -306,7 +311,7 @@ static void prof_event_hook(VALUE trace_point, void* data)
|
|
306
311
|
thread_data->call_tree = call_tree;
|
307
312
|
|
308
313
|
// Push a new frame onto the stack for a new c-call or ruby call (into a method)
|
309
|
-
prof_frame_t* next_frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(
|
314
|
+
prof_frame_t* next_frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
|
310
315
|
next_frame->source_file = method->source_file;
|
311
316
|
next_frame->source_line = method->source_line;
|
312
317
|
break;
|
@@ -347,12 +352,12 @@ void prof_install_hook(VALUE self)
|
|
347
352
|
RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
|
348
353
|
RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
|
349
354
|
RUBY_EVENT_LINE,
|
350
|
-
prof_event_hook,
|
355
|
+
prof_event_hook, (void*)self);
|
351
356
|
rb_ary_push(profile->tracepoints, event_tracepoint);
|
352
357
|
|
353
358
|
if (profile->measurer->track_allocations)
|
354
359
|
{
|
355
|
-
VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook,
|
360
|
+
VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook, (void*)self);
|
356
361
|
rb_ary_push(profile->tracepoints, allocation_tracepoint);
|
357
362
|
}
|
358
363
|
|
@@ -377,7 +382,7 @@ prof_profile_t* prof_get_profile(VALUE self)
|
|
377
382
|
{
|
378
383
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
379
384
|
ending up in endless recursion. */
|
380
|
-
return
|
385
|
+
return RTYPEDDATA_DATA(self);
|
381
386
|
}
|
382
387
|
|
383
388
|
static int collect_threads(st_data_t key, st_data_t value, st_data_t result)
|
@@ -399,19 +404,19 @@ static int mark_threads(st_data_t key, st_data_t value, st_data_t result)
|
|
399
404
|
return ST_CONTINUE;
|
400
405
|
}
|
401
406
|
|
402
|
-
static int
|
407
|
+
static int prof_profile_mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
403
408
|
{
|
404
409
|
prof_method_t* method = (prof_method_t*)value;
|
405
410
|
prof_method_mark(method);
|
406
411
|
return ST_CONTINUE;
|
407
412
|
}
|
408
413
|
|
409
|
-
static void
|
414
|
+
static void prof_profile_mark(void* data)
|
410
415
|
{
|
416
|
+
prof_profile_t* profile = (prof_profile_t*)data;
|
411
417
|
rb_gc_mark(profile->tracepoints);
|
412
418
|
rb_gc_mark(profile->running);
|
413
419
|
rb_gc_mark(profile->paused);
|
414
|
-
rb_gc_mark(profile->tracepoints);
|
415
420
|
|
416
421
|
// If GC stress is true (useful for debugging), when threads_table_create is called in the
|
417
422
|
// allocate method Ruby will immediately call this mark method. Thus the threads_tbl will be NULL.
|
@@ -419,14 +424,14 @@ static void prof_mark(prof_profile_t* profile)
|
|
419
424
|
rb_st_foreach(profile->threads_tbl, mark_threads, 0);
|
420
425
|
|
421
426
|
if (profile->exclude_methods_tbl)
|
422
|
-
rb_st_foreach(profile->exclude_methods_tbl,
|
427
|
+
rb_st_foreach(profile->exclude_methods_tbl, prof_profile_mark_methods, 0);
|
423
428
|
}
|
424
429
|
|
425
|
-
/* Freeing the profile creates a cascade of freeing.
|
426
|
-
|
427
|
-
|
428
|
-
static void prof_free(prof_profile_t* profile)
|
430
|
+
/* Freeing the profile creates a cascade of freeing. It frees its threads table, which frees
|
431
|
+
each thread and its associated call treee and methods. */
|
432
|
+
static void prof_profile_ruby_gc_free(void* data)
|
429
433
|
{
|
434
|
+
prof_profile_t* profile = (prof_profile_t*)data;
|
430
435
|
profile->last_thread_data = NULL;
|
431
436
|
|
432
437
|
threads_table_free(profile->threads_tbl);
|
@@ -454,11 +459,29 @@ static void prof_free(prof_profile_t* profile)
|
|
454
459
|
xfree(profile);
|
455
460
|
}
|
456
461
|
|
462
|
+
size_t prof_profile_size(const void* data)
|
463
|
+
{
|
464
|
+
return sizeof(prof_profile_t);
|
465
|
+
}
|
466
|
+
|
467
|
+
static const rb_data_type_t profile_type =
|
468
|
+
{
|
469
|
+
.wrap_struct_name = "Profile",
|
470
|
+
.function =
|
471
|
+
{
|
472
|
+
.dmark = prof_profile_mark,
|
473
|
+
.dfree = prof_profile_ruby_gc_free,
|
474
|
+
.dsize = prof_profile_size,
|
475
|
+
},
|
476
|
+
.data = NULL,
|
477
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
478
|
+
};
|
479
|
+
|
457
480
|
static VALUE prof_allocate(VALUE klass)
|
458
481
|
{
|
459
482
|
VALUE result;
|
460
483
|
prof_profile_t* profile;
|
461
|
-
result =
|
484
|
+
result = TypedData_Make_Struct(klass, prof_profile_t, &profile_type, profile);
|
462
485
|
profile->threads_tbl = threads_table_create();
|
463
486
|
profile->exclude_threads_tbl = NULL;
|
464
487
|
profile->include_threads_tbl = NULL;
|
@@ -846,7 +869,7 @@ static VALUE prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
|
|
846
869
|
|
847
870
|
if (!method)
|
848
871
|
{
|
849
|
-
method = prof_method_create(klass, msym, Qnil, 0);
|
872
|
+
method = prof_method_create(self, klass, msym, Qnil, 0);
|
850
873
|
method_table_insert(profile->exclude_methods_tbl, method->key, method);
|
851
874
|
}
|
852
875
|
|
@@ -870,7 +893,7 @@ VALUE prof_profile_load(VALUE self, VALUE data)
|
|
870
893
|
for (int i = 0; i < rb_array_len(threads); i++)
|
871
894
|
{
|
872
895
|
VALUE thread = rb_ary_entry(threads, i);
|
873
|
-
thread_data_t* thread_data =
|
896
|
+
thread_data_t* thread_data = prof_get_thread(thread);
|
874
897
|
rb_st_insert(profile->threads_tbl, (st_data_t)thread_data->fiber_id, (st_data_t)thread_data);
|
875
898
|
}
|
876
899
|
|
data/ext/ruby_prof/rp_profile.h
CHANGED
data/ext/ruby_prof/rp_stack.h
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
/* Temporary object that maintains profiling information
|
11
11
|
for active methods. They are created and destroyed
|
12
12
|
as the program moves up and down its stack. */
|
13
|
-
typedef struct
|
13
|
+
typedef struct prof_frame_t
|
14
14
|
{
|
15
15
|
/* Caching prof_method_t values significantly
|
16
16
|
increases performance. */
|
@@ -34,7 +34,7 @@ void prof_frame_pause(prof_frame_t*, double current_measurement);
|
|
34
34
|
void prof_frame_unpause(prof_frame_t*, double current_measurement);
|
35
35
|
|
36
36
|
/* Current stack of active methods.*/
|
37
|
-
typedef struct
|
37
|
+
typedef struct prof_stack_t
|
38
38
|
{
|
39
39
|
prof_frame_t* start;
|
40
40
|
prof_frame_t* end;
|
data/ext/ruby_prof/rp_thread.c
CHANGED
@@ -46,11 +46,14 @@ static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
|
|
46
46
|
|
47
47
|
size_t prof_thread_size(const void* data)
|
48
48
|
{
|
49
|
-
return sizeof(
|
49
|
+
return sizeof(thread_data_t);
|
50
50
|
}
|
51
51
|
|
52
52
|
void prof_thread_mark(void* data)
|
53
53
|
{
|
54
|
+
if (!data)
|
55
|
+
return;
|
56
|
+
|
54
57
|
thread_data_t* thread = (thread_data_t*)data;
|
55
58
|
|
56
59
|
if (thread->object != Qnil)
|
@@ -75,8 +78,11 @@ void prof_thread_mark(void* data)
|
|
75
78
|
|
76
79
|
void prof_thread_ruby_gc_free(void* data)
|
77
80
|
{
|
78
|
-
|
79
|
-
|
81
|
+
if (data)
|
82
|
+
{
|
83
|
+
thread_data_t* thread_data = (thread_data_t*)data;
|
84
|
+
thread_data->object = Qnil;
|
85
|
+
}
|
80
86
|
}
|
81
87
|
|
82
88
|
static void prof_thread_free(thread_data_t* thread_data)
|
@@ -85,9 +91,7 @@ static void prof_thread_free(thread_data_t* thread_data)
|
|
85
91
|
yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
|
86
92
|
if (thread_data->object != Qnil)
|
87
93
|
{
|
88
|
-
|
89
|
-
RDATA(thread_data->object)->dfree = NULL;
|
90
|
-
RDATA(thread_data->object)->data = NULL;
|
94
|
+
RTYPEDDATA(thread_data->object)->data = NULL;
|
91
95
|
thread_data->object = Qnil;
|
92
96
|
}
|
93
97
|
|
@@ -101,11 +105,24 @@ static void prof_thread_free(thread_data_t* thread_data)
|
|
101
105
|
xfree(thread_data);
|
102
106
|
}
|
103
107
|
|
108
|
+
static const rb_data_type_t thread_type =
|
109
|
+
{
|
110
|
+
.wrap_struct_name = "ThreadInfo",
|
111
|
+
.function =
|
112
|
+
{
|
113
|
+
.dmark = prof_thread_mark,
|
114
|
+
.dfree = prof_thread_ruby_gc_free,
|
115
|
+
.dsize = prof_thread_size,
|
116
|
+
},
|
117
|
+
.data = NULL,
|
118
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
119
|
+
};
|
120
|
+
|
104
121
|
VALUE prof_thread_wrap(thread_data_t* thread)
|
105
122
|
{
|
106
123
|
if (thread->object == Qnil)
|
107
124
|
{
|
108
|
-
thread->object =
|
125
|
+
thread->object = TypedData_Wrap_Struct(cRpThread, &thread_type, thread);
|
109
126
|
}
|
110
127
|
return thread->object;
|
111
128
|
}
|
@@ -117,11 +134,11 @@ static VALUE prof_thread_allocate(VALUE klass)
|
|
117
134
|
return thread_data->object;
|
118
135
|
}
|
119
136
|
|
120
|
-
|
137
|
+
thread_data_t* prof_get_thread(VALUE self)
|
121
138
|
{
|
122
139
|
/* Can't use Data_Get_Struct because that triggers the event hook
|
123
140
|
ending up in endless recursion. */
|
124
|
-
thread_data_t* result =
|
141
|
+
thread_data_t* result = RTYPEDDATA_DATA(self);
|
125
142
|
if (!result)
|
126
143
|
rb_raise(rb_eRuntimeError, "This RubyProf::Thread instance has already been freed, likely because its profile has been freed.");
|
127
144
|
|
@@ -299,7 +316,7 @@ static VALUE prof_thread_methods(VALUE self)
|
|
299
316
|
/* :nodoc: */
|
300
317
|
static VALUE prof_thread_dump(VALUE self)
|
301
318
|
{
|
302
|
-
thread_data_t* thread_data =
|
319
|
+
thread_data_t* thread_data = RTYPEDDATA_DATA(self);
|
303
320
|
|
304
321
|
VALUE result = rb_hash_new();
|
305
322
|
rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
|
@@ -312,7 +329,7 @@ static VALUE prof_thread_dump(VALUE self)
|
|
312
329
|
/* :nodoc: */
|
313
330
|
static VALUE prof_thread_load(VALUE self, VALUE data)
|
314
331
|
{
|
315
|
-
thread_data_t* thread_data =
|
332
|
+
thread_data_t* thread_data = RTYPEDDATA_DATA(self);
|
316
333
|
|
317
334
|
VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
|
318
335
|
thread_data->call_tree = prof_get_call_tree(call_tree);
|
@@ -323,7 +340,7 @@ static VALUE prof_thread_load(VALUE self, VALUE data)
|
|
323
340
|
for (int i = 0; i < rb_array_len(methods); i++)
|
324
341
|
{
|
325
342
|
VALUE method = rb_ary_entry(methods, i);
|
326
|
-
prof_method_t* method_data =
|
343
|
+
prof_method_t* method_data = RTYPEDDATA_DATA(method);
|
327
344
|
method_table_insert(thread_data->method_table, method_data->key, method_data);
|
328
345
|
}
|
329
346
|
|
@@ -332,7 +349,7 @@ static VALUE prof_thread_load(VALUE self, VALUE data)
|
|
332
349
|
|
333
350
|
void rp_init_thread(void)
|
334
351
|
{
|
335
|
-
cRpThread = rb_define_class_under(mProf, "Thread",
|
352
|
+
cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
|
336
353
|
rb_undef_method(CLASS_OF(cRpThread), "new");
|
337
354
|
rb_define_alloc_func(cRpThread, prof_thread_allocate);
|
338
355
|
|