ruby-prof 1.4.5-x64-mingw-ucrt → 1.5.0-x64-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cba10a97d617a1790367cfdb878483d8cf0808d7eddf0d6df9c232f3a069caa
4
- data.tar.gz: '03992e76849aa6d15751f80525b4b96432a7be8e89efdc79e511bad30849dcac'
3
+ metadata.gz: dc83e5cdca77c3e07af7ad99f69acfbf8dde2013c435855d996925db9dcd586c
4
+ data.tar.gz: 9c14d146c1194db666cec9f9ab05e4a798bd0a6ed5c535d850be581ddf8734fa
5
5
  SHA512:
6
- metadata.gz: 7924a150c8ed70cc2292a41a448907f45c298f92b6aca51ed29b9c19fa8454dfba3b3e388471975e663a51bb752edd95166c75eb43ed442ee5ace68214e18a1e
7
- data.tar.gz: c4d20eb492adbc12502d7254be171d58b5f035c377d661af771073689357d95ad1f4f6ef9cd7e206cb6c023021c15a371a9ea23de6998bac3fbb4fcaf7358dfc
6
+ metadata.gz: 56e6c6a054fed0bdbb08a41ead10898495efb4201c19f2bd3572f3b0ac939e1770c3c4f1bb82da4399ba27f99ed135447e004263d57af86a0888a6a6a349f1fc
7
+ data.tar.gz: 251eba317457ec6ea583b25656c062f92e8f55a35c7279f39a1f9987234fd9b481fbfd6ab72d71af34541ca49aad2b0feacefbc4d44ca75a4486ec003cb2fa19
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ 1.5.0 (2023-02-06)
2
+ =====================
3
+ * Add new Profile#merge! method that merges results for threads/fibers that share the same root method (Charlie Savage)
4
+ * Expand API to allow creation of +Measurement+, +CallTree+, +MethodInfo+ and +Thread+ instances. This
5
+ was done to make is possible to write tests for the new Profile#merge! functionality (Charlie Savage)
6
+
1
7
  1.4.5 (2022-12-27)
2
8
  =====================
3
9
  * Look for ruby_prof extension built on install in the lib directory (Charlie Savage)
data/Rakefile CHANGED
@@ -61,7 +61,7 @@ end
61
61
  desc "Generate rdoc documentation"
62
62
  RDoc::Task.new("rdoc") do |rdoc|
63
63
  rdoc.rdoc_dir = 'doc'
64
- rdoc.title = "ruby-prof"
64
+ rdoc.title = "ruby-prof"
65
65
  # Show source inline with line numbers
66
66
  rdoc.options << "--line-numbers"
67
67
  # Make the readme file the start page for the generated html
@@ -242,13 +242,13 @@ static VALUE prof_allocation_dump(VALUE self)
242
242
 
243
243
  VALUE result = rb_hash_new();
244
244
 
245
- rb_hash_aset(result, ID2SYM(rb_intern("key")), INT2FIX(allocation->key));
245
+ rb_hash_aset(result, ID2SYM(rb_intern("key")), ULL2NUM(allocation->key));
246
246
  rb_hash_aset(result, ID2SYM(rb_intern("klass_name")), prof_allocation_klass_name(self));
247
247
  rb_hash_aset(result, ID2SYM(rb_intern("klass_flags")), INT2FIX(allocation->klass_flags));
248
248
  rb_hash_aset(result, ID2SYM(rb_intern("source_file")), allocation->source_file);
249
249
  rb_hash_aset(result, ID2SYM(rb_intern("source_line")), INT2FIX(allocation->source_line));
250
250
  rb_hash_aset(result, ID2SYM(rb_intern("count")), INT2FIX(allocation->count));
251
- rb_hash_aset(result, ID2SYM(rb_intern("memory")), LONG2FIX(allocation->memory));
251
+ rb_hash_aset(result, ID2SYM(rb_intern("memory")), ULL2NUM(allocation->memory));
252
252
 
253
253
  return result;
254
254
  }
@@ -259,13 +259,13 @@ static VALUE prof_allocation_load(VALUE self, VALUE data)
259
259
  prof_allocation_t* allocation = prof_get_allocation(self);
260
260
  allocation->object = self;
261
261
 
262
- allocation->key = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
262
+ allocation->key = RB_NUM2ULL(rb_hash_aref(data, ID2SYM(rb_intern("key"))));
263
263
  allocation->klass_name = rb_hash_aref(data, ID2SYM(rb_intern("klass_name")));
264
264
  allocation->klass_flags = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("klass_flags"))));
265
265
  allocation->source_file = rb_hash_aref(data, ID2SYM(rb_intern("source_file")));
266
266
  allocation->source_line = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("source_line"))));
267
267
  allocation->count = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("count"))));
268
- allocation->memory = FIX2LONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
268
+ allocation->memory = NUM2ULONG(rb_hash_aref(data, ID2SYM(rb_intern("memory"))));
269
269
 
270
270
  return data;
271
271
  }
@@ -9,6 +9,7 @@ VALUE cRpCallTree;
9
9
  prof_call_tree_t* prof_call_tree_create(prof_method_t* method, prof_call_tree_t* parent, VALUE source_file, int source_line)
10
10
  {
11
11
  prof_call_tree_t* result = ALLOC(prof_call_tree_t);
12
+ result->owner = OWNER_C;
12
13
  result->method = method;
13
14
  result->parent = parent;
14
15
  result->object = Qnil;
@@ -23,34 +24,12 @@ prof_call_tree_t* prof_call_tree_create(prof_method_t* method, prof_call_tree_t*
23
24
 
24
25
  prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other)
25
26
  {
26
- prof_call_tree_t* result = ALLOC(prof_call_tree_t);
27
- result->children = rb_st_init_numtable();
28
- result->object = Qnil;
29
- result->visits = 0;
30
-
31
- result->method = other->method;
32
- result->parent = other->parent;
33
- result->source_line = other->source_line;
34
- result->source_file = other->source_file;
35
-
36
- result->measurement = prof_measurement_create();
37
- result->measurement->called = other->measurement->called;
38
- result->measurement->total_time = other->measurement->total_time;
39
- result->measurement->self_time = other->measurement->self_time;
40
- result->measurement->wait_time = other->measurement->wait_time;
41
- result->measurement->object = Qnil;
27
+ prof_call_tree_t* result = prof_call_tree_create(other->method, other->parent, other->source_file, other->source_line);
28
+ result->measurement = prof_measurement_copy(other->measurement);
42
29
 
43
30
  return result;
44
31
  }
45
32
 
46
- void prof_call_tree_merge(prof_call_tree_t* result, prof_call_tree_t* other)
47
- {
48
- result->measurement->called += other->measurement->called;
49
- result->measurement->total_time += other->measurement->total_time;
50
- result->measurement->self_time += other->measurement->self_time;
51
- result->measurement->wait_time += other->measurement->wait_time;
52
- }
53
-
54
33
  static int prof_call_tree_collect_children(st_data_t key, st_data_t value, st_data_t result)
55
34
  {
56
35
  prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
@@ -89,15 +68,6 @@ void prof_call_tree_mark(void* data)
89
68
  rb_st_foreach(call_tree->children, prof_call_tree_mark_children, 0);
90
69
  }
91
70
 
92
- static void prof_call_tree_ruby_gc_free(void* data)
93
- {
94
- if (data)
95
- {
96
- prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
97
- call_tree->object = Qnil;
98
- }
99
- }
100
-
101
71
  static int prof_call_tree_free_children(st_data_t key, st_data_t value, st_data_t data)
102
72
  {
103
73
  prof_call_tree_t* call_tree = (prof_call_tree_t*)value;
@@ -126,6 +96,27 @@ void prof_call_tree_free(prof_call_tree_t* call_tree_data)
126
96
  xfree(call_tree_data);
127
97
  }
128
98
 
99
+ static void prof_call_tree_ruby_gc_free(void* data)
100
+ {
101
+ prof_call_tree_t* call_tree = (prof_call_tree_t*)data;
102
+
103
+ if (!call_tree)
104
+ {
105
+ // Object has already been freed by C code
106
+ return;
107
+ }
108
+ else if (call_tree->owner == OWNER_RUBY)
109
+ {
110
+ // Ruby owns this object, we need to free the underlying C struct
111
+ prof_call_tree_free(call_tree);
112
+ }
113
+ else
114
+ {
115
+ // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
116
+ call_tree->object = Qnil;
117
+ }
118
+ }
119
+
129
120
  size_t prof_call_tree_size(const void* data)
130
121
  {
131
122
  return sizeof(prof_call_tree_t);
@@ -156,6 +147,8 @@ VALUE prof_call_tree_wrap(prof_call_tree_t* call_tree)
156
147
  static VALUE prof_call_tree_allocate(VALUE klass)
157
148
  {
158
149
  prof_call_tree_t* call_tree = prof_call_tree_create(NULL, NULL, Qnil, 0);
150
+ // This object is being created by Ruby
151
+ call_tree->owner = OWNER_RUBY;
159
152
  call_tree->object = prof_call_tree_wrap(call_tree);
160
153
  return call_tree->object;
161
154
  }
@@ -213,10 +206,26 @@ void prof_call_tree_add_parent(prof_call_tree_t* self, prof_call_tree_t* parent)
213
206
  void prof_call_tree_add_child(prof_call_tree_t* self, prof_call_tree_t* child)
214
207
  {
215
208
  call_tree_table_insert(self->children, child->method->key, child);
209
+
210
+ // The child is now managed by C since its parent will free it
211
+ child->owner = OWNER_C;
216
212
  }
217
213
 
218
214
  /* ======= RubyProf::CallTree ========*/
219
215
 
216
+ /* call-seq:
217
+ new(method_info) -> call_tree
218
+
219
+ Creates a new CallTree instance. +Klass+ should be a reference to
220
+ a Ruby class and +method_name+ a symbol identifying one of its instance methods.*/
221
+ static VALUE prof_call_tree_initialize(VALUE self, VALUE method_info)
222
+ {
223
+ prof_call_tree_t* call_tree_ptr = prof_get_call_tree(self);
224
+ call_tree_ptr->method = prof_get_method(method_info);
225
+
226
+ return self;
227
+ }
228
+
220
229
  /* call-seq:
221
230
  parent -> call_tree
222
231
 
@@ -242,6 +251,29 @@ static VALUE prof_call_tree_children(VALUE self)
242
251
  return result;
243
252
  }
244
253
 
254
+ /* call-seq:
255
+ add_child(call_tree) -> call_tree
256
+
257
+ Adds the specified call_tree as a child. If the method represented by the call tree is
258
+ already a child than a IndexError is thrown.
259
+
260
+ The returned value is the added child*/
261
+ static VALUE prof_call_tree_add_child_ruby(VALUE self, VALUE child)
262
+ {
263
+ prof_call_tree_t* parent_ptr = prof_get_call_tree(self);
264
+ prof_call_tree_t* child_ptr = prof_get_call_tree(child);
265
+
266
+ prof_call_tree_t* existing_ptr = call_tree_table_lookup(parent_ptr->children, child_ptr->method->key);
267
+ if (existing_ptr)
268
+ {
269
+ rb_raise(rb_eIndexError, "Child call tree already exists");
270
+ }
271
+
272
+ prof_call_tree_add_parent(child_ptr, parent_ptr);
273
+
274
+ return child;
275
+ }
276
+
245
277
  /* call-seq:
246
278
  called -> MethodInfo
247
279
 
@@ -294,12 +326,65 @@ static VALUE prof_call_tree_line(VALUE self)
294
326
  return INT2FIX(result->source_line);
295
327
  }
296
328
 
329
+ static int prof_call_tree_merge_children(st_data_t key, st_data_t value, st_data_t data)
330
+ {
331
+ prof_call_tree_t* other_child = (prof_call_tree_t*)value;
332
+ prof_call_tree_t* self = (prof_call_tree_t*)data;
333
+
334
+ st_data_t self_child;
335
+ if (rb_st_lookup(self->children, other_child->method->key, &self_child))
336
+ {
337
+ prof_call_tree_merge_internal((prof_call_tree_t*)self_child, other_child);
338
+ }
339
+ else
340
+ {
341
+ prof_call_tree_t* copy = prof_call_tree_copy(other_child);
342
+ prof_call_tree_add_child(self, copy);
343
+ }
344
+ return ST_CONTINUE;
345
+ }
346
+
347
+ void prof_call_tree_merge_internal(prof_call_tree_t* self, prof_call_tree_t* other)
348
+ {
349
+ // Make sure the methods are the same
350
+ if (self->method->key != other->method->key)
351
+ return;
352
+
353
+ // Make sure the parents are the same.
354
+ // 1. They can both be set and be equal
355
+ // 2. They can both be unset (null)
356
+ if (self->parent && other->parent)
357
+ {
358
+ if (self->parent->method->key != other->parent->method->key)
359
+ return;
360
+ }
361
+ else if (self->parent || other->parent)
362
+ {
363
+ return;
364
+ }
365
+
366
+ prof_measurement_merge_internal(self->measurement, other->measurement);
367
+ prof_measurement_merge_internal(self->method->measurement, other->method->measurement);
368
+
369
+ rb_st_foreach(other->children, prof_call_tree_merge_children, (st_data_t)self);
370
+ }
371
+
372
+ VALUE prof_call_tree_merge(VALUE self, VALUE other)
373
+ {
374
+ prof_call_tree_t* source = prof_get_call_tree(self);
375
+ prof_call_tree_t* destination = prof_get_call_tree(other);
376
+ prof_call_tree_merge_internal(source, destination);
377
+ return other;
378
+ }
379
+
297
380
  /* :nodoc: */
298
381
  static VALUE prof_call_tree_dump(VALUE self)
299
382
  {
300
383
  prof_call_tree_t* call_tree_data = prof_get_call_tree(self);
301
384
  VALUE result = rb_hash_new();
302
385
 
386
+ rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(call_tree_data->owner));
387
+
303
388
  rb_hash_aset(result, ID2SYM(rb_intern("measurement")), prof_measurement_wrap(call_tree_data->measurement));
304
389
 
305
390
  rb_hash_aset(result, ID2SYM(rb_intern("source_file")), call_tree_data->source_file);
@@ -320,6 +405,8 @@ static VALUE prof_call_tree_load(VALUE self, VALUE data)
320
405
  prof_call_tree_t* call_tree = prof_get_call_tree(self);
321
406
  call_tree->object = self;
322
407
 
408
+ call_tree->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
409
+
323
410
  VALUE measurement = rb_hash_aref(data, ID2SYM(rb_intern("measurement")));
324
411
  call_tree->measurement = prof_get_measurement(measurement);
325
412
 
@@ -350,18 +437,21 @@ void rp_init_call_tree()
350
437
  {
351
438
  /* CallTree */
352
439
  cRpCallTree = rb_define_class_under(mProf, "CallTree", rb_cObject);
353
- rb_undef_method(CLASS_OF(cRpCallTree), "new");
354
440
  rb_define_alloc_func(cRpCallTree, prof_call_tree_allocate);
441
+ rb_define_method(cRpCallTree, "initialize", prof_call_tree_initialize, 1);
355
442
 
356
- rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
357
- rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
358
443
  rb_define_method(cRpCallTree, "target", prof_call_tree_target, 0);
359
444
  rb_define_method(cRpCallTree, "measurement", prof_call_tree_measurement, 0);
445
+ rb_define_method(cRpCallTree, "parent", prof_call_tree_parent, 0);
446
+ rb_define_method(cRpCallTree, "children", prof_call_tree_children, 0);
447
+ rb_define_method(cRpCallTree, "add_child", prof_call_tree_add_child_ruby, 1);
360
448
 
361
449
  rb_define_method(cRpCallTree, "depth", prof_call_tree_depth, 0);
362
450
  rb_define_method(cRpCallTree, "source_file", prof_call_tree_source_file, 0);
363
451
  rb_define_method(cRpCallTree, "line", prof_call_tree_line, 0);
364
452
 
453
+ rb_define_method(cRpCallTree, "merge!", prof_call_tree_merge, 1);
454
+
365
455
  rb_define_method(cRpCallTree, "_dump_data", prof_call_tree_dump, 0);
366
456
  rb_define_method(cRpCallTree, "_load_data", prof_call_tree_load, 1);
367
457
  }
@@ -13,6 +13,7 @@ extern VALUE cRpCallTree;
13
13
  /* Callers and callee information for a method. */
14
14
  typedef struct prof_call_tree_t
15
15
  {
16
+ prof_owner_t owner;
16
17
  prof_method_t* method;
17
18
  struct prof_call_tree_t* parent;
18
19
  st_table* children; /* Call infos that this call info calls */
@@ -27,7 +28,7 @@ typedef struct prof_call_tree_t
27
28
 
28
29
  prof_call_tree_t* prof_call_tree_create(prof_method_t* method, prof_call_tree_t* parent, VALUE source_file, int source_line);
29
30
  prof_call_tree_t* prof_call_tree_copy(prof_call_tree_t* other);
30
- void prof_call_tree_merge(prof_call_tree_t* result, prof_call_tree_t* other);
31
+ void prof_call_tree_merge_internal(prof_call_tree_t* destination, prof_call_tree_t* other);
31
32
  void prof_call_tree_mark(void* data);
32
33
  prof_call_tree_t* call_tree_table_lookup(st_table* table, st_data_t key);
33
34
 
@@ -1,7 +1,6 @@
1
1
  /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
- #include "rp_aggregate_call_tree.h"
5
4
  #include "rp_call_trees.h"
6
5
  #include "rp_measurement.h"
7
6
 
@@ -68,11 +67,11 @@ void prof_call_trees_ruby_gc_free(void* data)
68
67
  }
69
68
  }
70
69
 
71
- static int prof_call_trees_collect_aggregates(st_data_t key, st_data_t value, st_data_t data)
70
+ static int prof_call_trees_collect(st_data_t key, st_data_t value, st_data_t data)
72
71
  {
73
72
  VALUE result = (VALUE)data;
74
73
  prof_call_tree_t* call_tree_data = (prof_call_tree_t*)value;
75
- VALUE aggregate_call_tree = prof_aggregate_call_tree_wrap(call_tree_data);
74
+ VALUE aggregate_call_tree = prof_call_tree_wrap(call_tree_data);
76
75
  rb_ary_push(result, aggregate_call_tree);
77
76
 
78
77
  return ST_CONTINUE;
@@ -87,11 +86,16 @@ static int prof_call_trees_collect_callees(st_data_t key, st_data_t value, st_da
87
86
 
88
87
  if (rb_st_lookup(callers, call_tree_data->method->key, (st_data_t*)&aggregate_call_tree_data))
89
88
  {
90
- prof_call_tree_merge(aggregate_call_tree_data, call_tree_data);
89
+ prof_measurement_merge_internal(aggregate_call_tree_data->measurement, call_tree_data->measurement);
91
90
  }
92
91
  else
93
92
  {
93
+ // Copy the call tree so we don't touch the original and give Ruby ownerhip
94
+ // of it so that it is freed on GC
94
95
  aggregate_call_tree_data = prof_call_tree_copy(call_tree_data);
96
+ aggregate_call_tree_data->owner = OWNER_RUBY;
97
+
98
+
95
99
  rb_st_insert(callers, call_tree_data->method->key, (st_data_t)aggregate_call_tree_data);
96
100
  }
97
101
 
@@ -210,17 +214,21 @@ VALUE prof_call_trees_callers(VALUE self)
210
214
 
211
215
  if (rb_st_lookup(callers, parent->method->key, (st_data_t*)&aggregate_call_tree_data))
212
216
  {
213
- prof_call_tree_merge(aggregate_call_tree_data, *p_call_tree);
217
+ prof_measurement_merge_internal(aggregate_call_tree_data->measurement, (*p_call_tree)->measurement);
214
218
  }
215
219
  else
216
220
  {
221
+ // Copy the call tree so we don't touch the original and give Ruby ownerhip
222
+ // of it so that it is freed on GC
217
223
  aggregate_call_tree_data = prof_call_tree_copy(*p_call_tree);
224
+ aggregate_call_tree_data->owner = OWNER_RUBY;
225
+
218
226
  rb_st_insert(callers, parent->method->key, (st_data_t)aggregate_call_tree_data);
219
227
  }
220
228
  }
221
229
 
222
230
  VALUE result = rb_ary_new_capa((long)callers->num_entries);
223
- rb_st_foreach(callers, prof_call_trees_collect_aggregates, result);
231
+ rb_st_foreach(callers, prof_call_trees_collect, result);
224
232
  rb_st_free_table(callers);
225
233
  return result;
226
234
  }
@@ -240,7 +248,7 @@ VALUE prof_call_trees_callees(VALUE self)
240
248
  }
241
249
 
242
250
  VALUE result = rb_ary_new_capa((long)callees->num_entries);
243
- rb_st_foreach(callees, prof_call_trees_collect_aggregates, result);
251
+ rb_st_foreach(callees, prof_call_trees_collect, result);
244
252
  rb_st_free_table(callees);
245
253
  return result;
246
254
  }
@@ -43,6 +43,7 @@ double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg)
43
43
  prof_measurement_t* prof_measurement_create(void)
44
44
  {
45
45
  prof_measurement_t* result = ALLOC(prof_measurement_t);
46
+ result->owner = OWNER_C;
46
47
  result->total_time = 0;
47
48
  result->self_time = 0;
48
49
  result->wait_time = 0;
@@ -51,6 +52,52 @@ prof_measurement_t* prof_measurement_create(void)
51
52
  return result;
52
53
  }
53
54
 
55
+ /* call-seq:
56
+ new(total_time, self_time, wait_time, called) -> Measurement
57
+
58
+ Creates a new measuremen instance. */
59
+ static VALUE prof_measurement_initialize(VALUE self, VALUE total_time, VALUE self_time, VALUE wait_time, VALUE called)
60
+ {
61
+ prof_measurement_t* result = prof_get_measurement(self);
62
+
63
+ result->total_time = NUM2DBL(total_time);
64
+ result->self_time = NUM2DBL(self_time);
65
+ result->wait_time = NUM2DBL(wait_time);
66
+ result->called = NUM2INT(called);
67
+ result->object = self;
68
+ return self;
69
+ }
70
+
71
+ prof_measurement_t* prof_measurement_copy(prof_measurement_t* other)
72
+ {
73
+ prof_measurement_t* result = prof_measurement_create();
74
+ result->called = other->called;
75
+ result->total_time = other->total_time;
76
+ result->self_time = other->self_time;
77
+ result->wait_time = other->wait_time;
78
+
79
+ return result;
80
+ }
81
+
82
+ static VALUE prof_measurement_initialize_copy(VALUE self, VALUE other)
83
+ {
84
+ // This object was created by Ruby either via Measurment#clone or Measurement#dup
85
+ // and thus prof_measurement_allocate was called so the object is owned by Ruby
86
+
87
+ if (self == other)
88
+ return self;
89
+
90
+ prof_measurement_t* self_ptr = prof_get_measurement(self);
91
+ prof_measurement_t* other_ptr = prof_get_measurement(other);
92
+
93
+ self_ptr->called = other_ptr->called;
94
+ self_ptr->total_time = other_ptr->total_time;
95
+ self_ptr->self_time = other_ptr->self_time;
96
+ self_ptr->wait_time = other_ptr->wait_time;
97
+
98
+ return self;
99
+ }
100
+
54
101
  void prof_measurement_mark(void* data)
55
102
  {
56
103
  if (!data) return;
@@ -61,16 +108,6 @@ void prof_measurement_mark(void* data)
61
108
  rb_gc_mark(measurement_data->object);
62
109
  }
63
110
 
64
- static void prof_measurement_ruby_gc_free(void* data)
65
- {
66
- if (data)
67
- {
68
- // Measurements are freed by their owning object (call info or method)
69
- prof_measurement_t* measurement = (prof_measurement_t*)data;
70
- measurement->object = Qnil;
71
- }
72
- }
73
-
74
111
  void prof_measurement_free(prof_measurement_t* measurement)
75
112
  {
76
113
  /* Has this measurement object been accessed by Ruby? If
@@ -84,6 +121,27 @@ void prof_measurement_free(prof_measurement_t* measurement)
84
121
  xfree(measurement);
85
122
  }
86
123
 
124
+ static void prof_measurement_ruby_gc_free(void* data)
125
+ {
126
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
127
+
128
+ if (!measurement)
129
+ {
130
+ // Object has already been freed by C code
131
+ return;
132
+ }
133
+ else if (measurement->owner == OWNER_RUBY)
134
+ {
135
+ // Ruby owns this object, we need to free the underlying C struct
136
+ prof_measurement_free(measurement);
137
+ }
138
+ else
139
+ {
140
+ // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
141
+ measurement->object = Qnil;
142
+ }
143
+ }
144
+
87
145
  size_t prof_measurement_size(const void* data)
88
146
  {
89
147
  return sizeof(prof_measurement_t);
@@ -114,6 +172,8 @@ VALUE prof_measurement_wrap(prof_measurement_t* measurement)
114
172
  static VALUE prof_measurement_allocate(VALUE klass)
115
173
  {
116
174
  prof_measurement_t* measurement = prof_measurement_create();
175
+ // This object is being created by Ruby
176
+ measurement->owner = OWNER_RUBY;
117
177
  measurement->object = prof_measurement_wrap(measurement);
118
178
  return measurement->object;
119
179
  }
@@ -140,6 +200,17 @@ static VALUE prof_measurement_total_time(VALUE self)
140
200
  return rb_float_new(result->total_time);
141
201
  }
142
202
 
203
+ /* call-seq:
204
+ total_time=value -> value
205
+
206
+ Sets the call count to n. */
207
+ static VALUE prof_measurement_set_total_time(VALUE self, VALUE value)
208
+ {
209
+ prof_measurement_t* result = prof_get_measurement(self);
210
+ result->total_time = NUM2DBL(value);
211
+ return value;
212
+ }
213
+
143
214
  /* call-seq:
144
215
  self_time -> float
145
216
 
@@ -152,6 +223,17 @@ prof_measurement_self_time(VALUE self)
152
223
  return rb_float_new(result->self_time);
153
224
  }
154
225
 
226
+ /* call-seq:
227
+ self_time=value -> value
228
+
229
+ Sets the call count to value. */
230
+ static VALUE prof_measurement_set_self_time(VALUE self, VALUE value)
231
+ {
232
+ prof_measurement_t* result = prof_get_measurement(self);
233
+ result->self_time = NUM2DBL(value);
234
+ return value;
235
+ }
236
+
155
237
  /* call-seq:
156
238
  wait_time -> float
157
239
 
@@ -163,6 +245,17 @@ static VALUE prof_measurement_wait_time(VALUE self)
163
245
  return rb_float_new(result->wait_time);
164
246
  }
165
247
 
248
+ /* call-seq:
249
+ wait_time=value -> value
250
+
251
+ Sets the wait time to value. */
252
+ static VALUE prof_measurement_set_wait_time(VALUE self, VALUE value)
253
+ {
254
+ prof_measurement_t* result = prof_get_measurement(self);
255
+ result->wait_time = NUM2DBL(value);
256
+ return value;
257
+ }
258
+
166
259
  /* call-seq:
167
260
  called -> int
168
261
 
@@ -174,23 +267,44 @@ static VALUE prof_measurement_called(VALUE self)
174
267
  }
175
268
 
176
269
  /* call-seq:
177
- called=n -> n
270
+ called=value -> value
178
271
 
179
- Sets the call count to n. */
180
- static VALUE prof_measurement_set_called(VALUE self, VALUE called)
272
+ Sets the call count to value. */
273
+ static VALUE prof_measurement_set_called(VALUE self, VALUE value)
181
274
  {
182
- prof_measurement_t* result = prof_get_measurement(self);
183
- result->called = NUM2INT(called);
184
- return called;
275
+ prof_measurement_t* result = prof_get_measurement(self);
276
+ result->called = NUM2INT(value);
277
+ return value;
185
278
  }
186
279
 
187
280
  /* :nodoc: */
188
- static VALUE
189
- prof_measurement_dump(VALUE self)
281
+ void prof_measurement_merge_internal(prof_measurement_t* self, prof_measurement_t* other)
282
+ {
283
+ self->called += other->called;
284
+ self->total_time += other->total_time;
285
+ self->self_time += other->self_time;
286
+ self->wait_time += other->wait_time;
287
+ }
288
+
289
+ /* call-seq:
290
+ merge(other)
291
+
292
+ Adds the content of other measurement to this measurement */
293
+ VALUE prof_measurement_merge(VALUE self, VALUE other)
294
+ {
295
+ prof_measurement_t* self_ptr = prof_get_measurement(self);
296
+ prof_measurement_t* other_ptr = prof_get_measurement(other);
297
+ prof_measurement_merge_internal(self_ptr, other_ptr);
298
+ return self;
299
+ }
300
+
301
+ /* :nodoc: */
302
+ static VALUE prof_measurement_dump(VALUE self)
190
303
  {
191
304
  prof_measurement_t* measurement_data = prof_get_measurement(self);
192
305
  VALUE result = rb_hash_new();
193
306
 
307
+ rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(measurement_data->owner));
194
308
  rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
195
309
  rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
196
310
  rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
@@ -206,6 +320,7 @@ prof_measurement_load(VALUE self, VALUE data)
206
320
  prof_measurement_t* measurement = prof_get_measurement(self);
207
321
  measurement->object = self;
208
322
 
323
+ measurement->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
209
324
  measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
210
325
  measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
211
326
  measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
@@ -223,14 +338,19 @@ void rp_init_measure()
223
338
  rp_init_measure_memory();
224
339
 
225
340
  cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cObject);
226
- rb_undef_method(CLASS_OF(cRpMeasurement), "new");
227
341
  rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);
228
342
 
343
+ rb_define_method(cRpMeasurement, "initialize", prof_measurement_initialize, 4);
344
+ rb_define_method(cRpMeasurement, "initialize_copy", prof_measurement_initialize_copy, 1);
345
+ rb_define_method(cRpMeasurement, "merge!", prof_measurement_merge, 1);
229
346
  rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
230
347
  rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
231
348
  rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
349
+ rb_define_method(cRpMeasurement, "total_time=", prof_measurement_set_total_time, 1);
232
350
  rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
351
+ rb_define_method(cRpMeasurement, "self_time=", prof_measurement_set_self_time, 1);
233
352
  rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
353
+ rb_define_method(cRpMeasurement, "wait_time=", prof_measurement_set_wait_time, 1);
234
354
 
235
355
  rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
236
356
  rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
@@ -29,6 +29,7 @@ typedef struct prof_measurer_t
29
29
  /* Callers and callee information for a method. */
30
30
  typedef struct prof_measurement_t
31
31
  {
32
+ prof_owner_t owner;
32
33
  double total_time;
33
34
  double self_time;
34
35
  double wait_time;
@@ -40,10 +41,12 @@ prof_measurer_t* prof_measurer_create(prof_measure_mode_t measure, bool track_al
40
41
  double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg);
41
42
 
42
43
  prof_measurement_t* prof_measurement_create(void);
44
+ prof_measurement_t* prof_measurement_copy(prof_measurement_t* other);
43
45
  void prof_measurement_free(prof_measurement_t* measurement);
44
46
  VALUE prof_measurement_wrap(prof_measurement_t* measurement);
45
47
  prof_measurement_t* prof_get_measurement(VALUE self);
46
48
  void prof_measurement_mark(void* data);
49
+ void prof_measurement_merge_internal(prof_measurement_t* destination, prof_measurement_t* other);
47
50
 
48
51
  void rp_init_measure(void);
49
52